Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (181465 => 181466)
--- trunk/Source/_javascript_Core/ChangeLog 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-03-13 01:11:15 UTC (rev 181466)
@@ -1,3 +1,99 @@
+2015-03-12 Ryosuke Niwa <[email protected]>
+
+ "this" should be in TDZ until super is called in the constructor of a derived class
+ https://bugs.webkit.org/show_bug.cgi?id=142527
+
+ Reviewed by Mark Hahnenberg.
+
+ DFG and FTL implementations co-authored by Filip Pizlo.
+
+ In ES6 class syntax, "this" register must be in the "temporal dead zone" (TDZ) and throw ReferenceError until
+ super() is called inside the constructor of a derived class.
+
+ Added op_check_tdz, a new OP code, which throws a reference error when the first operand is an empty value
+ to all tiers of JIT and LLint. The op code throws in the slow path on the basis that a TDZ error should be
+ a programming error and not a part of the programs' normal control flow. In DFG, this op code is represented
+ by a no-op must-generate node CheckNotEmpty modeled after CheckCell.
+
+ Also made the constructor of a derived class assign the empty value to "this" register rather than undefined
+ so that ThisNode can emit the op_check_tdz to check the initialized-ness of "this" in such a constructor.
+
+ * bytecode/BytecodeList.json: Added op_check_tdz.
+ * bytecode/BytecodeUseDef.h:
+ (JSC::computeUsesForBytecodeOffset): Ditto.
+ (JSC::computeDefsForBytecodeOffset): Ditto.
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dumpBytecode): Ditto.
+ * bytecode/ExitKind.cpp:
+ (JSC::exitKindToString): Added TDZFailure.
+ * bytecode/ExitKind.h: Ditto.
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::BytecodeGenerator): Assign the empty value to "this" register to indicate it's in TDZ.
+ (JSC::BytecodeGenerator::emitTDZCheck): Added.
+ (JSC::BytecodeGenerator::emitReturn): Emit the TDZ check since "this" can still be in TDZ if super() was never
+ called. e.g. class B extends A { constructor() { } }
+ * bytecompiler/BytecodeGenerator.h:
+ * bytecompiler/NodesCodegen.cpp:
+ (JSC::ThisNode::emitBytecode): Always emit the TDZ check if we're inside the constructor of a derived class.
+ We can't omit this check even if the result was ignored per spec.
+ * dfg/DFGAbstractInterpreterInlines.h:
+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): Previously, empty value could never appear
+ in a local variable. This is no longer true so generalize this code. Also added the support for CheckNotEmpty.
+ Like CheckCell, we phantomize this DFG node in the constant folding phase if the type of the operand is already
+ found to be not empty. Otherwise filter out SpecEmpty.
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::parseBlock): Added op_check_tdz.
+ * dfg/DFGCapabilities.cpp:
+ (JSC::DFG::capabilityLevel): op_check_tdz can be compiled and inlined.
+ * dfg/DFGClobberize.h:
+ (JSC::DFG::clobberize): CheckNotEmpty doesn't read or write values.
+ * dfg/DFGConstantFoldingPhase.cpp:
+ (JSC::DFG::ConstantFoldingPhase::foldConstants): Convert CheckNotEmpty to a phantom if non-emptiness had already
+ been proven for the operand prior to this node.
+ * dfg/DFGDoesGC.cpp:
+ (JSC::DFG::doesGC): CheckNotEmpty does not trigger GC.
+ * dfg/DFGFixupPhase.cpp:
+ (JSC::DFG::FixupPhase::fixupNode): CheckNotEmpty is a no-op in the fixup phase.
+ * dfg/DFGNodeType.h: CheckNotEmpty cannot be removed even if the result was ignored. See ThisNode::emitBytecode.
+ * dfg/DFGPredictionPropagationPhase.cpp:
+ (JSC::DFG::PredictionPropagationPhase::propagate): CheckNotEmpty doesn't return any value.
+ * dfg/DFGSafeToExecute.h:
+ (JSC::DFG::safeToExecute): CheckNotEmpty doesn't load from heap so it's safe.
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile): Speculative the operand to be not empty. OSR exit if the speculation fails.
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile): Ditto.
+ * ftl/FTLCapabilities.cpp:
+ (JSC::FTL::canCompile): CheckNotEmpty can be compiled in FTL.
+ * ftl/FTLLowerDFGToLLVM.cpp:
+ (JSC::FTL::LowerDFGToLLVM::compileNode): Calls compileCheckNotEmpty for CheckNotEmpty.
+ (JSC::FTL::LowerDFGToLLVM::compileCheckNotEmpty): OSR exit with "TDZFailure" if the operand is not empty.
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass): Added op_check_tdz.
+ (JSC::JIT::privateCompileSlowCases): Ditto.
+ * jit/JIT.h:
+ * jit/JITOpcodes.cpp:
+ (JSC::JIT::emit_op_check_tdz): Implements op_check_tdz in Baseline JIT.
+ (JSC::JIT::emitSlow_op_check_tdz): Ditto.
+ * jit/JITOpcodes32_64.cpp:
+ (JSC::JIT::emit_op_check_tdz): Ditto.
+ (JSC::JIT::emitSlow_op_check_tdz): Ditto.
+ * llint/LowLevelInterpreter32_64.asm: Implements op_check_tdz in LLint.
+ * llint/LowLevelInterpreter64.asm: Ditto.
+ * runtime/CommonSlowPaths.cpp:
+ (JSC::SLOW_PATH_DECL): Throws a reference error for op_check_tdz. Shared by LLint and Baseline JIT.
+ * runtime/CommonSlowPaths.h:
+ * tests/stress/class-syntax-no-loop-tdz.js: Added.
+ * tests/stress/class-syntax-no-tdz-in-catch.js: Added.
+ * tests/stress/class-syntax-no-tdz-in-conditional.js: Added.
+ * tests/stress/class-syntax-no-tdz-in-loop-no-inline-super.js: Added.
+ * tests/stress/class-syntax-no-tdz-in-loop.js: Added.
+ * tests/stress/class-syntax-no-tdz.js: Added.
+ * tests/stress/class-syntax-tdz-in-catch.js: Added.
+ * tests/stress/class-syntax-tdz-in-conditional.js: Added.
+ * tests/stress/class-syntax-tdz-in-loop.js: Added.
+ * tests/stress/class-syntax-tdz.js: Added.
+
2015-03-12 Yusuke Suzuki <[email protected]>
Integrate MapData into JSMap and JSSet
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeList.json (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeList.json 2015-03-13 01:11:15 UTC (rev 181466)
@@ -11,6 +11,7 @@
{ "name" : "op_create_arguments", "length" : 3 },
{ "name" : "op_create_this", "length" : 4 },
{ "name" : "op_to_this", "length" : 4 },
+ { "name" : "op_check_tdz", "length" : 2 },
{ "name" : "op_new_object", "length" : 4 },
{ "name" : "op_new_array", "length" : 5 },
{ "name" : "op_new_array_with_size", "length" : 4 },
Modified: trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecode/BytecodeUseDef.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -56,6 +56,7 @@
return;
case op_get_scope:
case op_to_this:
+ case op_check_tdz:
case op_pop_scope:
case op_profile_will_call:
case op_profile_did_call:
@@ -364,6 +365,7 @@
case op_mov:
case op_new_object:
case op_to_this:
+ case op_check_tdz:
case op_init_lazy_reg:
case op_get_scope:
case op_create_arguments:
Modified: trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecode/CodeBlock.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -789,6 +789,11 @@
out.print(" ", (++it)->u.toThisStatus);
break;
}
+ case op_check_tdz: {
+ int r0 = (++it)->u.operand;
+ printLocationOpAndRegisterOperand(out, exec, location, it, "op_check_tdz", r0);
+ break;
+ }
case op_new_object: {
int r0 = (++it)->u.operand;
unsigned inferredInlineCapacity = (++it)->u.operand;
Modified: trunk/Source/_javascript_Core/bytecode/ExitKind.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecode/ExitKind.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecode/ExitKind.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -70,6 +70,8 @@
return "NotStringObject";
case VarargsOverflow:
return "VarargsOverflow";
+ case TDZFailure:
+ return "TDZFailure";
case Uncountable:
return "Uncountable";
case UncountableInvalidation:
Modified: trunk/Source/_javascript_Core/bytecode/ExitKind.h (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecode/ExitKind.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecode/ExitKind.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -47,6 +47,7 @@
ExoticObjectMode, // We exited because some exotic object that we were accessing was in an exotic mode (like Arguments with slow arguments).
NotStringObject, // We exited because we shouldn't have attempted to optimize string object access.
VarargsOverflow, // We exited because a varargs call passed more arguments than we expected.
+ TDZFailure, // We exited because we were in the TDZ and accessed the variable.
Uncountable, // We exited for none of the above reasons, and we should not count it. Most uses of this should be viewed as a FIXME.
UncountableInvalidation, // We exited because the code block was invalidated; this means that we've already counted the reasons why the code block was invalidated.
WatchdogTimerFired, // We exited because we need to service the watchdog timer.
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -404,7 +404,7 @@
if (constructorKindIsDerived()) {
m_newTargetRegister = addVar();
emitMove(m_newTargetRegister, &m_thisRegister);
- emitLoad(&m_thisRegister, jsNull());
+ emitMove(&m_thisRegister, addConstantEmptyValue());
} else
emitCreateThis(&m_thisRegister);
} else if (functionNode->usesThis() || codeBlock->usesEval()) {
@@ -1556,6 +1556,12 @@
return dst;
}
+void BytecodeGenerator::emitTDZCheck(RegisterID* target)
+{
+ emitOpcode(op_check_tdz);
+ instructions().append(target->index());
+}
+
RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst)
{
size_t begin = instructions().size();
@@ -1907,12 +1913,16 @@
}
bool thisMightBeUninitialized = constructorKindIsDerived();
- if (isConstructor() && (src->index() != m_thisRegister.index() || thisMightBeUninitialized)) {
+ bool srcIsThis = src->index() == m_thisRegister.index();
+ if (isConstructor() && (!srcIsThis || thisMightBeUninitialized)) {
RefPtr<Label> isObjectOrUndefinedLabel = newLabel();
+ if (srcIsThis && thisMightBeUninitialized)
+ emitTDZCheck(src);
+
emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectOrUndefinedLabel.get());
- if (constructorKindIsDerived()) {
+ if (thisMightBeUninitialized) {
emitJumpIfTrue(emitIsUndefined(newTemporary(), src), isObjectOrUndefinedLabel.get());
emitThrowTypeError("Cannot return a non-object type in the constructor of a derived class.");
} else
Modified: trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecompiler/BytecodeGenerator.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -458,6 +458,7 @@
RegisterID* emitUnaryNoDstOp(OpcodeID, RegisterID* src);
RegisterID* emitCreateThis(RegisterID* dst);
+ void emitTDZCheck(RegisterID* target);
RegisterID* emitNewObject(RegisterID* dst);
RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision
Modified: trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/bytecompiler/NodesCodegen.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -144,6 +144,9 @@
RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
+ if (generator.constructorKindIsDerived())
+ generator.emitTDZCheck(generator.thisRegister());
+
if (dst == generator.ignoredResult())
return 0;
Modified: trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGAbstractInterpreterInlines.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -143,16 +143,7 @@
}
case ExtractOSREntryLocal: {
- if (!(node->unlinkedLocal().isArgument())
- && m_graph.m_lazyVars.get(node->unlinkedLocal().toLocal())) {
- // This is kind of pessimistic - we could know in some cases that the
- // DFG code at the point of the OSR had already initialized the lazy
- // variable. But maybe this is fine, since we're inserting OSR
- // entrypoints very early in the pipeline - so any lazy initializations
- // ought to be hoisted out anyway.
- forNode(node).makeBytecodeTop();
- } else
- forNode(node).makeHeapTop();
+ forNode(node).makeBytecodeTop();
break;
}
@@ -1865,11 +1856,21 @@
ASSERT(value);
break;
}
-
filterByValue(node->child1(), *node->cellOperand());
break;
}
+
+ case CheckNotEmpty: {
+ AbstractValue& value = forNode(node->child1());
+ if (!(value.m_type & SpecEmpty)) {
+ m_state.setFoundConstants(true);
+ break;
+ }
+ filter(value, ~SpecEmpty);
+ break;
+ }
+
case CheckInBounds: {
JSValue left = forNode(node->child1()).value();
JSValue right = forNode(node->child2()).value();
Modified: trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGByteCodeParser.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -2822,6 +2822,12 @@
NEXT_OPCODE(op_mov);
}
+ case op_check_tdz: {
+ Node* op = get(VirtualRegister(currentInstruction[1].u.operand));
+ addToGraph(CheckNotEmpty, op);
+ NEXT_OPCODE(op_check_tdz);
+ }
+
case op_check_has_instance:
addToGraph(CheckHasInstance, get(VirtualRegister(currentInstruction[3].u.operand)));
NEXT_OPCODE(op_check_has_instance);
Modified: trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGCapabilities.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -99,6 +99,7 @@
case op_enter:
case op_touch_entry:
case op_to_this:
+ case op_check_tdz:
case op_create_this:
case op_bitand:
case op_bitor:
Modified: trunk/Source/_javascript_Core/dfg/DFGClobberize.h (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGClobberize.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -254,7 +254,11 @@
case CheckCell:
def(PureValue(CheckCell, AdjacencyList(AdjacencyList::Fixed, node->child1()), node->cellOperand()));
return;
-
+
+ case CheckNotEmpty:
+ def(PureValue(CheckNotEmpty, AdjacencyList(AdjacencyList::Fixed, node->child1())));
+ return;
+
case ConstantStoragePointer:
def(PureValue(node, node->storagePointer()));
return;
Modified: trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGConstantFoldingPhase.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -185,7 +185,15 @@
eliminated = true;
break;
}
-
+
+ case CheckNotEmpty: {
+ if (m_state.forNode(node->child1()).m_type & SpecEmpty)
+ break;
+ node->convertToPhantom();
+ eliminated = true;
+ break;
+ }
+
case CheckInBounds: {
JSValue left = m_state.forNode(node->child1()).value();
JSValue right = m_state.forNode(node->child2()).value();
Modified: trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGDoesGC.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -105,6 +105,7 @@
case PutGlobalVar:
case VarInjectionWatchpoint:
case CheckCell:
+ case CheckNotEmpty:
case AllocationProfileWatchpoint:
case RegExpExec:
case RegExpTest:
Modified: trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGFixupPhase.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -1269,6 +1269,7 @@
case CountExecution:
case ForceOSRExit:
case CheckBadCell:
+ case CheckNotEmpty:
case CheckWatchdogTimer:
case Unreachable:
case ExtractOSREntryLocal:
Modified: trunk/Source/_javascript_Core/dfg/DFGNodeType.h (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGNodeType.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -191,6 +191,7 @@
macro(NotifyWrite, NodeMustGenerate) \
macro(VarInjectionWatchpoint, NodeMustGenerate) \
macro(CheckCell, NodeMustGenerate) \
+ macro(CheckNotEmpty, NodeMustGenerate) \
macro(CheckBadCell, NodeMustGenerate) \
macro(AllocationProfileWatchpoint, NodeMustGenerate) \
macro(CheckInBounds, NodeMustGenerate) \
Modified: trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGPredictionPropagationPhase.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -626,6 +626,7 @@
case SetArgument:
case CheckStructure:
case CheckCell:
+ case CheckNotEmpty:
case CheckBadCell:
case PutStructure:
case TearOffArguments:
Modified: trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGSafeToExecute.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -178,6 +178,7 @@
case VarInjectionWatchpoint:
case CheckCell:
case CheckBadCell:
+ case CheckNotEmpty:
case AllocationProfileWatchpoint:
case RegExpExec:
case RegExpTest:
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT32_64.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -3758,6 +3758,14 @@
break;
}
+ case CheckNotEmpty: {
+ JSValueOperand operand(this, node->child1());
+ GPRReg tagGPR = operand.tagGPR();
+ speculationCheck(TDZFailure, JSValueSource(), nullptr, m_jit.branch32(JITCompiler::Equal, tagGPR, TrustedImm32(JSValue::EmptyValueTag)));
+ noResult(node);
+ break;
+ }
+
case GetExecutable: {
SpeculateCellOperand function(this, node->child1());
GPRTemporary result(this, Reuse, function);
Modified: trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/dfg/DFGSpeculativeJIT64.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -3842,7 +3842,15 @@
noResult(node);
break;
}
-
+
+ case CheckNotEmpty: {
+ JSValueOperand operand(this, node->child1());
+ GPRReg gpr = operand.gpr();
+ speculationCheck(TDZFailure, JSValueSource(), nullptr, m_jit.branchTest64(JITCompiler::Zero, gpr));
+ noResult(node);
+ break;
+ }
+
case GetExecutable: {
SpeculateCellOperand function(this, node->child1());
GPRTemporary result(this, Reuse, function);
Modified: trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/ftl/FTLCapabilities.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -110,6 +110,7 @@
case StringCharAt:
case CheckCell:
case CheckBadCell:
+ case CheckNotEmpty:
case StringCharCodeAt:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
Modified: trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/ftl/FTLLowerDFGToLLVM.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -546,6 +546,9 @@
case CheckCell:
compileCheckCell();
break;
+ case CheckNotEmpty:
+ compileCheckNotEmpty();
+ break;
case CheckBadCell:
compileCheckBadCell();
break;
@@ -1862,7 +1865,12 @@
{
terminate(BadCell);
}
-
+
+ void compileCheckNotEmpty()
+ {
+ speculate(TDZFailure, noValue(), nullptr, m_out.isZero64(lowJSValue(m_node->child1())));
+ }
+
void compileGetExecutable()
{
LValue cell = lowCell(m_node->child1());
Modified: trunk/Source/_javascript_Core/jit/JIT.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/jit/JIT.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/jit/JIT.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -201,6 +201,7 @@
DEFINE_OP(op_construct)
DEFINE_OP(op_create_this)
DEFINE_OP(op_to_this)
+ DEFINE_OP(op_check_tdz)
DEFINE_OP(op_init_lazy_reg)
DEFINE_OP(op_create_arguments)
DEFINE_OP(op_debug)
@@ -375,6 +376,7 @@
DEFINE_SLOWCASE_OP(op_construct_varargs)
DEFINE_SLOWCASE_OP(op_construct)
DEFINE_SLOWCASE_OP(op_to_this)
+ DEFINE_SLOWCASE_OP(op_check_tdz)
DEFINE_SLOWCASE_OP(op_create_this)
DEFINE_SLOWCASE_OP(op_div)
DEFINE_SLOWCASE_OP(op_eq)
Modified: trunk/Source/_javascript_Core/jit/JIT.h (181465 => 181466)
--- trunk/Source/_javascript_Core/jit/JIT.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/jit/JIT.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -468,6 +468,7 @@
void emit_op_construct(Instruction*);
void emit_op_create_this(Instruction*);
void emit_op_to_this(Instruction*);
+ void emit_op_check_tdz(Instruction*);
void emit_op_create_arguments(Instruction*);
void emit_op_debug(Instruction*);
void emit_op_del_by_id(Instruction*);
@@ -572,6 +573,7 @@
void emitSlow_op_construct(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_to_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_create_this(Instruction*, Vector<SlowCaseEntry>::iterator&);
+ void emitSlow_op_check_tdz(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_div(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_eq(Instruction*, Vector<SlowCaseEntry>::iterator&);
void emitSlow_op_get_callee(Instruction*, Vector<SlowCaseEntry>::iterator&);
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -756,6 +756,19 @@
slowPathCall.call();
}
+void JIT::emit_op_check_tdz(Instruction* currentInstruction)
+{
+ emitGetVirtualRegister(currentInstruction[1].u.operand, regT0);
+ addSlowCase(branchTest64(Zero, regT0));
+}
+
+void JIT::emitSlow_op_check_tdz(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ linkSlowCase(iter);
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_tdz_error);
+ slowPathCall.call();
+}
+
void JIT::emit_op_profile_will_call(Instruction* currentInstruction)
{
Jump profilerDone = branchTestPtr(Zero, AbsoluteAddress(m_vm->enabledProfilerAddress()));
Modified: trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/jit/JITOpcodes32_64.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -997,6 +997,19 @@
slowPathCall.call();
}
+void JIT::emit_op_check_tdz(Instruction* currentInstruction)
+{
+ emitLoadTag(currentInstruction[1].u.operand, regT0);
+ addSlowCase(branch32(Equal, regT0, TrustedImm32(JSValue::EmptyValueTag)));
+}
+
+void JIT::emitSlow_op_check_tdz(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter)
+{
+ linkSlowCase(iter);
+ JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_tdz_error);
+ slowPathCall.call();
+}
+
void JIT::emit_op_profile_will_call(Instruction* currentInstruction)
{
load32(m_vm->enabledProfilerAddress(), regT0);
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm (181465 => 181466)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter32_64.asm 2015-03-13 01:11:15 UTC (rev 181466)
@@ -803,6 +803,16 @@
dispatch(4)
+_llint_op_check_tdz:
+ traceExecution()
+ loadpFromInstruction(1, t0)
+ bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opNotTDZ
+ callSlowPath(_slow_path_throw_tdz_error)
+
+.opNotTDZ:
+ dispatch(2)
+
+
_llint_op_mov:
traceExecution()
loadi 8[PC], t1
Modified: trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm (181465 => 181466)
--- trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/llint/LowLevelInterpreter64.asm 2015-03-13 01:11:15 UTC (rev 181466)
@@ -687,6 +687,17 @@
dispatch(4)
+_llint_op_check_tdz:
+ traceExecution()
+ loadpFromInstruction(1, t0)
+ loadq [cfr, t0, 8], t0
+ bqneq t0, ValueEmpty, .opNotTDZ
+ callSlowPath(_slow_path_throw_tdz_error)
+
+.opNotTDZ:
+ dispatch(2)
+
+
_llint_op_mov:
traceExecution()
loadisFromInstruction(2, t1)
Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp (181465 => 181466)
--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.cpp 2015-03-13 01:11:15 UTC (rev 181466)
@@ -257,6 +257,12 @@
RETURN(v1.toThis(exec, exec->codeBlock()->isStrictMode() ? StrictMode : NotStrictMode));
}
+SLOW_PATH_DECL(slow_path_throw_tdz_error)
+{
+ BEGIN();
+ THROW(createReferenceError(exec, "Cannot access uninitialized variable."));
+}
+
SLOW_PATH_DECL(slow_path_not)
{
BEGIN();
Modified: trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h (181465 => 181466)
--- trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h 2015-03-13 00:57:10 UTC (rev 181465)
+++ trunk/Source/_javascript_Core/runtime/CommonSlowPaths.h 2015-03-13 01:11:15 UTC (rev 181466)
@@ -187,6 +187,7 @@
SLOW_PATH_HIDDEN_DECL(slow_path_enter);
SLOW_PATH_HIDDEN_DECL(slow_path_get_callee);
SLOW_PATH_HIDDEN_DECL(slow_path_to_this);
+SLOW_PATH_HIDDEN_DECL(slow_path_throw_tdz_error);
SLOW_PATH_HIDDEN_DECL(slow_path_not);
SLOW_PATH_HIDDEN_DECL(slow_path_eq);
SLOW_PATH_HIDDEN_DECL(slow_path_neq);
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-loop-tdz.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-loop-tdz.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-loop-tdz.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,21 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ for (var j = 0; j < 10; j++) {
+ if (!j)
+ super();
+ else
+ this;
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-catch.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-catch.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-catch.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,20 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ try {
+ this;
+ } catch (e) {
+ super();
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-conditional.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-conditional.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-conditional.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,19 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor(accessThisBeforeSuper) {
+ if (accessThisBeforeSuper)
+ this;
+ else
+ super();
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B(false);
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop-no-inline-super.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop-no-inline-super.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop-no-inline-super.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,26 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+noInline(A);
+
+class B extends A {
+ constructor() {
+ var values = [];
+ for (var j = 0; j < 100; j++) {
+ if (j == 1)
+ super();
+ else if (j > 2)
+ this;
+ else
+ values.push(i);
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz-in-loop.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,24 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ var values = [];
+ for (var j = 0; j < 100; j++) {
+ if (j == 1)
+ super();
+ else if (j > 2)
+ this;
+ else
+ values.push(i);
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-no-tdz.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,17 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ super();
+ this;
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-catch.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-catch.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-catch.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,31 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ try {
+ this;
+ } catch (e) {
+ this;
+ super();
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i) {
+ var exception = null;
+ try {
+ new B(false);
+ } catch (e) {
+ exception = e;
+ if (!(e instanceof ReferenceError))
+ throw "Exception thrown in iteration " + i + " was not a reference error";
+ }
+ if (!exception)
+ throw "Exception not thrown for an unitialized this at iteration " + i;
+}
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-conditional.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-conditional.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-conditional.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,29 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor(accessThisBeforeSuper) {
+ if (accessThisBeforeSuper)
+ this;
+ else {
+ this;
+ super();
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i) {
+ var exception = null;
+ try {
+ new B(false);
+ } catch (e) {
+ exception = e;
+ }
+ if (!exception)
+ throw "Exception not thrown for an unitialized this at iteration " + i;
+}
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-loop.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-loop.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz-in-loop.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,31 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ for (var j = 0; j < 100; j++) {
+ if (j)
+ super();
+ else
+ this;
+ }
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i) {
+ var exception = null;
+ try {
+ new B();
+ } catch (e) {
+ exception = e;
+ if (!(e instanceof ReferenceError))
+ throw "Exception thrown in iteration " + i + " was not a reference error";
+ }
+ if (!exception)
+ throw "Exception not thrown for an unitialized this at iteration " + i;
+}
Added: trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz.js (0 => 181466)
--- trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz.js (rev 0)
+++ trunk/Source/_javascript_Core/tests/stress/class-syntax-tdz.js 2015-03-13 01:11:15 UTC (rev 181466)
@@ -0,0 +1,27 @@
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ this;
+ super();
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i) {
+ var exception;
+ try {
+ new B();
+ } catch (e) {
+ exception = e;
+ if (!(e instanceof ReferenceError))
+ throw "Exception thrown in iteration " + i + " was not a reference error";
+ }
+ if (!exception)
+ throw "Exception not thrown for an unitialized this at iteration " + i;
+}