calling super() a second time in a constructor should throw
authorgskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 18:40:41 +0000 (18:40 +0000)
committergskachkov@gmail.com <gskachkov@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 26 Apr 2016 18:40:41 +0000 (18:40 +0000)
https://bugs.webkit.org/show_bug.cgi?id=151113

Reviewed by Saam Barati and Keith Miller.

Source/JavaScriptCore:

Currently, our implementation checks if 'super()' was called in a constructor more
than once and raises a RuntimeError before the second call. According to the spec
we need to raise an error just after the second super() is finished and before
the new 'this' is assigned https://esdiscuss.org/topic/duplicate-super-call-behaviour.
To implement this behavior this patch adds a new op code, op_is_empty, that is used
to check if 'this' is empty.

* bytecode/BytecodeList.json:
* bytecode/BytecodeUseDef.h:
(JSC::computeUsesForBytecodeOffset):
(JSC::computeDefsForBytecodeOffset):
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dumpBytecode):
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitIsEmpty):
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::FunctionCallValueNode::emitBytecode):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::parseBlock):
* dfg/DFGCapabilities.cpp:
(JSC::DFG::capabilityLevel):
* dfg/DFGClobberize.h:
(JSC::DFG::clobberize):
* dfg/DFGDoesGC.cpp:
(JSC::DFG::doesGC):
* dfg/DFGFixupPhase.cpp:
(JSC::DFG::FixupPhase::fixupNode):
* dfg/DFGNodeType.h:
* dfg/DFGPredictionPropagationPhase.cpp:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGSpeculativeJIT32_64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* dfg/DFGSpeculativeJIT64.cpp:
(JSC::DFG::SpeculativeJIT::compile):
* ftl/FTLCapabilities.cpp:
(JSC::FTL::canCompile):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNode):
(JSC::FTL::DFG::LowerDFGToB3::compileIsEmpty):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
* jit/JIT.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_is_empty):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_is_empty):
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* tests/stress/class-syntax-double-constructor.js: Added.

LayoutTests:

* js/class-syntax-super-expected.txt:
* js/script-tests/class-syntax-super.js:

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

30 files changed:
LayoutTests/ChangeLog
LayoutTests/js/class-syntax-super-expected.txt
LayoutTests/js/script-tests/class-syntax-super.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/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCapabilities.cpp
Source/JavaScriptCore/dfg/DFGClobberize.h
Source/JavaScriptCore/dfg/DFGDoesGC.cpp
Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
Source/JavaScriptCore/dfg/DFGNodeType.h
Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
Source/JavaScriptCore/ftl/FTLCapabilities.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Source/JavaScriptCore/jit/JIT.cpp
Source/JavaScriptCore/jit/JIT.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/tests/stress/class-syntax-double-constructor.js [new file with mode: 0644]

index eb96f8f..09bd1f6 100644 (file)
@@ -1,3 +1,13 @@
+2016-04-26  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        calling super() a second time in a constructor should throw
+        https://bugs.webkit.org/show_bug.cgi?id=151113
+
+        Reviewed by Saam Barati and Keith Miller.
+
+        * js/class-syntax-super-expected.txt:
+        * js/script-tests/class-syntax-super.js:
+
 2016-04-26  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r200083.
index ec0ff57..89c60be 100644 (file)
@@ -49,6 +49,7 @@ PASS (function () { eval("super.method()");})():::SyntaxError: super is not vali
 PASS (function () { eval("super()");})():::SyntaxError: super is not valid in this context.
 PASS new (class { constructor() { (function () { eval("super()");})(); } }):::SyntaxError: super is not valid in this context.
 PASS (new (class { method() { (function () { eval("super.method()");})(); }})).method():::SyntaxError: super is not valid in this context.
+PASS new (class extends Base { constructor() { super(); super();}}):::ReferenceError: 'super()' can't be called more than once in a constructor.
 PASS successfullyParsed
 
 TEST COMPLETE
