2
0

JsrtDebugManager.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "JsrtPch.h"
  6. #ifdef ENABLE_SCRIPT_DEBUGGING
  7. #include "JsrtDebugManager.h"
  8. #include "JsrtDebugEventObject.h"
  9. #include "JsrtDebugUtils.h"
  10. #include "JsrtDebuggerObject.h"
  11. #include "RuntimeDebugPch.h"
  12. #include "screrror.h" // For CompileScriptException
  13. JsrtDebugManager::JsrtDebugManager(ThreadContext* threadContext) :
  14. HostDebugContext(nullptr),
  15. threadContext(threadContext),
  16. debugEventCallback(nullptr),
  17. callbackState(nullptr),
  18. resumeAction(BREAKRESUMEACTION_CONTINUE),
  19. debugObjectArena(nullptr),
  20. debuggerObjectsManager(nullptr),
  21. debugDocumentManager(nullptr),
  22. stackFrames(nullptr),
  23. breakOnExceptionAttributes(JsDiagBreakOnExceptionAttributeUncaught)
  24. {
  25. Assert(threadContext != nullptr);
  26. }
  27. JsrtDebugManager::~JsrtDebugManager()
  28. {
  29. if (this->debuggerObjectsManager != nullptr)
  30. {
  31. Adelete(this->debugObjectArena, this->debuggerObjectsManager);
  32. this->debuggerObjectsManager = nullptr;
  33. }
  34. if (this->debugDocumentManager != nullptr)
  35. {
  36. Adelete(this->debugObjectArena, this->debugDocumentManager);
  37. this->debugDocumentManager = nullptr;
  38. }
  39. if (this->debugObjectArena != nullptr)
  40. {
  41. this->threadContext->GetRecycler()->UnregisterExternalGuestArena(this->debugObjectArena);
  42. HeapDelete(this->debugObjectArena);
  43. this->debugObjectArena = nullptr;
  44. }
  45. this->debugEventCallback = nullptr;
  46. this->callbackState = nullptr;
  47. this->threadContext = nullptr;
  48. }
  49. void JsrtDebugManager::SetDebugEventCallback(JsDiagDebugEventCallback debugEventCallback, void* callbackState)
  50. {
  51. Assert(this->debugEventCallback == nullptr);
  52. Assert(this->callbackState == nullptr);
  53. this->debugEventCallback = debugEventCallback;
  54. this->callbackState = callbackState;
  55. }
  56. void* JsrtDebugManager::GetAndClearCallbackState()
  57. {
  58. void* currentCallbackState = this->callbackState;
  59. this->debugEventCallback = nullptr;
  60. this->callbackState = nullptr;
  61. return currentCallbackState;
  62. }
  63. bool JsrtDebugManager::IsDebugEventCallbackSet() const
  64. {
  65. return this->debugEventCallback != nullptr;
  66. }
  67. bool JsrtDebugManager::CanHalt(Js::InterpreterHaltState* haltState)
  68. {
  69. // This is registered as the callback for inline breakpoints.
  70. // We decide here if we are at a reasonable stop location that has source code.
  71. Assert(haltState->IsValid());
  72. Js::FunctionBody* pCurrentFuncBody = haltState->GetFunction();
  73. int byteOffset = haltState->GetCurrentOffset();
  74. Js::FunctionBody::StatementMap* map = pCurrentFuncBody->GetMatchingStatementMapFromByteCode(byteOffset, false);
  75. // Resolve the dummy ret code.
  76. return map != nullptr && (!pCurrentFuncBody->GetIsGlobalFunc() || !Js::FunctionBody::IsDummyGlobalRetStatement(&map->sourceSpan));
  77. }
  78. void JsrtDebugManager::DispatchHalt(Js::InterpreterHaltState* haltState)
  79. {
  80. switch (haltState->stopType)
  81. {
  82. case Js::STOP_BREAKPOINT: /*JsDiagDebugEventBreakpoint*/
  83. case Js::STOP_INLINEBREAKPOINT: /*JsDiagDebugEventDebuggerStatement*/
  84. case Js::STOP_ASYNCBREAK: /*JsDiagDebugEventAsyncBreak*/
  85. this->ReportBreak(haltState);
  86. break;
  87. case Js::STOP_STEPCOMPLETE: /*JsDiagDebugEventStepComplete*/
  88. this->SetResumeType(BREAKRESUMEACTION_CONTINUE);
  89. this->ReportBreak(haltState);
  90. break;
  91. case Js::STOP_EXCEPTIONTHROW: /*JsDiagDebugEventRuntimeException*/
  92. this->ReportExceptionBreak(haltState);
  93. break;
  94. case Js::STOP_DOMMUTATIONBREAKPOINT:
  95. case Js::STOP_MUTATIONBREAKPOINT:
  96. AssertMsg(false, "Not yet handled");
  97. break;
  98. default:
  99. AssertMsg(false, "Unhandled stop type");
  100. }
  101. this->HandleResume(haltState, this->resumeAction);
  102. }
  103. bool JsrtDebugManager::CanAllowBreakpoints()
  104. {
  105. return true;
  106. }
  107. void JsrtDebugManager::CleanupHalt()
  108. {
  109. }
  110. bool JsrtDebugManager::IsInClosedState()
  111. {
  112. return this->debugEventCallback == nullptr;
  113. }
  114. bool JsrtDebugManager::IsExceptionReportingEnabled()
  115. {
  116. return this->GetBreakOnException() != JsDiagBreakOnExceptionAttributeNone;
  117. }
  118. bool JsrtDebugManager::IsFirstChanceExceptionEnabled()
  119. {
  120. return (this->GetBreakOnException() & JsDiagBreakOnExceptionAttributeFirstChance) == JsDiagBreakOnExceptionAttributeFirstChance;
  121. }
  122. HRESULT JsrtDebugManager::DbgRegisterFunction(Js::ScriptContext* scriptContext, Js::FunctionBody* functionBody, DWORD_PTR dwDebugSourceContext, LPCWSTR title)
  123. {
  124. Js::Utf8SourceInfo* utf8SourceInfo = functionBody->GetUtf8SourceInfo();
  125. if (!utf8SourceInfo->GetIsLibraryCode() && !utf8SourceInfo->HasDebugDocument())
  126. {
  127. JsrtDebugDocumentManager* debugDocumentManager = this->GetDebugDocumentManager();
  128. Assert(debugDocumentManager != nullptr);
  129. Js::DebugDocument* debugDocument = HeapNewNoThrow(Js::DebugDocument, utf8SourceInfo, functionBody);
  130. if (debugDocument != nullptr)
  131. {
  132. utf8SourceInfo->SetDebugDocument(debugDocument);
  133. }
  134. // Raising events during the middle of a source reparse allows the host to reenter the
  135. // script context and cause memory race conditions. Suppressing these events during a
  136. // reparse prevents the issue. Since the host was already expected to call JsDiagGetScripts
  137. // once the attach is completed to get the list of parsed scripts, there is no change in
  138. // behavior.
  139. if (this->debugEventCallback != nullptr &&
  140. !scriptContext->GetDebugContext()->GetIsReparsingSource())
  141. {
  142. JsrtDebugEventObject debugEventObject(scriptContext);
  143. Js::DynamicObject* eventDataObject = debugEventObject.GetEventDataObject();
  144. JsrtDebugUtils::AddSourceMetadataToObject(eventDataObject, utf8SourceInfo);
  145. this->CallDebugEventCallback(JsDiagDebugEventSourceCompile, eventDataObject, scriptContext, false /*isBreak*/);
  146. }
  147. }
  148. return S_OK;
  149. }
  150. #if ENABLE_TTD
  151. void JsrtDebugManager::ReportScriptCompile_TTD(Js::FunctionBody* body, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException, bool notify)
  152. {
  153. if(this->debugEventCallback == nullptr)
  154. {
  155. return;
  156. }
  157. Js::ScriptContext* scriptContext = utf8SourceInfo->GetScriptContext();
  158. JsrtDebugEventObject debugEventObject(scriptContext);
  159. Js::DynamicObject* eventDataObject = debugEventObject.GetEventDataObject();
  160. JsrtDebugDocumentManager* debugDocumentManager = this->GetDebugDocumentManager();
  161. Assert(debugDocumentManager != nullptr);
  162. // Create DebugDocument and then report JsDiagDebugEventSourceCompile event
  163. Js::DebugDocument* debugDocument = HeapNewNoThrow(Js::DebugDocument, utf8SourceInfo, body);
  164. if(debugDocument != nullptr)
  165. {
  166. utf8SourceInfo->SetDebugDocument(debugDocument);
  167. }
  168. JsrtDebugUtils::AddSourceMetadataToObject(eventDataObject, utf8SourceInfo);
  169. if(notify)
  170. {
  171. this->CallDebugEventCallback(JsDiagDebugEventSourceCompile, eventDataObject, scriptContext, false /*isBreak*/);
  172. }
  173. }
  174. #endif
  175. void JsrtDebugManager::ReportScriptCompile(Js::JavascriptFunction* scriptFunction, Js::Utf8SourceInfo* utf8SourceInfo, CompileScriptException* compileException)
  176. {
  177. if (this->debugEventCallback != nullptr)
  178. {
  179. Js::ScriptContext* scriptContext = utf8SourceInfo->GetScriptContext();
  180. JsrtDebugEventObject debugEventObject(scriptContext);
  181. Js::DynamicObject* eventDataObject = debugEventObject.GetEventDataObject();
  182. JsDiagDebugEvent jsDiagDebugEvent = JsDiagDebugEventCompileError;
  183. if (scriptFunction == nullptr)
  184. {
  185. // Report JsDiagDebugEventCompileError event
  186. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::error, compileException->ei.bstrDescription, ::SysStringLen(compileException->ei.bstrDescription), scriptContext);
  187. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::line, compileException->line, scriptContext);
  188. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::column, compileException->ichMin - compileException->ichMinLine - 1, scriptContext); // Converted to 0-based
  189. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::sourceText, compileException->bstrLine, ::SysStringLen(compileException->bstrLine), scriptContext);
  190. }
  191. else
  192. {
  193. JsrtDebugDocumentManager* debugDocumentManager = this->GetDebugDocumentManager();
  194. Assert(debugDocumentManager != nullptr);
  195. // Create DebugDocument and then report JsDiagDebugEventSourceCompile event
  196. Js::DebugDocument* debugDocument = HeapNewNoThrow(Js::DebugDocument, utf8SourceInfo, scriptFunction->GetFunctionBody());
  197. if (debugDocument != nullptr)
  198. {
  199. utf8SourceInfo->SetDebugDocument(debugDocument);
  200. }
  201. jsDiagDebugEvent = JsDiagDebugEventSourceCompile;
  202. }
  203. JsrtDebugUtils::AddSourceMetadataToObject(eventDataObject, utf8SourceInfo);
  204. this->CallDebugEventCallback(jsDiagDebugEvent, eventDataObject, scriptContext, false /*isBreak*/);
  205. }
  206. }
  207. void JsrtDebugManager::ReportBreak(Js::InterpreterHaltState* haltState)
  208. {
  209. if (this->debugEventCallback != nullptr)
  210. {
  211. Js::FunctionBody* functionBody = haltState->GetFunction();
  212. Assert(functionBody != nullptr);
  213. Js::Utf8SourceInfo* utf8SourceInfo = functionBody->GetUtf8SourceInfo();
  214. int currentByteCodeOffset = haltState->GetCurrentOffset();
  215. Js::ScriptContext* scriptContext = utf8SourceInfo->GetScriptContext();
  216. JsDiagDebugEvent jsDiagDebugEvent = this->GetDebugEventFromStopType(haltState->stopType);
  217. JsrtDebugEventObject debugEventObject(scriptContext);
  218. Js::DynamicObject* eventDataObject = debugEventObject.GetEventDataObject();
  219. Js::ProbeContainer* probeContainer = scriptContext->GetDebugContext()->GetProbeContainer();
  220. if (jsDiagDebugEvent == JsDiagDebugEventBreakpoint)
  221. {
  222. UINT bpId = 0;
  223. probeContainer->MapProbesUntil([&](int i, Js::Probe* pProbe)
  224. {
  225. Js::BreakpointProbe* bp = (Js::BreakpointProbe*)pProbe;
  226. if (bp->Matches(functionBody, utf8SourceInfo->GetDebugDocument(), currentByteCodeOffset))
  227. {
  228. bpId = bp->GetId();
  229. return true;
  230. }
  231. return false;
  232. });
  233. AssertMsg(bpId != 0, "How come we don't have a breakpoint id for JsDiagDebugEventBreakpoint");
  234. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::breakpointId, bpId, scriptContext);
  235. }
  236. JsrtDebugUtils::AddScriptIdToObject(eventDataObject, utf8SourceInfo);
  237. JsrtDebugUtils::AddLineColumnToObject(eventDataObject, functionBody, currentByteCodeOffset);
  238. JsrtDebugUtils::AddSourceLengthAndTextToObject(eventDataObject, functionBody, currentByteCodeOffset);
  239. this->CallDebugEventCallbackForBreak(jsDiagDebugEvent, eventDataObject, scriptContext);
  240. }
  241. }
  242. void JsrtDebugManager::ReportExceptionBreak(Js::InterpreterHaltState* haltState)
  243. {
  244. if (this->debugEventCallback != nullptr)
  245. {
  246. Assert(haltState->stopType == Js::STOP_EXCEPTIONTHROW);
  247. Js::Utf8SourceInfo* utf8SourceInfo = haltState->GetFunction()->GetUtf8SourceInfo();
  248. Js::ScriptContext* scriptContext = utf8SourceInfo->GetScriptContext();
  249. JsDiagDebugEvent jsDiagDebugEvent = JsDiagDebugEventRuntimeException;
  250. JsrtDebugEventObject debugEventObject(scriptContext);
  251. Js::DynamicObject* eventDataObject = debugEventObject.GetEventDataObject();
  252. JsrtDebugUtils::AddScriptIdToObject(eventDataObject, utf8SourceInfo);
  253. Js::FunctionBody* functionBody = haltState->topFrame->GetFunction();
  254. Assert(functionBody != nullptr);
  255. int currentByteCodeOffset = haltState->topFrame->GetByteCodeOffset();
  256. JsrtDebugUtils::AddLineColumnToObject(eventDataObject, functionBody, currentByteCodeOffset);
  257. JsrtDebugUtils::AddSourceLengthAndTextToObject(eventDataObject, functionBody, currentByteCodeOffset);
  258. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::uncaught, !haltState->exceptionObject->IsFirstChanceException(), scriptContext);
  259. Js::ResolvedObject resolvedObject;
  260. resolvedObject.scriptContext = scriptContext;
  261. resolvedObject.name = _u("{exception}");
  262. resolvedObject.typeId = Js::TypeIds_Error;
  263. resolvedObject.address = nullptr;
  264. resolvedObject.obj = scriptContext->GetDebugContext()->GetProbeContainer()->GetExceptionObject();
  265. if (resolvedObject.obj == nullptr)
  266. {
  267. resolvedObject.obj = resolvedObject.scriptContext->GetLibrary()->GetUndefined();
  268. }
  269. JsrtDebuggerObjectBase::CreateDebuggerObject<JsrtDebuggerObjectProperty>(this->GetDebuggerObjectsManager(), resolvedObject, scriptContext, /* forceSetValueProp */ false, [&](Js::Var marshaledObj)
  270. {
  271. JsrtDebugUtils::AddPropertyToObject(eventDataObject, JsrtDebugPropertyId::exception, marshaledObj, scriptContext);
  272. });
  273. this->CallDebugEventCallbackForBreak(jsDiagDebugEvent, eventDataObject, scriptContext);
  274. }
  275. }
  276. void JsrtDebugManager::HandleResume(Js::InterpreterHaltState* haltState, BREAKRESUMEACTION resumeAction)
  277. {
  278. Assert(resumeAction != BREAKRESUMEACTION_ABORT);
  279. Js::ScriptContext* scriptContext = haltState->framePointers->Peek()->GetScriptContext();
  280. scriptContext->GetThreadContext()->GetDebugManager()->stepController.HandleResumeAction(haltState, resumeAction);
  281. }
  282. void JsrtDebugManager::SetResumeType(BREAKRESUMEACTION resumeAction)
  283. {
  284. this->resumeAction = resumeAction;
  285. }
  286. bool JsrtDebugManager::EnableAsyncBreak(Js::ScriptContext* scriptContext)
  287. {
  288. if (!scriptContext->IsDebugContextInitialized())
  289. {
  290. // Although the script context exists, it hasn't been fully initialized yet.
  291. return false;
  292. }
  293. Js::ProbeContainer* probeContainer = scriptContext->GetDebugContext()->GetProbeContainer();
  294. // This can be called when we are already at break
  295. if (!probeContainer->IsAsyncActivate())
  296. {
  297. probeContainer->AsyncActivate(this);
  298. if (Js::Configuration::Global.EnableJitInDebugMode())
  299. {
  300. scriptContext->GetThreadContext()->GetDebugManager()->GetDebuggingFlags()->SetForceInterpreter(true);
  301. }
  302. return true;
  303. }
  304. return false;
  305. }
  306. void JsrtDebugManager::CallDebugEventCallback(JsDiagDebugEvent debugEvent, Js::DynamicObject* eventDataObject, Js::ScriptContext* scriptContext, bool isBreak)
  307. {
  308. class AutoClear
  309. {
  310. public:
  311. AutoClear(JsrtDebugManager* jsrtDebug, void* dispatchHaltFrameAddress)
  312. {
  313. this->jsrtDebugManager = jsrtDebug;
  314. this->jsrtDebugManager->GetThreadContext()->GetDebugManager()->SetDispatchHaltFrameAddress(dispatchHaltFrameAddress);
  315. }
  316. ~AutoClear()
  317. {
  318. if (jsrtDebugManager->debuggerObjectsManager != nullptr)
  319. {
  320. jsrtDebugManager->GetDebuggerObjectsManager()->ClearAll();
  321. }
  322. if (jsrtDebugManager->stackFrames != nullptr)
  323. {
  324. Adelete(jsrtDebugManager->GetDebugObjectArena(), jsrtDebugManager->stackFrames);
  325. jsrtDebugManager->stackFrames = nullptr;
  326. }
  327. this->jsrtDebugManager->GetThreadContext()->GetDebugManager()->SetDispatchHaltFrameAddress(nullptr);
  328. this->jsrtDebugManager = nullptr;
  329. }
  330. private:
  331. JsrtDebugManager* jsrtDebugManager;
  332. };
  333. auto funcPtr = [&]()
  334. {
  335. if (isBreak)
  336. {
  337. void *frameAddress = _AddressOfReturnAddress();
  338. // If we are reporting break we should clear all objects after call returns
  339. // Save the frame address, when asking for stack we will only give stack which is under this address
  340. // because host can execute javascript after break which should not be part of stack.
  341. AutoClear autoClear(this, frameAddress);
  342. this->debugEventCallback(debugEvent, eventDataObject, this->callbackState);
  343. }
  344. else
  345. {
  346. this->debugEventCallback(debugEvent, eventDataObject, this->callbackState);
  347. }
  348. };
  349. if (scriptContext->GetThreadContext()->IsScriptActive())
  350. {
  351. BEGIN_LEAVE_SCRIPT(scriptContext)
  352. {
  353. funcPtr();
  354. }
  355. END_LEAVE_SCRIPT(scriptContext);
  356. }
  357. else
  358. {
  359. funcPtr();
  360. }
  361. }
  362. void JsrtDebugManager::CallDebugEventCallbackForBreak(JsDiagDebugEvent debugEvent, Js::DynamicObject* eventDataObject, Js::ScriptContext* scriptContext)
  363. {
  364. AutoSetDispatchHaltFlag autoSetDispatchHaltFlag(scriptContext, scriptContext->GetThreadContext());
  365. this->CallDebugEventCallback(debugEvent, eventDataObject, scriptContext, true /*isBreak*/);
  366. for (Js::ScriptContext *tempScriptContext = scriptContext->GetThreadContext()->GetScriptContextList();
  367. tempScriptContext != nullptr && !tempScriptContext->IsClosed();
  368. tempScriptContext = tempScriptContext->next)
  369. {
  370. tempScriptContext->GetDebugContext()->GetProbeContainer()->AsyncDeactivate();
  371. }
  372. if (Js::Configuration::Global.EnableJitInDebugMode())
  373. {
  374. scriptContext->GetThreadContext()->GetDebugManager()->GetDebuggingFlags()->SetForceInterpreter(false);
  375. }
  376. }
  377. Js::DynamicObject* JsrtDebugManager::GetScript(Js::Utf8SourceInfo* utf8SourceInfo)
  378. {
  379. Js::DynamicObject* scriptObject = utf8SourceInfo->GetScriptContext()->GetLibrary()->CreateObject();
  380. JsrtDebugUtils::AddSourceMetadataToObject(scriptObject, utf8SourceInfo);
  381. return scriptObject;
  382. }
  383. Js::JavascriptArray* JsrtDebugManager::GetScripts(Js::ScriptContext* scriptContext)
  384. {
  385. Js::JavascriptArray* scriptsArray = scriptContext->GetLibrary()->CreateArray();
  386. int index = 0;
  387. for (Js::ScriptContext *tempScriptContext = scriptContext->GetThreadContext()->GetScriptContextList();
  388. tempScriptContext != nullptr && !tempScriptContext->IsClosed();
  389. tempScriptContext = tempScriptContext->next)
  390. {
  391. tempScriptContext->MapScript([&](Js::Utf8SourceInfo* utf8SourceInfo)
  392. {
  393. if (!utf8SourceInfo->GetIsLibraryCode() && utf8SourceInfo->HasDebugDocument())
  394. {
  395. bool isCallerLibraryCode = false;
  396. bool isDynamic = utf8SourceInfo->IsDynamic();
  397. if (isDynamic)
  398. {
  399. // If the code is dynamic (eval or new Function) only return the script if parent is non-library
  400. Js::Utf8SourceInfo* callerUtf8SourceInfo = utf8SourceInfo->GetCallerUtf8SourceInfo();
  401. while (callerUtf8SourceInfo != nullptr && !isCallerLibraryCode)
  402. {
  403. isCallerLibraryCode = callerUtf8SourceInfo->GetIsLibraryCode();
  404. callerUtf8SourceInfo = callerUtf8SourceInfo->GetCallerUtf8SourceInfo();
  405. }
  406. }
  407. if (!isCallerLibraryCode)
  408. {
  409. Js::DynamicObject* sourceObj = this->GetScript(utf8SourceInfo);
  410. if (sourceObj != nullptr)
  411. {
  412. Js::Var marshaledObj = Js::CrossSite::MarshalVar(scriptContext, sourceObj);
  413. scriptsArray->DirectSetItemAt(index, marshaledObj);
  414. index++;
  415. }
  416. }
  417. }
  418. });
  419. }
  420. return scriptsArray;
  421. }
  422. Js::DynamicObject* JsrtDebugManager::GetSource(Js::ScriptContext* scriptContext, uint scriptId)
  423. {
  424. Js::Utf8SourceInfo* utf8SourceInfo = nullptr;
  425. for (Js::ScriptContext *tempScriptContext = this->threadContext->GetScriptContextList();
  426. tempScriptContext != nullptr && utf8SourceInfo == nullptr && !tempScriptContext->IsClosed();
  427. tempScriptContext = tempScriptContext->next)
  428. {
  429. tempScriptContext->MapScript([&](Js::Utf8SourceInfo* sourceInfo) -> bool
  430. {
  431. if (sourceInfo->IsInDebugMode() && sourceInfo->GetSourceInfoId() == scriptId)
  432. {
  433. utf8SourceInfo = sourceInfo;
  434. return true;
  435. }
  436. return false;
  437. });
  438. }
  439. Js::DynamicObject* sourceObject = nullptr;
  440. if (utf8SourceInfo != nullptr)
  441. {
  442. sourceObject = (Js::DynamicObject*)Js::CrossSite::MarshalVar(utf8SourceInfo->GetScriptContext(), scriptContext->GetLibrary()->CreateObject());
  443. JsrtDebugUtils::AddSourceMetadataToObject(sourceObject, utf8SourceInfo);
  444. JsrtDebugUtils::AddSourceToObject(sourceObject, utf8SourceInfo);
  445. }
  446. return sourceObject;
  447. }
  448. Js::JavascriptArray* JsrtDebugManager::GetStackFrames(Js::ScriptContext* scriptContext)
  449. {
  450. if (this->stackFrames == nullptr)
  451. {
  452. this->stackFrames = Anew(this->GetDebugObjectArena(), JsrtDebugStackFrames, this);
  453. }
  454. return this->stackFrames->StackFrames(scriptContext);
  455. }
  456. bool JsrtDebugManager::TryGetFrameObjectFromFrameIndex(Js::ScriptContext *scriptContext, uint frameIndex, JsrtDebuggerStackFrame ** debuggerStackFrame)
  457. {
  458. if (this->stackFrames == nullptr)
  459. {
  460. this->GetStackFrames(scriptContext);
  461. }
  462. return this->stackFrames->TryGetFrameObjectFromFrameIndex(frameIndex, debuggerStackFrame);
  463. }
  464. Js::DynamicObject* JsrtDebugManager::SetBreakPoint(Js::ScriptContext* scriptContext, Js::Utf8SourceInfo* utf8SourceInfo, UINT lineNumber, UINT columnNumber)
  465. {
  466. Js::DebugDocument* debugDocument = utf8SourceInfo->GetDebugDocument();
  467. if (debugDocument != nullptr && SUCCEEDED(utf8SourceInfo->EnsureLineOffsetCacheNoThrow()) && lineNumber < utf8SourceInfo->GetLineCount())
  468. {
  469. charcount_t charPosition = 0;
  470. charcount_t byteOffset = 0;
  471. utf8SourceInfo->GetCharPositionForLineInfo((charcount_t)lineNumber, &charPosition, &byteOffset);
  472. long ibos = charPosition + columnNumber + 1;
  473. Js::StatementLocation statement;
  474. if (!debugDocument->GetStatementLocation(ibos, &statement))
  475. {
  476. return nullptr;
  477. }
  478. // Don't see a use case for supporting multiple breakpoints at same location.
  479. // If a breakpoint already exists, just return that
  480. Js::BreakpointProbe* probe = debugDocument->FindBreakpoint(statement);
  481. if (probe == nullptr)
  482. {
  483. probe = debugDocument->SetBreakPoint(statement, BREAKPOINT_ENABLED);
  484. if(probe == nullptr)
  485. {
  486. return nullptr;
  487. }
  488. this->GetDebugDocumentManager()->AddDocument(probe->GetId(), debugDocument);
  489. }
  490. probe->GetStatementLocation(&statement);
  491. Js::DynamicObject* bpObject = (Js::DynamicObject*)Js::CrossSite::MarshalVar(debugDocument->GetUtf8SourceInfo()->GetScriptContext(), scriptContext->GetLibrary()->CreateObject());
  492. JsrtDebugUtils::AddPropertyToObject(bpObject, JsrtDebugPropertyId::breakpointId, probe->GetId(), scriptContext);
  493. JsrtDebugUtils::AddLineColumnToObject(bpObject, statement.function, statement.bytecodeSpan.begin);
  494. JsrtDebugUtils::AddScriptIdToObject(bpObject, utf8SourceInfo);
  495. return bpObject;
  496. }
  497. return nullptr;
  498. }
  499. void JsrtDebugManager::GetBreakpoints(Js::JavascriptArray** bpsArray, Js::ScriptContext* scriptContext)
  500. {
  501. Js::ScriptContext* arrayScriptContext = (*bpsArray)->GetScriptContext();
  502. Js::ProbeContainer* probeContainer = scriptContext->GetDebugContext()->GetProbeContainer();
  503. probeContainer->MapProbes([&](int i, Js::Probe* pProbe)
  504. {
  505. Js::BreakpointProbe* bp = (Js::BreakpointProbe*)pProbe;
  506. Js::DynamicObject* bpObject = scriptContext->GetLibrary()->CreateObject();
  507. JsrtDebugUtils::AddPropertyToObject(bpObject, JsrtDebugPropertyId::breakpointId, bp->GetId(), scriptContext);
  508. JsrtDebugUtils::AddLineColumnToObject(bpObject, bp->GetFunctionBody(), bp->GetBytecodeOffset());
  509. Js::Utf8SourceInfo* utf8SourceInfo = bp->GetDbugDocument()->GetUtf8SourceInfo();
  510. JsrtDebugUtils::AddScriptIdToObject(bpObject, utf8SourceInfo);
  511. Js::Var marshaledObj = Js::CrossSite::MarshalVar(arrayScriptContext, bpObject);
  512. Js::JavascriptOperators::OP_SetElementI((Js::Var)(*bpsArray), Js::JavascriptNumber::ToVar((*bpsArray)->GetLength(), arrayScriptContext), marshaledObj, arrayScriptContext);
  513. });
  514. }
  515. #if ENABLE_TTD
  516. Js::BreakpointProbe* JsrtDebugManager::SetBreakpointHelper_TTD(int64 desiredBpId, Js::ScriptContext* scriptContext, Js::Utf8SourceInfo* utf8SourceInfo, UINT lineNumber, UINT columnNumber, BOOL* isNewBP)
  517. {
  518. *isNewBP = FALSE;
  519. Js::DebugDocument* debugDocument = utf8SourceInfo->GetDebugDocument();
  520. if(debugDocument != nullptr && SUCCEEDED(utf8SourceInfo->EnsureLineOffsetCacheNoThrow()) && lineNumber < utf8SourceInfo->GetLineCount())
  521. {
  522. charcount_t charPosition = 0;
  523. charcount_t byteOffset = 0;
  524. utf8SourceInfo->GetCharPositionForLineInfo((charcount_t)lineNumber, &charPosition, &byteOffset);
  525. long ibos = charPosition + columnNumber + 1;
  526. Js::StatementLocation statement;
  527. if(!debugDocument->GetStatementLocation(ibos, &statement))
  528. {
  529. return nullptr;
  530. }
  531. // Don't see a use case for supporting multiple breakpoints at same location.
  532. // If a breakpoint already exists, just return that
  533. Js::BreakpointProbe* probe = debugDocument->FindBreakpoint(statement);
  534. TTDAssert(probe == nullptr || desiredBpId == -1, "We shouldn't be resetting this BP unless it was cleared earlier!");
  535. if(probe == nullptr)
  536. {
  537. probe = debugDocument->SetBreakPoint_TTDWbpId(desiredBpId, statement);
  538. *isNewBP = TRUE;
  539. this->GetDebugDocumentManager()->AddDocument(probe->GetId(), debugDocument);
  540. }
  541. return probe;
  542. }
  543. return nullptr;
  544. }
  545. #endif
  546. JsrtDebuggerObjectsManager* JsrtDebugManager::GetDebuggerObjectsManager()
  547. {
  548. if (this->debuggerObjectsManager == nullptr)
  549. {
  550. this->debuggerObjectsManager = Anew(this->GetDebugObjectArena(), JsrtDebuggerObjectsManager, this);
  551. }
  552. return this->debuggerObjectsManager;
  553. }
  554. void JsrtDebugManager::ClearDebuggerObjects()
  555. {
  556. if (this->debuggerObjectsManager != nullptr)
  557. {
  558. this->debuggerObjectsManager->ClearAll();
  559. }
  560. }
  561. ArenaAllocator* JsrtDebugManager::GetDebugObjectArena()
  562. {
  563. if (this->debugObjectArena == nullptr)
  564. {
  565. this->debugObjectArena = HeapNew(ArenaAllocator, _u("DebugObjectArena"), this->threadContext->GetPageAllocator(), Js::Throw::OutOfMemory);
  566. this->threadContext->GetRecycler()->RegisterExternalGuestArena(this->debugObjectArena);
  567. }
  568. return this->debugObjectArena;
  569. }
  570. JsrtDebugDocumentManager* JsrtDebugManager::GetDebugDocumentManager()
  571. {
  572. if (this->debugDocumentManager == nullptr)
  573. {
  574. this->debugDocumentManager = Anew(this->GetDebugObjectArena(), JsrtDebugDocumentManager, this);
  575. }
  576. return this->debugDocumentManager;
  577. }
  578. void JsrtDebugManager::ClearDebugDocument(Js::ScriptContext* scriptContext)
  579. {
  580. if (this->debugDocumentManager != nullptr)
  581. {
  582. this->debugDocumentManager->ClearDebugDocument(scriptContext);
  583. }
  584. }
  585. void JsrtDebugManager::ClearBreakpointDebugDocumentDictionary()
  586. {
  587. if (this->debugDocumentManager != nullptr)
  588. {
  589. this->debugDocumentManager->ClearBreakpointDebugDocumentDictionary();
  590. }
  591. }
  592. bool JsrtDebugManager::RemoveBreakpoint(UINT breakpointId)
  593. {
  594. if (this->debugDocumentManager != nullptr)
  595. {
  596. return this->GetDebugDocumentManager()->RemoveBreakpoint(breakpointId);
  597. }
  598. return false;
  599. }
  600. void JsrtDebugManager::SetBreakOnException(JsDiagBreakOnExceptionAttributes exceptionAttributes)
  601. {
  602. this->breakOnExceptionAttributes = exceptionAttributes;
  603. }
  604. JsDiagBreakOnExceptionAttributes JsrtDebugManager::GetBreakOnException()
  605. {
  606. return this->breakOnExceptionAttributes;
  607. }
  608. JsDiagDebugEvent JsrtDebugManager::GetDebugEventFromStopType(Js::StopType stopType)
  609. {
  610. switch (stopType)
  611. {
  612. case Js::STOP_BREAKPOINT: return JsDiagDebugEventBreakpoint;
  613. case Js::STOP_INLINEBREAKPOINT: return JsDiagDebugEventDebuggerStatement;
  614. case Js::STOP_STEPCOMPLETE: return JsDiagDebugEventStepComplete;
  615. case Js::STOP_EXCEPTIONTHROW: return JsDiagDebugEventRuntimeException;
  616. case Js::STOP_ASYNCBREAK: return JsDiagDebugEventAsyncBreak;
  617. case Js::STOP_MUTATIONBREAKPOINT:
  618. default:
  619. Assert("Unhandled stoptype");
  620. break;
  621. }
  622. return JsDiagDebugEventBreakpoint;
  623. }
  624. #endif