[ES6] Implement computed accessors
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Sep 2015 19:43:58 +0000 (19:43 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 8 Sep 2015 19:43:58 +0000 (19:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=147883

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Implement the computed accessors functionality for class syntax and object literal syntax.
Added new opcodes, op_put_getter_by_val and op_put_setter_by_val. LLInt and baseline JIT support them.
As the same to the other accessor opcodes (like op_put_getter_by_id etc.), DFG / FTL does not support
them. This is handled here[1].

[1]: https://bugs.webkit.org/show_bug.cgi?id=148860

* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitPutGetterByVal):
(JSC::BytecodeGenerator::emitPutSetterByVal):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::PropertyListNode::emitBytecode):
* 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_val):
(JSC::JIT::emit_op_put_setter_by_val):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::emit_op_put_getter_by_val):
(JSC::JIT::emit_op_put_setter_by_val):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* parser/ASTBuilder.h:
(JSC::ASTBuilder::createGetterOrSetterProperty):
* parser/Parser.cpp:
(JSC::Parser<LexerType>::parseClass):
(JSC::Parser<LexerType>::parseGetterSetter):
* parser/SyntaxChecker.h:
(JSC::SyntaxChecker::createGetterOrSetterProperty):
* tests/es6.yaml:
* tests/stress/computed-accessor-parsing.js: Added.
(testShouldNotThrow):
(testShouldThrow):
(Val.prototype.get string_appeared_here):
(Val):
* tests/stress/computed-accessor.js: Added.
(shouldBe):
(.):
* tests/stress/duplicate-computed-accessors.js: Added.
(shouldBe):

LayoutTests:

Updated the existing tests.

* js/parser-syntax-check-expected.txt:
* js/script-tests/parser-syntax-check.js:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/js/parser-syntax-check-expected.txt
LayoutTests/js/script-tests/parser-syntax-check.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/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/Parser.cpp
Source/JavaScriptCore/parser/SyntaxChecker.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/stress/computed-accessor-parsing.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/computed-accessor.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/duplicate-computed-accessors.js [new file with mode: 0644]

index 6efa60c..127bce6 100644 (file)
@@ -1,3 +1,15 @@
+2015-09-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement computed accessors
+        https://bugs.webkit.org/show_bug.cgi?id=147883
+
+        Reviewed by Geoffrey Garen.
+
+        Updated the existing tests.
+
+        * js/parser-syntax-check-expected.txt:
+        * js/script-tests/parser-syntax-check.js:
+
 2015-09-08  Chris Dumez  <cdumez@apple.com>
 
         document.importNode(node, deep): deep's default value should be false
index d25805a..d726c77 100644 (file)
@@ -859,12 +859,16 @@ PASS Invalid: "({set x(){}})"
 PASS Invalid: "function f() { ({set x(){}}) }"
 PASS Invalid: "({set x(a,b){}})"
 PASS Invalid: "function f() { ({set x(a,b){}}) }"
-PASS Invalid: "({get [x](){}})"
-PASS Invalid: "function f() { ({get [x](){}}) }"
+PASS Valid:   "({get [x](){}})"
+PASS Valid:   "function f() { ({get [x](){}}) }"
+PASS Invalid: "({get [x (){}})"
+PASS Invalid: "function f() { ({get [x (){}}) }"
 PASS Invalid: "({set [x](){}})"
 PASS Invalid: "function f() { ({set [x](){}}) }"
-PASS Invalid: "({set [x](x){}})"
-PASS Invalid: "function f() { ({set [x](x){}}) }"
+PASS Valid:   "({set [x](x){}})"
+PASS Valid:   "function f() { ({set [x](x){}}) }"
+PASS Invalid: "({set [x (x){}})"
+PASS Invalid: "function f() { ({set [x (x){}}) }"
 PASS Invalid: "({[...x]: 1})"
 PASS Invalid: "function f() { ({[...x]: 1}) }"
 PASS Invalid: "function f({a, a}) {}"
index 0195ac2..e3a5eea 100644 (file)
@@ -522,9 +522,11 @@ invalid("({get x(a){}})")
 invalid("({get x(a,b){}})")
 invalid("({set x(){}})")
 invalid("({set x(a,b){}})")
-invalid("({get [x](){}})")
+valid("({get [x](){}})")
+invalid("({get [x (){}})")
 invalid("({set [x](){}})")
-invalid("({set [x](x){}})")
+valid("({set [x](x){}})")
+invalid("({set [x (x){}})")
 invalid("({[...x]: 1})")
 invalid("function f({a, a}) {}");
 invalid("function f({a}, a) {}");
index 311756c..24b98c9 100644 (file)
@@ -1,3 +1,67 @@
+2015-09-08  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [ES6] Implement computed accessors
+        https://bugs.webkit.org/show_bug.cgi?id=147883
+
+        Reviewed by Geoffrey Garen.
+
+        Patch by Yusuke Suzuki <utatane.tea@gmail.com> and Matthew Mirman <mmirman@apple.com>.
+
+        Implement the computed accessors functionality for class syntax and object literal syntax.
+        Added new opcodes, op_put_getter_by_val and op_put_setter_by_val. LLInt and baseline JIT support them.
+        As the same to the other accessor opcodes (like op_put_getter_by_id etc.), DFG / FTL does not support
+        them. This is handled here[1].
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=148860
+
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitPutGetterByVal):
+        (JSC::BytecodeGenerator::emitPutSetterByVal):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::PropertyListNode::emitBytecode):
+        * 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_val):
+        (JSC::JIT::emit_op_put_setter_by_val):
+        * jit/JITPropertyAccess32_64.cpp:
+        (JSC::JIT::emit_op_put_getter_by_val):
+        (JSC::JIT::emit_op_put_setter_by_val):
+        * llint/LLIntSlowPaths.cpp:
+        (JSC::LLInt::LLINT_SLOW_PATH_DECL):
+        * llint/LLIntSlowPaths.h:
+        * llint/LowLevelInterpreter.asm:
+        * parser/ASTBuilder.h:
+        (JSC::ASTBuilder::createGetterOrSetterProperty):
+        * parser/Parser.cpp:
+        (JSC::Parser<LexerType>::parseClass):
+        (JSC::Parser<LexerType>::parseGetterSetter):
+        * parser/SyntaxChecker.h:
+        (JSC::SyntaxChecker::createGetterOrSetterProperty):
+        * tests/es6.yaml:
+        * tests/stress/computed-accessor-parsing.js: Added.
+        (testShouldNotThrow):
+        (testShouldThrow):
+        (Val.prototype.get string_appeared_here):
+        (Val):
+        * tests/stress/computed-accessor.js: Added.
+        (shouldBe):
+        (.):
+        * tests/stress/duplicate-computed-accessors.js: Added.
+        (shouldBe):
+
 2015-09-08  Saam barati  <sbarati@apple.com>
 
         baseline JIT should emit better code for UnresolvedProperty in resolve_scope/get_from_scope/put_to_scope
