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.
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/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: http://svn.webkit.org/repository/webkit/trunk@200083 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 6294270..83a079c 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -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 Youenn Fablet <youenn.fablet@crf.canon.fr>
Drop [UsePointersEvenForNonNullableObjectArguments] from SpeechSynthesis
diff --git a/LayoutTests/js/class-syntax-super-expected.txt b/LayoutTests/js/class-syntax-super-expected.txt
index ec0ff57..89c60be 100644
--- a/LayoutTests/js/class-syntax-super-expected.txt
+++ b/LayoutTests/js/class-syntax-super-expected.txt
@@ -49,6 +49,7 @@
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
diff --git a/LayoutTests/js/script-tests/class-syntax-super.js b/LayoutTests/js/script-tests/class-syntax-super.js
index 376cb78..f73a52c 100644
--- a/LayoutTests/js/script-tests/class-syntax-super.js
+++ b/LayoutTests/js/script-tests/class-syntax-super.js
@@ -135,4 +135,5 @@
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;
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 3d9ba59..7786758 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,63 @@
+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/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-25 Ryosuke Niwa <rniwa@webkit.org>
Remove the build flag for template elements
diff --git a/Source/JavaScriptCore/bytecode/BytecodeList.json b/Source/JavaScriptCore/bytecode/BytecodeList.json
index e8eb9b6..6723c92 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeList.json
+++ b/Source/JavaScriptCore/bytecode/BytecodeList.json
@@ -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 },
diff --git a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
index 33c04a8..fb05608 100644
--- a/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
+++ b/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
@@ -147,6 +147,7 @@
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 @@
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:
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 9677c7c..a149e29 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -1066,6 +1066,10 @@
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;
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index 7b4b083..8b3a6aa 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -4118,6 +4118,14 @@
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)
{
{
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
index 0fa9081..0961e02 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
@@ -626,6 +626,7 @@
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);
diff --git a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
index 9505897..387e142 100644
--- a/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
+++ b/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
@@ -763,10 +763,21 @@
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;
diff --git a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
index 2982c47..df2ab98 100644
--- a/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
+++ b/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
@@ -980,6 +980,7 @@
break;
}
+ case IsEmpty:
case IsArrayObject:
case IsJSArray:
case IsArrayConstructor:
@@ -1068,6 +1069,9 @@
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;
@@ -1098,6 +1102,21 @@
}
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
diff --git a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
index c8c4059..ffae115 100644
--- a/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
+++ b/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
@@ -3721,7 +3721,11 @@
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));
diff --git a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
index 7370138..1e92538 100644
--- a/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
+++ b/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
@@ -132,6 +132,7 @@
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:
diff --git a/Source/JavaScriptCore/dfg/DFGClobberize.h b/Source/JavaScriptCore/dfg/DFGClobberize.h
index 2e0528a..b242d13 100644
--- a/Source/JavaScriptCore/dfg/DFGClobberize.h
+++ b/Source/JavaScriptCore/dfg/DFGClobberize.h
@@ -155,6 +155,7 @@
case CompareStrictEq:
case IsJSArray:
case IsArrayConstructor:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
index 294fbf1..123f954 100644
--- a/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
+++ b/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
@@ -156,6 +156,7 @@
case IsArrayObject:
case IsJSArray:
case IsArrayConstructor:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
index 63fc60c..0beced6 100644
--- a/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
+++ b/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
@@ -1530,6 +1530,7 @@
case IsArrayObject:
case IsJSArray:
case IsArrayConstructor:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/dfg/DFGNodeType.h b/Source/JavaScriptCore/dfg/DFGNodeType.h
index 529f571..38dd106 100644
--- a/Source/JavaScriptCore/dfg/DFGNodeType.h
+++ b/Source/JavaScriptCore/dfg/DFGNodeType.h
@@ -307,6 +307,7 @@
macro(IsArrayObject, NodeMustGenerate | NodeResultBoolean) \
macro(IsJSArray, NodeResultBoolean) \
macro(IsArrayConstructor, NodeResultBoolean) \
+ macro(IsEmpty, NodeResultBoolean) \
macro(IsUndefined, NodeResultBoolean) \
macro(IsBoolean, NodeResultBoolean) \
macro(IsNumber, NodeResultBoolean) \
diff --git a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
index 030e541..93c35aa 100644
--- a/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
+++ b/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
@@ -256,6 +256,7 @@
case IsArrayObject:
case IsJSArray:
case IsArrayConstructor:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
index fe8801b..c475b9e 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
@@ -4413,6 +4413,14 @@
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);
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
index a75c75f..27b8c7d 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
@@ -4432,6 +4432,17 @@
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());
diff --git a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
index cd0ad28..334f4ab 100644
--- a/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
+++ b/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
@@ -179,6 +179,7 @@
case IsArrayObject:
case IsJSArray:
case IsArrayConstructor:
+ case IsEmpty:
case IsUndefined:
case IsBoolean:
case IsNumber:
diff --git a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
index 3f4d902..3bb85fa 100644
--- a/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
+++ b/Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
@@ -841,6 +841,9 @@
case InvalidationPoint:
compileInvalidationPoint();
break;
+ case IsEmpty:
+ compileIsEmpty();
+ break;
case IsUndefined:
compileIsUndefined();
break;
@@ -5758,6 +5761,11 @@
// 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()
{
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index 2d74cf6..96e4ec2 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -235,6 +235,7 @@
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)
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index 0819b7e..0c3f11d 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -511,6 +511,7 @@
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*);
diff --git a/Source/JavaScriptCore/jit/JITOpcodes.cpp b/Source/JavaScriptCore/jit/JITOpcodes.cpp
index dd8219c..9acb530 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes.cpp
@@ -175,6 +175,18 @@
// 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)
{
diff --git a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
index cc4b8f8..690e41c 100644
--- a/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
+++ b/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
@@ -290,6 +290,17 @@
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)
{
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
index 5649797..b0f5a3d 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
@@ -1212,6 +1212,17 @@
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
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
index 7b83eee..c602553 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
@@ -1098,6 +1098,18 @@
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
index 0000000..71fac04
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/class-syntax-double-constructor.js
@@ -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');