index 376cb78..f73a52c 100644 (file)
@@ -135,4 +135,5 @@ shouldThrow('(function () { eval("super()");})()', '"SyntaxError: super is not v
 shouldThrow('new (class { constructor() { (function () { eval("super()");})(); } })', '"SyntaxError: super is not valid in this context."');
 shouldThrow('(new (class { method() { (function () { eval("super.method()");})(); }})).method()', '"SyntaxError: super is not valid in this context."');
 
+shouldThrow('new (class extends Base { constructor() { super(); super();}})', '"ReferenceError: \'super()\' can\'t be called more than once in a constructor."');
 var successfullyParsed = true;
index ca0e9fc..90b7f4b 100644 (file)
@@ -1,3 +1,64 @@
+2016-04-26  Skachkov Oleksandr  <gskachkov@gmail.com>
+
+        calling super() a second time in a constructor should throw
+        https://bugs.webkit.org/show_bug.cgi?id=151113
+
+        Reviewed by Saam Barati.
+
+        Currently, our implementation checks if 'super()' was called in a constructor more
+        than once and raises a RuntimeError before the second call. According to the spec
+        we need to raise an error just after the second super() is finished and before
+        the new 'this' is assigned https://esdiscuss.org/topic/duplicate-super-call-behaviour.
+        To implement this behavior this patch adds a new op code, op_is_empty, that is used
+        to check if 'this' is empty.
+
+        * bytecode/BytecodeList.json:
+        * bytecode/BytecodeUseDef.h:
+        (JSC::computeUsesForBytecodeOffset):
+        (JSC::computeDefsForBytecodeOffset):
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::dumpBytecode):
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitIsEmpty):
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::FunctionCallValueNode::emitBytecode):
+        * dfg/DFGAbstractInterpreterInlines.h:
+        (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
+        * dfg/DFGByteCodeParser.cpp:
+        (JSC::DFG::ByteCodeParser::parseBlock):
+        * dfg/DFGCapabilities.cpp:
+        (JSC::DFG::capabilityLevel):
+        * dfg/DFGClobberize.h:
+        (JSC::DFG::clobberize):
+        * dfg/DFGDoesGC.cpp:
+        (JSC::DFG::doesGC):
+        * dfg/DFGFixupPhase.cpp:
+        (JSC::DFG::FixupPhase::fixupNode):
+        * dfg/DFGNodeType.h:
+        * dfg/DFGPredictionPropagationPhase.cpp:
+        * dfg/DFGSafeToExecute.h:
+        (JSC::DFG::safeToExecute):
+        * dfg/DFGSpeculativeJIT32_64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * dfg/DFGSpeculativeJIT64.cpp:
+        (JSC::DFG::SpeculativeJIT::compile):
+        * ftl/FTLCapabilities.cpp:
+        (JSC::FTL::canCompile):
+        * ftl/FTLLowerDFGToB3.cpp:
+        (JSC::FTL::DFG::LowerDFGToB3::compileNode):
+        (JSC::FTL::DFG::LowerDFGToB3::compileIsEmpty):
+        * jit/JIT.cpp:
+        (JSC::JIT::privateCompileMainPass):
+        * jit/JIT.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_is_empty):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_is_empty):
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * tests/stress/class-syntax-double-constructor.js: Added.
+
 2016-04-26  Mark Lam  <mark.lam@apple.com>
 
         Changed jsc options title to be more descriptive.
index e8eb9b6..6723c92 100644 (file)
@@ -49,6 +49,7 @@
             { "name" : "op_instanceof", "length" : 4 },
             { "name" : "op_instanceof_custom", "length" : 5 },
             { "name" : "op_typeof", "length" : 3 },
+            { "name" : "op_is_empty", "length" : 3 },
             { "name" : "op_is_undefined", "length" : 3 },
             { "name" : "op_is_boolean", "length" : 3 },
             { "name" : "op_is_number", "length" : 3 },