index 020ec24..f99e645 100644 (file)
@@ -74,6 +74,8 @@
             { "name" : "op_put_getter_by_id", "length" : 5 },
             { "name" : "op_put_setter_by_id", "length" : 5 },
             { "name" : "op_put_getter_setter", "length" : 6 },
+            { "name" : "op_put_getter_by_val", "length" : 5 },
+            { "name" : "op_put_setter_by_val", "length" : 5 },
             { "name" : "op_jmp", "length" : 2 },
             { "name" : "op_jtrue", "length" : 3 },
             { "name" : "op_jfalse", "length" : 3 },
index f92267c..af775b3 100644 (file)
@@ -117,6 +117,13 @@ void computeUsesForBytecodeOffset(
         functor(codeBlock, instruction, opcodeID, instruction[5].u.operand);
         return;
     }
+    case op_put_getter_by_val:
+    case op_put_setter_by_val: {
+        functor(codeBlock, instruction, opcodeID, instruction[1].u.operand);
+        functor(codeBlock, instruction, opcodeID, instruction[2].u.operand);
+        functor(codeBlock, instruction, opcodeID, instruction[4].u.operand);
+        return;
+    }
     case op_get_property_enumerator:
     case op_get_enumerable_length:
     case op_new_func_exp:
@@ -278,6 +285,8 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, unsigned bytecodeOffset,
     case op_put_getter_by_id:
     case op_put_setter_by_id:
     case op_put_getter_setter:
+    case op_put_getter_by_val:
+    case op_put_setter_by_val:
     case op_put_by_val:
     case op_put_by_val_direct:
     case op_put_by_index:
index d746e5b..b90fe8f 100644 (file)
@@ -1118,6 +1118,24 @@ void CodeBlock::dumpBytecode(
             out.printf("%s, %s, %d, %s, %s", registerName(r0).data(), idName(id0, identifier(id0)).data(), n0, registerName(r1).data(), registerName(r2).data());
             break;
         }
+        case op_put_getter_by_val: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            int n0 = (++it)->u.operand;
+            int r2 = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "put_getter_by_val");
+            out.printf("%s, %s, %d, %s", registerName(r0).data(), registerName(r1).data(), n0, registerName(r2).data());
+            break;
+        }
+        case op_put_setter_by_val: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            int n0 = (++it)->u.operand;
+            int r2 = (++it)->u.operand;
+            printLocationAndOp(out, exec, location, it, "put_setter_by_val");
+            out.printf("%s, %s, %d, %s", registerName(r0).data(), registerName(r1).data(), n0, registerName(r2).data());
+            break;
+        }
         case op_del_by_id: {
             int r0 = (++it)->u.operand;
             int r1 = (++it)->u.operand;
index 03a0fad..437353f 100644 (file)
@@ -2163,6 +2163,24 @@ void BytecodeGenerator::emitPutGetterSetter(RegisterID* base, const Identifier&
     instructions().append(setter->index());
 }
 
+void BytecodeGenerator::emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned attributes, RegisterID* getter)
+{
+    emitOpcode(op_put_getter_by_val);
+    instructions().append(base->index());
+    instructions().append(property->index());
+    instructions().append(attributes);
+    instructions().append(getter->index());
+}
+
+void BytecodeGenerator::emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned attributes, RegisterID* setter)
+{
+    emitOpcode(op_put_setter_by_val);
+    instructions().append(base->index());
+    instructions().append(property->index());
+    instructions().append(attributes);
+    instructions().append(setter->index());
+}
+
 RegisterID* BytecodeGenerator::emitDeleteById(RegisterID* dst, RegisterID* base, const Identifier& property)
 {
     emitOpcode(op_del_by_id);
index 40598d1..769dc39 100644 (file)
@@ -538,6 +538,8 @@ namespace JSC {
         void emitPutGetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* getter);
         void emitPutSetterById(RegisterID* base, const Identifier& property, unsigned propertyDescriptorOptions, RegisterID* setter);
         void emitPutGetterSetter(RegisterID* base, const Identifier& property, unsigned attributes, RegisterID* getter, RegisterID* setter);
+        void emitPutGetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* getter);
+        void emitPutSetterByVal(RegisterID* base, RegisterID* property, unsigned propertyDescriptorOptions, RegisterID* setter);
         
         ExpectedFunction expectedFunctionForIdentifier(const Identifier&);
         RegisterID* emitCall(RegisterID* dst, RegisterID* func, ExpectedFunction, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
index 9b34643..0529e32 100644 (file)
@@ -487,19 +487,30 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
                 continue;
             }
 
-            RegisterID* value = generator.emitNode(node->m_assign);
+            RefPtr<RegisterID> value = generator.emitNode(node->m_assign);
             bool isClassProperty = node->needsSuperBinding();
             if (isClassProperty)
-                emitPutHomeObject(generator, value, dst);
+                emitPutHomeObject(generator, value.get(), dst);
+            unsigned attribute = isClassProperty ? (Accessor | DontEnum) : Accessor;
 
             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) {
+                // Computed accessors.
+                if (node->m_type & PropertyNode::Computed) {
+                    RefPtr<RegisterID> propertyName = generator.emitNode(node->m_expression);
+                    if (node->m_type & PropertyNode::Getter)
+                        generator.emitPutGetterByVal(dst, propertyName.get(), attribute, value.get());
+                    else
+                        generator.emitPutSetterByVal(dst, propertyName.get(), attribute, value.get());
+                    continue;
+                }
+
                 if (node->m_type & PropertyNode::Getter)
-                    generator.emitPutGetterById(dst, *node->name(), Accessor, value);
+                    generator.emitPutGetterById(dst, *node->name(), attribute, value.get());
                 else
-                    generator.emitPutSetterById(dst, *node->name(), Accessor, value);
+                    generator.emitPutSetterById(dst, *node->name(), attribute, value.get());
                 continue;
             }
 
@@ -544,7 +555,7 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             if (isClassProperty && pair.second)
                 emitPutHomeObject(generator, secondReg, dst);
 
-            generator.emitPutGetterSetter(dst, *node->name(), isClassProperty ? (Accessor | DontEnum) : Accessor, getterReg.get(), setterReg.get());
+            generator.emitPutGetterSetter(dst, *node->name(), attribute, getterReg.get(), setterReg.get());
         }
     }
 
index 3984f11..6dc4faa 100644 (file)
@@ -279,6 +279,8 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_put_getter_by_id)
         DEFINE_OP(op_put_setter_by_id)
         DEFINE_OP(op_put_getter_setter)
+        DEFINE_OP(op_put_getter_by_val)
+        DEFINE_OP(op_put_setter_by_val)
 
         DEFINE_OP(op_ret)
         DEFINE_OP(op_rshift)
