| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeLanguagePch.h"
- #include "Language/JavascriptFunctionArgIndex.h"
- #include "Language/InterpreterStackFrame.h"
- #define FAligned(VALUE, TYPE) ((((LONG_PTR)VALUE) & (sizeof(TYPE)-1)) == 0)
- #define AlignIt(VALUE, TYPE) (~(~((LONG_PTR)(VALUE) + (sizeof(TYPE)-1)) | (sizeof(TYPE)-1)))
- namespace Js
- {
- Js::ArgumentsObject * JavascriptCallStackLayout::GetArgumentsObject() const
- {
- return (Js::ArgumentsObject *)((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject];
- }
- Js::Var* JavascriptCallStackLayout::GetArgumentsObjectLocation() const
- {
- return (Js::Var *)&((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject];
- }
- void JavascriptCallStackLayout::SetArgumentsObject(Js::ArgumentsObject * obj)
- {
- ((void **)this)[JavascriptFunctionArgIndex_ArgumentsObject] = obj;
- }
- Js::Var JavascriptCallStackLayout::GetOffset(int offset) const
- {
- Js::Var *varPtr = (Js::Var *)(((char *)this) + offset);
- Assert(FAligned(varPtr, Js::Var));
- return *varPtr;
- }
- double JavascriptCallStackLayout::GetDoubleAtOffset(int offset) const
- {
- double *dblPtr = (double *)(((char *)this) + offset);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag))
- {
- Assert(FAligned(dblPtr, double));
- }
- #endif
- return *dblPtr;
- }
- int32 JavascriptCallStackLayout::GetInt32AtOffset(int offset) const
- {
- int32 *intPtr = (int32 *)(((char *)this) + offset);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.IsEnabled(Js::CheckAlignmentFlag))
- {
- Assert(FAligned(intPtr, int32));
- }
- #endif
- return *intPtr;
- }
- SIMDValue JavascriptCallStackLayout::GetSimdValueAtOffset(int offset) const
- {
- return *((SIMDValue *)(((char *)this) + offset));
- }
- char * JavascriptCallStackLayout::GetValueChangeOffset(int offset) const
- {
- Js::Var *varPtr = (Js::Var *)(((char *)this) + offset);
- Assert(FAligned(varPtr, Js::Var));
- return (char *)varPtr;
- }
- ForInObjectEnumerator * JavascriptCallStackLayout::GetForInObjectEnumeratorArrayAtOffset(int offset) const
- {
- return (ForInObjectEnumerator *)(((char *)this) + offset);
- }
- JavascriptCallStackLayout *JavascriptCallStackLayout::FromFramePointer(void *const framePointer)
- {
- return
- reinterpret_cast<JavascriptCallStackLayout *>(
- static_cast<void **>(framePointer) + (JavascriptFunctionArgIndex_Function - JavascriptFunctionArgIndex_Frame));
- }
- void* const JavascriptCallStackLayout::ToFramePointer(JavascriptCallStackLayout* callstackLayout)
- {
- return
- reinterpret_cast<void * const>(
- reinterpret_cast<void **>(callstackLayout) - (JavascriptFunctionArgIndex_Function - JavascriptFunctionArgIndex_Frame));
- }
- Js::Var* JavascriptCallStackLayout::GetArgv() const
- {
- return const_cast<Js::Var*>(&this->args[0]);
- }
- ScriptContext* JavascriptStackWalker::GetCurrentScriptContext() const
- {
- return this->GetCurrentInterpreterFrame() ? this->GetCurrentInterpreterFrame()->GetScriptContext() : this->scriptContext;
- }
- Var JavascriptStackWalker::GetCurrentArgumentsObject() const
- {
- #if ENABLE_PROFILE_INFO
- if (interpreterFrame)
- #else
- Assert(interpreterFrame);
- #endif
- {
- return interpreterFrame->GetArgumentsObject();
- }
- #if ENABLE_NATIVE_CODEGEN
- else
- {
- if (inlinedFramesBeingWalked)
- {
- return inlinedFrameWalker.GetArgumentsObject();
- }
- else
- {
- return this->GetCurrentNativeArgumentsObject();
- }
- }
- #endif
- }
- void JavascriptStackWalker::SetCurrentArgumentsObject(Var args)
- {
- #if ENABLE_NATIVE_CODEGEN
- if (interpreterFrame)
- #else
- Assert(interpreterFrame);
- #endif
- {
- interpreterFrame->SetArgumentsObject(args);
- }
- #if ENABLE_NATIVE_CODEGEN
- else
- {
- if (inlinedFramesBeingWalked)
- {
- inlinedFrameWalker.SetArgumentsObject(args);
- }
- else
- {
- this->SetCurrentNativeArgumentsObject(args);
- }
- }
- #endif
- }
- Var JavascriptStackWalker::GetPermanentArguments() const
- {
- Assert(IsJavascriptFrame());
- AssertMsg(this->GetCurrentFunction()->IsScriptFunction(), "GetPermanentArguments should not be called for non-script function as there is no slot allocated for it.");
- const uint32 paramCount = GetCallInfo().Count;
- if (paramCount == 0)
- {
- // glob function doesn't allocate ArgumentsObject slot on stack
- return nullptr;
- }
- // Get the heap-allocated args for this frame.
- Var args = this->GetCurrentArgumentsObject();
- if (args && VarIs<ArgumentsObject>(args))
- {
- args = ((ArgumentsObject*)args)->GetHeapArguments();
- }
- return args;
- }
- BOOL JavascriptStackWalker::WalkToArgumentsFrame(ArgumentsObject *args)
- {
- // Move the walker up the stack until we find the given arguments object on the frame.
- while (this->Walk(/*includeInlineFrame*/ true))
- {
- if (this->IsJavascriptFrame())
- {
- Var currArgs = this->GetCurrentArgumentsObject();
- if (currArgs == args)
- {
- return TRUE;
- }
- }
- }
- return FALSE;
- }
- void JavascriptStackWalker::GetThis(Var* pVarThis, int moduleId) const
- {
- #if ENABLE_NATIVE_CODEGEN
- if (inlinedFramesBeingWalked)
- {
- if (inlinedFrameWalker.GetArgc() == 0)
- {
- *pVarThis = JavascriptOperators::OP_GetThis(this->scriptContext->GetLibrary()->GetUndefined(), moduleId, scriptContext);
- }
- else
- {
- *pVarThis = inlinedFrameWalker.GetThisObject();
- Assert(*pVarThis);
- }
- }
- else
- #endif
- {
- const CallInfo callInfo = this->GetCallInfo();
- if (callInfo.Count == 0)
- {
- *pVarThis = JavascriptOperators::OP_GetThis(scriptContext->GetLibrary()->GetUndefined(), moduleId, scriptContext);
- }
- else
- {
- *pVarThis = this->GetThisFromFrame();
- }
- }
- if (*pVarThis == nullptr)
- {
- *pVarThis = this->scriptContext->GetLibrary()->GetNull();
- }
- }
- BOOL IsEval(CallInfo callInfo)
- {
- return (callInfo.Flags & CallFlags_Eval) != 0;
- }
- BOOL JavascriptStackWalker::IsCallerGlobalFunction() const
- {
- const CallInfo callInfo = this->GetCallInfo();
- JavascriptFunction* function = this->GetCurrentFunction();
- if (IsLibraryStackFrameEnabled(this->scriptContext) && !function->IsScriptFunction())
- {
- return false; // native library code can't be global function
- }
- FunctionInfo* funcInfo = function->GetFunctionInfo();
- if (funcInfo->HasParseableInfo())
- {
- return funcInfo->GetParseableFunctionInfo()->GetIsGlobalFunc() || IsEval(callInfo);
- }
- else
- {
- AssertMsg(FALSE, "Here we should only have script functions which were already parsed/deserialized.");
- return callInfo.Count == 0 || IsEval(callInfo);
- }
- }
- BOOL JavascriptStackWalker::IsEvalCaller() const
- {
- const CallInfo callInfo = this->GetCallInfo();
- return (callInfo.Flags & CallFlags_Eval) != 0;
- }
- Var JavascriptStackWalker::GetCurrentNativeArgumentsObject() const
- {
- Assert(this->IsJavascriptFrame() && this->interpreterFrame == nullptr);
- return this->GetCurrentArgv()[JavascriptFunctionArgIndex_ArgumentsObject];
- }
- void JavascriptStackWalker::SetCurrentNativeArgumentsObject(Var args)
- {
- Assert(this->IsJavascriptFrame() && this->interpreterFrame == nullptr);
- this->GetCurrentArgv()[JavascriptFunctionArgIndex_ArgumentsObject] = args;
- }
- Js::Var * JavascriptStackWalker::GetJavascriptArgs(bool boxArgsAndDeepCopy) const
- {
- Assert(this->IsJavascriptFrame());
- #if ENABLE_NATIVE_CODEGEN
- if (inlinedFramesBeingWalked)
- {
- return inlinedFrameWalker.GetArgv(/* includeThis */ false, boxArgsAndDeepCopy);
- }
- else
- #endif
- if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
- {
- JavascriptGenerator* gen = VarTo<JavascriptGenerator>(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
- return gen->GetArguments().Values;
- }
- else
- {
- return &this->GetCurrentArgv()[JavascriptFunctionArgIndex_SecondScriptArg];
- }
- }
- uint32 JavascriptStackWalker::GetByteCodeOffset() const
- {
- uint32 offset = 0;
- if (this->IsJavascriptFrame())
- {
- if (this->interpreterFrame)
- {
- if (this->TryGetByteCodeOffsetFromInterpreterFrame(offset))
- {
- return offset;
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- if (TryGetByteCodeOffsetFromNativeFrame(offset))
- {
- return offset;
- }
- #endif
- }
- return offset;
- }
- bool JavascriptStackWalker::TryGetByteCodeOffsetFromInterpreterFrame(uint32& offset) const
- {
- #if ENABLE_NATIVE_CODEGEN
- if (this->lastInternalFrameInfo.codeAddress != nullptr)
- {
- return false;
- }
- #endif
- offset = this->interpreterFrame->GetReader()->GetCurrentOffset();
- if (offset == 0)
- {
- // This will be the case when we are broken on the debugger on very first statement (due to async break).
- // Or the interpreter loop can throw OOS on entrance before executing bytecode.
- }
- else
- {
- // Note : For many cases, we move the m_currentLocation of ByteCodeReader already to next available opcode.
- // This could create problem in binding the exception to proper line offset.
- // Reducing by 1 will make sure the current offset falls under, current executing opcode.
- offset--;
- }
- return true;
- }
- #if ENABLE_NATIVE_CODEGEN
- bool JavascriptStackWalker::TryGetByteCodeOffsetFromNativeFrame(uint32& offset) const
- {
- DWORD_PTR pCodeAddr;
- if (this->lastInternalFrameInfo.codeAddress != nullptr)
- {
- pCodeAddr = (DWORD_PTR)this->lastInternalFrameInfo.codeAddress;
- }
- else
- {
- pCodeAddr = (DWORD_PTR)this->GetCurrentCodeAddr();
- }
- // If the current instruction's return address is the beginning of the next statement then we will show error for the next line, which would be completely wrong.
- // The quick fix would be to look the address which is at least lesser than current return address.
- // Assert to verify at what places this can happen.
- Assert(pCodeAddr);
- if (pCodeAddr)
- {
- #if defined(_M_ARM)
- // Note that DWORD_PTR is not actually a pointer type (!) but is simple unsigned long/__int64 (see BaseTsd.h).
- // Thus, decrement would be by 1 byte and not 4 bytes as in pointer arithmetic. That's exactly what we need.
- // For ARM the 'return address' is always odd and is 'next instr addr' + 1 byte, so to get to the BLX instr, we need to subtract 2 bytes from it.
- AssertMsg(pCodeAddr % 2 == 1, "Got even number for pCodeAddr! It's expected to be return address, which should be odd.");
- pCodeAddr--;
- #endif
- pCodeAddr--;
- }
- bool usedInternalFrameInfo = false;
- uint loopNum = GetLoopNumber(usedInternalFrameInfo);
- JavascriptFunction *function = nullptr;
- FunctionBody *inlinee = nullptr;
- function = usedInternalFrameInfo ? this->GetCachedInternalFrameInfo().function : this->GetCurrentFunctionFromPhysicalFrame();
- // If there are inlined frames on the stack, we have to be able to return the byte code offsets of those inlined calls
- // from their respective callers. But, we can use the current native address as IP for only the topmost inlined frame.
- // TryGetByteCodeOffsetOfInlinee takes care of these conditions and sets up the offset of an inlinee in 'offset', if the
- // current inlinee frame is not the topmost of the inlinee frames.
- if (HasInlinedFramesOnStack() && TryGetByteCodeOffsetOfInlinee(function, loopNum, pCodeAddr, &inlinee, offset, usedInternalFrameInfo))
- {
- return true;
- }
- StatementData data;
- if (function->GetFunctionBody() && function->GetFunctionBody()->GetMatchingStatementMapFromNativeAddress(pCodeAddr, data, loopNum, inlinee))
- {
- offset = data.bytecodeBegin;
- return true;
- }
- return false;
- }
- uint JavascriptStackWalker::GetLoopNumber(bool& usedInternalFrameInfo) const
- {
- uint loopNum = LoopHeader::NoLoop;
- if (this->lastInternalFrameInfo.codeAddress != nullptr)
- {
- if (this->lastInternalFrameInfo.frameType == InternalFrameType_LoopBody)
- {
- AnalysisAssert(this->interpreterFrame);
- loopNum = this->interpreterFrame->GetCurrentLoopNum();
- Assert(loopNum != LoopHeader::NoLoop);
- usedInternalFrameInfo = true;
- }
- }
- else
- {
- if (this->IsCurrentPhysicalFrameForLoopBody())
- {
- // Internal frame but codeAddress on lastInternalFrameInfo not set. We must be in an inlined frame in the loop body.
- Assert(this->tempInterpreterFrame);
- loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
- Assert(loopNum != LoopHeader::NoLoop);
- usedInternalFrameInfo = false;
- }
- }
- return loopNum;
- }
- bool JavascriptStackWalker::TryGetByteCodeOffsetOfInlinee(Js::JavascriptFunction* parentFunction, uint loopNum, DWORD_PTR pCodeAddr, Js::FunctionBody** inlinee, uint32& offset, bool useInternalFrameInfo) const
- {
- // For inlined frames, translation from native offset -> source code happens in two steps.
- // The native offset is first translated into a statement index using the physical frame's
- // source context info. This statement index is then looked up in the *inlinee*'s source
- // context info to get the bytecode offset.
- //
- // For all inlined frames contained within a physical frame we have only one offset == (IP - entry).
- // Since we can't use that to get the other inlined callers' IPs, we save the IP of all inlined
- // callers in their "callinfo" (See InlineeCallInfo). The top most inlined frame uses the IP
- // of the physical frame. All other inlined frames use the InlineeStartOffset stored in their call info
- // to calculate the byte code offset of the callsite of the inlinee they called.
- StatementData data;
- uint32 inlineeOffset = 0;
- *inlinee = InlinedFramesBeingWalked() ? inlinedFrameWalker.GetFunctionObject()->GetFunctionBody() : nullptr;
- InlinedFrameWalker tmpFrameWalker;
- if (InlinedFramesBeingWalked())
- {
- // Inlined frames are being walked right now. The top most frame is where the IP is.
- if (!inlinedFrameWalker.IsTopMostFrame())
- {
- inlineeOffset = inlinedFrameWalker.GetCurrentInlineeOffset();
- }
- }
- else if (ScriptFunction::Test(parentFunction) && HasInlinedFramesOnStack())
- {
- // Inlined frames are not being walked right now. However, if there
- // are inlined frames on the stack the InlineeCallInfo of the first inlined frame
- // has the native offset of the current physical frame.
- Assert(!*inlinee);
- InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, VarTo<ScriptFunction>(parentFunction), PreviousInterpreterFrameIsFromBailout(), loopNum, this, useInternalFrameInfo, false /*noAlloc*/);
- inlineeOffset = tmpFrameWalker.GetBottomMostInlineeOffset();
- tmpFrameWalker.Close();
- }
- if (inlineeOffset != 0 &&
- parentFunction->GetFunctionBody()->GetMatchingStatementMapFromNativeOffset(pCodeAddr, inlineeOffset, data, loopNum, *inlinee))
- {
- offset = data.bytecodeBegin;
- return true;
- }
- return false;
- }
- bool JavascriptStackWalker::PreviousInterpreterFrameIsFromBailout() const
- {
- if (lastInternalFrameInfo.codeAddress)
- {
- return lastInternalFrameInfo.previousInterpreterFrameIsFromBailout;
- }
- return this->previousInterpreterFrameIsFromBailout;
- }
- bool JavascriptStackWalker::InlinedFramesBeingWalked() const
- {
- if (lastInternalFrameInfo.codeAddress)
- {
- return false;
- }
- return this->inlinedFramesBeingWalked;
- }
- bool JavascriptStackWalker::HasInlinedFramesOnStack() const
- {
- if (lastInternalFrameInfo.codeAddress)
- {
- return lastInternalFrameInfo.hasInlinedFramesOnStack;
- }
- return this->hasInlinedFramesOnStack;
- }
- #endif
- bool JavascriptStackWalker::GetSourcePosition(const WCHAR** sourceFileName, ULONG* line, LONG* column)
- {
- uint byteCodeoffset = this->GetByteCodeOffset();
- if(byteCodeoffset)
- {
- Js::FunctionBody* functionBody = this->GetCurrentFunction()->GetFunctionBody();
- if (functionBody->GetLineCharOffset(byteCodeoffset, line, column))
- {
- if(functionBody->GetUtf8SourceInfo()->IsDynamic())
- {
- *sourceFileName = _u("Dynamic Code");
- }
- else
- {
- *sourceFileName = functionBody->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->url;
- }
- return true;
- }
- }
- return false;
- }
- Js::JavascriptFunction * JavascriptStackWalker::UpdateFrame(bool includeInlineFrames)
- {
- this->isJavascriptFrame = this->CheckJavascriptFrame(includeInlineFrames);
- if (this->IsJavascriptFrame())
- {
- // In case we have a cross site thunk, update the script context
- Js::JavascriptFunction *function = this->GetCurrentFunction();
- #if ENABLE_NATIVE_CODEGEN
- bool isCurrentPhysicalFrameForLoopBody = this->IsCurrentPhysicalFrameForLoopBody();
- #endif
- if (this->interpreterFrame)
- {
- #if ENABLE_NATIVE_CODEGEN
- if (lastInternalFrameInfo.codeAddress != nullptr)
- {
- this->previousInterpreterFrameIsForLoopBody = true;
- }
- #endif
- // We might've bailed out of an inlinee, so check if there were any inlinees.
- if (this->interpreterFrame->TestFlags(InterpreterStackFrameFlags_FromBailOut))
- {
- previousInterpreterFrameIsFromBailout = true;
- #if ENABLE_NATIVE_CODEGEN
- Assert(!inlinedFramesBeingWalked);
- if (includeInlineFrames)
- {
- int loopNum = -1;
- if (isCurrentPhysicalFrameForLoopBody)
- {
- loopNum = this->tempInterpreterFrame->GetCurrentLoopNum();
- }
- bool hasInlinedFramesOnStack = InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame,
- VarTo<ScriptFunction>(function), true /*fromBailout*/, loopNum, this, false /*useInternalFrameInfo*/, false /*noAlloc*/);
- if (hasInlinedFramesOnStack)
- {
- // We're now back in the state where currentFrame == physical frame of the inliner, but
- // since interpreterFrame != null, we'll pick values from the interpreterFrame (the bailout
- // frame of the inliner). Set a flag to tell the stack walker that it needs to start from the
- // inlinee frames on the stack when Walk() is called.
- this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
- this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
- Assert(inlinedFramesBeingWalked);
- Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == inlinedFrameWalker.GetFunctionObject());
- }
- else
- {
- Assert(!isCurrentPhysicalFrameForLoopBody);
- }
- }
- else if (isCurrentPhysicalFrameForLoopBody)
- {
- // Getting here is only possible when the current interpreterFrame is for a function which
- // encountered a bailout after getting inlined in a jitted loop body. If we are not including
- // inlined frames in the stack walk, we need to set the codeAddress on lastInternalFrameInfo,
- // which would have otherwise been set upon closing the inlinedFrameWalker, now.
- // Note that we already have an assert in CheckJavascriptFrame to ensure this.
- SetCachedInternalFrameInfo(InternalFrameType_LoopBody, function, false /*hasInlinedFramesOnStack*/, true /*previousInterpreterFrameIsFromBailout*/);
- }
- #else
- // How did we bail out when JIT was disabled?
- Assert(false);
- #endif
- }
- else
- {
- Assert(StackScriptFunction::GetCurrentFunctionObject(this->interpreterFrame->GetJavascriptFunction()) == function);
- previousInterpreterFrameIsFromBailout = false;
- }
- }
- else if (!this->isNativeLibraryFrame)
- {
- #if ENABLE_NATIVE_CODEGEN
- Assert(!HasInlinedFramesOnStack() || (includeInlineFrames && isCurrentPhysicalFrameForLoopBody));
- if (!HasInlinedFramesOnStack() && includeInlineFrames)
- {
- // Check whether there are inlined frames nested in this native frame. The corresponding check for
- // a jitted loop body frame should have been done in CheckJavascriptFrame
- Assert(lastInternalFrameInfo.codeAddress == nullptr);
- bool inlinedFramesFound = InlinedFrameWalker::FromPhysicalFrame(
- inlinedFrameWalker,
- currentFrame,
- VarTo<ScriptFunction>(function),
- false, // fromBailout
- -1, // loopNum
- nullptr,// walker
- false, // useInternalFrameInfo
- false // noAlloc
- );
- if (inlinedFramesFound)
- {
- this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
- this->hasInlinedFramesOnStack = true;
- Assert(inlinedFramesBeingWalked);
- }
- }
- #endif
- }
- this->scriptContext = function->GetScriptContext();
- return function;
- }
- return nullptr;
- }
- #if ENABLE_NATIVE_CODEGEN
- void JavascriptStackWalker::WalkAndClearInlineeFrameCallInfoOnException(void *tryHandlerAddrOfReturnAddr)
- {
- // Walk the stack and when we find the first native frame, we clear the inlinee's callinfo for this frame
- // It is sufficient we stop at the first native frame which had the enclosing try-catch
- // TODO : Revisit when we start inlining functions with try-catch/try-finally
- while (this->Walk(true))
- {
- if (JavascriptFunction::IsNativeAddress(this->scriptContext, this->currentFrame.GetInstructionPointer()))
- {
- if (HasInlinedFramesOnStack())
- {
- for (int index = inlinedFrameWalker.GetFrameCount() - 1; index >= 0; index--)
- {
- auto inlinedFrame = inlinedFrameWalker.GetFrameAtIndex(index);
- inlinedFrame->callInfo.Clear();
- }
- }
- }
- if (this->currentFrame.GetAddressOfReturnAddress() == tryHandlerAddrOfReturnAddr)
- {
- break;
- }
- }
- }
- #endif
- // Note: noinline is to make sure that when we unwind to the unwindToAddress, there is at least one frame to unwind.
- _NOINLINE
- JavascriptStackWalker::JavascriptStackWalker(ScriptContext * scriptContext, bool useEERContext, PVOID returnAddress, bool _forceFullWalk /*=false*/) :
- inlinedFrameCallInfo(CallFlags_None, 0), shouldDetectPartiallyInitializedInterpreterFrame(true), forceFullWalk(_forceFullWalk),
- previousInterpreterFrameIsFromBailout(false), previousInterpreterFrameIsForLoopBody(false), hasInlinedFramesOnStack(false)
- {
- if (scriptContext == NULL)
- {
- Throw::InternalError();
- }
- this->scriptContext = scriptContext;
- // Pull the current script state from the thread context.
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- this->entryExitRecord = threadContext->GetScriptEntryExit();
- this->nativeLibraryEntry = threadContext->PeekNativeLibraryEntry();
- this->prevNativeLibraryEntry = nullptr;
- this->interpreterFrame = NULL;
- this->isJavascriptFrame = false;
- this->isNativeLibraryFrame = false;
- if (entryExitRecord->frameIdOfScriptExitFunction != NULL)
- {
- // We're currently outside the script, so grab the frame from which we left.
- this->scriptContext = entryExitRecord->scriptContext;
- this->isInitialFrame = this->currentFrame.InitializeByFrameId(entryExitRecord->frameIdOfScriptExitFunction, this->scriptContext);
- }
- else
- {
- // Just start with the caller
- this->isInitialFrame = this->currentFrame.InitializeByReturnAddress(_ReturnAddress(), this->scriptContext);
- }
- if (useEERContext)
- {
- this->tempInterpreterFrame = this->scriptContext->GetThreadContext()->GetLeafInterpreterFrame();
- }
- else
- {
- // We need to generate stack for the passed script context, so use the leaf interpreter frame for passed script context
- this->tempInterpreterFrame = scriptContext->GetThreadContext()->GetLeafInterpreterFrame();
- }
- inlinedFramesBeingWalked = false;
- }
- BOOL JavascriptStackWalker::Walk(bool includeInlineFrames)
- {
- // Walk one frame up the call stack.
- this->interpreterFrame = NULL;
- #if ENABLE_NATIVE_CODEGEN
- if (lastInternalFrameInfo.codeAddress != nullptr && this->previousInterpreterFrameIsForLoopBody)
- {
- this->previousInterpreterFrameIsForLoopBody = false;
- ClearCachedInternalFrameInfo();
- }
- if (inlinedFramesBeingWalked)
- {
- Assert(includeInlineFrames);
- inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
- if (!inlinedFramesBeingWalked)
- {
- inlinedFrameWalker.Close();
- if ((this->IsCurrentPhysicalFrameForLoopBody()))
- {
- // Done walking inlined frames in a loop body, cache the native code address now
- // in order to skip the loop body frame.
- this->SetCachedInternalFrameInfo(InternalFrameType_LoopBody, this->GetCurrentFunctionFromPhysicalFrame(), true /*hasInlinedFramesOnStack*/, previousInterpreterFrameIsFromBailout);
- isJavascriptFrame = false;
- }
- }
- return true;
- }
- #endif
- this->hasInlinedFramesOnStack = false;
- if (this->isInitialFrame)
- {
- this->isInitialFrame = false; // Only walk initial frame once
- }
- else if (!this->currentFrame.Next())
- {
- this->isJavascriptFrame = false;
- return false;
- }
- // If we're at the entry from a host frame, hop to the frame from which we left the script.
- if (AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfInstructionPointer(), this->entryExitRecord->addrOfReturnAddrOfScriptEntryFunction))
- {
- BOOL hasCaller = this->entryExitRecord->hasCaller || this->forceFullWalk;
- #ifdef CHECK_STACKWALK_EXCEPTION
- BOOL ignoreStackWalkException = this->entryExitRecord->ignoreStackWalkException;
- #endif
- this->entryExitRecord = this->entryExitRecord->next;
- if (this->entryExitRecord == NULL)
- {
- this->isJavascriptFrame = false;
- return false;
- }
- if (!hasCaller && !this->scriptContext->IsDiagnosticsScriptContext())
- {
- #ifdef CHECK_STACKWALK_EXCEPTION
- if (!ignoreStackWalkException)
- {
- AssertMsg(false, "walk pass no caller frame");
- }
- #endif
- this->isJavascriptFrame = false;
- return false;
- }
- this->scriptContext = this->entryExitRecord->scriptContext;
- this->currentFrame.SkipToFrame(this->entryExitRecord->frameIdOfScriptExitFunction);
- }
- this->UpdateFrame(includeInlineFrames);
- return true;
- }
- BOOL JavascriptStackWalker::GetCallerWithoutInlinedFrames(_Out_opt_ JavascriptFunction ** ppFunc)
- {
- return GetCaller(ppFunc, /*includeInlineFrames*/ false);
- }
- BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction ** ppFunc, bool includeInlineFrames)
- {
- while (this->Walk(includeInlineFrames))
- {
- if (this->IsJavascriptFrame())
- {
- Assert(entryExitRecord != NULL);
- if (ppFunc)
- {
- *ppFunc = this->GetCurrentFunction();
- }
- AssertMsg(!this->shouldDetectPartiallyInitializedInterpreterFrame, "must have skipped first frame if needed");
- return true;
- }
- }
- if (ppFunc)
- {
- *ppFunc = nullptr;
- }
- return false;
- }
- BOOL JavascriptStackWalker::GetNonLibraryCodeCaller(_Out_opt_ JavascriptFunction ** ppFunc)
- {
- while (this->GetCaller(ppFunc))
- {
- Assert(ppFunc != nullptr);
- __analysis_assume(ppFunc != nullptr);
- if (!(*ppFunc)->IsLibraryCode())
- {
- return true;
- }
- }
- return false;
- }
- /*static*/
- bool JavascriptStackWalker::IsLibraryStackFrameEnabled(Js::ScriptContext * scriptContext)
- {
- Assert(scriptContext != nullptr);
- return CONFIG_FLAG(LibraryStackFrame);
- }
- // Check if a function is a display caller: user code, or native library / boundary script library code
- bool JavascriptStackWalker::IsDisplayCaller(JavascriptFunction* func)
- {
- FunctionBody* body = func->GetFunctionBody();
- if (IsLibraryStackFrameEnabled(func->GetScriptContext()))
- {
- return !func->IsScriptFunction() || !body->GetUtf8SourceInfo()->GetIsLibraryCode() || body->IsPublicLibraryCode();
- }
- else
- {
- return !body->GetUtf8SourceInfo()->GetIsLibraryCode();
- }
- }
- bool JavascriptStackWalker::GetDisplayCaller(_Out_opt_ JavascriptFunction ** ppFunc)
- {
- while (this->GetCaller(ppFunc))
- {
- Assert(ppFunc != nullptr);
- __analysis_assume(ppFunc != nullptr);
- if (IsDisplayCaller(*ppFunc))
- {
- return true;
- }
- }
- return false;
- }
- PCWSTR JavascriptStackWalker::GetCurrentNativeLibraryEntryName() const
- {
- Assert(IsLibraryStackFrameEnabled(this->scriptContext)
- && this->prevNativeLibraryEntry
- && this->prevNativeLibraryEntry->next == this->nativeLibraryEntry);
- return this->prevNativeLibraryEntry->name;
- }
- // WalkToTarget skips internal frames
- BOOL JavascriptStackWalker::WalkToTarget(JavascriptFunction * funcTarget)
- {
- // Walk up the call stack until we find the frame that belongs to the given function.
- while (this->Walk(/*includeInlineFrames*/ true))
- {
- if (this->IsJavascriptFrame() && this->GetCurrentFunction() == funcTarget)
- {
- // Skip internal names
- Assert( !(this->GetCallInfo().Flags & CallFlags_InternalFrame) );
- return true;
- }
- }
- return false;
- }
- bool JavascriptStackWalker::AlignAndCheckAddressOfReturnAddressMatch(void* addressOfReturnAddress, void* nativeLibraryEntryAddress)
- {
- return addressOfReturnAddress == nativeLibraryEntryAddress
- #if defined(_M_IX86)
- // Under some odd cases on x86, addressOfReturnAddress and stashed entry address need to be aligned.
- // This happens when code is generated using two stack pointers. One or both have the address of
- // return address offset by 4, 8, or 12.
- || (((uint)nativeLibraryEntryAddress - (uint)addressOfReturnAddress < 0x10) &&
- *(void**)addressOfReturnAddress == *(void**)nativeLibraryEntryAddress
- )
- #endif
- ;
- }
- void ** JavascriptStackWalker::GetCurrentArgv() const
- {
- Assert(this->IsJavascriptFrame());
- Assert(this->interpreterFrame != nullptr ||
- (this->prevNativeLibraryEntry && AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfReturnAddress(), this->prevNativeLibraryEntry->addr)) ||
- JavascriptFunction::IsNativeAddress(this->scriptContext, (void*)this->currentFrame.GetInstructionPointer()));
- bool isNativeAddr = (this->interpreterFrame == nullptr) &&
- (!this->prevNativeLibraryEntry || !AlignAndCheckAddressOfReturnAddressMatch(this->currentFrame.GetAddressOfReturnAddress(), this->prevNativeLibraryEntry->addr));
- void ** argv = currentFrame.GetArgv(isNativeAddr, false /*shouldCheckForNativeAddr*/);
- Assert(argv);
- return argv;
- }
- bool JavascriptStackWalker::CheckJavascriptFrame(bool includeInlineFrames)
- {
- this->isNativeLibraryFrame = false; // Clear previous result
- void * codeAddr = this->currentFrame.GetInstructionPointer();
- if (this->tempInterpreterFrame && codeAddr == this->tempInterpreterFrame->GetReturnAddress())
- {
- bool isBailoutInterpreter = this->tempInterpreterFrame->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut);
- // We need to skip over the first interpreter frame on the stack if it is the partially initialized frame
- // otherwise it is a real frame and we should continue.
- // For fully initialized frames (PushPopHelper was called) the thunk stack addr is equal or below addressOfReturnAddress
- // as the latter one is obtained in InterpreterStackFrame::InterpreterThunk called by the thunk.
- bool isPartiallyInitializedFrame = this->shouldDetectPartiallyInitializedInterpreterFrame &&
- this->currentFrame.GetAddressOfReturnAddress(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/) < this->tempInterpreterFrame->GetAddressOfReturnAddress();
- this->shouldDetectPartiallyInitializedInterpreterFrame = false;
- if (isPartiallyInitializedFrame)
- {
- return false; // Skip it.
- }
- void ** argv = this->currentFrame.GetArgv(isBailoutInterpreter /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
- if (argv == nullptr)
- {
- // NOTE: When we switch to walking the stack ourselves and skip non engine frames, this should never happen.
- return false;
- }
- this->interpreterFrame = this->tempInterpreterFrame;
- this->tempInterpreterFrame = this->interpreterFrame->GetPreviousFrame();
- #if ENABLE_NATIVE_CODEGEN
- #if DBG
- if (((CallInfo const *)&argv[JavascriptFunctionArgIndex_CallInfo])->Flags & CallFlags_InternalFrame)
- {
- // The return address of the interpreterFrame is the same as the entryPoint for a jitted loop body.
- // This can only ever happen when we have bailed out from a function inlined in the loop body. We
- // wouldn't have created a new interpreterFrame if the bailout were from the loop body itself.
- Assert(this->interpreterFrame->TestFlags(Js::InterpreterStackFrameFlags_FromBailOut));
- InlinedFrameWalker tmpFrameWalker;
- Assert(InlinedFrameWalker::FromPhysicalFrame(tmpFrameWalker, currentFrame, Js::VarTo<Js::ScriptFunction>(argv[JavascriptFunctionArgIndex_Function]),
- true /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, true /*noAlloc*/));
- tmpFrameWalker.Close();
- }
- #endif //DBG
- #endif //ENABLE_NATIVE_CODEGEN
- return true;
- }
- if (IsLibraryStackFrameEnabled(this->scriptContext) && this->nativeLibraryEntry)
- {
- void* addressOfReturnAddress = this->currentFrame.GetAddressOfReturnAddress();
- void* nativeLibraryEntryAddress = this->nativeLibraryEntry->addr;
- AssertMsg(addressOfReturnAddress <= nativeLibraryEntryAddress, "Missed matching native library entry?");
- if (AlignAndCheckAddressOfReturnAddressMatch(addressOfReturnAddress, nativeLibraryEntryAddress))
- {
- this->isNativeLibraryFrame = true;
- this->shouldDetectPartiallyInitializedInterpreterFrame = false;
- this->prevNativeLibraryEntry = this->nativeLibraryEntry; // Saves match in prevNativeLibraryEntry
- this->nativeLibraryEntry = this->nativeLibraryEntry->next;
- return true;
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- BOOL isNativeAddr = JavascriptFunction::IsNativeAddress(this->scriptContext, codeAddr);
- if (isNativeAddr)
- {
- this->shouldDetectPartiallyInitializedInterpreterFrame = false;
- void ** argv = this->currentFrame.GetArgv(true /*isCurrentContextNative*/, false /*shouldCheckForNativeAddr*/);
- if (argv == nullptr)
- {
- // NOTE: When we switch to walking the stack ourselves and skip non engine frames, this should never happen.
- return false;
- }
- ScriptFunction* funcObj = Js::VarTo<Js::ScriptFunction>(argv[JavascriptFunctionArgIndex_Function]);
- if (funcObj->GetFunctionBody()->GetIsAsmjsMode())
- {
- return false;
- }
- // Note: this check has to happen after asm.js check, because call info is not valid for asm.js
- if (((CallInfo const *)&argv[JavascriptFunctionArgIndex_CallInfo])->Flags & CallFlags_InternalFrame)
- {
- if (includeInlineFrames &&
- InlinedFrameWalker::FromPhysicalFrame(inlinedFrameWalker, currentFrame, Js::VarTo<Js::ScriptFunction>(argv[JavascriptFunctionArgIndex_Function]),
- false /*fromBailout*/, this->tempInterpreterFrame->GetCurrentLoopNum(), this, false /*useInternalFrameInfo*/, false /*noAlloc*/))
- {
- // Found inlined frames in a jitted loop body. We dont want to skip the inlined frames; walk all of them before setting codeAddress on lastInternalFrameInfo.
- // DeepCopy here because, if there is an inlinee in a loop body, FromPhysicalFrame won't be called from UpdateFrame
- this->inlinedFramesBeingWalked = inlinedFrameWalker.Next(inlinedFrameCallInfo);
- this->hasInlinedFramesOnStack = true;
- Assert(inlinedFramesBeingWalked);
- return true;
- }
- SetCachedInternalFrameInfo(InternalFrameType_LoopBody, funcObj, false /*hasInlinedFramesOnStack*/, previousInterpreterFrameIsFromBailout);
- return false;
- }
- return true;
- }
- #endif
- return false;
- }
- void * JavascriptStackWalker::GetCurrentCodeAddr() const
- {
- return this->currentFrame.GetInstructionPointer();
- }
- JavascriptFunction * JavascriptStackWalker::GetCurrentFunction(bool includeInlinedFrames /* = true */) const
- {
- Assert(this->IsJavascriptFrame());
- #if ENABLE_NATIVE_CODEGEN
- if (includeInlinedFrames && inlinedFramesBeingWalked)
- {
- return inlinedFrameWalker.GetFunctionObject();
- }
- else
- #endif
- if (this->isNativeLibraryFrame)
- {
- // Return saved function. Do not read from stack as compiler may stackpack/optimize args.
- return VarTo<JavascriptFunction>(this->prevNativeLibraryEntry->function);
- }
- else
- {
- return StackScriptFunction::GetCurrentFunctionObject((JavascriptFunction *)this->GetCurrentArgv()[JavascriptFunctionArgIndex_Function]);
- }
- }
- void JavascriptStackWalker::SetCurrentFunction(JavascriptFunction * function)
- {
- Assert(this->IsJavascriptFrame());
- #if ENABLE_NATIVE_CODEGEN
- if (inlinedFramesBeingWalked)
- {
- inlinedFrameWalker.SetFunctionObject(function);
- }
- else
- #endif
- {
- this->GetCurrentArgv()[JavascriptFunctionArgIndex_Function] = function;
- }
- }
- JavascriptFunction *JavascriptStackWalker::GetCurrentFunctionFromPhysicalFrame() const
- {
- return GetCurrentFunction(false);
- }
- CallInfo JavascriptStackWalker::GetCallInfo(bool includeInlinedFrames /* = true */) const
- {
- Assert(this->IsJavascriptFrame());
- CallInfo callInfo;
- if (includeInlinedFrames && inlinedFramesBeingWalked)
- {
- // Since we don't support inlining constructors yet, its questionable if we should handle the
- // hidden frame display here?
- callInfo = inlinedFrameCallInfo;
- }
- else if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
- {
- JavascriptGenerator* gen = VarTo<JavascriptGenerator>(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
- callInfo = gen->GetArguments().Info;
- }
- else if (this->isNativeLibraryFrame)
- {
- // Return saved callInfo. Do not read from stack as compiler may stackpack/optimize args.
- callInfo = this->prevNativeLibraryEntry->callInfo;
- }
- else
- {
- callInfo = *(CallInfo const *)&this->GetCurrentArgv()[JavascriptFunctionArgIndex_CallInfo];
- }
- if (callInfo.Flags & Js::CallFlags_ExtraArg)
- {
- callInfo.Flags = (CallFlags)(callInfo.Flags & ~Js::CallFlags_ExtraArg);
- }
- return callInfo;
- }
- CallInfo JavascriptStackWalker::GetCallInfoFromPhysicalFrame() const
- {
- return GetCallInfo(false);
- }
- Var JavascriptStackWalker::GetThisFromFrame() const
- {
- Assert(!inlinedFramesBeingWalked);
- Assert(this->IsJavascriptFrame());
- if (this->GetCurrentFunction()->GetFunctionInfo()->IsCoroutine())
- {
- JavascriptGenerator* gen = VarTo<JavascriptGenerator>(this->GetCurrentArgv()[JavascriptFunctionArgIndex_This]);
- return gen->GetArguments()[0];
- }
- return this->GetCurrentArgv()[JavascriptFunctionArgIndex_This];
- }
- #if ENABLE_NATIVE_CODEGEN
- void JavascriptStackWalker::ClearCachedInternalFrameInfo()
- {
- this->lastInternalFrameInfo.Clear();
- }
- void JavascriptStackWalker::SetCachedInternalFrameInfo(InternalFrameType frameType, JavascriptFunction* function, bool hasInlinedFramesOnStack, bool previousInterpreterFrameIsFromBailout)
- {
- if (!this->lastInternalFrameInfo.codeAddress)
- {
- this->lastInternalFrameInfo.Set(
- this->GetCurrentCodeAddr(),
- this->currentFrame.GetFrame(),
- this->currentFrame.GetStackCheckCodeHeight(),
- frameType,
- function,
- hasInlinedFramesOnStack,
- previousInterpreterFrameIsFromBailout);
- }
- }
- #endif
- bool JavascriptStackWalker::IsCurrentPhysicalFrameForLoopBody() const
- {
- return !!(this->GetCallInfoFromPhysicalFrame().Flags & CallFlags_InternalFrame);
- }
- bool JavascriptStackWalker::IsWalkable(ScriptContext *scriptContext)
- {
- if (scriptContext == NULL)
- {
- return false;
- }
- ThreadContext *threadContext = scriptContext->GetThreadContext();
- if (threadContext == NULL)
- {
- return false;
- }
- return (threadContext->GetScriptEntryExit() != NULL);
- }
- BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, ScriptContext* scriptContext)
- {
- if (!IsWalkable(scriptContext))
- {
- if (ppFunc)
- {
- *ppFunc = nullptr;
- }
- return FALSE;
- }
- JavascriptStackWalker walker(scriptContext);
- return walker.GetCaller(ppFunc);
- }
- BOOL JavascriptStackWalker::GetCaller(_Out_opt_ JavascriptFunction** ppFunc, uint32* byteCodeOffset, ScriptContext* scriptContext)
- {
- JavascriptStackWalker walker(scriptContext);
- if (walker.GetCaller(ppFunc))
- {
- *byteCodeOffset = walker.GetByteCodeOffset();
- return TRUE;
- }
- return FALSE;
- }
- void JavascriptStackWalker::GetThis(Var* pThis, int moduleId, ScriptContext* scriptContext)
- {
- JavascriptStackWalker walker(scriptContext);
- JavascriptFunction* caller;
- if (walker.GetCaller(&caller))
- {
- walker.GetThis(pThis, moduleId);
- }
- }
- void JavascriptStackWalker::GetThis(Var* pThis, int moduleId, JavascriptFunction* func, ScriptContext* scriptContext)
- {
- JavascriptStackWalker walker(scriptContext);
- JavascriptFunction* caller;
- while (walker.GetCaller(&caller))
- {
- if (caller == func)
- {
- walker.GetThis(pThis, moduleId);
- return;
- }
- }
- }
- // Try to see whether there is a top-most javascript frame, and if there is return true if it's native.
- // Returns true if top most frame is javascript frame, in this case the isNative parameter receives true
- // when top-most frame is native, false otherwise.
- // Returns false if top most frame is not a JavaScript frame.
- /* static */
- bool JavascriptStackWalker::TryIsTopJavaScriptFrameNative(ScriptContext* scriptContext, bool* isNative, bool ignoreLibraryCode /* = false */)
- {
- Assert(scriptContext);
- Assert(isNative);
- Js::JavascriptFunction* caller;
- Js::JavascriptStackWalker walker(scriptContext);
- BOOL isSuccess;
- if (ignoreLibraryCode)
- {
- isSuccess = walker.GetNonLibraryCodeCaller(&caller);
- }
- else
- {
- isSuccess = walker.GetCaller(&caller);
- }
- if (isSuccess)
- {
- *isNative = (walker.GetCurrentInterpreterFrame() == NULL);
- return true;
- }
- return false;
- }
- #if ENABLE_NATIVE_CODEGEN
- bool InlinedFrameWalker::FromPhysicalFrame(InlinedFrameWalker& self, StackFrame& physicalFrame, Js::ScriptFunction *parent, bool fromBailout,
- int loopNum, const JavascriptStackWalker * const stackWalker, bool useInternalFrameInfo, bool noAlloc)
- {
- bool inlinedFramesFound = false;
- FunctionBody* parentFunctionBody = parent->GetFunctionBody();
- EntryPointInfo *entryPointInfo;
- if (loopNum != -1)
- {
- Assert(stackWalker);
- }
- void *nativeCodeAddress = nullptr;
- void *framePointer = nullptr;
- if (loopNum != -1 && useInternalFrameInfo)
- {
- Assert(stackWalker->GetCachedInternalFrameInfo().codeAddress != nullptr);
- InternalFrameInfo lastInternalFrameInfo = stackWalker->GetCachedInternalFrameInfo();
- nativeCodeAddress = lastInternalFrameInfo.codeAddress;
- framePointer = lastInternalFrameInfo.framePointer;
- }
- else
- {
- nativeCodeAddress = physicalFrame.GetInstructionPointer();
- framePointer = physicalFrame.GetFrame();
- }
- if (loopNum != -1)
- {
- entryPointInfo = (Js::EntryPointInfo*)parentFunctionBody->GetLoopEntryPointInfoFromNativeAddress((DWORD_PTR)nativeCodeAddress, loopNum);
- }
- else
- {
- entryPointInfo = (Js::EntryPointInfo*)parentFunctionBody->GetEntryPointFromNativeAddress((DWORD_PTR)nativeCodeAddress);
- }
- AssertMsg(entryPointInfo != nullptr, "Inlined frame should resolve to the right parent address");
- if (entryPointInfo->HasInlinees())
- {
- void *entry = reinterpret_cast<void*>(entryPointInfo->GetNativeAddress());
- InlinedFrameWalker::InlinedFrame *outerMostFrame = InlinedFrame::FromPhysicalFrame(physicalFrame, stackWalker, entry, entryPointInfo, useInternalFrameInfo);
- if (!outerMostFrame)
- {
- return inlinedFramesFound;
- }
- if (!fromBailout)
- {
- InlineeFrameRecord* record = entryPointInfo->FindInlineeFrame((void*)nativeCodeAddress);
- if (record)
- {
- record->RestoreFrames(parent->GetFunctionBody(), outerMostFrame, JavascriptCallStackLayout::FromFramePointer(framePointer), false /* boxValues */);
- }
- }
- if (outerMostFrame->callInfo.Count)
- {
- inlinedFramesFound = true;
- if (noAlloc)
- {
- return inlinedFramesFound;
- }
- int32 frameCount = 0;
- InlinedFrameWalker::InlinedFrame *frameIterator = outerMostFrame;
- while (frameIterator->callInfo.Count)
- {
- frameCount++;
- frameIterator = frameIterator->Next();
- }
- InlinedFrameWalker::InlinedFrame **frames = HeapNewArray(InlinedFrameWalker::InlinedFrame*, frameCount);
- frameIterator = outerMostFrame;
- for (int index = frameCount - 1; index >= 0; index--)
- {
- Assert(frameIterator);
- frames[index] = frameIterator;
- frameIterator = frameIterator->Next();
- }
- self.Initialize(frameCount, frames, parent);
- }
- }
- return inlinedFramesFound;
- }
- void InlinedFrameWalker::Close()
- {
- parentFunction = nullptr;
- HeapDeleteArray(frameCount, frames);
- frames = nullptr;
- currentIndex = -1;
- frameCount = 0;
- }
- bool InlinedFrameWalker::Next(CallInfo& callInfo)
- {
- MoveNext();
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- if (currentFrame)
- {
- callInfo.Flags = CallFlags_None;
- callInfo.Count = (currentFrame->callInfo.Count & 0xFFFF);
- }
- return currentFrame != nullptr;
- }
- size_t InlinedFrameWalker::GetArgc() const
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- return currentFrame->callInfo.Count;
- }
- // Note: the boxArgsAndDeepCopy parameter should be true when a copy of the JS args must be ensured to
- // be on the heap. This results in a new array of Vars with deep copied boxed values (where
- // appropriate).
- // Otherwise, this parameter should be false. For instance, if the args will only be used
- // internally to gather type info, the values are not boxed (so, some Vars may still be on
- // the stack) and the array of the current frame is returned.
- Js::Var *InlinedFrameWalker::GetArgv(bool includeThis, bool boxArgsAndDeepCopy) const
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- uint firstArg = includeThis ? InlinedFrameArgIndex_This : InlinedFrameArgIndex_SecondScriptArg;
- size_t argCount = this->GetArgc() - firstArg;
- Js::Var *args;
- if (!boxArgsAndDeepCopy)
- {
- args = ¤tFrame->argv[firstArg];
- }
- else
- {
- args = RecyclerNewArray(parentFunction->GetScriptContext()->GetRecycler(), Js::Var, argCount);
- for (size_t i = 0; i < argCount; i++)
- {
- args[i] = currentFrame->argv[firstArg + i];
- }
- this->FinalizeStackValues(args, argCount, true /*deepCopy*/);
- }
- return args;
- }
- void InlinedFrameWalker::FinalizeStackValues(__in_ecount(argCount) Js::Var args[], size_t argCount, bool deepCopy) const
- {
- ScriptContext *scriptContext = this->GetFunctionObject()->GetScriptContext();
- for (size_t i = 0; i < argCount; i++)
- {
- args[i] = Js::JavascriptOperators::BoxStackInstance(args[i], scriptContext, false /*allowStackFunction*/, deepCopy);
- }
- }
- Js::JavascriptFunction *InlinedFrameWalker::GetFunctionObject() const
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- return StackScriptFunction::GetCurrentFunctionObject(currentFrame->function);
- }
- void InlinedFrameWalker::SetFunctionObject(Js::JavascriptFunction * function)
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- currentFrame->function = function;
- }
- Js::Var InlinedFrameWalker::GetArgumentsObject() const
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- return currentFrame->arguments;
- }
- void InlinedFrameWalker::SetArgumentsObject(Js::Var arguments)
- {
- InlinedFrameWalker::InlinedFrame *currentFrame = (InlinedFrameWalker::InlinedFrame *)GetCurrentFrame();
- Assert(currentFrame);
- currentFrame->arguments = arguments;
- }
- Js::Var InlinedFrameWalker::GetThisObject() const
- {
- InlinedFrameWalker::InlinedFrame *const currentFrame = GetCurrentFrame();
- Assert(currentFrame);
- return currentFrame->argv[InlinedFrameArgIndex_This];
- }
- bool InlinedFrameWalker::IsCallerPhysicalFrame() const
- {
- return currentIndex == (frameCount - 1);
- }
- bool InlinedFrameWalker::IsTopMostFrame() const
- {
- return currentIndex == 0;
- }
- uint32 InlinedFrameWalker::GetCurrentInlineeOffset() const
- {
- Assert(!IsTopMostFrame());
- Assert(currentIndex);
- return GetFrameAtIndex(currentIndex - 1)->callInfo.InlineeStartOffset;
- }
- uint32 InlinedFrameWalker::GetBottomMostInlineeOffset() const
- {
- Assert(frameCount);
- return GetFrameAtIndex(frameCount - 1)->callInfo.InlineeStartOffset;
- }
- Js::JavascriptFunction *InlinedFrameWalker::GetBottomMostFunctionObject() const
- {
- Assert(frameCount);
- return GetFrameAtIndex(frameCount - 1)->function;
- }
- InlinedFrameWalker::InlinedFrame *const InlinedFrameWalker::GetCurrentFrame() const
- {
- return GetFrameAtIndex(currentIndex);
- }
- InlinedFrameWalker::InlinedFrame *const InlinedFrameWalker::GetFrameAtIndex(signed index) const
- {
- Assert(frames);
- Assert(frameCount);
- InlinedFrameWalker::InlinedFrame *frame = nullptr;
- if (index < frameCount)
- {
- frame = frames[index];
- }
- return frame;
- }
- void InlinedFrameWalker::MoveNext()
- {
- currentIndex++;
- }
- void InlinedFrameWalker::Initialize(int32 frameCount, __in_ecount(frameCount) InlinedFrame **frames, Js::ScriptFunction *parent)
- {
- Assert(!parentFunction);
- Assert(!this->frames);
- Assert(!this->frameCount);
- Assert(currentIndex == -1);
- this->parentFunction = parent;
- this->frames = frames;
- this->frameCount = frameCount;
- this->currentIndex = -1;
- }
- InlinedFrameWalker::InlinedFrame* InlinedFrameWalker::InlinedFrame::FromPhysicalFrame(StackFrame& currentFrame, const JavascriptStackWalker * const stackWalker, void *entry, EntryPointInfo* entryPointInfo, bool useInternalFrameInfo)
- {
- // If the current javascript frame is a native frame, get the inlined frame from it, otherwise
- // it may be possible that current frame is the interpreter frame for a jitted loop body
- // If the loop body had some inlinees in it, retrieve the inlined frame using the cached info,
- // viz. instruction pointer, frame pointer, and stackCheckCodeHeight, about the loop body frame.
- struct InlinedFrame *inlinedFrame = nullptr;
- void *codeAddr, *framePointer;
- size_t stackCheckCodeHeight;
- if (useInternalFrameInfo)
- {
- codeAddr = stackWalker->GetCachedInternalFrameInfo().codeAddress;
- framePointer = stackWalker->GetCachedInternalFrameInfo().framePointer;
- stackCheckCodeHeight = stackWalker->GetCachedInternalFrameInfo().stackCheckCodeHeight;
- }
- else
- {
- codeAddr = currentFrame.GetInstructionPointer();
- framePointer = currentFrame.GetFrame();
- stackCheckCodeHeight = currentFrame.GetStackCheckCodeHeight();
- }
- if (!StackFrame::IsInStackCheckCode(entry, codeAddr, stackCheckCodeHeight))
- {
- inlinedFrame = (struct InlinedFrame *)(((uint8 *)framePointer) - entryPointInfo->GetFrameHeight());
- }
- return inlinedFrame;
- }
- void InternalFrameInfo::Set(
- void *codeAddress,
- void *framePointer,
- size_t stackCheckCodeHeight,
- InternalFrameType frameType,
- JavascriptFunction* function,
- bool hasInlinedFramesOnStack,
- bool previousInterpreterFrameIsFromBailout)
- {
- // We skip a jitted loop body's native frame when walking the stack and refer to the loop body's interpreter frame to get the function.
- // However, if the loop body has inlinees, to retrieve inlinee frames we need to cache some info about the loop body's native frame.
- this->codeAddress = codeAddress;
- this->framePointer = framePointer;
- this->stackCheckCodeHeight = stackCheckCodeHeight;
- this->frameType = frameType;
- this->function = function;
- this->hasInlinedFramesOnStack = hasInlinedFramesOnStack;
- this->previousInterpreterFrameIsFromBailout = previousInterpreterFrameIsFromBailout;
- }
- void InternalFrameInfo::Clear()
- {
- this->codeAddress = nullptr;
- this->framePointer = nullptr;
- this->stackCheckCodeHeight = (uint)-1;
- this->frameType = InternalFrameType_None;
- this->function = nullptr;
- this->hasInlinedFramesOnStack = false;
- this->previousInterpreterFrameIsFromBailout = false;
- }
- #endif
- #if DBG
- // Force a stack walk which till we find an interpreter frame
- // This will ensure inlined frames are decoded.
- bool JavascriptStackWalker::ValidateTopJitFrame(Js::ScriptContext* scriptContext)
- {
- if (!Configuration::Global.flags.ValidateInlineStack)
- {
- return true;
- }
- Js::JavascriptStackWalker walker(scriptContext);
- Js::JavascriptFunction* function;
- while (walker.GetCaller(&function))
- {
- Assert(function);
- if (walker.GetCurrentInterpreterFrame() != nullptr)
- {
- break;
- }
- }
- // If no asserts have fired yet - we should have succeeded.
- return true;
- }
- #endif
- }
|