blob: 5ec06eb5bf00fb5f79a6504b1d0c3f693dd6fdad [file]
//-------------------------------------------------------------------------------------------------------
// 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
};
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<ReturnedValue*> ReturnedValueList;
class DiagStackFrame;
typedef JsUtil::Stack<DiagStackFrame*> DiagStack;
typedef WeakArenaReference<DiagStack> WeakDiagStack;
struct InterpreterHaltState
{
StopType stopType;
const FunctionBody* executingFunction;
DiagStackFrame* topFrame;
DiagStack* framePointers;
ReferencedArenaAdapter* referencedDiagnosticArena;
JavascriptExceptionObject* exceptionObject;
StringBuilder<ArenaAllocator>* 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<FunctionBody> 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<Probe*, ArenaAllocator> ProbeList;
}