index 33c04a8..fb05608 100644 (file)
@@ -147,6 +147,7 @@ void computeUsesForBytecodeOffset(
     case op_get_by_id:
     case op_get_array_length:
     case op_typeof:
+    case op_is_empty:
     case op_is_undefined:
     case op_is_boolean:
     case op_is_number:
@@ -376,6 +377,7 @@ void computeDefsForBytecodeOffset(CodeBlock* codeBlock, BytecodeBasicBlock* bloc
     case op_instanceof_custom:
     case op_get_by_val:
     case op_typeof:
+    case op_is_empty:
     case op_is_undefined:
     case op_is_boolean:
     case op_is_number:
index 9677c7c..a149e29 100644 (file)
@@ -1066,6 +1066,10 @@ void CodeBlock::dumpBytecode(
             printUnaryOp(out, exec, location, it, "typeof");
             break;
         }
+        case op_is_empty: {
+            printUnaryOp(out, exec, location, it, "is_empty");
+            break;
+        }
         case op_is_undefined: {
             printUnaryOp(out, exec, location, it, "is_undefined");
             break;
index 7b4b083..8b3a6aa 100644 (file)
@@ -4118,6 +4118,14 @@ RegisterID* BytecodeGenerator::emitIsUndefined(RegisterID* dst, RegisterID* src)
     return dst;
 }
 
+RegisterID* BytecodeGenerator::emitIsEmpty(RegisterID* dst, RegisterID* src)
+{
+    emitOpcode(op_is_empty);
+    instructions().append(dst->index());
+    instructions().append(src->index());
+    return dst;
+}
+    
 RegisterID* BytecodeGenerator::emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node)
 {
     {
index 0fa9081..0961e02 100644 (file)
@@ -626,6 +626,7 @@ namespace JSC {
 
         RegisterID* emitIsObject(RegisterID* dst, RegisterID* src);
         RegisterID* emitIsUndefined(RegisterID* dst, RegisterID* src);
+        RegisterID* emitIsEmpty(RegisterID* dst, RegisterID* src);
         void emitRequireObjectCoercible(RegisterID* value, const String& error);
 
         RegisterID* emitIteratorNext(RegisterID* dst, RegisterID* iterator, const ThrowableExpressionData* node);
index 9505897..387e142 100644 (file)
@@ -763,10 +763,21 @@ RegisterID* FunctionCallValueNode::emitBytecode(BytecodeGenerator& generator, Re
         ASSERT(generator.constructorKind() == ConstructorKind::Derived || generator.derivedContextType() == DerivedContextType::DerivedConstructorContext);
         generator.emitMove(callArguments.thisRegister(), generator.newTarget());
         RegisterID* ret = generator.emitConstruct(returnValue.get(), func.get(), NoExpectedFunction, callArguments, divot(), divotStart(), divotEnd());
+
+        bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
+        bool doWeUseArrowFunctionInConstructor = isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext();
+
+        if (generator.isDerivedConstructorContext() || (doWeUseArrowFunctionInConstructor && generator.isSuperCallUsedInInnerArrowFunction()))
+            generator.emitLoadThisFromArrowFunctionLexicalEnvironment();
+        
+        RefPtr<Label> thisIsEmptyLabel = generator.newLabel();
+        generator.emitJumpIfTrue(generator.emitIsEmpty(generator.newTemporary(), generator.thisRegister()), thisIsEmptyLabel.get());
+        generator.emitThrowReferenceError(ASCIILiteral("'super()' can't be called more than once in a constructor."));
+        generator.emitLabel(thisIsEmptyLabel.get());
+
         generator.emitMove(generator.thisRegister(), ret);
         
-        bool isConstructorKindDerived = generator.constructorKind() == ConstructorKind::Derived;
-        if (generator.isDerivedConstructorContext() || (isConstructorKindDerived && generator.needsToUpdateArrowFunctionContext()))
+        if (generator.isDerivedConstructorContext() || doWeUseArrowFunctionInConstructor)
             generator.emitPutThisToArrowFunctionContextScope();
         
         return ret;
index 53081b2..0110afd 100644 (file)
@@ -996,6 +996,7 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         break;
     }
 
+    case IsEmpty:
     case IsArrayObject:
     case IsJSArray:
     case IsArrayConstructor:
@@ -1084,6 +1085,9 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             case IsRegExpObject:
                 setConstant(node, jsBoolean(child.value().isObject() && child.value().getObject()->type() == RegExpObjectType));
                 break;
+            case IsEmpty:
+                setConstant(node, jsBoolean(child.value().isEmpty()));
+                break;
             default:
                 constantWasSet = false;
                 break;
@@ -1114,6 +1118,21 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
             }
 
             break;
+        case IsEmpty: {
+            if (child.m_type && !(child.m_type & SpecEmpty)) {
+                setConstant(node, jsBoolean(false));
+                constantWasSet = true;
+                break;
+            }
+
+            if (child.m_type && !(child.m_type & ~SpecEmpty)) {
+                setConstant(node, jsBoolean(true));
+                constantWasSet = true;
+                break;
+            }
+
+            break;
+        }
         case IsUndefined:
             // FIXME: Use the masquerades-as-undefined watchpoint thingy.
             // https://bugs.webkit.org/show_bug.cgi?id=144456
index c8c4059..ffae115 100644 (file)
@@ -3721,7 +3721,11 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(InstanceOfCustom, value, constructor, hasInstanceValue));
             NEXT_OPCODE(op_instanceof_custom);
         }
-
+        case op_is_empty: {
+            Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
+            set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsEmpty, value));
+            NEXT_OPCODE(op_is_empty);
+        }
         case op_is_undefined: {
             Node* value = get(VirtualRegister(currentInstruction[2].u.operand));
             set(VirtualRegister(currentInstruction[1].u.operand), addToGraph(IsUndefined, value));
index 7370138..1e92538 100644 (file)
@@ -132,6 +132,7 @@ CapabilityLevel capabilityLevel(OpcodeID opcodeID, CodeBlock* codeBlock, Instruc
     case op_overrides_has_instance:
     case op_instanceof:
     case op_instanceof_custom:
+    case op_is_empty:
     case op_is_undefined:
     case op_is_boolean:
     case op_is_number:
index 2e0528a..b242d13 100644 (file)
@@ -155,6 +155,7 @@ void clobberize(Graph& graph, Node* node, const ReadFunctor& read, const WriteFu
     case CompareStrictEq:
     case IsJSArray:
     case IsArrayConstructor:
+    case IsEmpty:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index 294fbf1..123f954 100644 (file)
@@ -156,6 +156,7 @@ bool doesGC(Graph& graph, Node* node)
     case IsArrayObject:
     case IsJSArray:
     case IsArrayConstructor:
+    case IsEmpty:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index 63fc60c..0beced6 100644 (file)
@@ -1530,6 +1530,7 @@ private:
         case IsArrayObject:
         case IsJSArray:
         case IsArrayConstructor:
+        case IsEmpty:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
index 529f571..38dd106 100644 (file)
@@ -307,6 +307,7 @@ namespace JSC { namespace DFG {
     macro(IsArrayObject, NodeMustGenerate | NodeResultBoolean) \
     macro(IsJSArray, NodeResultBoolean) \
     macro(IsArrayConstructor, NodeResultBoolean) \
+    macro(IsEmpty, NodeResultBoolean) \
     macro(IsUndefined, NodeResultBoolean) \
     macro(IsBoolean, NodeResultBoolean) \
     macro(IsNumber, NodeResultBoolean) \
index 0972f2a..29f30bd 100644 (file)
@@ -764,6 +764,7 @@ private:
         case IsArrayObject:
         case IsJSArray:
         case IsArrayConstructor:
+        case IsEmpty:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
index 030e541..93c35aa 100644 (file)
@@ -256,6 +256,7 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case IsArrayObject:
     case IsJSArray:
     case IsArrayConstructor:
+    case IsEmpty:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index fe8801b..c475b9e 100644 (file)
@@ -4413,6 +4413,14 @@ void SpeculativeJIT::compile(Node* node)
         break;
     }
 
+    case IsEmpty: {        
+        JSValueOperand value(this, node->child1());
+        GPRTemporary result(this, Reuse, value, TagWord);
+        m_jit.comparePtr(JITCompiler::Equal, value.tagGPR(), TrustedImm32(JSValue::EmptyValueTag), result.gpr());
+        booleanResult(result.gpr(), node);
+        break;
+    }
+
     case IsUndefined: {
         JSValueOperand value(this, node->child1());
         GPRTemporary result(this);
index a75c75f..27b8c7d 100644 (file)
@@ -4432,6 +4432,17 @@ void SpeculativeJIT::compile(Node* node)
         compileInstanceOfCustom(node);
         break;
     }
+
+    case IsEmpty: {        
+        JSValueOperand value(this, node->child1());
+        GPRTemporary result(this, Reuse, value);
+
+        m_jit.comparePtr(JITCompiler::Equal, value.gpr(), TrustedImm32(JSValue::encode(JSValue())), result.gpr());
+        m_jit.or32(TrustedImm32(ValueFalse), result.gpr());
+
+        jsValueResult(result.gpr(), node, DataFormatJSBoolean);
+        break;
+    }
         
     case IsUndefined: {
         JSValueOperand value(this, node->child1());
index cd0ad28..334f4ab 100644 (file)
@@ -179,6 +179,7 @@ inline CapabilityLevel canCompile(Node* node)
     case IsArrayObject:
     case IsJSArray:
     case IsArrayConstructor:
+    case IsEmpty:
     case IsUndefined:
     case IsBoolean:
     case IsNumber:
index e320836..128f7a6 100644 (file)
@@ -842,6 +842,9 @@ private:
         case InvalidationPoint:
             compileInvalidationPoint();
             break;
+        case IsEmpty:
+            compileIsEmpty();
+            break;
         case IsUndefined:
             compileIsUndefined();
             break;
@@ -5759,6 +5762,11 @@ private:
         // When this abruptly terminates, it could read any heap location.
         patchpoint->effects.reads = HeapRange::top();
     }
+
+    void compileIsEmpty()
+    {
+        setBoolean(m_out.isZero64(lowJSValue(m_node->child1())));
+    }
     
     void compileIsUndefined()
     {
index 2d74cf6..96e4ec2 100644 (file)
@@ -235,6 +235,7 @@ void JIT::privateCompileMainPass()
         DEFINE_OP(op_overrides_has_instance)
         DEFINE_OP(op_instanceof)
         DEFINE_OP(op_instanceof_custom)
+        DEFINE_OP(op_is_empty)
         DEFINE_OP(op_is_undefined)
         DEFINE_OP(op_is_boolean)
         DEFINE_OP(op_is_number)
index 0819b7e..0c3f11d 100644 (file)
@@ -511,6 +511,7 @@ namespace JSC {
         void emit_op_overrides_has_instance(Instruction*);
         void emit_op_instanceof(Instruction*);
         void emit_op_instanceof_custom(Instruction*);
+        void emit_op_is_empty(Instruction*);
         void emit_op_is_undefined(Instruction*);
         void emit_op_is_boolean(Instruction*);
         void emit_op_is_number(Instruction*);
index dd8219c..9acb530 100644 (file)
@@ -175,6 +175,18 @@ void JIT::emit_op_instanceof_custom(Instruction*)
     // This always goes to slow path since we expect it to be rare.
     addSlowCase(jump());
 }
+    
+void JIT::emit_op_is_empty(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int value = currentInstruction[2].u.operand;
+
+    emitGetVirtualRegister(value, regT0);
+    compare64(Equal, regT0, TrustedImm32(JSValue::encode(JSValue())), regT0);
+
+    emitTagBool(regT0);
+    emitPutVirtualRegister(dst);
+}
 
 void JIT::emit_op_is_undefined(Instruction* currentInstruction)
 {
index cc4b8f8..690e41c 100644 (file)
@@ -290,6 +290,17 @@ void JIT::emitSlow_op_instanceof_custom(Instruction* currentInstruction, Vector<
     callOperation(operationInstanceOfCustom, regT1, regT0, regT2, regT4, regT3);
     emitStoreBool(dst, returnValueGPR);
 }
+    
+void JIT::emit_op_is_empty(Instruction* currentInstruction)
+{
+    int dst = currentInstruction[1].u.operand;
+    int value = currentInstruction[2].u.operand;
+    
+    emitLoad(value, regT1, regT0);
+    compare32(Equal, regT1, TrustedImm32(JSValue::EmptyValueTag), regT0);
+
+    emitStoreBool(dst, regT0);
+}
 
 void JIT::emit_op_is_undefined(Instruction* currentInstruction)
 {
index 5649797..b0f5a3d 100644 (file)
@@ -1212,6 +1212,17 @@ _llint_op_instanceof_custom:
     dispatch(5)
 
 
+_llint_op_is_empty:
+    traceExecution()
+    loadi 8[PC], t1
+    loadi 4[PC], t0
+    loadConstantOrVariable(t1, t2, t3)
+    cieq t2, EmptyValueTag, t3
+    storei BooleanTag, TagOffset[cfr, t0, 8]
+    storei t3, PayloadOffset[cfr, t0, 8]
+    dispatch(3)
+
+
 _llint_op_is_undefined:
     traceExecution()
     loadi 8[PC], t1
index 7b83eee..c602553 100644 (file)
@@ -1098,6 +1098,18 @@ _llint_op_instanceof_custom:
     callSlowPath(_llint_slow_path_instanceof_custom)
     dispatch(5)
 
+
+_llint_op_is_empty:
+    traceExecution()
+    loadisFromInstruction(2, t1)
+    loadisFromInstruction(1, t2)
+    loadConstantOrVariable(t1, t0)
+    cqeq t0, ValueEmpty, t3
+    orq ValueFalse, t3
+    storeq t3, [cfr, t2, 8]
+    dispatch(3)
+
+
 _llint_op_is_undefined:
     traceExecution()
     loadisFromInstruction(2, t1)
diff --git a/Source/JavaScriptCore/tests/stress/class-syntax-double-constructor.js b/Source/JavaScriptCore/tests/stress/class-syntax-double-constructor.js
new file mode 100644 (file)
index 0000000..71fac04
--- /dev/null
@@ -0,0 +1,183 @@
+const oneCallOfParentConstructor = 1;
+const twoCallOfParentConstructor = 2;
+
+function tryCatch(klass) {
+  let result = false;
+  try {
+    new klass();
+  } catch(e) {
+    result = e instanceof ReferenceError;
+  }
+  return result;
+}
+
+var testCase = function (actual, expected, message) {
+  if (actual !== expected) {
+    throw message + ". Expected '" + expected + "', but was '" + actual + "'";
+  }
+};
+
+let count = 0;
+class A {
+  constructor() {
+    this.id = 0;
+    count++;
+  }
+}
+
+class B extends A {
+  constructor() {
+    super();
+    super();
+    super();
+  }
+}
+
+testCase(tryCatch(B), true, 'Error: ReferenceError was not raised in case of two or more call super() #1');
+testCase(count, twoCallOfParentConstructor, 'Excpected two call of parent constructor #1');
+
+count = 0;
+class C extends A {
+  constructor() {
+    (()=>super())();
+    (()=>super())();
+    (()=>super())();
+  }
+}
+
+testCase(tryCatch(C), true, 'Error: ReferenceError was not raised in case of two or more call super() in arrrow function #2');
+testCase(count, twoCallOfParentConstructor, 'Excpected two call of parent constructor in arrow function #2');
+
+count = 0;
+class D extends A {
+  constructor() {
+    eval('super()');
+    eval('super()');
+    eval('super()');
+  }
+}
+
+testCase(tryCatch(D), true, 'Error: ReferenceError was not raised in case of two or more call super() in eval #3');
+testCase(count, twoCallOfParentConstructor, 'Excpected two call of parent constructor in eval #3');
+
+count = 0;
+class E extends A {
+  constructor() {
+    (()=>eval('super()'))();
+    (()=>eval('super()'))();
+    (()=>eval('super()'))();
+  }
+}
+
+testCase(tryCatch(E), true, 'Error: ReferenceError was not raised in case of two or more call super() in eval within arrow function #4');
+testCase(count, twoCallOfParentConstructor, 'Excpected two call of parent constructor in eval within arrow function #4');
+
+count = 0;
+class F extends A {
+    constructor() {
+        super();
+        var arrow = () => 'testValue';
+        arrow();
+    }
+}
+
+testCase(tryCatch(F), false, 'Error: ReferenceError was raised but should not be #5');
+testCase(count, oneCallOfParentConstructor, 'Excpected two call of parent constructor #5');
+
+count = 0;
+class G extends A {
+    constructor() {
+        super();
+        eval('(()=>"abc")()');
+    }
+}
+
+testCase(tryCatch(G), false, 'Error: ReferenceError was raised but should not be #6');
+testCase(count, oneCallOfParentConstructor, 'Excpected two call of parent constructor #6');
+
+count = 0;
+class H extends A {
+    constructor() {
+        eval('(()=>eval("super()"))()');
+        try {
+            eval('(()=>eval("super()"))()');
+        } catch(e) {
+          let result = e instanceof ReferenceError;
+          if (!result) throw new Error('Wrong type error');
+        }
+        try {
+            eval('(()=>eval("super()"))()');
+          } catch(e) {
+            let result = e instanceof ReferenceError;
+            if (!result) throw new Error('Wrong type error');
+          }
+        try {
+            eval('(()=>eval("super()"))()');
+        } catch(e) {
+            let result = e instanceof ReferenceError;
+            if (!result) throw new Error('Wrong type error');
+        }
+    }
+}
+
+testCase(tryCatch(H), false, 'Error: ReferenceError was raised but should not be #7');
+testCase(count, 4, 'Excpected two call of parent constructor #7');
+
+noInline(B);
+for (var i = 0; i < 10000; i++) {
+    count = 0;
+    let result = false;
+    try {
+        new B();
+    } catch(e) {
+        result = e instanceof ReferenceError;
+    }
+
+    testCase(result, true, '');
+    testCase(count, 2, '');
+}
+
+count = 0;
+class I extends A {
+  constructor() {
+    super();
+    (()=>super())();
+  }
+}
+
+testCase(tryCatch(I), true, 'Error: ReferenceError was not raised in case of two or more call super() #8');
+testCase(count, 2, 'Excpected two call of parent constructor #8');
+
+count = 0;
+class J extends A {
+  constructor() {
+    super();
+    eval('super()');
+  }
+}
+
+testCase(tryCatch(J), true, 'Error: ReferenceError was not raised in case of two or more call super() #9');
+testCase(count, 2, 'Excpected two call of parent constructor #9');
+
+let maxCount = 150000;
+class K extends A {
+  constructor(i) {
+    if (i % 2 === 0 )
+      super();
+    if (i % 2 !== 0 || maxCount === i)
+      super();
+  }
+}
+
+noInline(K);
+let result = false;
+try {
+    count = 0;
+    for (var i = 1; i <= maxCount; i++) {
+        new K(i);
+    }
+} catch (e) {
+    result = e instanceof ReferenceError;
+}
+testCase(result, true, 'Error: ReferenceError was not raised in case of two or more call super() #10');
+testCase(count, maxCount + 1, 'Excpected a lot of calls of parent constructor #10');