JsrtDebugManager.cpp 29 KB

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