index 9b773e4..9b9d63a 100644 (file)
@@ -567,6 +567,8 @@ namespace JSC {
         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_put_getter_by_val(Instruction*);
+        void emit_op_put_setter_by_val(Instruction*);
         void emit_op_ret(Instruction*);
         void emit_op_rshift(Instruction*);
         void emit_op_strcat(Instruction*);
@@ -766,6 +768,7 @@ namespace JSC {
         MacroAssembler::Call callOperation(V_JITOperation_ECC, RegisterID, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_ECIZC, RegisterID, const Identifier*, int32_t, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_ECIZCC, RegisterID, const Identifier*, int32_t, RegisterID, RegisterID);
+        MacroAssembler::Call callOperation(V_JITOperation_ECJZC, RegisterID, RegisterID, RegisterID, int32_t, RegisterID);
         MacroAssembler::Call callOperation(J_JITOperation_EE, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID);
         MacroAssembler::Call callOperation(J_JITOperation_EZSymtabJ, int, SymbolTable*, RegisterID);
@@ -777,6 +780,7 @@ namespace JSC {
 #endif
         MacroAssembler::Call callOperation(V_JITOperation_EJIdZJ, RegisterID, const Identifier*, int32_t, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_EJIdZJJ, RegisterID, const Identifier*, int32_t, RegisterID, RegisterID);
+        MacroAssembler::Call callOperation(V_JITOperation_EJJZJ, RegisterID, RegisterID, int32_t, RegisterID);
 #if USE(JSVALUE64)
         MacroAssembler::Call callOperation(F_JITOperation_EFJZZ, RegisterID, RegisterID, int32_t, RegisterID);
         MacroAssembler::Call callOperation(V_JITOperation_ESsiJJI, StructureStubInfo*, RegisterID, RegisterID, UniquedStringImpl*);
index 12107ae..d327673 100644 (file)
@@ -538,6 +538,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJIdZJJ ope
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJJZJ operation, RegisterID regOp1, RegisterID regOp2, int32_t op3, RegisterID regOp4)
+{
+    setupArgumentsWithExecState(regOp1, regOp2, TrustedImm32(op3), regOp4);
+    return appendCallWithExceptionCheck(operation);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJZ operation, RegisterID regOp1, int32_t op2)
 {
     setupArgumentsWithExecState(regOp1, TrustedImm32(op2));
@@ -666,6 +672,12 @@ ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_ECIZCC oper
     return appendCallWithExceptionCheck(operation);
 }
 
+ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_ECJZC operation, RegisterID arg1, RegisterID arg2Tag, RegisterID arg2Payload, int32_t arg3, RegisterID arg4)
+{
+    setupArgumentsWithExecState(arg1, arg2Payload, arg2Tag, TrustedImm32(arg3), arg4);
+    return appendCallWithExceptionCheck(operation);
+}
+
 ALWAYS_INLINE MacroAssembler::Call JIT::callOperation(V_JITOperation_EJ operation, RegisterID regOp1Tag, RegisterID regOp1Payload)
 {
     setupArgumentsWithExecState(EABI_32BIT_DUMMY_ARG regOp1Payload, regOp1Tag);
index d7dc508..b859e9e 100644 (file)
@@ -1410,6 +1410,23 @@ void JIT_OPERATION operationPutByIndex(ExecState* exec, EncodedJSValue encodedAr
     asArray(arrayValue)->putDirectIndex(exec, index, JSValue::decode(encodedValue));
 }
 
+enum class AccessorType {
+    Getter,
+    Setter
+};
+
+static void putAccessorByVal(ExecState* exec, JSObject* base, JSValue subscript, int32_t attribute, JSObject* accessor, AccessorType accessorType)
+{
+    auto propertyKey = subscript.toPropertyKey(exec);
+    if (exec->hadException())
+        return;
+
+    if (accessorType == AccessorType::Getter)
+        base->putGetter(exec, propertyKey, accessor, attribute);
+    else
+        base->putSetter(exec, propertyKey, accessor, attribute);
+}
+
 #if USE(JSVALUE64)
 void JIT_OPERATION operationPutGetterById(ExecState* exec, EncodedJSValue encodedObjectValue, Identifier* identifier, int32_t options, EncodedJSValue encodedGetterValue)
 {
@@ -1460,6 +1477,27 @@ void JIT_OPERATION operationPutGetterSetter(ExecState* exec, EncodedJSValue enco
         accessor->setSetter(vm, exec->lexicalGlobalObject(), asObject(setter));
     baseObj->putDirectAccessor(exec, *identifier, accessor, attribute);
 }
+
+void JIT_OPERATION operationPutGetterByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, int32_t attribute, EncodedJSValue encodedGetter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    JSObject* base = asObject(JSValue::decode(encodedBase));
+    JSValue subscript = JSValue::decode(encodedSubscript);
+    JSObject* getter = asObject(JSValue::decode(encodedGetter));
+    putAccessorByVal(exec, base, subscript, attribute, getter, AccessorType::Getter);
+}
+
+void JIT_OPERATION operationPutSetterByVal(ExecState* exec, EncodedJSValue encodedBase, EncodedJSValue encodedSubscript, int32_t attribute, EncodedJSValue encodedSetter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+    JSObject* base = asObject(JSValue::decode(encodedBase));
+    JSValue subscript = JSValue::decode(encodedSubscript);
+    JSObject* setter = asObject(JSValue::decode(encodedSetter));
+    putAccessorByVal(exec, base, subscript, attribute, setter, AccessorType::Setter);
+}
+
 #else
 void JIT_OPERATION operationPutGetterById(ExecState* exec, JSCell* object, Identifier* identifier, int32_t options, JSCell* getter)
 {
@@ -1505,6 +1543,23 @@ void JIT_OPERATION operationPutGetterSetter(ExecState* exec, JSCell* object, Ide
         accessor->setSetter(vm, exec->lexicalGlobalObject(), setter->getObject());
     baseObj->putDirectAccessor(exec, *identifier, accessor, attribute);
 }
+
+void JIT_OPERATION operationPutGetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* getter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(getter), AccessorType::Getter);
+}
+
+void JIT_OPERATION operationPutSetterByVal(ExecState* exec, JSCell* base, EncodedJSValue encodedSubscript, int32_t attribute, JSCell* setter)
+{
+    VM& vm = exec->vm();
+    NativeCallFrameTracer tracer(&vm, exec);
+
+    putAccessorByVal(exec, asObject(base), JSValue::decode(encodedSubscript), attribute, asObject(setter), AccessorType::Setter);
+}
+
 #endif
 
 void JIT_OPERATION operationPopScope(ExecState* exec, int32_t scopeReg)
index b559a83..6585f04 100644 (file)
@@ -190,6 +190,7 @@ 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_ECIZC)(ExecState*, JSCell*, Identifier*, int32_t, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECIZCC)(ExecState*, JSCell*, Identifier*, int32_t, JSCell*, JSCell*);
+typedef void JIT_OPERATION (*V_JITOperation_ECJZC)(ExecState*, JSCell*, EncodedJSValue, int32_t, JSCell*);
 typedef void JIT_OPERATION (*V_JITOperation_ECCIcf)(ExecState*, JSCell*, JSCell*, InlineCallFrame*);
 typedef void JIT_OPERATION (*V_JITOperation_ECJJ)(ExecState*, JSCell*, EncodedJSValue, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_ECPSPS)(ExecState*, JSCell*, void*, size_t, void*, size_t);
