blob: a1f9ef649c236ccad0b34465ea07531dd7f6882d [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 "JsrtInternal.h"
#include "RuntimeDebugPch.h"
#include "ThreadContextTlsEntry.h"
#include "JsrtDebugUtils.h"
#include "Codex/Utf8Helper.h"
#define VALIDATE_IS_DEBUGGING(jsrtDebugManager) \
if (jsrtDebugManager == nullptr || !jsrtDebugManager->IsDebugEventCallbackSet()) \
{ \
return JsErrorDiagNotInDebugMode; \
}
#define VALIDATE_RUNTIME_IS_AT_BREAK(runtime) \
if (runtime->GetThreadContext()->GetDebugManager() == nullptr || !runtime->GetThreadContext()->GetDebugManager()->IsAtDispatchHalt()) \
{ \
return JsErrorDiagNotAtBreak; \
}
#define VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext) \
if (threadContext->GetRecycler() && threadContext->GetRecycler()->IsHeapEnumInProgress()) \
{ \
return JsErrorHeapEnumInProgress; \
} \
else if (threadContext->IsInThreadServiceCallback()) \
{ \
return JsErrorInThreadServiceCallback; \
} \
else if (threadContext->IsInScript()) \
{ \
return JsErrorRuntimeInUse; \
} \
ThreadContextScope scope(threadContext); \
if (!scope.IsValid()) \
{ \
return JsErrorWrongThread; \
}
#endif
CHAKRA_API JsDiagStartDebugging(
_In_ JsRuntimeHandle runtimeHandle,
_In_ JsDiagDebugEventCallback debugEventCallback,
_In_opt_ void* callbackState)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);
PARAM_NOT_NULL(debugEventCallback);
JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
ThreadContext * threadContext = runtime->GetThreadContext();
VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext);
if (runtime->GetJsrtDebugManager() != nullptr && runtime->GetJsrtDebugManager()->IsDebugEventCallbackSet())
{
return JsErrorDiagAlreadyInDebugMode;
}
// Create the debug object to save callback function and data
runtime->EnsureJsrtDebugManager();
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
jsrtDebugManager->SetDebugEventCallback(debugEventCallback, callbackState);
if (threadContext->GetDebugManager() != nullptr)
{
threadContext->GetDebugManager()->SetLocalsDisplayFlags(Js::DebugManager::LocalsDisplayFlags::LocalsDisplayFlags_NoGroupMethods);
}
for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
scriptContext != nullptr && !scriptContext->IsClosed();
scriptContext = scriptContext->next)
{
Assert(!scriptContext->IsScriptContextInDebugMode());
Js::DebugContext* debugContext = scriptContext->GetDebugContext();
if (debugContext->GetHostDebugContext() == nullptr)
{
debugContext->SetHostDebugContext(jsrtDebugManager);
}
HRESULT hr;
if (FAILED(hr = scriptContext->OnDebuggerAttached()))
{
Debugger_AttachDetach_unrecoverable_error(hr); // Inconsistent state, we can't continue from here
return JsErrorFatal;
}
// ScriptContext might get closed in OnDebuggerAttached
if (!scriptContext->IsClosed())
{
Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer();
probeContainer->InitializeInlineBreakEngine(jsrtDebugManager);
probeContainer->InitializeDebuggerScriptOptionCallback(jsrtDebugManager);
}
}
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagStopDebugging(
_In_ JsRuntimeHandle runtimeHandle,
_Out_opt_ void** callbackState)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);
if (callbackState != nullptr)
{
*callbackState = nullptr;
}
JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
ThreadContext * threadContext = runtime->GetThreadContext();
VALIDATE_RUNTIME_STATE_FOR_START_STOP_DEBUGGING(threadContext);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
scriptContext != nullptr && !scriptContext->IsClosed();
scriptContext = scriptContext->next)
{
Assert(scriptContext->IsScriptContextInDebugMode());
HRESULT hr;
if (FAILED(hr = scriptContext->OnDebuggerDetached()))
{
Debugger_AttachDetach_unrecoverable_error(hr); // Inconsistent state, we can't continue from here
return JsErrorFatal;
}
Js::DebugContext* debugContext = scriptContext->GetDebugContext();
Js::ProbeContainer* probeContainer = debugContext->GetProbeContainer();
probeContainer->UninstallInlineBreakpointProbe(nullptr);
probeContainer->UninstallDebuggerScriptOptionCallback();
jsrtDebugManager->ClearBreakpointDebugDocumentDictionary();
}
void* cbState = jsrtDebugManager->GetAndClearCallbackState();
if (callbackState != nullptr)
{
*callbackState = cbState;
}
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagGetScripts(
_Out_ JsValueRef *scriptsArray)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(scriptsArray);
*scriptsArray = JS_INVALID_REFERENCE;
JsrtContext *currentContext = JsrtContext::GetCurrent();
JsrtDebugManager* jsrtDebugManager = currentContext->GetRuntime()->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
Js::JavascriptArray* scripts = jsrtDebugManager->GetScripts(scriptContext);
if (scripts != nullptr)
{
*scriptsArray = scripts;
return JsNoError;
}
return JsErrorDiagUnableToPerformAction;
});
#endif
}
CHAKRA_API JsDiagGetSource(
_In_ unsigned int scriptId,
_Out_ JsValueRef *source)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(source);
*source = JS_INVALID_REFERENCE;
JsrtContext *currentContext = JsrtContext::GetCurrent();
JsrtDebugManager* jsrtDebugManager = currentContext->GetRuntime()->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
Js::DynamicObject* sourceObject = jsrtDebugManager->GetSource(scriptContext, scriptId);
if (sourceObject != nullptr)
{
*source = sourceObject;
return JsNoError;
}
return JsErrorInvalidArgument;
});
#endif
}
CHAKRA_API JsDiagRequestAsyncBreak(
_In_ JsRuntimeHandle runtimeHandle)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);
JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
for (Js::ScriptContext *scriptContext = runtime->GetThreadContext()->GetScriptContextList();
scriptContext != nullptr && !scriptContext->IsClosed();
scriptContext = scriptContext->next)
{
jsrtDebugManager->EnableAsyncBreak(scriptContext);
}
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagGetBreakpoints(
_Out_ JsValueRef *breakpoints)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext* scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(breakpoints);
*breakpoints = JS_INVALID_REFERENCE;
JsrtContext* currentContext = JsrtContext::GetCurrent();
JsrtRuntime* runtime = currentContext->GetRuntime();
ThreadContextScope scope(runtime->GetThreadContext());
if (!scope.IsValid())
{
return JsErrorWrongThread;
}
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
Js::JavascriptArray* bpsArray = currentContext->GetScriptContext()->GetLibrary()->CreateArray();
for (Js::ScriptContext* currentScriptContext = runtime->GetThreadContext()->GetScriptContextList();
currentScriptContext != nullptr && !currentScriptContext->IsClosed();
currentScriptContext = currentScriptContext->next)
{
jsrtDebugManager->GetBreakpoints(&bpsArray, currentScriptContext);
}
*breakpoints = bpsArray;
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagSetBreakpoint(
_In_ unsigned int scriptId,
_In_ unsigned int lineNumber,
_In_ unsigned int columnNumber,
_Out_ JsValueRef *breakpoint)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext* scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(breakpoint);
*breakpoint = JS_INVALID_REFERENCE;
JsrtContext* currentContext = JsrtContext::GetCurrent();
JsrtRuntime* runtime = currentContext->GetRuntime();
ThreadContextScope scope(runtime->GetThreadContext());
if (!scope.IsValid())
{
return JsErrorWrongThread;
}
VALIDATE_IS_DEBUGGING(runtime->GetJsrtDebugManager());
Js::Utf8SourceInfo* utf8SourceInfo = nullptr;
for (Js::ScriptContext* currentScriptContext = runtime->GetThreadContext()->GetScriptContextList();
currentScriptContext != nullptr && utf8SourceInfo == nullptr && !currentScriptContext->IsClosed();
currentScriptContext = currentScriptContext->next)
{
currentScriptContext->MapScript([&](Js::Utf8SourceInfo* sourceInfo) -> bool
{
if (sourceInfo->GetSourceInfoId() == scriptId)
{
utf8SourceInfo = sourceInfo;
return true;
}
return false;
});
}
if (utf8SourceInfo != nullptr && utf8SourceInfo->HasDebugDocument())
{
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
Js::DynamicObject* bpObject = jsrtDebugManager->SetBreakPoint(currentContext->GetScriptContext(), utf8SourceInfo, lineNumber, columnNumber);
if(bpObject != nullptr)
{
*breakpoint = bpObject;
return JsNoError;
}
return JsErrorDiagUnableToPerformAction;
}
return JsErrorDiagObjectNotFound;
});
#endif
}
CHAKRA_API JsDiagRemoveBreakpoint(
_In_ unsigned int breakpointId)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext* scriptContext) -> JsErrorCode {
JsrtContext* currentContext = JsrtContext::GetCurrent();
JsrtRuntime* runtime = currentContext->GetRuntime();
ThreadContextScope scope(runtime->GetThreadContext());
if (!scope.IsValid())
{
return JsErrorWrongThread;
}
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
if (!jsrtDebugManager->RemoveBreakpoint(breakpointId))
{
return JsErrorInvalidArgument;
}
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagSetBreakOnException(
_In_ JsRuntimeHandle runtimeHandle,
_In_ JsDiagBreakOnExceptionAttributes exceptionAttributes)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);
JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
jsrtDebugManager->SetBreakOnException(exceptionAttributes);
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagGetBreakOnException(
_In_ JsRuntimeHandle runtimeHandle,
_Out_ JsDiagBreakOnExceptionAttributes* exceptionAttributes)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return GlobalAPIWrapper_NoRecord([&]() -> JsErrorCode {
VALIDATE_INCOMING_RUNTIME_HANDLE(runtimeHandle);
PARAM_NOT_NULL(exceptionAttributes);
*exceptionAttributes = JsDiagBreakOnExceptionAttributeNone;
JsrtRuntime * runtime = JsrtRuntime::FromHandle(runtimeHandle);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
*exceptionAttributes = jsrtDebugManager->GetBreakOnException();
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagSetStepType(
_In_ JsDiagStepType stepType)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<true>([&](Js::ScriptContext * scriptContext) -> JsErrorCode {
JsrtContext *currentContext = JsrtContext::GetCurrent();
JsrtRuntime* runtime = currentContext->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
if (stepType == JsDiagStepTypeStepIn)
{
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_INTO);
}
else if (stepType == JsDiagStepTypeStepOut)
{
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OUT);
}
else if (stepType == JsDiagStepTypeStepOver)
{
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OVER);
}
else if (stepType == JsDiagStepTypeStepBack)
{
#if ENABLE_TTD
ThreadContext* threadContext = runtime->GetThreadContext();
if(!threadContext->IsRuntimeInTTDMode())
{
//Don't want to fail hard when user accidentally clicks this so pring message and step forward
fprintf(stderr, "Must be in replay mode to use reverse-step - launch with \"--replay-debug\" flag in Node.");
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_STEP_OVER);
}
else
{
threadContext->TTDExecutionInfo->SetPendingTTDStepBackMove();
//don't worry about BP suppression because we are just going to throw after we return
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE);
}
#else
return JsErrorInvalidArgument;
#endif
}
else if (stepType == JsDiagStepTypeReverseContinue)
{
#if ENABLE_TTD
ThreadContext* threadContext = runtime->GetThreadContext();
if(!threadContext->IsRuntimeInTTDMode())
{
//Don't want to fail hard when user accidentally clicks this so pring message and step forward
fprintf(stderr, "Must be in replay mode to use reverse-continue - launch with \"--replay-debug\" flag in Node.");
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE);
}
else
{
threadContext->TTDExecutionInfo->SetPendingTTDReverseContinueMove(JsTTDMoveMode::JsTTDMoveScanIntervalForContinue);
//don't worry about BP suppression because we are just going to throw after we return
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE);
}
#else
return JsErrorInvalidArgument;
#endif
}
else if (stepType == JsDiagStepTypeContinue)
{
jsrtDebugManager->SetResumeType(BREAKRESUMEACTION_CONTINUE);
}
return JsNoError;
});
#endif
}
CHAKRA_API JsTTDDiagWriteLog(_In_reads_(uriLength) const char* uri, _In_ size_t uriLength)
{
#if !ENABLE_TTD
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<true>([&](Js::ScriptContext * scriptContext) -> JsErrorCode {
if (!scriptContext->GetThreadContext()->IsRuntimeInTTDMode() || !scriptContext->GetThreadContext()->TTDLog->CanWriteInnerLoopTrace())
{
return JsErrorDiagUnableToPerformAction;
}
JsrtContext *currentContext = JsrtContext::GetCurrent();
JsrtRuntime* runtime = currentContext->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
TTD::TTDebuggerSourceLocation cloc;
jsrtDebugManager->GetThreadContext()->TTDExecutionInfo->GetTimeAndPositionForDebugger(cloc);
jsrtDebugManager->GetThreadContext()->TTDLog->InnerLoopEmitLog(cloc, uri, uriLength);
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagGetFunctionPosition(
_In_ JsValueRef function,
_Out_ JsValueRef *functionPosition)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
VALIDATE_INCOMING_REFERENCE(function, scriptContext);
PARAM_NOT_NULL(functionPosition);
*functionPosition = JS_INVALID_REFERENCE;
if (!Js::RecyclableObject::Is(function) || !Js::ScriptFunction::Is(function))
{
return JsErrorInvalidArgument;
}
Js::ScriptFunction* jsFunction = Js::ScriptFunction::FromVar(function);
BOOL fParsed = jsFunction->GetParseableFunctionInfo()->IsFunctionParsed();
if (!fParsed)
{
Js::JavascriptFunction::DeferredParseCore(&jsFunction, fParsed);
}
if (fParsed)
{
Js::FunctionBody* functionBody = jsFunction->GetFunctionBody();
if (functionBody != nullptr)
{
Js::Utf8SourceInfo* utf8SourceInfo = functionBody->GetUtf8SourceInfo();
if (utf8SourceInfo != nullptr && !utf8SourceInfo->GetIsLibraryCode())
{
ULONG lineNumber = functionBody->GetLineNumber();
ULONG columnNumber = functionBody->GetColumnNumber();
uint startOffset = functionBody->GetStatementStartOffset(0);
ULONG firstStatementLine;
LONG firstStatementColumn;
if (functionBody->GetLineCharOffsetFromStartChar(startOffset, &firstStatementLine, &firstStatementColumn))
{
Js::DynamicObject* funcPositionObject = (Js::DynamicObject*)Js::CrossSite::MarshalVar(utf8SourceInfo->GetScriptContext(), scriptContext->GetLibrary()->CreateObject());
if (funcPositionObject != nullptr)
{
JsrtDebugUtils::AddScriptIdToObject(funcPositionObject, utf8SourceInfo);
JsrtDebugUtils::AddFileNameOrScriptTypeToObject(funcPositionObject, utf8SourceInfo);
JsrtDebugUtils::AddPropertyToObject(funcPositionObject, JsrtDebugPropertyId::line, (uint32)lineNumber, scriptContext);
JsrtDebugUtils::AddPropertyToObject(funcPositionObject, JsrtDebugPropertyId::column, (uint32)columnNumber, scriptContext);
JsrtDebugUtils::AddPropertyToObject(funcPositionObject, JsrtDebugPropertyId::firstStatementLine, (uint32)firstStatementLine, scriptContext);
JsrtDebugUtils::AddPropertyToObject(funcPositionObject, JsrtDebugPropertyId::firstStatementColumn, (int32)firstStatementColumn, scriptContext);
*functionPosition = funcPositionObject;
return JsNoError;
}
}
}
}
}
return JsErrorDiagObjectNotFound;
});
#endif
}
CHAKRA_API JsDiagGetStackTrace(
_Out_ JsValueRef *stackTrace)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(stackTrace);
*stackTrace = JS_INVALID_REFERENCE;
JsrtContext* context = JsrtContext::GetCurrent();
JsrtRuntime* runtime = context->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
*stackTrace = jsrtDebugManager->GetStackFrames(scriptContext);
return JsNoError;
});
#endif
}
CHAKRA_API JsDiagGetStackProperties(
_In_ unsigned int stackFrameIndex,
_Out_ JsValueRef *properties)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(properties);
*properties = JS_INVALID_REFERENCE;
JsrtContext* context = JsrtContext::GetCurrent();
JsrtRuntime* runtime = context->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
JsrtDebuggerStackFrame* debuggerStackFrame = nullptr;
if (!jsrtDebugManager->TryGetFrameObjectFromFrameIndex(scriptContext, stackFrameIndex, &debuggerStackFrame))
{
return JsErrorDiagObjectNotFound;
}
Js::DynamicObject* localsObject = debuggerStackFrame->GetLocalsObject(scriptContext);
if (localsObject != nullptr)
{
*properties = localsObject;
return JsNoError;
}
return JsErrorDiagUnableToPerformAction;
});
#endif
}
CHAKRA_API JsDiagGetProperties(
_In_ unsigned int objectHandle,
_In_ unsigned int fromCount,
_In_ unsigned int totalCount,
_Out_ JsValueRef *propertiesObject)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(propertiesObject);
*propertiesObject = JS_INVALID_REFERENCE;
JsrtContext* context = JsrtContext::GetCurrent();
JsrtRuntime* runtime = context->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
JsrtDebuggerObjectBase* debuggerObject = nullptr;
if (!jsrtDebugManager->GetDebuggerObjectsManager()->TryGetDebuggerObjectFromHandle(objectHandle, &debuggerObject) || debuggerObject == nullptr)
{
return JsErrorDiagInvalidHandle;
}
Js::DynamicObject* properties = debuggerObject->GetChildren(scriptContext, fromCount, totalCount);
if (properties != nullptr)
{
*propertiesObject = properties;
return JsNoError;
}
return JsErrorDiagUnableToPerformAction;
});
#endif
}
CHAKRA_API JsDiagGetObjectFromHandle(
_In_ unsigned int objectHandle,
_Out_ JsValueRef *handleObject)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPIWrapper_NoRecord<false>([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(handleObject);
*handleObject = JS_INVALID_REFERENCE;
JsrtContext* context = JsrtContext::GetCurrent();
JsrtRuntime* runtime = context->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
JsrtDebuggerObjectBase* debuggerObject = nullptr;
if (!jsrtDebugManager->GetDebuggerObjectsManager()->TryGetDebuggerObjectFromHandle(objectHandle, &debuggerObject) || debuggerObject == nullptr)
{
return JsErrorDiagInvalidHandle;
}
Js::DynamicObject* object = debuggerObject->GetJSONObject(scriptContext, /* forceSetValueProp */ false);
if (object != nullptr)
{
*handleObject = object;
return JsNoError;
}
return JsErrorDiagUnableToPerformAction;
});
#endif
}
CHAKRA_API JsDiagEvaluate(
_In_ JsValueRef expressionVal,
_In_ unsigned int stackFrameIndex,
_In_ JsParseScriptAttributes parseAttributes,
_In_ bool forceSetValueProp,
_Out_ JsValueRef *evalResult)
{
#ifndef ENABLE_SCRIPT_DEBUGGING
return JsErrorCategoryUsage;
#else
return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
PARAM_NOT_NULL(expressionVal);
PARAM_NOT_NULL(evalResult);
bool isArrayBuffer = Js::ArrayBuffer::Is(expressionVal),
isString = false;
bool isUtf8 = !(parseAttributes & JsParseScriptAttributeArrayBufferIsUtf16Encoded);
if (!isArrayBuffer)
{
isString = Js::JavascriptString::Is(expressionVal);
if (!isString)
{
return JsErrorInvalidArgument;
}
}
const size_t len = isArrayBuffer ?
Js::ArrayBuffer::FromVar(expressionVal)->GetByteLength() :
Js::JavascriptString::FromVar(expressionVal)->GetLength();
if (len > INT_MAX)
{
return JsErrorInvalidArgument;
}
const WCHAR* expression;
utf8::NarrowToWide wide_expression;
if (isArrayBuffer && isUtf8)
{
wide_expression.Initialize(
(const char*)Js::ArrayBuffer::FromVar(expressionVal)->GetBuffer(), len);
if (!wide_expression)
{
return JsErrorOutOfMemory;
}
expression = wide_expression;
}
else
{
expression = !isArrayBuffer ?
Js::JavascriptString::FromVar(expressionVal)->GetSz() // String
:
(const WCHAR*)Js::ArrayBuffer::FromVar(expressionVal)->GetBuffer(); // ArrayBuffer;
}
*evalResult = JS_INVALID_REFERENCE;
JsrtContext* context = JsrtContext::GetCurrent();
JsrtRuntime* runtime = context->GetRuntime();
VALIDATE_RUNTIME_IS_AT_BREAK(runtime);
JsrtDebugManager* jsrtDebugManager = runtime->GetJsrtDebugManager();
VALIDATE_IS_DEBUGGING(jsrtDebugManager);
JsrtDebuggerStackFrame* debuggerStackFrame = nullptr;
if (!jsrtDebugManager->TryGetFrameObjectFromFrameIndex(scriptContext, stackFrameIndex, &debuggerStackFrame))
{
return JsErrorDiagObjectNotFound;
}
Js::DynamicObject* result = nullptr;
bool success = debuggerStackFrame->Evaluate(scriptContext, expression, static_cast<int>(len), false, forceSetValueProp, &result);
if (result != nullptr)
{
*evalResult = result;
}
return success ? JsNoError : JsErrorScriptException;
}, false /*allowInObjectBeforeCollectCallback*/, true /*scriptExceptionAllowed*/);
#endif
}