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.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@181466
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2015-03-12 Ryosuke Niwa <rniwa@webkit.org>
+
+ "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 <utatane.tea@gmail.com>
Integrate MapData into JSMap and JSSet
{ "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 },
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:
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:
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;
return "NotStringObject";
case VarargsOverflow:
return "VarargsOverflow";
+ case TDZFailure:
+ return "TDZFailure";
case Uncountable:
return "Uncountable";
case UncountableInvalidation:
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.
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()) {
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();
}
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
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
RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst)
{
+ if (generator.constructorKindIsDerived())
+ generator.emitTDZCheck(generator.thisRegister());
+
if (dst == generator.ignoredResult())
return 0;
}
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;
}
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();
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);
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:
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;
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();
case PutGlobalVar:
case VarInjectionWatchpoint:
case CheckCell:
+ case CheckNotEmpty:
case AllocationProfileWatchpoint:
case RegExpExec:
case RegExpTest:
case CountExecution:
case ForceOSRExit:
case CheckBadCell:
+ case CheckNotEmpty:
case CheckWatchdogTimer:
case Unreachable:
case ExtractOSREntryLocal:
macro(NotifyWrite, NodeMustGenerate) \
macro(VarInjectionWatchpoint, NodeMustGenerate) \
macro(CheckCell, NodeMustGenerate) \
+ macro(CheckNotEmpty, NodeMustGenerate) \
macro(CheckBadCell, NodeMustGenerate) \
macro(AllocationProfileWatchpoint, NodeMustGenerate) \
macro(CheckInBounds, NodeMustGenerate) \
case SetArgument:
case CheckStructure:
case CheckCell:
+ case CheckNotEmpty:
case CheckBadCell:
case PutStructure:
case TearOffArguments:
case VarInjectionWatchpoint:
case CheckCell:
case CheckBadCell:
+ case CheckNotEmpty:
case AllocationProfileWatchpoint:
case RegExpExec:
case RegExpTest:
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);
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);
case StringCharAt:
case CheckCell:
case CheckBadCell:
+ case CheckNotEmpty:
case StringCharCodeAt:
case AllocatePropertyStorage:
case ReallocatePropertyStorage:
case CheckCell:
compileCheckCell();
break;
+ case CheckNotEmpty:
+ compileCheckNotEmpty();
+ break;
case CheckBadCell:
compileCheckBadCell();
break;
{
terminate(BadCell);
}
-
+
+ void compileCheckNotEmpty()
+ {
+ speculate(TDZFailure, noValue(), nullptr, m_out.isZero64(lowJSValue(m_node->child1())));
+ }
+
void compileGetExecutable()
{
LValue cell = lowCell(m_node->child1());
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)
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)
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*);
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&);
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()));
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);
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
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)
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();
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);
--- /dev/null
+//@ 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();
--- /dev/null
+//@ 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();
--- /dev/null
+//@ 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);
--- /dev/null
+//@ 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();
--- /dev/null
+//@ 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();
--- /dev/null
+//@ skip
+
+class A {
+ constructor() { }
+}
+
+class B extends A {
+ constructor() {
+ super();
+ this;
+ }
+}
+
+noInline(B);
+
+for (var i = 0; i < 100000; ++i)
+ new B();
--- /dev/null
+//@ 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;
+}
--- /dev/null
+//@ 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;
+}
--- /dev/null
+//@ 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;
+}
--- /dev/null
+//@ 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;
+}