@@ -200,6 +201,7 @@ typedef void JIT_OPERATION (*V_JITOperation_EJ)(ExecState*, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJCI)(ExecState*, EncodedJSValue, JSCell*, UniquedStringImpl*);
 typedef void JIT_OPERATION (*V_JITOperation_EJIdZJ)(ExecState*, EncodedJSValue, Identifier*, int32_t, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJIdZJJ)(ExecState*, EncodedJSValue, Identifier*, int32_t, EncodedJSValue, EncodedJSValue);
+typedef void JIT_OPERATION (*V_JITOperation_EJJZJ)(ExecState*, EncodedJSValue, EncodedJSValue, int32_t, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJJJ)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue);
 typedef void JIT_OPERATION (*V_JITOperation_EJJJAp)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ArrayProfile*);
 typedef void JIT_OPERATION (*V_JITOperation_EJJJBy)(ExecState*, EncodedJSValue, EncodedJSValue, EncodedJSValue, ByValInfo*);
@@ -312,10 +314,14 @@ void JIT_OPERATION operationPutByIndex(ExecState*, EncodedJSValue, int32_t, Enco
 void JIT_OPERATION operationPutGetterById(ExecState*, EncodedJSValue, Identifier*, int32_t options, EncodedJSValue) WTF_INTERNAL;
 void JIT_OPERATION operationPutSetterById(ExecState*, EncodedJSValue, Identifier*, int32_t options, EncodedJSValue) WTF_INTERNAL;
 void JIT_OPERATION operationPutGetterSetter(ExecState*, EncodedJSValue, Identifier*, int32_t attribute, EncodedJSValue, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutGetterByVal(ExecState*, EncodedJSValue, EncodedJSValue, int32_t attribute, EncodedJSValue) WTF_INTERNAL;
+void JIT_OPERATION operationPutSetterByVal(ExecState*, EncodedJSValue, EncodedJSValue, int32_t attribute, EncodedJSValue) WTF_INTERNAL;
 #else
 void JIT_OPERATION operationPutGetterById(ExecState*, JSCell*, Identifier*, int32_t options, JSCell*) WTF_INTERNAL;
 void JIT_OPERATION operationPutSetterById(ExecState*, JSCell*, Identifier*, int32_t options, JSCell*) WTF_INTERNAL;
 void JIT_OPERATION operationPutGetterSetter(ExecState*, JSCell*, Identifier*, int32_t attribute, JSCell*, JSCell*) WTF_INTERNAL;
+void JIT_OPERATION operationPutGetterByVal(ExecState*, JSCell*, EncodedJSValue, int32_t attribute, JSCell*) WTF_INTERNAL;
+void JIT_OPERATION operationPutSetterByVal(ExecState*, JSCell*, EncodedJSValue, int32_t attribute, JSCell*) WTF_INTERNAL;
 #endif
 void JIT_OPERATION operationPushFunctionNameScope(ExecState*, int32_t, SymbolTable*, EncodedJSValue) WTF_INTERNAL;
 void JIT_OPERATION operationPopScope(ExecState*, int32_t) WTF_INTERNAL;
index 3d93d9c..ff8553d 100644 (file)
@@ -533,6 +533,24 @@ void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
     callOperation(operationPutGetterSetter, regT0, &m_codeBlock->identifier(currentInstruction[2].u.operand), attribute, regT1, regT2);
 }
 
+void JIT::emit_op_put_getter_by_val(Instruction* currentInstruction)
+{
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
+    emitGetVirtualRegister(currentInstruction[2].u.operand, regT1);
+    int32_t attributes = currentInstruction[3].u.operand;
+    emitGetVirtualRegister(currentInstruction[4].u.operand, regT2);
+    callOperation(operationPutGetterByVal, regT0, regT1, attributes, regT2);
+}
+
+void JIT::emit_op_put_setter_by_val(Instruction* currentInstruction)
+{
+    emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
+    emitGetVirtualRegister(currentInstruction[2].u.operand, regT1);
+    int32_t attributes = currentInstruction[3].u.operand;
+    emitGetVirtualRegister(currentInstruction[4].u.operand, regT2);
+    callOperation(operationPutSetterByVal, regT0, regT1, attributes, regT2);
+}
+
 void JIT::emit_op_del_by_id(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
index ca2e0b0..b6dec21 100644 (file)
@@ -95,6 +95,32 @@ void JIT::emit_op_put_getter_setter(Instruction* currentInstruction)
     callOperation(operationPutGetterSetter, regT1, &m_codeBlock->identifier(property), attribute, regT3, regT4);
 }
 
+void JIT::emit_op_put_getter_by_val(Instruction* currentInstruction)
+{
+    int base = currentInstruction[1].u.operand;
+    int property = currentInstruction[2].u.operand;
+    int32_t attributes = currentInstruction[3].u.operand;
+    int getter = currentInstruction[4].u.operand;
+
+    emitLoadPayload(base, regT2);
+    emitLoad(property, regT1, regT0);
+    emitLoadPayload(getter, regT3);
+    callOperation(operationPutGetterByVal, regT2, regT1, regT0, attributes, regT3);
+}
+
+void JIT::emit_op_put_setter_by_val(Instruction* currentInstruction)
+{
+    int base = currentInstruction[1].u.operand;
+    int property = currentInstruction[2].u.operand;
+    int32_t attributes = currentInstruction[3].u.operand;
+    int getter = currentInstruction[4].u.operand;
+
+    emitLoadPayload(base, regT2);
+    emitLoad(property, regT1, regT0);
+    emitLoadPayload(getter, regT3);
+    callOperation(operationPutSetterByVal, regT2, regT1, regT0, attributes, regT3);
+}
+
 void JIT::emit_op_del_by_id(Instruction* currentInstruction)
 {
     int dst = currentInstruction[1].u.operand;
index 3fb485d..f07b418 100644 (file)
@@ -899,6 +899,44 @@ LLINT_SLOW_PATH_DECL(slow_path_put_getter_setter)
     LLINT_END();
 }
 
+LLINT_SLOW_PATH_DECL(slow_path_put_getter_by_val)
+{
+    LLINT_BEGIN();
+    ASSERT(LLINT_OP(1).jsValue().isObject());
+    JSObject* baseObj = asObject(LLINT_OP(1).jsValue());
+    JSValue subscript = LLINT_OP_C(2).jsValue();
+
+    unsigned options = pc[3].u.operand;
+
+    JSValue getter = LLINT_OP(4).jsValue();
+    ASSERT(getter.isObject());
+
+    auto property = subscript.toPropertyKey(exec);
+    LLINT_CHECK_EXCEPTION();
+
+    baseObj->putGetter(exec, property, asObject(getter), options);
+    LLINT_END();
+}
+
+LLINT_SLOW_PATH_DECL(slow_path_put_setter_by_val)
+{
+    LLINT_BEGIN();
+    ASSERT(LLINT_OP(1).jsValue().isObject());
+    JSObject* baseObj = asObject(LLINT_OP(1).jsValue());
+    JSValue subscript = LLINT_OP_C(2).jsValue();
+
+    unsigned options = pc[3].u.operand;
+
+    JSValue setter = LLINT_OP(4).jsValue();
+    ASSERT(setter.isObject());
+
+    auto property = subscript.toPropertyKey(exec);
+    LLINT_CHECK_EXCEPTION();
+
+    baseObj->putSetter(exec, property, asObject(setter), options);
+    LLINT_END();
+}
+
 LLINT_SLOW_PATH_DECL(slow_path_jtrue)
 {
     LLINT_BEGIN();
index eae042c..7a971c0 100644 (file)
@@ -82,6 +82,8 @@ 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_put_getter_by_val);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_put_setter_by_val);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_jtrue);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_jfalse);
 LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_jless);
