//------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once namespace Js { class MutationBreakpoint; enum StopType { STOP_BREAKPOINT, STOP_INLINEBREAKPOINT, STOP_STEPCOMPLETE, STOP_EXCEPTIONTHROW, STOP_ASYNCBREAK, STOP_MUTATIONBREAKPOINT, STOP_DOMMUTATIONBREAKPOINT }; struct ReturnedValue { ReturnedValue() {} ReturnedValue(Js::Var _returnedValue, Js::JavascriptFunction * _calledFunction, bool _isValueOfReturnStatement) : returnedValue(_returnedValue), calledFunction(_calledFunction), isValueOfReturnStatement(_isValueOfReturnStatement) { if (isValueOfReturnStatement) { Assert(returnedValue == nullptr); Assert(calledFunction == nullptr); } } Field(Js::Var) returnedValue; Field(Js::JavascriptFunction *) calledFunction; Field(bool) isValueOfReturnStatement; }; typedef JsUtil::List ReturnedValueList; class DiagStackFrame; typedef JsUtil::Stack DiagStack; typedef WeakArenaReference WeakDiagStack; struct InterpreterHaltState { StopType stopType; const FunctionBody* executingFunction; DiagStackFrame* topFrame; DiagStack* framePointers; ReferencedArenaAdapter* referencedDiagnosticArena; JavascriptExceptionObject* exceptionObject; StringBuilder* stringBuilder; MutationBreakpoint* activeMutationBP; InterpreterHaltState(StopType _stopType, const FunctionBody* _executingFunction, MutationBreakpoint* _activeMutationBP = nullptr); FunctionBody* GetFunction(); int GetCurrentOffset(); void SetCurrentOffset(int offset); bool IsValid() const; }; struct HaltCallback { virtual bool CanHalt(InterpreterHaltState* pHaltState) = 0; virtual void DispatchHalt(InterpreterHaltState* pHaltState) = 0; virtual void CleanupHalt() = 0; virtual bool IsInClosedState() { return false; } // Mentions the policy if the hitting a breakpoint is allowed (based on the fact whether we are at callback from the breakpoint) virtual bool CanAllowBreakpoints() { return false; } }; struct Probe : HaltCallback { virtual bool Install(Js::ScriptContext* pScriptContext) = 0; virtual bool Uninstall(Js::ScriptContext* pScriptContext) = 0; }; enum StepType : BYTE { STEP_NONE, STEP_IN = 0x01, STEP_OVER = 0x02, STEP_OUT = 0x04, STEP_DOCUMENT = 0x08, // On entry of a jitted function, need to bailout to handle stepping if in STEP_IN mode, // or STEP_OVER (e.g. STEP_OVER at the end of this function, and it is called again by a // library caller). STEP_BAILOUT = STEP_IN | STEP_OVER, }; struct DebuggerOptionsCallback { virtual bool IsExceptionReportingEnabled() { return true; } virtual bool IsFirstChanceExceptionEnabled() { return false; } virtual bool IsNonUserCodeSupportEnabled() { return false; } virtual bool IsLibraryStackFrameSupportEnabled() { return false; } }; class StepController { friend class ProbeManager; friend class ProbeContainer; StepType stepType; int byteOffset; RecyclerRootPtr body; FunctionBody::StatementMap* statementMap; int frameCountWhenSet; int returnedValueRecordingDepth; DWORD_PTR frameAddrWhenSet; uint scriptIdWhenSet; bool stepCompleteOnInlineBreakpoint; ScriptContext *pActivatedContext; ReturnedValueList *returnedValueList; public: StepController(); ~StepController() { this->Deactivate(); } bool IsActive(); void Activate(StepType stepType, InterpreterHaltState* haltState); void Deactivate(InterpreterHaltState* haltState = nullptr); bool IsStepComplete_AllowingFalsePositives(InterpreterStackFrame * stackFrame); bool IsStepComplete(InterpreterHaltState* haltState, HaltCallback *haltCallback, OpCode originalOpcode); bool ContinueFromInlineBreakpoint(); ScriptContext* GetActivatedContext() const { return this->pActivatedContext; } const StepType* GetAddressOfStepType() const { return &stepType; } void* GetAddressOfScriptIdWhenSet() const { return (void*)&scriptIdWhenSet; } void* GetAddressOfFrameAddress() const { return (void*)&frameAddrWhenSet; } void SetFrameAddr(DWORD_PTR value) { this->frameAddrWhenSet = value; } void AddToReturnedValueContainer(Js::Var returnValue, Js::JavascriptFunction * function, bool isValueOfReturnStatement); void AddReturnToReturnedValueContainer(); void StartRecordingCall(); void EndRecordingCall(Js::Var returnValue, Js::JavascriptFunction * function); ReturnedValueList* GetReturnedValueList() const { return this->returnedValueList; } void ResetReturnedValueList(); void HandleResumeAction(Js::InterpreterHaltState* haltState, BREAKRESUMEACTION resumeAction); private: uint GetScriptId(_In_ FunctionBody* body); }; // This is separate from the step controller because it is the only case where activation // happens while the script is running. class AsyncBreakController { private: HaltCallback* haltCallback; public: AsyncBreakController(); void Activate(HaltCallback* haltCallback); void Deactivate(); bool IsBreak(); bool IsAtStoppingLocation(InterpreterHaltState* haltState); void DispatchAndReset(InterpreterHaltState* haltState); }; typedef JsUtil::List ProbeList; }