//------------------------------------------------------------------------------------------------------- // 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([&](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([&](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([&](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([&](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([&](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([&](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([&](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([&](Js::ScriptContext *scriptContext) -> JsErrorCode { VALIDATE_INCOMING_REFERENCE(function, scriptContext); PARAM_NOT_NULL(functionPosition); *functionPosition = JS_INVALID_REFERENCE; if (!Js::VarIs(function) || !Js::VarIs(function)) { return JsErrorInvalidArgument; } Js::ScriptFunction* jsFunction = Js::VarTo(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([&](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([&](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([&](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([&](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::VarIs(expressionVal), isString = false; bool isUtf8 = !(parseAttributes & JsParseScriptAttributeArrayBufferIsUtf16Encoded); if (!isArrayBuffer) { isString = Js::VarIs(expressionVal); if (!isString) { return JsErrorInvalidArgument; } } const size_t len = isArrayBuffer ? Js::VarTo(expressionVal)->GetByteLength() : Js::VarTo(expressionVal)->GetLength(); if (len > INT_MAX) { return JsErrorInvalidArgument; } const WCHAR* expression; utf8::NarrowToWide wide_expression; if (isArrayBuffer && isUtf8) { wide_expression.Initialize( (const char*)Js::VarTo(expressionVal)->GetBuffer(), len); if (!wide_expression) { return JsErrorOutOfMemory; } expression = wide_expression; } else { expression = !isArrayBuffer ? Js::VarTo(expressionVal)->GetSz() // String : (const WCHAR*)Js::VarTo(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(len), false, forceSetValueProp, &result); if (result != nullptr) { *evalResult = result; } return success ? JsNoError : JsErrorScriptException; }, false /*allowInObjectBeforeCollectCallback*/, true /*scriptExceptionAllowed*/); #endif }