index aef291c..1904820 100644 (file)
@@ -1199,6 +1199,18 @@ _llint_op_put_getter_setter:
     dispatch(6)
 
 
+_llint_op_put_getter_by_val:
+    traceExecution()
+    callSlowPath(_llint_slow_path_put_getter_by_val)
+    dispatch(5)
+
+
+_llint_op_put_setter_by_val:
+    traceExecution()
+    callSlowPath(_llint_slow_path_put_setter_by_val)
+    dispatch(5)
+
+
 _llint_op_jtrue:
     traceExecution()
     jumpTrueOrFalse(
index 5b099ec..4854c9c 100644 (file)
@@ -388,7 +388,17 @@ public:
         FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
         return new (m_parserArena) PropertyNode(*name, funcExpr, type, PropertyNode::Unknown, superBinding);
     }
-    
+
+    NEVER_INLINE PropertyNode* createGetterOrSetterProperty(const JSTokenLocation& location, PropertyNode::Type type, bool,
+        ExpressionNode* name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding)
+    {
+        ASSERT(name);
+        functionInfo.body->setLoc(functionInfo.startLine, functionInfo.endLine, location.startOffset, location.lineStartOffset);
+        SourceCode source = m_sourceCode->subExpression(functionInfo.startOffset, functionInfo.endOffset, functionInfo.startLine, functionInfo.bodyStartColumn);
+        FuncExprNode* funcExpr = new (m_parserArena) FuncExprNode(location, m_vm->propertyNames->nullIdentifier, functionInfo.body, source);
+        return new (m_parserArena) PropertyNode(name, funcExpr, type, PropertyNode::Unknown, superBinding);
+    }
+
     NEVER_INLINE PropertyNode* createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation& location, PropertyNode::Type type, bool,
         double name, const ParserFunctionInfo<ASTBuilder>& functionInfo, SuperBinding superBinding)
     {
index c9be245..04fd3b8 100644 (file)
@@ -1977,7 +1977,7 @@ template <class TreeBuilder> TreeClassExpression Parser<LexerType>::parseClass(T
             ident = m_token.m_data.ident;
             ASSERT(ident);
             next();
-            if (match(IDENT) || match(STRING) || match(DOUBLE) || match(INTEGER)) {
+            if (match(IDENT) || match(STRING) || match(DOUBLE) || match(INTEGER) || match(OPENBRACKET)) {
                 isGetter = *ident == propertyNames.get;
                 isSetter = *ident == propertyNames.set;
             }
@@ -2910,18 +2910,28 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
 {
     const Identifier* stringPropertyName = 0;
     double numericPropertyName = 0;
-    if (m_token.m_type == IDENT || m_token.m_type == STRING || isLETMaskedAsIDENT()) {
+    TreeExpression computedPropertyName = 0;
+
+    JSTokenLocation location(tokenLocation());
+
+    if (match(IDENT) || match(STRING) || isLETMaskedAsIDENT()) {
         stringPropertyName = m_token.m_data.ident;
         semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->prototype,
             "Cannot declare a static method named 'prototype'");
         semanticFailIfTrue(superBinding == SuperBinding::Needed && *stringPropertyName == m_vm->propertyNames->constructor,
             "Cannot declare a getter or setter named 'constructor'");
-    } else if (m_token.m_type == DOUBLE || m_token.m_type == INTEGER)
+        next();
+    } else if (match(DOUBLE) || match(INTEGER)) {
         numericPropertyName = m_token.m_data.doubleValue;
-    else
+        next();
+    } else if (match(OPENBRACKET)) {
+        next();
+        computedPropertyName = parseAssignmentExpression(context);
+        failIfFalse(computedPropertyName, "Cannot parse computed property name");
+        handleProductionOrFail(CLOSEBRACKET, "]", "end", "computed property name");
+    } else
         failDueToUnexpectedToken();
-    JSTokenLocation location(tokenLocation());
-    next();
+
     ParserFunctionInfo<TreeBuilder> info;
     if (type & PropertyNode::Getter) {
         failIfFalse(match(OPENPAREN), "Expected a parameter list for getter definition");
@@ -2932,8 +2942,13 @@ template <class TreeBuilder> TreeProperty Parser<LexerType>::parseGetterSetter(T
         failIfFalse((parseFunctionInfo(context, FunctionNoRequirements, SourceParseMode::SetterMode, false, constructorKind, superBinding,
             getterOrSetterStartOffset, info, StandardFunctionParseType)), "Cannot parse setter definition");
     }
+
     if (stringPropertyName)
         return context.createGetterOrSetterProperty(location, type, strict, stringPropertyName, info, superBinding);
+
+    if (computedPropertyName)
+        return context.createGetterOrSetterProperty(location, static_cast<PropertyNode::Type>(type | PropertyNode::Computed), strict, computedPropertyName, info, superBinding);
+
     return context.createGetterOrSetterProperty(const_cast<VM*>(m_vm), m_parserArena, location, type, strict, numericPropertyName, info, superBinding);
 }
 
index d469ab8..3db524f 100644 (file)
@@ -280,6 +280,10 @@ public:
             return Property(type);
         return Property(name, type);
     }
+    Property createGetterOrSetterProperty(const JSTokenLocation&, PropertyNode::Type type, bool, int, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding)
+    {
+        return Property(type);
+    }
     Property createGetterOrSetterProperty(VM* vm, ParserArena& parserArena, const JSTokenLocation&, PropertyNode::Type type, bool strict, double name, const ParserFunctionInfo<SyntaxChecker>&, SuperBinding)
     {
         if (!strict)
index 56b37bc..90bf262 100644 (file)
   cmd: runES6 :normal
 - path: es6/class_class_statement.js
   cmd: runES6 :normal
+- path: es6/class_computed_accessor_properties.js
+  cmd: runES6 :normal
 - path: es6/class_computed_names_temporal_dead_zone.js
   cmd: runES6 :normal
 - path: es6/class_computed_prototype_methods.js
   cmd: runES6 :normal
+- path: es6/class_computed_static_accessor_properties.js
+  cmd: runES6 :normal
 - path: es6/class_computed_static_methods.js
   cmd: runES6 :normal
 - path: es6/class_constructor.js
   cmd: runES6 :normal
 - path: es6/Object.prototype.__proto___set_prototype.js
   cmd: runES6 :normal
+- path: es6/object_literal_extensions_computed_accessors.js
+  cmd: runES6 :normal
 - path: es6/object_literal_extensions_computed_properties.js
   cmd: runES6 :normal
 - path: es6/object_literal_extensions_computed_shorthand_methods.js
   cmd: runES6 :fail
 - path: es6/class_class_name_is_lexically_scoped.js
   cmd: runES6 :fail
-- path: es6/class_computed_accessor_properties.js
-  cmd: runES6 :fail
-- path: es6/class_computed_static_accessor_properties.js
-  cmd: runES6 :fail
 - path: es6/destructuring_computed_properties.js
   cmd: runES6 :fail
 - path: es6/destructuring_defaults_in_parameters_separate_scope.js
   cmd: runES6 :fail
 - path: es6/non-strict_function_semantics_hoisted_block-level_function_declaration.js
   cmd: runES6 :fail
-- path: es6/object_literal_extensions_computed_accessors.js
-  cmd: runES6 :fail
 - path: es6/Promise_basic_functionality.js
   cmd: runES6 :fail
 - path: es6/Promise_constructor_requires_new.js
diff --git a/Source/JavaScriptCore/tests/stress/computed-accessor-parsing.js b/Source/JavaScriptCore/tests/stress/computed-accessor-parsing.js
new file mode 100644 (file)
index 0000000..3be9184
--- /dev/null
@@ -0,0 +1,112 @@
+function testShouldNotThrow(str) {
+    eval("function foo(){ var obj = {"+str+", x : 5 };}");
+}
+
+function testShouldThrow(str) {
+    var didThrow = false;
+
+    try {
+        eval("var obj = {"+str+"};");
+    } catch(e) {
+        didThrow = true;
+    }
+
+    if (!didThrow) throw new Error("Should Throw");;
+}
+
+testShouldNotThrow("get [x] () { return 1 }");
+
+testShouldNotThrow("get [x] () { return 1 }");
+testShouldNotThrow("set [x] (value) { valueSet = value }");
+
+testShouldNotThrow("get[x] () { return 1 }");
+testShouldNotThrow("set[x] (value) { valueSet = value }");
+
+testShouldNotThrow("get [x]() { return 1 }");
+testShouldNotThrow("set [x](value) { valueSet = value }");
+
+testShouldNotThrow("get[x]() { return 1 }");
+testShouldNotThrow("set[x](value) { valueSet = value }");
+
+testShouldNotThrow("get [1] () { return 1 }");
+testShouldNotThrow("set [1] (value) { valueSet = value }");
+
+testShouldNotThrow("get[1] () { return 1 }");
+testShouldNotThrow("set[1] (value) { valueSet = value }");
+
+testShouldNotThrow("get [1]() { return 1 }");
+testShouldNotThrow("set [1](value) { valueSet = value }");
+
+testShouldNotThrow("get[1]() { return 1 }");
+testShouldNotThrow("set[1](value) { valueSet = value }");
+
+testShouldNotThrow("get [{ a : 'hi'}] () { return 1 }");
+testShouldNotThrow("set [{ b : 'ho'}] (value) { valueSet = value }");
+
+testShouldNotThrow("get[{ a : 'hi'}] () { return 1 }");
+testShouldNotThrow("set[{ b : 'hi'}] (value) { valueSet = value }");
+
+testShouldNotThrow("get [{ a : 'hi'}]() { return 1 }");
+testShouldNotThrow("set [{ b : 'hi'}](value) { valueSet = value }");
+
+testShouldNotThrow("get[{ a : 'hi'}]() { return 1 }");
+testShouldNotThrow("set[{ b : 'hi'}](value) { valueSet = value }");
+
+testShouldNotThrow("get [ { a : 'hi'} ] () { return 1 }");
+testShouldNotThrow("set [ { b : 'hi'} ] (value) { valueSet = value }");
+
+testShouldNotThrow("get [{ a : 'hi'} ] () { return 1 }");
+testShouldNotThrow("set [{ b : 'hi'} ] (value) { valueSet = value }");
+
+testShouldNotThrow("get [ { a : 'hi'}] () { return 1 }");
+testShouldNotThrow("set [ { b : 'hi'}] (value) { valueSet = value }");
+
+testShouldNotThrow("get[ { a : 'hi'}] () { return 1 }");
+testShouldNotThrow("set[ { b : 'hi'}] (value) { valueSet = value }");
+
+testShouldNotThrow("get[ { a : 'hi'}]() { return 1 }");
+testShouldNotThrow("set[ { b : 'hi'}](value) { valueSet = value }");
+
+testShouldNotThrow("get [{ a : 'hi'} ]() { return 1 }");
+testShouldNotThrow("set [{ b : 'hi'} ](value) { valueSet = value }");
+
+testShouldNotThrow("get[{ [\"goats\"] : 'hi'} ]() { return 1 }");
+testShouldNotThrow("set[{ [1] : 'hi'}] (value) { valueSet = value }");
+
+testShouldNotThrow("get[{ [\"goats\"] : 'hi'} ]() { return 1 }");
+testShouldNotThrow("set[{ get [1]() { return 'hi' } }] (value) { valueSet = value }");
+
+
+testShouldThrow("get [] () { return 1 }");
+testShouldThrow("set [] (value) { valueSet = value }");
+
+testShouldThrow("get [ () { return 1 }");
+testShouldThrow("set [ (value) { valueSet = value }");
+
+
+testShouldThrow("get [ () { return 1 }]");
+testShouldThrow("set [ (value) { valueSet = value }]");
+
+testShouldThrow("geting [1] () { return 1 }");
+testShouldThrow("seting [2] (value) { valueSet = value }");
+
+testShouldThrow("geting [1] () { return 1 }");
+testShouldThrow("seting [2] (value) { valueSet = value }");
+
+testShouldThrow("g [1] () { return 1 }");
+testShouldThrow("s [2] (value) { valueSet = value }");
+
+
+testShouldThrow("get [1] (), a : 5");
+testShouldThrow("set [2] (value), a : 5");
+
+testShouldThrow("get [1]{ return 5}, a : 5");
+testShouldThrow("set [2]{ return 4; }, a : 5");
+
+// getters and setters work in classes.
+testShouldNotThrow("x : class Val { get x() { return 4; } }");
+testShouldNotThrow("x : class Val { get [2] () { return 4; } }");
+
+class Val{
+    get ['hi']() { return 4; }
+};
diff --git a/Source/JavaScriptCore/tests/stress/computed-accessor.js b/Source/JavaScriptCore/tests/stress/computed-accessor.js
new file mode 100644 (file)
index 0000000..6ef923d
--- /dev/null
@@ -0,0 +1,536 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage) {
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    shouldBe(String(error), errorMessage);
+}
+
+// Class.
+(function () {
+    {
+        class A {
+            get ['a' + 'b']() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a.ab, 42);
+        a.ab = 20000;
+        shouldBe(a.ab, 42);
+    }
+
+    {
+        class A {
+            get ['a' + '0']() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a.a0, 42);
+        a.a0 = 20000;
+        shouldBe(a.a0, 42);
+    }
+
+    {
+        class A {
+            get ['1' + '0']() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a[10], 42);
+        a[10] = 20000;
+        shouldBe(a[10], 42);
+    }
+
+    {
+        class A {
+            get [0.1]() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a[0.1], 42);
+        a[0.1] = 20000;
+        shouldBe(a[0.1], 42);
+    }
+
+    {
+        class A {
+            get [10.50]() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a[10.5], 42);
+        a[10.5] = 20000;
+        shouldBe(a[10.5], 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            get [hello()]() {
+                return 42;
+            }
+        }
+        let a = new A();
+        shouldBe(a.ok, 42);
+        a.ok = 20000;
+        shouldBe(a.ok, 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            get [hello()]() {
+                return 42;
+            }
+        }
+        class Derived extends A { }
+
+        let a = new Derived();
+        shouldBe(a.ok, 42);
+        a.ok = 20000;
+        shouldBe(a.ok, 42);
+    }
+
+    {
+        class A {
+            set ['a' + 'b'](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a.ab = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.ab, undefined);
+    }
+
+    {
+        class A {
+            set ['a' + '0'](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a.a0 = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.a0, undefined);
+    }
+
+    {
+        class A {
+            set ['1' + '0'](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a[10] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[10], undefined);
+    }
+
+    {
+        class A {
+            set [0.1](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a[0.1] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[0.1], undefined);
+    }
+
+    {
+        class A {
+            set [10.50](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a[10.5] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[10.5], undefined);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            set [hello()](value) {
+                this.value = value;
+            }
+        }
+        let a = new A();
+        a.ok = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.ok, undefined);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            set [hello()](value) {
+                this.value = value;
+            }
+        }
+        class Derived extends A { }
+
+        let a = new Derived();
+        a.ok = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.ok, undefined);
+    }
+}());
+
+// Class static.
+(function () {
+    {
+        class A {
+            static get ['a' + 'b']() {
+                return 42;
+            }
+        }
+        shouldBe(A.ab, 42);
+        A.ab = 20000;
+        shouldBe(A.ab, 42);
+    }
+
+    {
+        class A {
+            static get ['a' + '0']() {
+                return 42;
+            }
+        }
+        shouldBe(A.a0, 42);
+        A.a0 = 20000;
+        shouldBe(A.a0, 42);
+    }
+
+    {
+        class A {
+            static get ['1' + '0']() {
+                return 42;
+            }
+        }
+        shouldBe(A[10], 42);
+        A[10] = 20000;
+        shouldBe(A[10], 42);
+    }
+
+    {
+        class A {
+            static get [0.1]() {
+                return 42;
+            }
+        }
+        shouldBe(A[0.1], 42);
+        A[0.1] = 20000;
+        shouldBe(A[0.1], 42);
+    }
+
+    {
+        class A {
+            static get [10.50]() {
+                return 42;
+            }
+        }
+        shouldBe(A[10.5], 42);
+        A[10.5] = 20000;
+        shouldBe(A[10.5], 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            static get [hello()]() {
+                return 42;
+            }
+        }
+        shouldBe(A.ok, 42);
+        A.ok = 20000;
+        shouldBe(A.ok, 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            static get [hello()]() {
+                return 42;
+            }
+        }
+        class Derived extends A { }
+
+        shouldBe(Derived.ok, 42);
+        Derived.ok = 20000;
+        shouldBe(Derived.ok, 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            static get [hello()]() {
+                return 42;
+            }
+        }
+        class Derived extends A { }
+
+        shouldBe(Derived.ok, 42);
+        Derived.ok = 20000;
+        shouldBe(Derived.ok, 42);
+    }
+
+    {
+        class A {
+            static set ['a' + 'b'](value) {
+                this.value = value;
+            }
+        }
+        A.ab = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A.ab, undefined);
+    }
+
+    {
+        class A {
+            static set ['a' + '0'](value) {
+                this.value = value;
+            }
+        }
+        A.a0 = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A.a0, undefined);
+    }
+
+    {
+        class A {
+            static set ['1' + '0'](value) {
+                this.value = value;
+            }
+        }
+        A[10] = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A[10], undefined);
+    }
+
+    {
+        class A {
+            static set [0.1](value) {
+                this.value = value;
+            }
+        }
+        A[0.1] = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A[0.1], undefined);
+    }
+
+    {
+        class A {
+            static set [10.50](value) {
+                this.value = value;
+            }
+        }
+        A[10.5] = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A[10.5], undefined);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            static set [hello()](value) {
+                this.value = value;
+            }
+        }
+        A.ok = 42;
+        shouldBe(A.value, 42);
+        shouldBe(A.ok, undefined);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        class A {
+            static set [hello()](value) {
+                this.value = value;
+            }
+        }
+        class Derived extends A { }
+
+        Derived.ok = 42;
+        shouldBe(Derived.value, 42);
+        shouldBe(Derived.ok, undefined);
+    }
+}());
+
+
+// Object.
+(function () {
+    {
+        var a = {
+            get ['a' + 'b']() {
+                return 42;
+            }
+        }
+        shouldBe(a.ab, 42);
+        a.ab = 20000;
+        shouldBe(a.ab, 42);
+    }
+
+    {
+        var a = {
+            get ['a' + '0']() {
+                return 42;
+            }
+        }
+        shouldBe(a.a0, 42);
+        a.a0 = 20000;
+        shouldBe(a.a0, 42);
+    }
+
+    {
+        var a = {
+            get ['1' + '0']() {
+                return 42;
+            }
+        }
+        shouldBe(a[10], 42);
+        a[10] = 20000;
+        shouldBe(a[10], 42);
+    }
+
+    {
+        var a = {
+            get [0.1]() {
+                return 42;
+            }
+        }
+        shouldBe(a[0.1], 42);
+        a[0.1] = 20000;
+        shouldBe(a[0.1], 42);
+    }
+
+    {
+        var a = {
+            get [10.50]() {
+                return 42;
+            }
+        }
+        shouldBe(a[10.5], 42);
+        a[10.5] = 20000;
+        shouldBe(a[10.5], 42);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        var a = {
+            get [hello()]() {
+                return 42;
+            }
+        }
+        shouldBe(a.ok, 42);
+        a.ok = 20000;
+        shouldBe(a.ok, 42);
+    }
+
+    {
+        var a = {
+            set ['a' + 'b'](value) {
+                this.value = value;
+            }
+        }
+        a.ab = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.ab, undefined);
+    }
+
+    {
+        var a = {
+            set ['a' + '0'](value) {
+                this.value = value;
+            }
+        }
+        a.a0 = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.a0, undefined);
+    }
+
+    {
+        var a = {
+            set ['1' + '0'](value) {
+                this.value = value;
+            }
+        }
+        a[10] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[10], undefined);
+    }
+
+    {
+        var a = {
+            set [0.1](value) {
+                this.value = value;
+            }
+        }
+        a[0.1] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[0.1], undefined);
+    }
+
+    {
+        var a = {
+            set [10.50](value) {
+                this.value = value;
+            }
+        }
+        a[10.5] = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a[10.5], undefined);
+    }
+
+    {
+        function hello() {
+            return 'ok';
+        }
+        var a = {
+            set [hello()](value) {
+                this.value = value;
+            }
+        }
+        a.ok = 42;
+        shouldBe(a.value, 42);
+        shouldBe(a.ok, undefined);
+    }
+}());
diff --git a/Source/JavaScriptCore/tests/stress/duplicate-computed-accessors.js b/Source/JavaScriptCore/tests/stress/duplicate-computed-accessors.js
new file mode 100644 (file)
index 0000000..663313e
--- /dev/null
@@ -0,0 +1,402 @@
+function shouldBe(actual, expected) {
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+// Class methods.
+(function () {
+    var method1 = 'taste';
+    var method2 = 'taste';
+
+    class Cocoa {
+        get [method1]() {
+            return 'awesome';
+        }
+
+        get [method2]() {
+            return 'great';
+        }
+    }
+
+    var cocoa = new Cocoa();
+    shouldBe(cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        get [method1()]() {
+            return 'awesome';
+        }
+
+        get [method2()]() {
+            return 'great';
+        }
+    }
+
+    var cocoa = new Cocoa();
+    shouldBe(cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        get [method1()]() {
+            return this.value;
+        }
+
+        set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    var cocoa = new Cocoa();
+    shouldBe(cocoa.taste, undefined);
+    cocoa.taste = 'great';
+    shouldBe(cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        get 'taste'() {
+            return 'bad';
+        }
+
+        get [method1()]() {
+            return this.value;
+        }
+
+        set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    var cocoa = new Cocoa();
+    shouldBe(cocoa.taste, undefined);
+    cocoa.taste = 'great';
+    shouldBe(cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        get [method1()]() {
+            return this.value;
+        }
+
+        set [method2()](value) {
+            this.value = value;
+        }
+
+        get 'taste'() {
+            return 'awesome';
+        }
+
+        set taste(value) {
+        }
+    }
+
+    var cocoa = new Cocoa();
+    shouldBe(cocoa.taste, 'awesome');
+    cocoa.taste = 'great';
+    shouldBe(cocoa.taste, 'awesome');
+}());
+
+// Class static methods.
+(function () {
+    var method1 = 'taste';
+    var method2 = 'taste';
+
+    class Cocoa {
+        static get [method1]() {
+            return 'awesome';
+        }
+
+        static get [method2]() {
+            return 'great';
+        }
+    }
+
+    shouldBe(Cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        static get [method1()]() {
+            return 'awesome';
+        }
+
+        static get [method2()]() {
+            return 'great';
+        }
+    }
+
+    shouldBe(Cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        static get [method1()]() {
+            return this.value;
+        }
+
+        static set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    shouldBe(Cocoa.taste, undefined);
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        static get 'taste'() {
+            return 'bad';
+        }
+
+        static get [method1()]() {
+            return this.value;
+        }
+
+        static set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    shouldBe(Cocoa.taste, undefined);
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    class Cocoa {
+        static get [method1()]() {
+            return this.value;
+        }
+
+        static set [method2()](value) {
+            this.value = value;
+        }
+
+        static get 'taste'() {
+            return 'awesome';
+        }
+
+        static set taste(value) {
+        }
+    }
+
+    shouldBe(Cocoa.taste, 'awesome');
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'awesome');
+}());
+
+// Object.
+(function () {
+    var method1 = 'taste';
+    var method2 = 'taste';
+
+    let Cocoa = {
+        get [method1]() {
+            return 'awesome';
+        },
+
+        get [method2]() {
+            return 'great';
+        }
+    }
+
+    shouldBe(Cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    let Cocoa = {
+        get [method1()]() {
+            return 'awesome';
+        },
+
+        get [method2()]() {
+            return 'great';
+        }
+    }
+
+    shouldBe(Cocoa.taste, "great");
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    let Cocoa = {
+        get [method1()]() {
+            return this.value;
+        },
+
+        set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    shouldBe(Cocoa.taste, undefined);
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    let Cocoa = {
+        get 'taste'() {
+            return 'bad';
+        },
+
+        get [method1()]() {
+            return this.value;
+        },
+
+        set [method2()](value) {
+            this.value = value;
+        }
+    }
+
+    shouldBe(Cocoa.taste, undefined);
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'great');
+}());
+
+(function () {
+    var counter = 0;
+    function method1() {
+        shouldBe(counter++, 0);
+        return 'taste';
+    }
+    function method2() {
+        shouldBe(counter++, 1);
+        return 'taste';
+    }
+
+    let Cocoa = {
+        get [method1()]() {
+            return this.value;
+        },
+
+        set [method2()](value) {
+            this.value = value;
+        },
+
+        get 'taste'() {
+            return 'awesome';
+        },
+
+        set taste(value) {
+        }
+    }
+
+    shouldBe(Cocoa.taste, 'awesome');
+    Cocoa.taste = 'great';
+    shouldBe(Cocoa.taste, 'awesome');
+}());