1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef jit_JitFrameIterator_h 8 #define jit_JitFrameIterator_h 9 10 #include "jsfun.h" 11 #include "jsscript.h" 12 #include "jstypes.h" 13 14 #include "jit/IonCode.h" 15 #include "jit/Snapshots.h" 16 17 #include "js/ProfilingFrameIterator.h" 18 19 namespace js { 20 class ActivationIterator; 21 } // namespace js 22 23 namespace js { 24 namespace jit { 25 26 typedef void * CalleeToken; 27 28 enum FrameType 29 { 30 // A JS frame is analagous to a js::InterpreterFrame, representing one scripted 31 // functon activation. IonJS frames are used by the optimizing compiler. 32 JitFrame_IonJS, 33 34 // JS frame used by the baseline JIT. 35 JitFrame_BaselineJS, 36 37 // Frame pushed for JIT stubs that make non-tail calls, so that the 38 // return address -> ICEntry mapping works. 39 JitFrame_BaselineStub, 40 JitFrame_IonStub, 41 42 // The entry frame is the initial prologue block transitioning from the VM 43 // into the Ion world. 44 JitFrame_Entry, 45 46 // A rectifier frame sits in between two JS frames, adapting argc != nargs 47 // mismatches in calls. 48 JitFrame_Rectifier, 49 50 // Ion IC calling a scripted getter/setter. 51 JitFrame_IonAccessorIC, 52 53 // An unwound JS frame is a JS frame signalling that its callee frame has been 54 // turned into an exit frame (see EnsureExitFrame). Used by Ion bailouts and 55 // Baseline exception unwinding. 56 JitFrame_Unwound_BaselineJS, 57 JitFrame_Unwound_IonJS, 58 JitFrame_Unwound_BaselineStub, 59 JitFrame_Unwound_IonStub, 60 JitFrame_Unwound_Rectifier, 61 JitFrame_Unwound_IonAccessorIC, 62 63 // An exit frame is necessary for transitioning from a JS frame into C++. 64 // From within C++, an exit frame is always the last frame in any 65 // JitActivation. 66 JitFrame_Exit, 67 68 // A bailout frame is a special IonJS jit frame after a bailout, and before 69 // the reconstruction of the BaselineJS frame. From within C++, a bailout 70 // frame is always the last frame in a JitActivation iff the bailout frame 71 // information is recorded on the JitActivation. 72 JitFrame_Bailout, 73 74 // A lazy link frame is a special exit frame where a IonJS frame is reused 75 // for linking the newly compiled code. A special frame is needed to 76 // work-around the fact that we can make stack patterns which are similar to 77 // unwound frames. As opposed to unwound frames, we still have to mark all 78 // the arguments of the original IonJS frame. 79 JitFrame_LazyLink 80 }; 81 82 enum ReadFrameArgsBehavior { 83 // Only read formals (i.e. [0 ... callee()->nargs] 84 ReadFrame_Formals, 85 86 // Only read overflown args (i.e. [callee()->nargs ... numActuals()] 87 ReadFrame_Overflown, 88 89 // Read all args (i.e. [0 ... numActuals()]) 90 ReadFrame_Actuals 91 }; 92 93 class CommonFrameLayout; 94 class JitFrameLayout; 95 class ExitFrameLayout; 96 97 class BaselineFrame; 98 99 class JitActivation; 100 101 // Iterate over the JIT stack to assert that all invariants are respected. 102 // - Check that all entry frames are aligned on JitStackAlignment. 103 // - Check that all rectifier frames keep the JitStackAlignment. 104 void AssertJitStackInvariants(JSContext* cx); 105 106 class JitFrameIterator 107 { 108 protected: 109 uint8_t* current_; 110 FrameType type_; 111 uint8_t* returnAddressToFp_; 112 size_t frameSize_; 113 114 private: 115 mutable const SafepointIndex* cachedSafepointIndex_; 116 const JitActivation* activation_; 117 118 void dumpBaseline() const; 119 120 public: 121 explicit JitFrameIterator(); 122 explicit JitFrameIterator(JSContext* cx); 123 explicit JitFrameIterator(const ActivationIterator& activations); 124 125 // Current frame information. type()126 FrameType type() const { 127 return type_; 128 } fp()129 uint8_t* fp() const { 130 return current_; 131 } activation()132 const JitActivation* activation() const { 133 return activation_; 134 } 135 current()136 CommonFrameLayout* current() const { 137 return (CommonFrameLayout*)current_; 138 } 139 140 inline uint8_t* returnAddress() const; 141 142 // Return the pointer of the JitFrame, the iterator is assumed to be settled 143 // on a scripted frame. 144 JitFrameLayout* jsFrame() const; 145 146 // Returns true iff this exit frame was created using EnsureExitFrame. 147 inline bool isFakeExitFrame() const; 148 149 inline ExitFrameLayout* exitFrame() const; 150 151 // Returns whether the JS frame has been invalidated and, if so, 152 // places the invalidated Ion script in |ionScript|. 153 bool checkInvalidation(IonScript** ionScript) const; 154 bool checkInvalidation() const; 155 isExitFrame()156 bool isExitFrame() const { 157 return type_ == JitFrame_Exit || type_ == JitFrame_LazyLink; 158 } isScripted()159 bool isScripted() const { 160 return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout; 161 } isBaselineJS()162 bool isBaselineJS() const { 163 return type_ == JitFrame_BaselineJS; 164 } isIonScripted()165 bool isIonScripted() const { 166 return type_ == JitFrame_IonJS || type_ == JitFrame_Bailout; 167 } isIonJS()168 bool isIonJS() const { 169 return type_ == JitFrame_IonJS; 170 } isIonStub()171 bool isIonStub() const { 172 return type_ == JitFrame_IonStub; 173 } isIonStubMaybeUnwound()174 bool isIonStubMaybeUnwound() const { 175 return type_ == JitFrame_IonStub || type_ == JitFrame_Unwound_IonStub; 176 } isIonAccessorICMaybeUnwound()177 bool isIonAccessorICMaybeUnwound() const { 178 return type_ == JitFrame_IonAccessorIC || type_ == JitFrame_Unwound_IonAccessorIC; 179 } isBailoutJS()180 bool isBailoutJS() const { 181 return type_ == JitFrame_Bailout; 182 } isBaselineStub()183 bool isBaselineStub() const { 184 return type_ == JitFrame_BaselineStub; 185 } isBaselineStubMaybeUnwound()186 bool isBaselineStubMaybeUnwound() const { 187 return type_ == JitFrame_BaselineStub || type_ == JitFrame_Unwound_BaselineStub; 188 } isRectifierMaybeUnwound()189 bool isRectifierMaybeUnwound() const { 190 return type_ == JitFrame_Rectifier || type_ == JitFrame_Unwound_Rectifier; 191 } 192 bool isBareExit() const; 193 template <typename T> bool isExitFrameLayout() const; 194 isEntry()195 bool isEntry() const { 196 return type_ == JitFrame_Entry; 197 } 198 bool isFunctionFrame() const; 199 200 bool isConstructing() const; 201 202 void* calleeToken() const; 203 JSFunction* callee() const; 204 JSFunction* maybeCallee() const; 205 unsigned numActualArgs() const; 206 JSScript* script() const; 207 void baselineScriptAndPc(JSScript** scriptRes, jsbytecode** pcRes) const; 208 Value* actualArgs() const; 209 210 // Returns the return address of the frame above this one (that is, the 211 // return address that returns back to the current frame). returnAddressToFp()212 uint8_t* returnAddressToFp() const { 213 return returnAddressToFp_; 214 } 215 216 // Previous frame information extracted from the current frame. 217 inline size_t prevFrameLocalSize() const; 218 inline FrameType prevType() const; 219 uint8_t* prevFp() const; 220 221 // Returns the stack space used by the current frame, in bytes. This does 222 // not include the size of its fixed header. frameSize()223 size_t frameSize() const { 224 MOZ_ASSERT(!isExitFrame()); 225 return frameSize_; 226 } 227 228 // Functions used to iterate on frames. When prevType is JitFrame_Entry, 229 // the current frame is the last frame. done()230 inline bool done() const { 231 return type_ == JitFrame_Entry; 232 } 233 JitFrameIterator& operator++(); 234 235 // Returns the IonScript associated with this JS frame. 236 IonScript* ionScript() const; 237 238 // Returns the IonScript associated with this JS frame; the frame must 239 // not be invalidated. 240 IonScript* ionScriptFromCalleeToken() const; 241 242 // Returns the Safepoint associated with this JS frame. Incurs a lookup 243 // overhead. 244 const SafepointIndex* safepoint() const; 245 246 // Returns the OSI index associated with this JS frame. Incurs a lookup 247 // overhead. 248 const OsiIndex* osiIndex() const; 249 250 // Returns the Snapshot offset associated with this JS frame. Incurs a 251 // lookup overhead. 252 SnapshotOffset snapshotOffset() const; 253 254 uintptr_t* spillBase() const; 255 MachineState machineState() const; 256 257 template <class Op> unaliasedForEachActual(Op op,ReadFrameArgsBehavior behavior)258 void unaliasedForEachActual(Op op, ReadFrameArgsBehavior behavior) const { 259 MOZ_ASSERT(isBaselineJS()); 260 261 unsigned nactual = numActualArgs(); 262 unsigned start, end; 263 switch (behavior) { 264 case ReadFrame_Formals: 265 start = 0; 266 end = callee()->nargs(); 267 break; 268 case ReadFrame_Overflown: 269 start = callee()->nargs(); 270 end = nactual; 271 break; 272 case ReadFrame_Actuals: 273 start = 0; 274 end = nactual; 275 } 276 277 Value* argv = actualArgs(); 278 for (unsigned i = start; i < end; i++) 279 op(argv[i]); 280 } 281 282 void dump() const; 283 284 inline BaselineFrame* baselineFrame() const; 285 286 #ifdef DEBUG 287 bool verifyReturnAddressUsingNativeToBytecodeMap(); 288 #else verifyReturnAddressUsingNativeToBytecodeMap()289 inline bool verifyReturnAddressUsingNativeToBytecodeMap() { return true; } 290 #endif 291 }; 292 293 class JitcodeGlobalTable; 294 295 class JitProfilingFrameIterator 296 { 297 uint8_t* fp_; 298 FrameType type_; 299 void* returnAddressToFp_; 300 301 inline JitFrameLayout* framePtr(); 302 inline JSScript* frameScript(); 303 bool tryInitWithPC(void* pc); 304 bool tryInitWithTable(JitcodeGlobalTable* table, void* pc, JSRuntime* rt, 305 bool forLastCallSite); 306 void fixBaselineDebugModeOSRReturnAddress(); 307 308 public: 309 JitProfilingFrameIterator(JSRuntime* rt, 310 const JS::ProfilingFrameIterator::RegisterState& state); 311 explicit JitProfilingFrameIterator(void* exitFrame); 312 313 void operator++(); done()314 bool done() const { return fp_ == nullptr; } 315 fp()316 void* fp() const { MOZ_ASSERT(!done()); return fp_; } stackAddress()317 void* stackAddress() const { return fp(); } frameType()318 FrameType frameType() const { MOZ_ASSERT(!done()); return type_; } returnAddressToFp()319 void* returnAddressToFp() const { MOZ_ASSERT(!done()); return returnAddressToFp_; } 320 }; 321 322 class RInstructionResults 323 { 324 // Vector of results of recover instructions. 325 typedef mozilla::Vector<RelocatableValue, 1, SystemAllocPolicy> Values; 326 mozilla::UniquePtr<Values, JS::DeletePolicy<Values> > results_; 327 328 // The frame pointer is used as a key to check if the current frame already 329 // bailed out. 330 JitFrameLayout* fp_; 331 332 // Record if we tried and succeed at allocating and filling the vector of 333 // recover instruction results, if needed. This flag is needed in order to 334 // avoid evaluating the recover instruction twice. 335 bool initialized_; 336 337 public: 338 explicit RInstructionResults(JitFrameLayout* fp); 339 RInstructionResults(RInstructionResults&& src); 340 341 RInstructionResults& operator=(RInstructionResults&& rhs); 342 343 ~RInstructionResults(); 344 345 bool init(JSContext* cx, uint32_t numResults); 346 bool isInitialized() const; 347 #ifdef DEBUG 348 size_t length() const; 349 #endif 350 351 JitFrameLayout* frame() const; 352 353 RelocatableValue& operator[](size_t index); 354 355 void trace(JSTracer* trc); 356 }; 357 358 struct MaybeReadFallback 359 { 360 enum NoGCValue { 361 NoGC_UndefinedValue, 362 NoGC_MagicOptimizedOut 363 }; 364 365 enum FallbackConsequence { 366 Fallback_Invalidate, 367 Fallback_DoNothing 368 }; 369 370 JSContext* maybeCx; 371 JitActivation* activation; 372 const JitFrameIterator* frame; 373 const NoGCValue unreadablePlaceholder_; 374 const FallbackConsequence consequence; 375 376 explicit MaybeReadFallback(const Value& placeholder = UndefinedValue()) maybeCxMaybeReadFallback377 : maybeCx(nullptr), 378 activation(nullptr), 379 frame(nullptr), 380 unreadablePlaceholder_(noGCPlaceholder(placeholder)), 381 consequence(Fallback_Invalidate) 382 { 383 } 384 385 MaybeReadFallback(JSContext* cx, JitActivation* activation, const JitFrameIterator* frame, 386 FallbackConsequence consequence = Fallback_Invalidate) maybeCxMaybeReadFallback387 : maybeCx(cx), 388 activation(activation), 389 frame(frame), 390 unreadablePlaceholder_(NoGC_UndefinedValue), 391 consequence(consequence) 392 { 393 } 394 canRecoverResultsMaybeReadFallback395 bool canRecoverResults() { return maybeCx; } 396 unreadablePlaceholderMaybeReadFallback397 Value unreadablePlaceholder() const { 398 if (unreadablePlaceholder_ == NoGC_MagicOptimizedOut) 399 return MagicValue(JS_OPTIMIZED_OUT); 400 return UndefinedValue(); 401 } 402 noGCPlaceholderMaybeReadFallback403 NoGCValue noGCPlaceholder(Value v) const { 404 if (v.isMagic(JS_OPTIMIZED_OUT)) 405 return NoGC_MagicOptimizedOut; 406 return NoGC_UndefinedValue; 407 } 408 }; 409 410 411 class RResumePoint; 412 class RSimdBox; 413 414 // Reads frame information in snapshot-encoding order (that is, outermost frame 415 // to innermost frame). 416 class SnapshotIterator 417 { 418 protected: 419 SnapshotReader snapshot_; 420 RecoverReader recover_; 421 JitFrameLayout* fp_; 422 const MachineState* machine_; 423 IonScript* ionScript_; 424 RInstructionResults* instructionResults_; 425 426 enum ReadMethod { 427 // Read the normal value. 428 RM_Normal = 1 << 0, 429 430 // Read the default value, or the normal value if there is no default. 431 RM_AlwaysDefault = 1 << 1, 432 433 // Try to read the normal value if it is readable, otherwise default to 434 // the Default value. 435 RM_NormalOrDefault = RM_Normal | RM_AlwaysDefault, 436 }; 437 438 private: 439 // Read a spilled register from the machine state. hasRegister(Register reg)440 bool hasRegister(Register reg) const { 441 return machine_->has(reg); 442 } fromRegister(Register reg)443 uintptr_t fromRegister(Register reg) const { 444 return machine_->read(reg); 445 } 446 hasRegister(FloatRegister reg)447 bool hasRegister(FloatRegister reg) const { 448 return machine_->has(reg); 449 } fromRegister(FloatRegister reg)450 double fromRegister(FloatRegister reg) const { 451 return machine_->read(reg); 452 } 453 454 // Read an uintptr_t from the stack. hasStack(int32_t offset)455 bool hasStack(int32_t offset) const { 456 return true; 457 } 458 uintptr_t fromStack(int32_t offset) const; 459 hasInstructionResult(uint32_t index)460 bool hasInstructionResult(uint32_t index) const { 461 return instructionResults_; 462 } hasInstructionResults()463 bool hasInstructionResults() const { 464 return instructionResults_; 465 } 466 Value fromInstructionResult(uint32_t index) const; 467 468 Value allocationValue(const RValueAllocation& a, ReadMethod rm = RM_Normal); 469 bool allocationReadable(const RValueAllocation& a, ReadMethod rm = RM_Normal); 470 void writeAllocationValuePayload(const RValueAllocation& a, Value v); 471 void warnUnreadableAllocation(); 472 473 private: 474 friend class RSimdBox; 475 const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const; 476 477 public: 478 // Handle iterating over RValueAllocations of the snapshots. readAllocation()479 inline RValueAllocation readAllocation() { 480 MOZ_ASSERT(moreAllocations()); 481 return snapshot_.readAllocation(); 482 } skip()483 Value skip() { 484 snapshot_.skipAllocation(); 485 return UndefinedValue(); 486 } 487 488 const RResumePoint* resumePoint() const; instruction()489 const RInstruction* instruction() const { 490 return recover_.instruction(); 491 } 492 493 uint32_t numAllocations() const; moreAllocations()494 inline bool moreAllocations() const { 495 return snapshot_.numAllocationsRead() < numAllocations(); 496 } 497 498 int32_t readOuterNumActualArgs() const; 499 500 // Used by recover instruction to store the value back into the instruction 501 // results array. 502 void storeInstructionResult(Value v); 503 504 public: 505 // Exhibits frame properties contained in the snapshot. 506 uint32_t pcOffset() const; resumeAfter()507 inline bool resumeAfter() const { 508 // Inline frames are inlined on calls, which are considered as being 509 // resumed on the Call as baseline will push the pc once we return from 510 // the call. 511 if (moreFrames()) 512 return false; 513 return recover_.resumeAfter(); 514 } bailoutKind()515 inline BailoutKind bailoutKind() const { 516 return snapshot_.bailoutKind(); 517 } 518 519 public: 520 // Read the next instruction available and get ready to either skip it or 521 // evaluate it. nextInstruction()522 inline void nextInstruction() { 523 MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations()); 524 recover_.nextInstruction(); 525 snapshot_.resetNumAllocationsRead(); 526 } 527 528 // Skip an Instruction by walking to the next instruction and by skipping 529 // all the allocations corresponding to this instruction. 530 void skipInstruction(); 531 moreInstructions()532 inline bool moreInstructions() const { 533 return recover_.moreInstructions(); 534 } 535 536 protected: 537 // Register a vector used for storing the results of the evaluation of 538 // recover instructions. This vector should be registered before the 539 // beginning of the iteration. This function is in charge of allocating 540 // enough space for all instructions results, and return false iff it fails. 541 bool initInstructionResults(MaybeReadFallback& fallback); 542 543 // This function is used internally for computing the result of the recover 544 // instructions. 545 bool computeInstructionResults(JSContext* cx, RInstructionResults* results) const; 546 547 public: 548 // Handle iterating over frames of the snapshots. 549 void nextFrame(); 550 void settleOnFrame(); 551 moreFrames()552 inline bool moreFrames() const { 553 // The last instruction is recovering the innermost frame, so as long as 554 // there is more instruction there is necesseray more frames. 555 return moreInstructions(); 556 } 557 558 public: 559 // Connect all informations about the current script in order to recover the 560 // content of baseline frames. 561 562 SnapshotIterator(const JitFrameIterator& iter, const MachineState* machineState); 563 SnapshotIterator(); 564 read()565 Value read() { 566 return allocationValue(readAllocation()); 567 } 568 569 // Read the |Normal| value unless it is not available and that the snapshot 570 // provides a |Default| value. This is useful to avoid invalidations of the 571 // frame while we are only interested in a few properties which are provided 572 // by the |Default| value. readWithDefault(RValueAllocation * alloc)573 Value readWithDefault(RValueAllocation* alloc) { 574 *alloc = RValueAllocation(); 575 RValueAllocation a = readAllocation(); 576 if (allocationReadable(a)) 577 return allocationValue(a); 578 579 *alloc = a; 580 return allocationValue(a, RM_AlwaysDefault); 581 } 582 583 Value maybeRead(const RValueAllocation& a, MaybeReadFallback& fallback); maybeRead(MaybeReadFallback & fallback)584 Value maybeRead(MaybeReadFallback& fallback) { 585 RValueAllocation a = readAllocation(); 586 return maybeRead(a, fallback); 587 } 588 589 void traceAllocation(JSTracer* trc); 590 591 template <class Op> readFunctionFrameArgs(Op & op,ArgumentsObject ** argsObj,Value * thisv,unsigned start,unsigned end,JSScript * script,MaybeReadFallback & fallback)592 void readFunctionFrameArgs(Op& op, ArgumentsObject** argsObj, Value* thisv, 593 unsigned start, unsigned end, JSScript* script, 594 MaybeReadFallback& fallback) 595 { 596 // Assumes that the common frame arguments have already been read. 597 if (script->argumentsHasVarBinding()) { 598 if (argsObj) { 599 Value v = read(); 600 if (v.isObject()) 601 *argsObj = &v.toObject().as<ArgumentsObject>(); 602 } else { 603 skip(); 604 } 605 } 606 607 if (thisv) 608 *thisv = maybeRead(fallback); 609 else 610 skip(); 611 612 unsigned i = 0; 613 if (end < start) 614 i = start; 615 616 for (; i < start; i++) 617 skip(); 618 for (; i < end; i++) { 619 // We are not always able to read values from the snapshots, some values 620 // such as non-gc things may still be live in registers and cause an 621 // error while reading the machine state. 622 Value v = maybeRead(fallback); 623 op(v); 624 } 625 } 626 627 // Iterate over all the allocations and return only the value of the 628 // allocation located at one index. 629 Value maybeReadAllocByIndex(size_t index); 630 631 #ifdef TRACK_SNAPSHOTS spewBailingFrom()632 void spewBailingFrom() const { 633 snapshot_.spewBailingFrom(); 634 } 635 #endif 636 }; 637 638 // Reads frame information in callstack order (that is, innermost frame to 639 // outermost frame). 640 class InlineFrameIterator 641 { 642 const JitFrameIterator* frame_; 643 SnapshotIterator start_; 644 SnapshotIterator si_; 645 uint32_t framesRead_; 646 647 // When the inline-frame-iterator is created, this variable is defined to 648 // UINT32_MAX. Then the first iteration of findNextFrame, which settle on 649 // the innermost frame, is used to update this counter to the number of 650 // frames contained in the recover buffer. 651 uint32_t frameCount_; 652 653 // The |calleeTemplate_| fields contains either the JSFunction or the 654 // template from which it is supposed to be cloned. The |calleeRVA_| is an 655 // Invalid value allocation, if the |calleeTemplate_| field is the effective 656 // JSFunction, and not its template. On the other hand, any other value 657 // allocation implies that the |calleeTemplate_| is the template JSFunction 658 // from which the effective one would be derived and cached by the Recover 659 // instruction result. 660 RootedFunction calleeTemplate_; 661 RValueAllocation calleeRVA_; 662 663 RootedScript script_; 664 jsbytecode* pc_; 665 uint32_t numActualArgs_; 666 667 // Register state, used by all snapshot iterators. 668 MachineState machine_; 669 670 struct Nop { operatorNop671 void operator()(const Value& v) { } 672 }; 673 674 private: 675 void findNextFrame(); 676 JSObject* computeScopeChain(Value scopeChainValue, MaybeReadFallback& fallback, 677 bool* hasCallObj = nullptr) const; 678 679 public: 680 InlineFrameIterator(JSContext* cx, const JitFrameIterator* iter); 681 InlineFrameIterator(JSRuntime* rt, const JitFrameIterator* iter); 682 InlineFrameIterator(JSContext* cx, const InlineFrameIterator* iter); 683 more()684 bool more() const { 685 return frame_ && framesRead_ < frameCount_; 686 } 687 688 // Due to optimizations, we are not always capable of reading the callee of 689 // inlined frames without invalidating the IonCode. This function might 690 // return either the effective callee of the JSFunction which might be used 691 // to create it. 692 // 693 // As such, the |calleeTemplate()| can be used to read most of the metadata 694 // which are conserved across clones. calleeTemplate()695 JSFunction* calleeTemplate() const { 696 MOZ_ASSERT(isFunctionFrame()); 697 return calleeTemplate_; 698 } maybeCalleeTemplate()699 JSFunction* maybeCalleeTemplate() const { 700 return calleeTemplate_; 701 } 702 703 JSFunction* callee(MaybeReadFallback& fallback) const; 704 numActualArgs()705 unsigned numActualArgs() const { 706 // The number of actual arguments of inline frames is recovered by the 707 // iteration process. It is recovered from the bytecode because this 708 // property still hold since the for inlined frames. This property does not 709 // hold for the parent frame because it can have optimize a call to 710 // js_fun_call or js_fun_apply. 711 if (more()) 712 return numActualArgs_; 713 714 return frame_->numActualArgs(); 715 } 716 717 template <class ArgOp, class LocalOp> readFrameArgsAndLocals(JSContext * cx,ArgOp & argOp,LocalOp & localOp,JSObject ** scopeChain,bool * hasCallObj,Value * rval,ArgumentsObject ** argsObj,Value * thisv,Value * newTarget,ReadFrameArgsBehavior behavior,MaybeReadFallback & fallback)718 void readFrameArgsAndLocals(JSContext* cx, ArgOp& argOp, LocalOp& localOp, 719 JSObject** scopeChain, bool* hasCallObj, 720 Value* rval, ArgumentsObject** argsObj, 721 Value* thisv, Value* newTarget, 722 ReadFrameArgsBehavior behavior, 723 MaybeReadFallback& fallback) const 724 { 725 SnapshotIterator s(si_); 726 727 // Read the scope chain. 728 if (scopeChain) { 729 Value scopeChainValue = s.maybeRead(fallback); 730 *scopeChain = computeScopeChain(scopeChainValue, fallback, hasCallObj); 731 } else { 732 s.skip(); 733 } 734 735 // Read return value. 736 if (rval) 737 *rval = s.read(); 738 else 739 s.skip(); 740 741 if (newTarget) { 742 // For now, only support reading new.target when we are reading 743 // overflown arguments. 744 MOZ_ASSERT(behavior != ReadFrame_Formals); 745 newTarget->setUndefined(); 746 } 747 748 // Read arguments, which only function frames have. 749 if (isFunctionFrame()) { 750 unsigned nactual = numActualArgs(); 751 unsigned nformal = calleeTemplate()->nargs(); 752 753 // Get the non overflown arguments, which are taken from the inlined 754 // frame, because it will have the updated value when JSOP_SETARG is 755 // done. 756 if (behavior != ReadFrame_Overflown) 757 s.readFunctionFrameArgs(argOp, argsObj, thisv, 0, nformal, script(), fallback); 758 759 if (behavior != ReadFrame_Formals) { 760 if (more()) { 761 // There is still a parent frame of this inlined frame. All 762 // arguments (also the overflown) are the last pushed values 763 // in the parent frame. To get the overflown arguments, we 764 // need to take them from there. 765 766 // The overflown arguments are not available in current frame. 767 // They are the last pushed arguments in the parent frame of 768 // this inlined frame. 769 InlineFrameIterator it(cx, this); 770 ++it; 771 unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0; 772 bool hasNewTarget = isConstructing(); 773 SnapshotIterator parent_s(it.snapshotIterator()); 774 775 // Skip over all slots until we get to the last slots 776 // (= arguments slots of callee) the +3 is for [this], [returnvalue], 777 // [scopechain], and maybe +1 for [argsObj] 778 MOZ_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj + hasNewTarget); 779 unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj - hasNewTarget; 780 for (unsigned j = 0; j < skip; j++) 781 parent_s.skip(); 782 783 // Get the overflown arguments 784 MaybeReadFallback unusedFallback; 785 parent_s.skip(); // scope chain 786 parent_s.skip(); // return value 787 parent_s.readFunctionFrameArgs(argOp, nullptr, nullptr, 788 nformal, nactual, it.script(), 789 fallback); 790 if (newTarget && isConstructing()) 791 *newTarget = parent_s.maybeRead(fallback); 792 } else { 793 // There is no parent frame to this inlined frame, we can read 794 // from the frame's Value vector directly. 795 Value* argv = frame_->actualArgs(); 796 for (unsigned i = nformal; i < nactual; i++) 797 argOp(argv[i]); 798 if (newTarget && isConstructing()) 799 *newTarget = argv[nactual]; 800 } 801 } 802 } 803 804 // At this point we've read all the formals in s, and can read the 805 // locals. 806 for (unsigned i = 0; i < script()->nfixed(); i++) 807 localOp(s.maybeRead(fallback)); 808 } 809 810 template <class Op> unaliasedForEachActual(JSContext * cx,Op op,ReadFrameArgsBehavior behavior,MaybeReadFallback & fallback)811 void unaliasedForEachActual(JSContext* cx, Op op, 812 ReadFrameArgsBehavior behavior, 813 MaybeReadFallback& fallback) const 814 { 815 Nop nop; 816 readFrameArgsAndLocals(cx, op, nop, nullptr, nullptr, nullptr, nullptr, 817 nullptr, nullptr, behavior, fallback); 818 } 819 script()820 JSScript* script() const { 821 return script_; 822 } pc()823 jsbytecode* pc() const { 824 return pc_; 825 } snapshotIterator()826 SnapshotIterator snapshotIterator() const { 827 return si_; 828 } 829 bool isFunctionFrame() const; 830 bool isConstructing() const; 831 scopeChain(MaybeReadFallback & fallback)832 JSObject* scopeChain(MaybeReadFallback& fallback) const { 833 SnapshotIterator s(si_); 834 835 // scopeChain 836 Value v = s.maybeRead(fallback); 837 return computeScopeChain(v, fallback); 838 } 839 thisArgument(MaybeReadFallback & fallback)840 Value thisArgument(MaybeReadFallback& fallback) const { 841 SnapshotIterator s(si_); 842 843 // scopeChain 844 s.skip(); 845 846 // return value 847 s.skip(); 848 849 // Arguments object. 850 if (script()->argumentsHasVarBinding()) 851 s.skip(); 852 853 return s.maybeRead(fallback); 854 } 855 856 InlineFrameIterator& operator++() { 857 findNextFrame(); 858 return *this; 859 } 860 861 void dump() const; 862 863 void resetOn(const JitFrameIterator* iter); 864 frame()865 const JitFrameIterator& frame() const { 866 return *frame_; 867 } 868 869 // Inline frame number, 0 for the outermost (non-inlined) frame. frameNo()870 size_t frameNo() const { 871 return frameCount() - framesRead_; 872 } frameCount()873 size_t frameCount() const { 874 MOZ_ASSERT(frameCount_ != UINT32_MAX); 875 return frameCount_; 876 } 877 878 private: 879 InlineFrameIterator() = delete; 880 InlineFrameIterator(const InlineFrameIterator& iter) = delete; 881 }; 882 883 } // namespace jit 884 } // namespace js 885 886 #endif /* jit_JitFrameIterator_h */ 887