Changeset 181466 in webkit
- Timestamp:
- Mar 12, 2015, 6:11:15 PM (10 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 10 added
- 31 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r181458 r181466 1 2015-03-12 Ryosuke Niwa <rniwa@webkit.org> 2 3 "this" should be in TDZ until super is called in the constructor of a derived class 4 https://bugs.webkit.org/show_bug.cgi?id=142527 5 6 Reviewed by Mark Hahnenberg. 7 8 DFG and FTL implementations co-authored by Filip Pizlo. 9 10 In ES6 class syntax, "this" register must be in the "temporal dead zone" (TDZ) and throw ReferenceError until 11 super() is called inside the constructor of a derived class. 12 13 Added op_check_tdz, a new OP code, which throws a reference error when the first operand is an empty value 14 to all tiers of JIT and LLint. The op code throws in the slow path on the basis that a TDZ error should be 15 a programming error and not a part of the programs' normal control flow. In DFG, this op code is represented 16 by a no-op must-generate node CheckNotEmpty modeled after CheckCell. 17 18 Also made the constructor of a derived class assign the empty value to "this" register rather than undefined 19 so that ThisNode can emit the op_check_tdz to check the initialized-ness of "this" in such a constructor. 20 21 * bytecode/BytecodeList.json: Added op_check_tdz. 22 * bytecode/BytecodeUseDef.h: 23 (JSC::computeUsesForBytecodeOffset): Ditto. 24 (JSC::computeDefsForBytecodeOffset): Ditto. 25 * bytecode/CodeBlock.cpp: 26 (JSC::CodeBlock::dumpBytecode): Ditto. 27 * bytecode/ExitKind.cpp: 28 (JSC::exitKindToString): Added TDZFailure. 29 * bytecode/ExitKind.h: Ditto. 30 * bytecompiler/BytecodeGenerator.cpp: 31 (JSC::BytecodeGenerator::BytecodeGenerator): Assign the empty value to "this" register to indicate it's in TDZ. 32 (JSC::BytecodeGenerator::emitTDZCheck): Added. 33 (JSC::BytecodeGenerator::emitReturn): Emit the TDZ check since "this" can still be in TDZ if super() was never 34 called. e.g. class B extends A { constructor() { } } 35 * bytecompiler/BytecodeGenerator.h: 36 * bytecompiler/NodesCodegen.cpp: 37 (JSC::ThisNode::emitBytecode): Always emit the TDZ check if we're inside the constructor of a derived class. 38 We can't omit this check even if the result was ignored per spec. 39 * dfg/DFGAbstractInterpreterInlines.h: 40 (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): Previously, empty value could never appear 41 in a local variable. This is no longer true so generalize this code. Also added the support for CheckNotEmpty. 42 Like CheckCell, we phantomize this DFG node in the constant folding phase if the type of the operand is already 43 found to be not empty. Otherwise filter out SpecEmpty. 44 * dfg/DFGByteCodeParser.cpp: 45 (JSC::DFG::ByteCodeParser::parseBlock): Added op_check_tdz. 46 * dfg/DFGCapabilities.cpp: 47 (JSC::DFG::capabilityLevel): op_check_tdz can be compiled and inlined. 48 * dfg/DFGClobberize.h: 49 (JSC::DFG::clobberize): CheckNotEmpty doesn't read or write values. 50 * dfg/DFGConstantFoldingPhase.cpp: 51 (JSC::DFG::ConstantFoldingPhase::foldConstants): Convert CheckNotEmpty to a phantom if non-emptiness had already 52 been proven for the operand prior to this node. 53 * dfg/DFGDoesGC.cpp: 54 (JSC::DFG::doesGC): CheckNotEmpty does not trigger GC. 55 * dfg/DFGFixupPhase.cpp: 56 (JSC::DFG::FixupPhase::fixupNode): CheckNotEmpty is a no-op in the fixup phase. 57 * dfg/DFGNodeType.h: CheckNotEmpty cannot be removed even if the result was ignored. See ThisNode::emitBytecode. 58 * dfg/DFGPredictionPropagationPhase.cpp: 59 (JSC::DFG::PredictionPropagationPhase::propagate): CheckNotEmpty doesn't return any value. 60 * dfg/DFGSafeToExecute.h: 61 (JSC::DFG::safeToExecute): CheckNotEmpty doesn't load from heap so it's safe. 62 * dfg/DFGSpeculativeJIT32_64.cpp: 63 (JSC::DFG::SpeculativeJIT::compile): Speculative the operand to be not empty. OSR exit if the speculation fails. 64 * dfg/DFGSpeculativeJIT64.cpp: 65 (JSC::DFG::SpeculativeJIT::compile): Ditto. 66 * ftl/FTLCapabilities.cpp: 67 (JSC::FTL::canCompile): CheckNotEmpty can be compiled in FTL. 68 * ftl/FTLLowerDFGToLLVM.cpp: 69 (JSC::FTL::LowerDFGToLLVM::compileNode): Calls compileCheckNotEmpty for CheckNotEmpty. 70 (JSC::FTL::LowerDFGToLLVM::compileCheckNotEmpty): OSR exit with "TDZFailure" if the operand is not empty. 71 * jit/JIT.cpp: 72 (JSC::JIT::privateCompileMainPass): Added op_check_tdz. 73 (JSC::JIT::privateCompileSlowCases): Ditto. 74 * jit/JIT.h: 75 * jit/JITOpcodes.cpp: 76 (JSC::JIT::emit_op_check_tdz): Implements op_check_tdz in Baseline JIT. 77 (JSC::JIT::emitSlow_op_check_tdz): Ditto. 78 * jit/JITOpcodes32_64.cpp: 79 (JSC::JIT::emit_op_check_tdz): Ditto. 80 (JSC::JIT::emitSlow_op_check_tdz): Ditto. 81 * llint/LowLevelInterpreter32_64.asm: Implements op_check_tdz in LLint. 82 * llint/LowLevelInterpreter64.asm: Ditto. 83 * runtime/CommonSlowPaths.cpp: 84 (JSC::SLOW_PATH_DECL): Throws a reference error for op_check_tdz. Shared by LLint and Baseline JIT. 85 * runtime/CommonSlowPaths.h: 86 * tests/stress/class-syntax-no-loop-tdz.js: Added. 87 * tests/stress/class-syntax-no-tdz-in-catch.js: Added. 88 * tests/stress/class-syntax-no-tdz-in-conditional.js: Added. 89 * tests/stress/class-syntax-no-tdz-in-loop-no-inline-super.js: Added. 90 * tests/stress/class-syntax-no-tdz-in-loop.js: Added. 91 * tests/stress/class-syntax-no-tdz.js: Added. 92 * tests/stress/class-syntax-tdz-in-catch.js: Added. 93 * tests/stress/class-syntax-tdz-in-conditional.js: Added. 94 * tests/stress/class-syntax-tdz-in-loop.js: Added. 95 * tests/stress/class-syntax-tdz.js: Added. 96 1 97 2015-03-12 Yusuke Suzuki <utatane.tea@gmail.com> 2 98 -
trunk/Source/JavaScriptCore/bytecode/BytecodeList.json
r180917 r181466 12 12 { "name" : "op_create_this", "length" : 4 }, 13 13 { "name" : "op_to_this", "length" : 4 }, 14 { "name" : "op_check_tdz", "length" : 2 }, 14 15 { "name" : "op_new_object", "length" : 4 }, 15 16 { "name" : "op_new_array", "length" : 5 }, -
trunk/Source/JavaScriptCore/bytecode/BytecodeUseDef.h
r180917 r181466 57 57 case op_get_scope: 58 58 case op_to_this: 59 case op_check_tdz: 59 60 case op_pop_scope: 60 61 case op_profile_will_call: … … 365 366 case op_new_object: 366 367 case op_to_this: 368 case op_check_tdz: 367 369 case op_init_lazy_reg: 368 370 case op_get_scope: -
trunk/Source/JavaScriptCore/bytecode/CodeBlock.cpp
r181456 r181466 788 788 out.print(" cache(struct = ", RawPointer(structure), ")"); 789 789 out.print(" ", (++it)->u.toThisStatus); 790 break; 791 } 792 case op_check_tdz: { 793 int r0 = (++it)->u.operand; 794 printLocationOpAndRegisterOperand(out, exec, location, it, "op_check_tdz", r0); 790 795 break; 791 796 } -
trunk/Source/JavaScriptCore/bytecode/ExitKind.cpp
r180703 r181466 71 71 case VarargsOverflow: 72 72 return "VarargsOverflow"; 73 case TDZFailure: 74 return "TDZFailure"; 73 75 case Uncountable: 74 76 return "Uncountable"; -
trunk/Source/JavaScriptCore/bytecode/ExitKind.h
r180703 r181466 48 48 NotStringObject, // We exited because we shouldn't have attempted to optimize string object access. 49 49 VarargsOverflow, // We exited because a varargs call passed more arguments than we expected. 50 TDZFailure, // We exited because we were in the TDZ and accessed the variable. 50 51 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. 51 52 UncountableInvalidation, // We exited because the code block was invalidated; this means that we've already counted the reasons why the code block was invalidated. -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
r181353 r181466 405 405 m_newTargetRegister = addVar(); 406 406 emitMove(m_newTargetRegister, &m_thisRegister); 407 emit Load(&m_thisRegister, jsNull());407 emitMove(&m_thisRegister, addConstantEmptyValue()); 408 408 } else 409 409 emitCreateThis(&m_thisRegister); … … 1557 1557 } 1558 1558 1559 void BytecodeGenerator::emitTDZCheck(RegisterID* target) 1560 { 1561 emitOpcode(op_check_tdz); 1562 instructions().append(target->index()); 1563 } 1564 1559 1565 RegisterID* BytecodeGenerator::emitNewObject(RegisterID* dst) 1560 1566 { … … 1908 1914 1909 1915 bool thisMightBeUninitialized = constructorKindIsDerived(); 1910 if (isConstructor() && (src->index() != m_thisRegister.index() || thisMightBeUninitialized)) { 1916 bool srcIsThis = src->index() == m_thisRegister.index(); 1917 if (isConstructor() && (!srcIsThis || thisMightBeUninitialized)) { 1911 1918 RefPtr<Label> isObjectOrUndefinedLabel = newLabel(); 1912 1919 1920 if (srcIsThis && thisMightBeUninitialized) 1921 emitTDZCheck(src); 1922 1913 1923 emitJumpIfTrue(emitIsObject(newTemporary(), src), isObjectOrUndefinedLabel.get()); 1914 1924 1915 if ( constructorKindIsDerived()) {1925 if (thisMightBeUninitialized) { 1916 1926 emitJumpIfTrue(emitIsUndefined(newTemporary(), src), isObjectOrUndefinedLabel.get()); 1917 1927 emitThrowTypeError("Cannot return a non-object type in the constructor of a derived class."); -
trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
r181293 r181466 459 459 460 460 RegisterID* emitCreateThis(RegisterID* dst); 461 void emitTDZCheck(RegisterID* target); 461 462 RegisterID* emitNewObject(RegisterID* dst); 462 463 RegisterID* emitNewArray(RegisterID* dst, ElementNode*, unsigned length); // stops at first elision -
trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
r181293 r181466 145 145 RegisterID* ThisNode::emitBytecode(BytecodeGenerator& generator, RegisterID* dst) 146 146 { 147 if (generator.constructorKindIsDerived()) 148 generator.emitTDZCheck(generator.thisRegister()); 149 147 150 if (dst == generator.ignoredResult()) 148 151 return 0; -
trunk/Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
r181035 r181466 144 144 145 145 case ExtractOSREntryLocal: { 146 if (!(node->unlinkedLocal().isArgument()) 147 && m_graph.m_lazyVars.get(node->unlinkedLocal().toLocal())) { 148 // This is kind of pessimistic - we could know in some cases that the 149 // DFG code at the point of the OSR had already initialized the lazy 150 // variable. But maybe this is fine, since we're inserting OSR 151 // entrypoints very early in the pipeline - so any lazy initializations 152 // ought to be hoisted out anyway. 153 forNode(node).makeBytecodeTop(); 154 } else 155 forNode(node).makeHeapTop(); 146 forNode(node).makeBytecodeTop(); 156 147 break; 157 148 } … … 1866 1857 break; 1867 1858 } 1868 1869 1859 filterByValue(node->child1(), *node->cellOperand()); 1870 1860 break; 1871 1861 } 1872 1862 1863 case CheckNotEmpty: { 1864 AbstractValue& value = forNode(node->child1()); 1865 if (!(value.m_type & SpecEmpty)) { 1866 m_state.setFoundConstants(true); 1867 break; 1868 } 1869 1870 filter(value, ~SpecEmpty); 1871 break; 1872 } 1873 1873 1874 case CheckInBounds: { 1874 1875 JSValue left = forNode(node->child1()).value(); -
trunk/Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
r181035 r181466 2821 2821 set(VirtualRegister(currentInstruction[1].u.operand), op); 2822 2822 NEXT_OPCODE(op_mov); 2823 } 2824 2825 case op_check_tdz: { 2826 Node* op = get(VirtualRegister(currentInstruction[1].u.operand)); 2827 addToGraph(CheckNotEmpty, op); 2828 NEXT_OPCODE(op_check_tdz); 2823 2829 } 2824 2830 -
trunk/Source/JavaScriptCore/dfg/DFGCapabilities.cpp
r180993 r181466 100 100 case op_touch_entry: 101 101 case op_to_this: 102 case op_check_tdz: 102 103 case op_create_this: 103 104 case op_bitand: -
trunk/Source/JavaScriptCore/dfg/DFGClobberize.h
r181035 r181466 255 255 def(PureValue(CheckCell, AdjacencyList(AdjacencyList::Fixed, node->child1()), node->cellOperand())); 256 256 return; 257 257 258 case CheckNotEmpty: 259 def(PureValue(CheckNotEmpty, AdjacencyList(AdjacencyList::Fixed, node->child1()))); 260 return; 261 258 262 case ConstantStoragePointer: 259 263 def(PureValue(node, node->storagePointer())); -
trunk/Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
r180691 r181466 186 186 break; 187 187 } 188 188 189 case CheckNotEmpty: { 190 if (m_state.forNode(node->child1()).m_type & SpecEmpty) 191 break; 192 node->convertToPhantom(); 193 eliminated = true; 194 break; 195 } 196 189 197 case CheckInBounds: { 190 198 JSValue left = m_state.forNode(node->child1()).value(); -
trunk/Source/JavaScriptCore/dfg/DFGDoesGC.cpp
r181035 r181466 106 106 case VarInjectionWatchpoint: 107 107 case CheckCell: 108 case CheckNotEmpty: 108 109 case AllocationProfileWatchpoint: 109 110 case RegExpExec: -
trunk/Source/JavaScriptCore/dfg/DFGFixupPhase.cpp
r181035 r181466 1270 1270 case ForceOSRExit: 1271 1271 case CheckBadCell: 1272 case CheckNotEmpty: 1272 1273 case CheckWatchdogTimer: 1273 1274 case Unreachable: -
trunk/Source/JavaScriptCore/dfg/DFGNodeType.h
r181035 r181466 192 192 macro(VarInjectionWatchpoint, NodeMustGenerate) \ 193 193 macro(CheckCell, NodeMustGenerate) \ 194 macro(CheckNotEmpty, NodeMustGenerate) \ 194 195 macro(CheckBadCell, NodeMustGenerate) \ 195 196 macro(AllocationProfileWatchpoint, NodeMustGenerate) \ -
trunk/Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp
r181035 r181466 627 627 case CheckStructure: 628 628 case CheckCell: 629 case CheckNotEmpty: 629 630 case CheckBadCell: 630 631 case PutStructure: -
trunk/Source/JavaScriptCore/dfg/DFGSafeToExecute.h
r181035 r181466 179 179 case CheckCell: 180 180 case CheckBadCell: 181 case CheckNotEmpty: 181 182 case AllocationProfileWatchpoint: 182 183 case RegExpExec: -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
r181334 r181466 3759 3759 } 3760 3760 3761 case CheckNotEmpty: { 3762 JSValueOperand operand(this, node->child1()); 3763 GPRReg tagGPR = operand.tagGPR(); 3764 speculationCheck(TDZFailure, JSValueSource(), nullptr, m_jit.branch32(JITCompiler::Equal, tagGPR, TrustedImm32(JSValue::EmptyValueTag))); 3765 noResult(node); 3766 break; 3767 } 3768 3761 3769 case GetExecutable: { 3762 3770 SpeculateCellOperand function(this, node->child1()); -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
r181035 r181466 3843 3843 break; 3844 3844 } 3845 3845 3846 case CheckNotEmpty: { 3847 JSValueOperand operand(this, node->child1()); 3848 GPRReg gpr = operand.gpr(); 3849 speculationCheck(TDZFailure, JSValueSource(), nullptr, m_jit.branchTest64(JITCompiler::Zero, gpr)); 3850 noResult(node); 3851 break; 3852 } 3853 3846 3854 case GetExecutable: { 3847 3855 SpeculateCellOperand function(this, node->child1()); -
trunk/Source/JavaScriptCore/ftl/FTLCapabilities.cpp
r181035 r181466 111 111 case CheckCell: 112 112 case CheckBadCell: 113 case CheckNotEmpty: 113 114 case StringCharCodeAt: 114 115 case AllocatePropertyStorage: -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
r181035 r181466 547 547 compileCheckCell(); 548 548 break; 549 case CheckNotEmpty: 550 compileCheckNotEmpty(); 551 break; 549 552 case CheckBadCell: 550 553 compileCheckBadCell(); … … 1863 1866 terminate(BadCell); 1864 1867 } 1865 1868 1869 void compileCheckNotEmpty() 1870 { 1871 speculate(TDZFailure, noValue(), nullptr, m_out.isZero64(lowJSValue(m_node->child1()))); 1872 } 1873 1866 1874 void compileGetExecutable() 1867 1875 { -
trunk/Source/JavaScriptCore/jit/JIT.cpp
r181343 r181466 202 202 DEFINE_OP(op_create_this) 203 203 DEFINE_OP(op_to_this) 204 DEFINE_OP(op_check_tdz) 204 205 DEFINE_OP(op_init_lazy_reg) 205 206 DEFINE_OP(op_create_arguments) … … 376 377 DEFINE_SLOWCASE_OP(op_construct) 377 378 DEFINE_SLOWCASE_OP(op_to_this) 379 DEFINE_SLOWCASE_OP(op_check_tdz) 378 380 DEFINE_SLOWCASE_OP(op_create_this) 379 381 DEFINE_SLOWCASE_OP(op_div) -
trunk/Source/JavaScriptCore/jit/JIT.h
r180917 r181466 469 469 void emit_op_create_this(Instruction*); 470 470 void emit_op_to_this(Instruction*); 471 void emit_op_check_tdz(Instruction*); 471 472 void emit_op_create_arguments(Instruction*); 472 473 void emit_op_debug(Instruction*); … … 573 574 void emitSlow_op_to_this(Instruction*, Vector<SlowCaseEntry>::iterator&); 574 575 void emitSlow_op_create_this(Instruction*, Vector<SlowCaseEntry>::iterator&); 576 void emitSlow_op_check_tdz(Instruction*, Vector<SlowCaseEntry>::iterator&); 575 577 void emitSlow_op_div(Instruction*, Vector<SlowCaseEntry>::iterator&); 576 578 void emitSlow_op_eq(Instruction*, Vector<SlowCaseEntry>::iterator&); -
trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp
r180917 r181466 757 757 } 758 758 759 void JIT::emit_op_check_tdz(Instruction* currentInstruction) 760 { 761 emitGetVirtualRegister(currentInstruction[1].u.operand, regT0); 762 addSlowCase(branchTest64(Zero, regT0)); 763 } 764 765 void JIT::emitSlow_op_check_tdz(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 766 { 767 linkSlowCase(iter); 768 JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_tdz_error); 769 slowPathCall.call(); 770 } 771 759 772 void JIT::emit_op_profile_will_call(Instruction* currentInstruction) 760 773 { -
trunk/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
r180917 r181466 998 998 } 999 999 1000 void JIT::emit_op_check_tdz(Instruction* currentInstruction) 1001 { 1002 emitLoadTag(currentInstruction[1].u.operand, regT0); 1003 addSlowCase(branch32(Equal, regT0, TrustedImm32(JSValue::EmptyValueTag))); 1004 } 1005 1006 void JIT::emitSlow_op_check_tdz(Instruction* currentInstruction, Vector<SlowCaseEntry>::iterator& iter) 1007 { 1008 linkSlowCase(iter); 1009 JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_throw_tdz_error); 1010 slowPathCall.call(); 1011 } 1012 1000 1013 void JIT::emit_op_profile_will_call(Instruction* currentInstruction) 1001 1014 { -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
r180917 r181466 802 802 callSlowPath(_llint_slow_path_new_object) 803 803 dispatch(4) 804 805 806 _llint_op_check_tdz: 807 traceExecution() 808 loadpFromInstruction(1, t0) 809 bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opNotTDZ 810 callSlowPath(_slow_path_throw_tdz_error) 811 812 .opNotTDZ: 813 dispatch(2) 804 814 805 815 -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
r180917 r181466 686 686 callSlowPath(_llint_slow_path_new_object) 687 687 dispatch(4) 688 689 690 _llint_op_check_tdz: 691 traceExecution() 692 loadpFromInstruction(1, t0) 693 loadq [cfr, t0, 8], t0 694 bqneq t0, ValueEmpty, .opNotTDZ 695 callSlowPath(_slow_path_throw_tdz_error) 696 697 .opNotTDZ: 698 dispatch(2) 688 699 689 700 -
trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
r180917 r181466 258 258 } 259 259 260 SLOW_PATH_DECL(slow_path_throw_tdz_error) 261 { 262 BEGIN(); 263 THROW(createReferenceError(exec, "Cannot access uninitialized variable.")); 264 } 265 260 266 SLOW_PATH_DECL(slow_path_not) 261 267 { -
trunk/Source/JavaScriptCore/runtime/CommonSlowPaths.h
r180587 r181466 188 188 SLOW_PATH_HIDDEN_DECL(slow_path_get_callee); 189 189 SLOW_PATH_HIDDEN_DECL(slow_path_to_this); 190 SLOW_PATH_HIDDEN_DECL(slow_path_throw_tdz_error); 190 191 SLOW_PATH_HIDDEN_DECL(slow_path_not); 191 192 SLOW_PATH_HIDDEN_DECL(slow_path_eq);
Note:
See TracChangeset
for help on using the changeset viewer.