blob: 9286c3faebb1e405a0c1c4bffac79a676821ba8c [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "JsrtPch.h"
#ifdef ENABLE_SCRIPT_DEBUGGING
#include "JsrtDebuggerObject.h"
#include "JsrtDebugUtils.h"
#include "JsrtDebugManager.h"
JsrtDebuggerObjectBase::JsrtDebuggerObjectBase(JsrtDebuggerObjectType type, JsrtDebuggerObjectsManager* debuggerObjectsManager) :
type(type),
debuggerObjectsManager(debuggerObjectsManager)
{
Assert(debuggerObjectsManager != nullptr);
this->handle = debuggerObjectsManager->GetNextHandle();
}
JsrtDebuggerObjectBase::~JsrtDebuggerObjectBase()
{
this->debuggerObjectsManager = nullptr;
}
JsrtDebuggerObjectsManager * JsrtDebuggerObjectBase::GetDebuggerObjectsManager()
{
return this->debuggerObjectsManager;
}
Js::DynamicObject * JsrtDebuggerObjectBase::GetChildren(Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
{
AssertMsg(false, "Wrong type for GetChildren");
return nullptr;
}
Js::DynamicObject * JsrtDebuggerObjectBase::GetChildren(WeakArenaReference<Js::IDiagObjectModelWalkerBase>* walkerRef, Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
{
Js::DynamicObject* childrensObject = scriptContext->GetLibrary()->CreateObject();
uint propertiesArrayCount = 0;
Js::JavascriptArray* propertiesArray = scriptContext->GetLibrary()->CreateArray();
uint debuggerOnlyPropertiesArrayCount = 0;
Js::JavascriptArray* debuggerOnlyPropertiesArray = scriptContext->GetLibrary()->CreateArray();
Js::IDiagObjectModelWalkerBase* walker = walkerRef->GetStrongReference();
uint32 childrensCount = 0;
if (walker != nullptr)
{
try
{
childrensCount = walker->GetChildrenCount();
}
catch (const Js::JavascriptException& err)
{
err.GetAndClear(); // discard exception object
}
if (fromCount < childrensCount)
{
for (uint32 i = fromCount; i < childrensCount && (propertiesArrayCount + debuggerOnlyPropertiesArrayCount) < totalCount; ++i)
{
Js::ResolvedObject resolvedObject;
try
{
walker->Get(i, &resolvedObject);
}
catch (const Js::JavascriptException& err)
{
Js::JavascriptExceptionObject* exception = err.GetAndClear();
Js::Var error = exception->GetThrownObject(scriptContext);
resolvedObject.obj = error;
resolvedObject.address = nullptr;
resolvedObject.scriptContext = exception->GetScriptContext();
resolvedObject.typeId = Js::JavascriptOperators::GetTypeId(error);
resolvedObject.name = _u("{error}");
resolvedObject.propId = Js::Constants::NoProperty;
}
AutoPtr<WeakArenaReference<Js::IDiagObjectModelDisplay>> objectDisplayWeakRef(resolvedObject.GetObjectDisplay());
Js::IDiagObjectModelDisplay* resolvedObjectDisplay = objectDisplayWeakRef->GetStrongReference();
if (resolvedObjectDisplay != nullptr)
{
JsrtDebuggerObjectBase* debuggerObject = JsrtDebuggerObjectProperty::Make(this->GetDebuggerObjectsManager(), objectDisplayWeakRef);
Js::DynamicObject* object = debuggerObject->GetJSONObject(resolvedObject.scriptContext, /* forceSetValueProp */ false);
Js::Var marshaledObj = Js::CrossSite::MarshalVar(scriptContext, object);
if (resolvedObjectDisplay->IsFake())
{
Js::JavascriptOperators::OP_SetElementI((Js::Var)debuggerOnlyPropertiesArray, Js::JavascriptNumber::ToVar(debuggerOnlyPropertiesArrayCount++, scriptContext), marshaledObj, scriptContext);
}
else
{
Js::JavascriptOperators::OP_SetElementI((Js::Var)propertiesArray, Js::JavascriptNumber::ToVar(propertiesArrayCount++, scriptContext), marshaledObj, scriptContext);
}
objectDisplayWeakRef->ReleaseStrongReference();
objectDisplayWeakRef.Detach();
}
}
}
walkerRef->ReleaseStrongReference();
}
JsrtDebugUtils::AddPropertyToObject(childrensObject, JsrtDebugPropertyId::totalPropertiesOfObject, childrensCount, scriptContext);
JsrtDebugUtils::AddPropertyToObject(childrensObject, JsrtDebugPropertyId::properties, propertiesArray, scriptContext);
JsrtDebugUtils::AddPropertyToObject(childrensObject, JsrtDebugPropertyId::debuggerOnlyProperties, debuggerOnlyPropertiesArray, scriptContext);
return childrensObject;
}
JsrtDebuggerObjectsManager::JsrtDebuggerObjectsManager(JsrtDebugManager* jsrtDebugManager) :
handleId(0),
jsrtDebugManager(jsrtDebugManager),
handleToDebuggerObjectsDictionary(nullptr),
dataToDebuggerObjectsDictionary(nullptr)
{
Assert(jsrtDebugManager != nullptr);
}
JsrtDebuggerObjectsManager::~JsrtDebuggerObjectsManager()
{
if (this->dataToDebuggerObjectsDictionary != nullptr)
{
AssertMsg(this->dataToDebuggerObjectsDictionary->Count() == 0, "Should have cleared all debugger objects by now?");
Adelete(this->GetDebugObjectArena(), this->dataToDebuggerObjectsDictionary);
this->dataToDebuggerObjectsDictionary = nullptr;
}
if (this->handleToDebuggerObjectsDictionary != nullptr)
{
AssertMsg(this->handleToDebuggerObjectsDictionary->Count() == 0, "Should have cleared all handle by now?");
Adelete(this->GetDebugObjectArena(), this->handleToDebuggerObjectsDictionary);
this->handleToDebuggerObjectsDictionary = nullptr;
}
}
void JsrtDebuggerObjectsManager::ClearAll()
{
if (this->dataToDebuggerObjectsDictionary != nullptr)
{
this->dataToDebuggerObjectsDictionary->Clear();
}
if (this->handleToDebuggerObjectsDictionary != nullptr)
{
this->handleToDebuggerObjectsDictionary->Map([this](uint handle, JsrtDebuggerObjectBase* debuggerObject) {
Adelete(this->GetDebugObjectArena(), debuggerObject);
});
this->handleToDebuggerObjectsDictionary->Clear();
}
this->handleId = 0;
}
ArenaAllocator * JsrtDebuggerObjectsManager::GetDebugObjectArena()
{
return this->GetJsrtDebugManager()->GetDebugObjectArena();
}
bool JsrtDebuggerObjectsManager::TryGetDebuggerObjectFromHandle(uint handle, JsrtDebuggerObjectBase ** debuggerObject)
{
if (this->handleToDebuggerObjectsDictionary == nullptr)
{
return false;
}
return this->handleToDebuggerObjectsDictionary->TryGetValue(handle, debuggerObject);
}
void JsrtDebuggerObjectsManager::AddToDebuggerObjectsDictionary(JsrtDebuggerObjectBase * debuggerObject)
{
Assert(debuggerObject != nullptr);
uint handle = debuggerObject->GetHandle();
Assert(handle > 0);
if (this->handleToDebuggerObjectsDictionary == nullptr)
{
this->handleToDebuggerObjectsDictionary = Anew(this->GetDebugObjectArena(), DebuggerObjectsDictionary, this->GetDebugObjectArena(), 10);
}
Assert(!this->handleToDebuggerObjectsDictionary->ContainsKey(handle));
int index = this->handleToDebuggerObjectsDictionary->Add(handle, debuggerObject);
Assert(index != -1);
}
void JsrtDebuggerObjectsManager::AddToDataToDebuggerObjectsDictionary(void * data, JsrtDebuggerObjectBase * debuggerObject)
{
Assert(data != nullptr);
Assert(debuggerObject != nullptr);
if (this->dataToDebuggerObjectsDictionary == nullptr)
{
this->dataToDebuggerObjectsDictionary = Anew(this->GetDebugObjectArena(), DataToDebuggerObjectsDictionary, this->GetDebugObjectArena(), 10);
}
Assert(!this->dataToDebuggerObjectsDictionary->ContainsKey(data));
int index = this->dataToDebuggerObjectsDictionary->Add(data, debuggerObject);
Assert(index != -1);
this->AddToDebuggerObjectsDictionary(debuggerObject);
}
bool JsrtDebuggerObjectsManager::TryGetDataFromDataToDebuggerObjectsDictionary(void * data, JsrtDebuggerObjectBase ** debuggerObject)
{
if (this->dataToDebuggerObjectsDictionary == nullptr)
{
return false;
}
return this->dataToDebuggerObjectsDictionary->TryGetValue(data, debuggerObject);
}
JsrtDebuggerStackFrame::JsrtDebuggerStackFrame(JsrtDebuggerObjectsManager * debuggerObjectsManager, Js::DiagStackFrame * stackFrame, uint frameIndex) :
debuggerObjectsManager(debuggerObjectsManager),
frameIndex(frameIndex),
stackFrame(stackFrame)
{
Assert(this->stackFrame != nullptr);
}
JsrtDebuggerStackFrame::~JsrtDebuggerStackFrame()
{
this->debuggerObjectsManager = nullptr;
this->stackFrame = nullptr;
}
Js::DynamicObject * JsrtDebuggerStackFrame::GetJSONObject(Js::ScriptContext* scriptContext)
{
Js::ScriptContext *frameScriptContext = stackFrame->GetScriptContext();
Js::DynamicObject* stackTraceObject = frameScriptContext->GetLibrary()->CreateObject();
Js::FunctionBody* functionBody = stackFrame->GetFunction();
Js::Utf8SourceInfo* utf8SourceInfo = functionBody->GetUtf8SourceInfo();
JsrtDebugUtils::AddPropertyToObject(stackTraceObject, JsrtDebugPropertyId::index, frameIndex, scriptContext);
JsrtDebugUtils::AddScriptIdToObject(stackTraceObject, utf8SourceInfo);
int currentByteCodeOffset = stackFrame->GetByteCodeOffset();
if (stackFrame->IsInterpreterFrame() && frameIndex != 0)
{
// For non-leaf interpreter frames back up 1 instruction so we see the caller
// rather than the statement after the caller
currentByteCodeOffset--;
}
JsrtDebugUtils::AddLineColumnToObject(stackTraceObject, functionBody, currentByteCodeOffset);
JsrtDebugUtils::AddSourceLengthAndTextToObject(stackTraceObject, functionBody, currentByteCodeOffset);
Js::JavascriptFunction* javascriptFunction = stackFrame->GetJavascriptFunction();
JsrtDebuggerObjectBase* functionObject = nullptr;
if (!this->debuggerObjectsManager->TryGetDataFromDataToDebuggerObjectsDictionary(javascriptFunction, &functionObject))
{
functionObject = JsrtDebuggerObjectFunction::Make(this->debuggerObjectsManager, javascriptFunction);
}
JsrtDebugUtils::AddPropertyToObject(stackTraceObject, JsrtDebugPropertyId::functionHandle, functionObject->GetHandle(), frameScriptContext);
return stackTraceObject;
}
Js::DynamicObject * JsrtDebuggerStackFrame::GetLocalsObject(Js::ScriptContext* scriptContext)
{
/*
{
"thisObject" : {},
"exception" : {},
"arguments" : {},
"returnValue" : {},
"functionCallsReturn" : [{}, {}],
"locals" : [],
"scopes" : [{}, {}],
"globals" : {}
}
*/
Js::DynamicObject* propertiesObject = scriptContext->GetLibrary()->CreateObject();
Js::Var returnValueObject = nullptr;
uint functionCallsReturnCount = 0;
Js::JavascriptArray* functionCallsReturn = scriptContext->GetLibrary()->CreateArray();
uint totalLocalsCount = 0;
Js::JavascriptArray* localsArray = scriptContext->GetLibrary()->CreateArray();
uint scopesCount = 0;
Js::JavascriptArray* scopesArray = scriptContext->GetLibrary()->CreateArray();
Js::DynamicObject* globalsObject = nullptr;
ReferencedArenaAdapter* pRefArena = scriptContext->GetThreadContext()->GetDebugManager()->GetDiagnosticArena();
Js::IDiagObjectModelDisplay* pLocalsDisplay = Anew(pRefArena->Arena(), Js::LocalsDisplay, this->stackFrame);
WeakArenaReference<Js::IDiagObjectModelWalkerBase>* objectModelWalker = pLocalsDisplay->CreateWalker();
if (objectModelWalker != nullptr)
{
Js::LocalsWalker* localsWalker = (Js::LocalsWalker*)objectModelWalker->GetStrongReference();
if (localsWalker != nullptr)
{
// If 'this' is available add 'thisObject'
Js::ResolvedObject thisResolvedObject;
{
ENFORCE_ENTRYEXITRECORD_HASCALLER(scriptContext);
thisResolvedObject.obj = this->stackFrame->GetThisFromFrame(&thisResolvedObject.address, localsWalker);
}
if (thisResolvedObject.obj != nullptr)
{
thisResolvedObject.scriptContext = scriptContext;
thisResolvedObject.name = _u("this");
thisResolvedObject.typeId = Js::JavascriptOperators::GetTypeId(thisResolvedObject.obj);
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(this->debuggerObjectsManager, thisResolvedObject, this->stackFrame->GetScriptContext(), false, [&](Js::Var marshaledObj)
{
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::thisObject, marshaledObj, scriptContext);
});
}
uint32 totalProperties = localsWalker->GetChildrenCount();
if (totalProperties > 0)
{
int index = 0;
Js::ResolvedObject resolvedObject;
resolvedObject.scriptContext = this->stackFrame->GetScriptContext();
// If we have a exception add 'exception'
if (Js::VariableWalkerBase::GetExceptionObject(index, this->stackFrame, &resolvedObject))
{
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(this->debuggerObjectsManager, resolvedObject, scriptContext, false, [&](Js::Var marshaledObj)
{
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::exception, marshaledObj, scriptContext);
});
}
// If user have not explicitly defined 'arguments' add 'arguments'
if (localsWalker->HasUserNotDefinedArguments() && localsWalker->CreateArgumentsObject(&resolvedObject))
{
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(this->debuggerObjectsManager, resolvedObject, scriptContext, false, [&](Js::Var marshaledObj)
{
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::arguments, marshaledObj, scriptContext);
});
}
Js::ReturnedValueList *returnedValueList = this->stackFrame->GetScriptContext()->GetDebugContext()->GetProbeContainer()->GetReturnedValueList();
// If we have return value(s) add them to 'returnValue' or 'functionCallsReturn'
if (returnedValueList != nullptr && returnedValueList->Count() > 0 && this->stackFrame->IsTopFrame())
{
for (int i = 0; i < returnedValueList->Count(); ++i)
{
Js::ReturnedValue * returnValue = returnedValueList->Item(i);
Js::VariableWalkerBase::GetReturnedValueResolvedObject(returnValue, this->stackFrame, &resolvedObject);
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(debuggerObjectsManager, resolvedObject, scriptContext, false, [&](Js::Var marshaledObj)
{
if (returnValue->isValueOfReturnStatement)
{
returnValueObject = marshaledObj;
}
else
{
Js::JavascriptOperators::OP_SetElementI((Js::Var)functionCallsReturn, Js::JavascriptNumber::ToVar(functionCallsReturnCount, scriptContext), marshaledObj, scriptContext);
functionCallsReturnCount++;
}
});
}
if (returnValueObject != nullptr)
{
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::returnValue, returnValueObject, scriptContext);
}
if (functionCallsReturnCount > 0)
{
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::functionCallsReturn, functionCallsReturn, scriptContext);
}
}
// Add all locals variable(s) available under 'locals'
uint32 localsCount = localsWalker->GetLocalVariablesCount();
for (uint32 i = 0; i < localsCount; ++i)
{
if (!localsWalker->GetLocal(i, &resolvedObject))
{
break;
}
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(debuggerObjectsManager, resolvedObject, scriptContext, false, [&](Js::Var marshaledObj)
{
Js::JavascriptOperators::OP_SetElementI((Js::Var)localsArray, Js::JavascriptNumber::ToVar(totalLocalsCount, scriptContext), marshaledObj, scriptContext);
totalLocalsCount++;
});
}
// Add all variable(s) captured under 'scopes'
index = 0;
BOOL foundGroup = TRUE;
while (foundGroup)
{
foundGroup = localsWalker->GetScopeObject(index++, &resolvedObject);
if (foundGroup == TRUE)
{
AutoPtr<WeakArenaReference<Js::IDiagObjectModelDisplay>> objectDisplayWeakRef(resolvedObject.GetObjectDisplay());
JsrtDebuggerObjectBase* debuggerObject = JsrtDebuggerObjectScope::Make(debuggerObjectsManager, objectDisplayWeakRef, scopesCount);
Js::DynamicObject* object = debuggerObject->GetJSONObject(resolvedObject.scriptContext, /* forceSetValueProp */ false);
Assert(object != nullptr);
Js::Var marshaledObj = Js::CrossSite::MarshalVar(scriptContext, object);
Js::JavascriptOperators::OP_SetElementI((Js::Var)scopesArray, Js::JavascriptNumber::ToVar(scopesCount, scriptContext), marshaledObj, scriptContext);
scopesCount++;
objectDisplayWeakRef.Detach();
}
}
// Add globals handle
if (localsWalker->GetGlobalsObject(&resolvedObject))
{
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectGlobalsNode>(this->debuggerObjectsManager, resolvedObject, scriptContext, false, [&](Js::Var marshaledObj)
{
globalsObject = (Js::DynamicObject*)marshaledObj;
});
}
}
objectModelWalker->ReleaseStrongReference();
HeapDelete(objectModelWalker);
}
Adelete(pRefArena->Arena(), pLocalsDisplay);
}
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::locals, localsArray, scriptContext);
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::scopes, scopesArray, scriptContext);
if (globalsObject == nullptr)
{
globalsObject = scriptContext->GetLibrary()->CreateObject();
}
JsrtDebugUtils::AddPropertyToObject(propertiesObject, JsrtDebugPropertyId::globals, globalsObject, scriptContext);
return propertiesObject;
}
bool JsrtDebuggerStackFrame::Evaluate(Js::ScriptContext* scriptContext, const char16 *source, int sourceLength, bool isLibraryCode, bool forceSetValueProp, Js::DynamicObject** evalResult)
{
*evalResult = nullptr;
bool success = false;
if (this->stackFrame != nullptr)
{
Js::ResolvedObject resolvedObject;
HRESULT hr = S_OK;
Js::ScriptContext* frameScriptContext = this->stackFrame->GetScriptContext();
Js::JavascriptExceptionObject *exceptionObject = nullptr;
{
BEGIN_JS_RUNTIME_CALL_EX_AND_TRANSLATE_EXCEPTION_AND_ERROROBJECT_TO_HRESULT_NESTED(frameScriptContext, false)
{
ENFORCE_ENTRYEXITRECORD_HASCALLER(frameScriptContext);
this->stackFrame->EvaluateImmediate(source, sourceLength, isLibraryCode, &resolvedObject);
}
END_JS_RUNTIME_CALL_AND_TRANSLATE_AND_GET_EXCEPTION_AND_ERROROBJECT_TO_HRESULT(hr, frameScriptContext, exceptionObject);
}
if (resolvedObject.obj == nullptr)
{
resolvedObject.name = _u("{exception}");
resolvedObject.typeId = Js::TypeIds_Error;
resolvedObject.address = nullptr;
if (exceptionObject != nullptr)
{
resolvedObject.obj = exceptionObject->GetThrownObject(scriptContext);
}
else
{
resolvedObject.obj = scriptContext->GetLibrary()->GetUndefined();
}
}
else
{
success = true;
}
if (resolvedObject.obj != nullptr)
{
resolvedObject.scriptContext = frameScriptContext;
charcount_t len = Js::JavascriptString::GetBufferLength(source);
resolvedObject.name = AnewNoThrowArray(this->debuggerObjectsManager->GetDebugObjectArena(), WCHAR, len + 1);
if (resolvedObject.name != nullptr)
{
wcscpy_s((WCHAR*)resolvedObject.name, len + 1, source);
}
else
{
// len can be big, if we failed just have empty string
resolvedObject.name = _u("");
}
resolvedObject.typeId = Js::JavascriptOperators::GetTypeId(resolvedObject.obj);
JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(this->debuggerObjectsManager, resolvedObject, scriptContext, forceSetValueProp, [&](Js::Var marshaledObj)
{
*evalResult = (Js::DynamicObject*)marshaledObj;
});
}
}
return success;
}
JsrtDebuggerObjectProperty::JsrtDebuggerObjectProperty(JsrtDebuggerObjectsManager* debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay) :
JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Property, debuggerObjectsManager),
objectDisplay(objectDisplay),
walkerRef(nullptr)
{
Assert(objectDisplay != nullptr);
}
JsrtDebuggerObjectProperty::~JsrtDebuggerObjectProperty()
{
if (this->objectDisplay != nullptr)
{
HeapDelete(this->objectDisplay);
this->objectDisplay = nullptr;
}
if (this->walkerRef != nullptr)
{
HeapDelete(this->walkerRef);
this->walkerRef = nullptr;
}
}
JsrtDebuggerObjectBase * JsrtDebuggerObjectProperty::Make(JsrtDebuggerObjectsManager* debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay)
{
JsrtDebuggerObjectBase* debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectProperty, debuggerObjectsManager, objectDisplay);
debuggerObjectsManager->AddToDebuggerObjectsDictionary(debuggerObject);
return debuggerObject;
}
Js::DynamicObject * JsrtDebuggerObjectProperty::GetJSONObject(Js::ScriptContext* scriptContext, bool forceSetValueProp)
{
Js::IDiagObjectModelDisplay* objectDisplayRef = this->objectDisplay->GetStrongReference();
Js::DynamicObject* propertyObject = nullptr;
if (objectDisplayRef != nullptr)
{
propertyObject = scriptContext->GetLibrary()->CreateObject();
LPCWSTR name = objectDisplayRef->Name();
JsrtDebugUtils::AddPropertyToObject(propertyObject, JsrtDebugPropertyId::name, name, wcslen(name), scriptContext);
JsrtDebugUtils::AddPropertyType(propertyObject, objectDisplayRef, scriptContext, forceSetValueProp); // Will add type, value, display, className, propertyAttributes
JsrtDebugUtils::AddPropertyToObject(propertyObject, JsrtDebugPropertyId::handle, this->GetHandle(), scriptContext);
this->objectDisplay->ReleaseStrongReference();
}
return propertyObject;
}
Js::DynamicObject* JsrtDebuggerObjectProperty::GetChildren(Js::ScriptContext* scriptContext, uint fromCount, uint totalCount)
{
Js::IDiagObjectModelDisplay* objectDisplayRef = objectDisplay->GetStrongReference();
if (objectDisplayRef == nullptr)
{
return nullptr;
}
if (this->walkerRef == nullptr)
{
this->walkerRef = objectDisplayRef->CreateWalker();
}
Js::DynamicObject* childrens = __super::GetChildren(this->walkerRef, scriptContext, fromCount, totalCount);
objectDisplay->ReleaseStrongReference();
return childrens;
}
JsrtDebuggerObjectScope::JsrtDebuggerObjectScope(JsrtDebuggerObjectsManager * debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay, uint index) :
JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Scope, debuggerObjectsManager),
objectDisplay(objectDisplay),
index(index),
walkerRef(nullptr)
{
Assert(this->objectDisplay != nullptr);
}
JsrtDebuggerObjectScope::~JsrtDebuggerObjectScope()
{
if (this->objectDisplay != nullptr)
{
HeapDelete(this->objectDisplay);
this->objectDisplay = nullptr;
}
if (this->walkerRef != nullptr)
{
HeapDelete(this->walkerRef);
this->walkerRef = nullptr;
}
}
JsrtDebuggerObjectBase * JsrtDebuggerObjectScope::Make(JsrtDebuggerObjectsManager * debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay, uint index)
{
JsrtDebuggerObjectBase* debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectScope, debuggerObjectsManager, objectDisplay, index);
debuggerObjectsManager->AddToDebuggerObjectsDictionary(debuggerObject);
return debuggerObject;
}
Js::DynamicObject * JsrtDebuggerObjectScope::GetJSONObject(Js::ScriptContext* scriptContext, bool forceSetValueProp)
{
Js::IDiagObjectModelDisplay* modelDisplay = this->objectDisplay->GetStrongReference();
Js::DynamicObject* scopeObject = nullptr;
if (modelDisplay != nullptr)
{
scopeObject = scriptContext->GetLibrary()->CreateObject();
JsrtDebugUtils::AddPropertyToObject(scopeObject, JsrtDebugPropertyId::index, this->index, scriptContext);
JsrtDebugUtils::AddPropertyToObject(scopeObject, JsrtDebugPropertyId::handle, this->GetHandle(), scriptContext);
this->objectDisplay->ReleaseStrongReference();
}
return scopeObject;
}
Js::DynamicObject * JsrtDebuggerObjectScope::GetChildren(Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
{
Js::IDiagObjectModelDisplay* objectDisplayRef = objectDisplay->GetStrongReference();
if (objectDisplayRef == nullptr)
{
return nullptr;
}
if (this->walkerRef == nullptr)
{
this->walkerRef = objectDisplayRef->CreateWalker();
}
Js::DynamicObject* childrens = __super::GetChildren(this->walkerRef, scriptContext, fromCount, totalCount);
objectDisplay->ReleaseStrongReference();
return childrens;
}
JsrtDebuggerObjectFunction::JsrtDebuggerObjectFunction(JsrtDebuggerObjectsManager* debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction) :
JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Function, debuggerObjectsManager),
javascriptFunction(javascriptFunction),
objectDisplay(nullptr),
walkerRef(nullptr)
{
}
JsrtDebuggerObjectFunction::~JsrtDebuggerObjectFunction()
{
if (this->objectDisplay != nullptr)
{
HeapDelete(this->objectDisplay);
this->objectDisplay = nullptr;
}
if (this->walkerRef != nullptr)
{
HeapDelete(this->walkerRef);
this->walkerRef = nullptr;
}
this->javascriptFunction = nullptr;
}
JsrtDebuggerObjectBase * JsrtDebuggerObjectFunction::Make(JsrtDebuggerObjectsManager * debuggerObjectsManager, Js::JavascriptFunction* javascriptFunction)
{
JsrtDebuggerObjectBase* debuggerObject = nullptr;
Assert(!debuggerObjectsManager->TryGetDataFromDataToDebuggerObjectsDictionary(javascriptFunction, &debuggerObject));
debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectFunction, debuggerObjectsManager, javascriptFunction);
debuggerObjectsManager->AddToDataToDebuggerObjectsDictionary(javascriptFunction, debuggerObject);
return debuggerObject;
}
Js::DynamicObject * JsrtDebuggerObjectFunction::GetJSONObject(Js::ScriptContext * scriptContext, bool forceSetValueProp)
{
Js::DynamicObject* functionObject = scriptContext->GetLibrary()->CreateObject();
Js::FunctionBody* functionBody = this->javascriptFunction->GetFunctionBody();
JsrtDebugUtils::AddScriptIdToObject(functionObject, functionBody->GetUtf8SourceInfo());
JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::line, (uint32)functionBody->GetLineNumber(), scriptContext);
JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::column, (uint32)functionBody->GetColumnNumber(), scriptContext);
JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::name, functionBody->GetDisplayName(), functionBody->GetDisplayNameLength(), scriptContext);
JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::type, scriptContext->GetLibrary()->GetFunctionTypeDisplayString(), scriptContext);
JsrtDebugUtils::AddPropertyToObject(functionObject, JsrtDebugPropertyId::handle, this->GetHandle(), scriptContext);
return functionObject;
}
Js::DynamicObject * JsrtDebuggerObjectFunction::GetChildren(Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
{
if (this->objectDisplay == nullptr)
{
Js::ResolvedObject functionResolvedObject;
functionResolvedObject.obj = this->javascriptFunction;
functionResolvedObject.scriptContext = scriptContext;
functionResolvedObject.name = _u("Function");
functionResolvedObject.typeId = Js::JavascriptOperators::GetTypeId(functionResolvedObject.obj);
this->objectDisplay = functionResolvedObject.GetObjectDisplay();
}
Js::IDiagObjectModelDisplay* objectDisplayRef = this->objectDisplay->GetStrongReference();
if (objectDisplayRef == nullptr)
{
return nullptr;
}
if (this->walkerRef == nullptr)
{
this->walkerRef = objectDisplayRef->CreateWalker();
}
Js::DynamicObject* childrens = __super::GetChildren(this->walkerRef, scriptContext, fromCount, totalCount);
this->objectDisplay->ReleaseStrongReference();
return childrens;
}
JsrtDebuggerObjectGlobalsNode::JsrtDebuggerObjectGlobalsNode(JsrtDebuggerObjectsManager* debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay) :
JsrtDebuggerObjectBase(JsrtDebuggerObjectType::Globals, debuggerObjectsManager),
objectDisplay(objectDisplay),
walkerRef(nullptr)
{
Assert(objectDisplay != nullptr);
}
JsrtDebuggerObjectGlobalsNode::~JsrtDebuggerObjectGlobalsNode()
{
if (this->objectDisplay != nullptr)
{
HeapDelete(this->objectDisplay);
this->objectDisplay = nullptr;
}
if (this->walkerRef != nullptr)
{
HeapDelete(this->walkerRef);
this->walkerRef = nullptr;
}
}
JsrtDebuggerObjectBase * JsrtDebuggerObjectGlobalsNode::Make(JsrtDebuggerObjectsManager * debuggerObjectsManager, WeakArenaReference<Js::IDiagObjectModelDisplay>* objectDisplay)
{
JsrtDebuggerObjectBase* debuggerObject = Anew(debuggerObjectsManager->GetDebugObjectArena(), JsrtDebuggerObjectGlobalsNode, debuggerObjectsManager, objectDisplay);
debuggerObjectsManager->AddToDebuggerObjectsDictionary(debuggerObject);
return debuggerObject;
}
Js::DynamicObject * JsrtDebuggerObjectGlobalsNode::GetJSONObject(Js::ScriptContext * scriptContext, bool forceSetValueProp)
{
Js::IDiagObjectModelDisplay* objectDisplayRef = this->objectDisplay->GetStrongReference();
Js::DynamicObject* globalsNode = nullptr;
if (objectDisplayRef != nullptr)
{
globalsNode = scriptContext->GetLibrary()->CreateObject();
JsrtDebugUtils::AddPropertyToObject(globalsNode, JsrtDebugPropertyId::handle, this->GetHandle(), scriptContext);
this->objectDisplay->ReleaseStrongReference();
}
return globalsNode;
}
Js::DynamicObject * JsrtDebuggerObjectGlobalsNode::GetChildren(Js::ScriptContext * scriptContext, uint fromCount, uint totalCount)
{
Js::IDiagObjectModelDisplay* objectDisplayRef = objectDisplay->GetStrongReference();
if (objectDisplayRef == nullptr)
{
return nullptr;
}
if (this->walkerRef == nullptr)
{
this->walkerRef = objectDisplayRef->CreateWalker();
}
Js::DynamicObject* childrens = __super::GetChildren(this->walkerRef, scriptContext, fromCount, totalCount);
objectDisplay->ReleaseStrongReference();
return childrens;
}
JsrtDebugStackFrames::JsrtDebugStackFrames(JsrtDebugManager* jsrtDebugManager):
framesDictionary(nullptr)
{
Assert(jsrtDebugManager != nullptr);
this->jsrtDebugManager = jsrtDebugManager;
}
JsrtDebugStackFrames::~JsrtDebugStackFrames()
{
if (this->framesDictionary != nullptr)
{
this->ClearFrameDictionary();
Adelete(this->jsrtDebugManager->GetDebugObjectArena(), this->framesDictionary);
this->framesDictionary = nullptr;
}
}
static int __cdecl DiagStackFrameSorter(void * dispatchHaltFrameAddress, const void * diagStackFrame1, const void * diagStackFrame2)
{
const DWORD_PTR *p1 = reinterpret_cast<const DWORD_PTR*>(diagStackFrame1);
const DWORD_PTR *p2 = reinterpret_cast<const DWORD_PTR*>(diagStackFrame2);
Js::DiagStackFrame * pStackFrame1 = (Js::DiagStackFrame *)(*p1);
Js::DiagStackFrame * pStackFrame2 = (Js::DiagStackFrame *)(*p2);
DWORD_PTR stackAddress1 = pStackFrame1->GetStackAddress();
DWORD_PTR stackAddress2 = pStackFrame2->GetStackAddress();
return stackAddress1 > stackAddress2 ? 1 : -1;
}
Js::JavascriptArray * JsrtDebugStackFrames::StackFrames(Js::ScriptContext * scriptContext)
{
Js::JavascriptArray* stackTraceArray = nullptr;
ThreadContext* threadContext = scriptContext->GetThreadContext();
DWORD_PTR dispatchHaltFrameAddress = threadContext->GetDebugManager()->GetDispatchHaltFrameAddress();
AssertMsg(dispatchHaltFrameAddress > 0, "Didn't set the dispatchHaltFrameAddress at time of break?");
if (dispatchHaltFrameAddress != 0)
{
if (this->framesDictionary == nullptr)
{
this->framesDictionary = Anew(this->jsrtDebugManager->GetDebugObjectArena(), FramesDictionary, this->jsrtDebugManager->GetDebugObjectArena(), 10);
}
else
{
this->ClearFrameDictionary();
}
typedef JsUtil::List<Js::DiagStackFrame*, ArenaAllocator> DiagStackFrameList;
DiagStackFrameList* stackList = Anew(this->jsrtDebugManager->GetDebugObjectArena(), DiagStackFrameList, this->jsrtDebugManager->GetDebugObjectArena(), 10);
// Walk all the script contexts and collect the frames which are below the address when break was reported.
for (Js::ScriptContext *tempScriptContext = threadContext->GetScriptContextList();
tempScriptContext != nullptr && tempScriptContext->IsScriptContextInDebugMode();
tempScriptContext = tempScriptContext->next)
{
Js::WeakDiagStack * framePointers = tempScriptContext->GetDebugContext()->GetProbeContainer()->GetFramePointers(dispatchHaltFrameAddress);
if (framePointers != nullptr)
{
Js::DiagStack* stackFrames = framePointers->GetStrongReference();
if (stackFrames != nullptr)
{
int count = stackFrames->Count();
for (int frameIndex = 0; frameIndex < count; ++frameIndex)
{
Js::DiagStackFrame* stackFrame = stackFrames->Peek(frameIndex);
stackList->Add(stackFrame);
}
}
framePointers->ReleaseStrongReference();
HeapDelete(framePointers);
}
}
// Frames can be from multiple contexts, sort them based on stack address
stackList->Sort(DiagStackFrameSorter, (void*)dispatchHaltFrameAddress);
stackTraceArray = scriptContext->GetLibrary()->CreateArray(stackList->Count(), stackList->Count());
stackList->Map([&](int index, Js::DiagStackFrame* stackFrame)
{
AssertMsg(index != 0 || stackFrame->IsTopFrame(), "Index 0 frame is not marked as top frame");
Js::DynamicObject* stackTraceObject = this->GetStackFrame(stackFrame, index);
Js::Var marshaledObj = Js::CrossSite::MarshalVar(scriptContext, stackTraceObject);
stackTraceArray->DirectSetItemAt(index, marshaledObj);
});
Adelete(this->jsrtDebugManager->GetDebugObjectArena(), stackList);
}
else
{
// Empty array
stackTraceArray = scriptContext->GetLibrary()->CreateArray(0, 0);
}
return stackTraceArray;
}
bool JsrtDebugStackFrames::TryGetFrameObjectFromFrameIndex(uint frameIndex, JsrtDebuggerStackFrame ** debuggerStackFrame)
{
if (this->framesDictionary != nullptr)
{
return this->framesDictionary->TryGetValue(frameIndex, debuggerStackFrame);
}
return false;
}
Js::DynamicObject * JsrtDebugStackFrames::GetStackFrame(Js::DiagStackFrame * stackFrame, uint frameIndex)
{
JsrtDebuggerStackFrame* debuggerStackFrame = Anew(this->jsrtDebugManager->GetDebugObjectArena(), JsrtDebuggerStackFrame, this->jsrtDebugManager->GetDebuggerObjectsManager(), stackFrame, frameIndex);
Assert(this->framesDictionary != nullptr);
this->framesDictionary->Add(frameIndex, debuggerStackFrame);
return debuggerStackFrame->GetJSONObject(stackFrame->GetScriptContext());
}
void JsrtDebugStackFrames::ClearFrameDictionary()
{
if (this->framesDictionary != nullptr)
{
this->framesDictionary->Map([this](uint handle, JsrtDebuggerStackFrame* debuggerStackFrame) {
Adelete(this->jsrtDebugManager->GetDebugObjectArena(), debuggerStackFrame);
});
this->framesDictionary->Clear();
}
}
#endif