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