ProbeContainer.cpp 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  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 "RuntimeDebugPch.h"
  6. #ifdef ENABLE_SCRIPT_DEBUGGING
  7. #include "Language/JavascriptStackWalker.h"
  8. #include "Language/InterpreterStackFrame.h"
  9. namespace Js
  10. {
  11. ProbeContainer::ProbeContainer() :
  12. diagProbeList(nullptr),
  13. pScriptContext(nullptr),
  14. debugManager(nullptr),
  15. haltCallbackProbe(nullptr),
  16. debuggerOptionsCallback(nullptr),
  17. pAsyncHaltCallback(nullptr),
  18. jsExceptionObject(nullptr),
  19. framePointers(nullptr),
  20. debugSessionNumber(0),
  21. tmpRegCount(0),
  22. bytecodeOffset(0),
  23. IsNextStatementChanged(false),
  24. isThrowInternal(false),
  25. forceBypassDebugEngine(false),
  26. isPrimaryBrokenToDebuggerContext(false),
  27. isForcedToEnterScriptStart(false),
  28. registeredFuncContextList(nullptr)
  29. {
  30. }
  31. ProbeContainer::~ProbeContainer()
  32. {
  33. this->Close();
  34. }
  35. void ProbeContainer::Close()
  36. {
  37. // Probe manager instance may go down early.
  38. if (this->pScriptContext)
  39. {
  40. debugManager = this->pScriptContext->GetThreadContext()->GetDebugManager();
  41. }
  42. else
  43. {
  44. debugManager = nullptr;
  45. }
  46. if (debugManager != nullptr && debugManager->stepController.pActivatedContext == pScriptContext)
  47. {
  48. debugManager->stepController.Deactivate();
  49. }
  50. #ifdef ENABLE_MUTATION_BREAKPOINT
  51. this->RemoveMutationBreakpointListIfNeeded();
  52. #endif
  53. pScriptContext = nullptr;
  54. debugManager = nullptr;
  55. }
  56. void ProbeContainer::Initialize(ScriptContext* pScriptContext)
  57. {
  58. if (!diagProbeList)
  59. {
  60. ArenaAllocator* global = pScriptContext->AllocatorForDiagnostics();
  61. diagProbeList = ProbeList::New(global);
  62. pendingProbeList = ProbeList::New(global);
  63. this->pScriptContext = pScriptContext;
  64. this->debugManager = this->pScriptContext->GetThreadContext()->GetDebugManager();
  65. this->pinnedPropertyRecords = JsUtil::List<const Js::PropertyRecord*>::New(this->pScriptContext->GetRecycler());
  66. this->pScriptContext->BindReference((void *)this->pinnedPropertyRecords);
  67. }
  68. }
  69. void ProbeContainer::StartRecordingCall()
  70. {
  71. Assert(this->pScriptContext->GetDebugContext() && this->pScriptContext->GetDebugContext()->IsDebuggerRecording());
  72. this->debugManager->stepController.StartRecordingCall();
  73. }
  74. void ProbeContainer::EndRecordingCall(Js::Var returnValue, Js::JavascriptFunction * function)
  75. {
  76. Assert(this->pScriptContext->GetDebugContext() && this->pScriptContext->GetDebugContext()->IsDebuggerRecording());
  77. this->debugManager->stepController.EndRecordingCall(returnValue, function);
  78. }
  79. ReturnedValueList* ProbeContainer::GetReturnedValueList() const
  80. {
  81. return this->debugManager->stepController.GetReturnedValueList();
  82. }
  83. void ProbeContainer::ResetReturnedValueList()
  84. {
  85. this->debugManager->stepController.ResetReturnedValueList();
  86. }
  87. void ProbeContainer::UpdateFramePointers(bool fMatchWithCurrentScriptContext, DWORD_PTR dispatchHaltFrameAddress)
  88. {
  89. ArenaAllocator* pDiagArena = debugManager->GetDiagnosticArena()->Arena();
  90. framePointers = Anew(pDiagArena, DiagStack, pDiagArena);
  91. JavascriptStackWalker walker(pScriptContext, !fMatchWithCurrentScriptContext, nullptr/*returnAddress*/, true/*forceFullWalk*/);
  92. DiagStack* tempFramePointers = Anew(pDiagArena, DiagStack, pDiagArena);
  93. const bool isLibraryFrameEnabledDebugger = IsLibraryStackFrameSupportEnabled();
  94. walker.WalkUntil([&](JavascriptFunction* func, ushort frameIndex) -> bool
  95. {
  96. if (isLibraryFrameEnabledDebugger || !func->IsLibraryCode())
  97. {
  98. DiagStackFrame* frm = nullptr;
  99. InterpreterStackFrame *interpreterFrame = walker.GetCurrentInterpreterFrame();
  100. ScriptContext* frameScriptContext = walker.GetCurrentScriptContext();
  101. Assert(frameScriptContext);
  102. if (!fMatchWithCurrentScriptContext && !frameScriptContext->IsScriptContextInDebugMode() && tempFramePointers->Count() == 0)
  103. {
  104. // this means the top frame is not in the debug mode. We shouldn't be stopping for this break.
  105. // This could happen if the exception happens on the diagnosticsScriptEngine.
  106. return true;
  107. }
  108. // Ignore frames which are not in debug mode, which can happen when diag engine calls into user engine under debugger
  109. // -- topmost frame is under debugger but some frames could be in non-debug mode as they are from diag engine.
  110. if (frameScriptContext->IsScriptContextInDebugMode() &&
  111. (!fMatchWithCurrentScriptContext || frameScriptContext == pScriptContext))
  112. {
  113. if (interpreterFrame)
  114. {
  115. if (dispatchHaltFrameAddress == 0 || interpreterFrame->GetStackAddress() > dispatchHaltFrameAddress)
  116. {
  117. frm = Anew(pDiagArena, DiagInterpreterStackFrame, interpreterFrame);
  118. }
  119. }
  120. else
  121. {
  122. void* stackAddress = walker.GetCurrentArgv();
  123. if (dispatchHaltFrameAddress == 0 || reinterpret_cast<DWORD_PTR>(stackAddress) > dispatchHaltFrameAddress)
  124. {
  125. #if ENABLE_NATIVE_CODEGEN
  126. if (func->IsScriptFunction())
  127. {
  128. frm = Anew(pDiagArena, DiagNativeStackFrame,
  129. ScriptFunction::FromVar(walker.GetCurrentFunction()), walker.GetByteCodeOffset(), stackAddress, walker.GetCurrentCodeAddr());
  130. }
  131. else
  132. #else
  133. Assert(!func->IsScriptFunction());
  134. #endif
  135. {
  136. frm = Anew(pDiagArena, DiagRuntimeStackFrame, func, walker.GetCurrentNativeLibraryEntryName(), stackAddress);
  137. }
  138. }
  139. }
  140. }
  141. if (frm)
  142. {
  143. tempFramePointers->Push(frm);
  144. }
  145. }
  146. return false;
  147. });
  148. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::UpdateFramePointers: detected %d frames (this=%p, fMatchWithCurrentScriptContext=%d)\n"),
  149. tempFramePointers->Count(), this, fMatchWithCurrentScriptContext);
  150. while (tempFramePointers->Count())
  151. {
  152. framePointers->Push(tempFramePointers->Pop());
  153. }
  154. }
  155. WeakDiagStack * ProbeContainer::GetFramePointers(DWORD_PTR dispatchHaltFrameAddress)
  156. {
  157. if (framePointers == nullptr || this->debugSessionNumber < debugManager->GetDebugSessionNumber())
  158. {
  159. UpdateFramePointers(/*fMatchWithCurrentScriptContext*/true, dispatchHaltFrameAddress);
  160. this->debugSessionNumber = debugManager->GetDebugSessionNumber();
  161. if ((framePointers->Count() > 0) &&
  162. debugManager->IsMatchTopFrameStackAddress(framePointers->Peek(0)))
  163. {
  164. framePointers->Peek(0)->SetIsTopFrame();
  165. }
  166. }
  167. ReferencedArenaAdapter* pRefArena = debugManager->GetDiagnosticArena();
  168. return HeapNew(WeakDiagStack,pRefArena,framePointers);
  169. }
  170. bool ProbeContainer::InitializeLocation(InterpreterHaltState* pHaltState, bool fMatchWithCurrentScriptContext)
  171. {
  172. Assert(debugManager);
  173. debugManager->SetCurrentInterpreterLocation(pHaltState);
  174. ArenaAllocator* pDiagArena = debugManager->GetDiagnosticArena()->Arena();
  175. UpdateFramePointers(fMatchWithCurrentScriptContext);
  176. pHaltState->framePointers = framePointers;
  177. pHaltState->stringBuilder = Anew(pDiagArena, StringBuilder<ArenaAllocator>, pDiagArena);
  178. if (pHaltState->framePointers->Count() > 0)
  179. {
  180. pHaltState->topFrame = pHaltState->framePointers->Peek(0);
  181. pHaltState->topFrame->SetIsTopFrame();
  182. }
  183. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::InitializeLocation (end): this=%p, pHaltState=%p, fMatch=%d, topFrame=%p\n"),
  184. this, pHaltState, fMatchWithCurrentScriptContext, pHaltState->topFrame);
  185. return true;
  186. }
  187. void ProbeContainer::DestroyLocation()
  188. {
  189. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DestroyLocation (start): this=%p, IsNextStatementChanged=%d, haltCallbackProbe=%p\n"),
  190. this, this->IsNextStatementChanged, haltCallbackProbe);
  191. if (IsNextStatementChanged)
  192. {
  193. Assert(bytecodeOffset != debugManager->stepController.byteOffset);
  194. // Note: when we dispatching an exception bytecodeOffset would be same as pProbeManager->pCurrentInterpreterLocation->GetCurrentOffset().
  195. debugManager->pCurrentInterpreterLocation->SetCurrentOffset(bytecodeOffset);
  196. IsNextStatementChanged = false;
  197. }
  198. framePointers = nullptr;
  199. // Reset the exception object.
  200. jsExceptionObject = nullptr;
  201. Assert(debugManager);
  202. debugManager->UnsetCurrentInterpreterLocation();
  203. pinnedPropertyRecords->Reset();
  204. // Guarding if the probe engine goes away when we are sitting at breakpoint.
  205. if (haltCallbackProbe)
  206. {
  207. // The clean up is called here to scriptengine's object to remove all DebugStackFrames
  208. haltCallbackProbe->CleanupHalt();
  209. }
  210. }
  211. bool ProbeContainer::CanDispatchHalt(InterpreterHaltState* pHaltState)
  212. {
  213. if (!haltCallbackProbe || haltCallbackProbe->IsInClosedState() || debugManager->IsAtDispatchHalt())
  214. {
  215. OUTPUT_VERBOSE_TRACE(Js::DebuggerPhase, _u("ProbeContainer::CanDispatchHalt: Not in break mode. pHaltState = %p\n"), pHaltState);
  216. return false;
  217. }
  218. return true;
  219. }
  220. void ProbeContainer::DispatchStepHandler(InterpreterHaltState* pHaltState, OpCode* pOriginalOpcode)
  221. {
  222. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: start: this=%p, pHaltState=%p, pOriginalOpcode=0x%x\n"), this, pHaltState, pOriginalOpcode);
  223. if (!CanDispatchHalt(pHaltState))
  224. {
  225. return;
  226. }
  227. TryFinally([&]()
  228. {
  229. InitializeLocation(pHaltState);
  230. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"),
  231. pHaltState, pHaltState->IsValid());
  232. if (pHaltState->IsValid()) // Only proceed if we find a valid top frame and that is the executing function
  233. {
  234. if (debugManager->stepController.IsStepComplete(pHaltState, haltCallbackProbe, *pOriginalOpcode))
  235. {
  236. OpCode oldOpcode = *pOriginalOpcode;
  237. pHaltState->GetFunction()->ProbeAtOffset(pHaltState->GetCurrentOffset(), pOriginalOpcode);
  238. pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  239. debugManager->stepController.Deactivate(pHaltState);
  240. haltCallbackProbe->DispatchHalt(pHaltState);
  241. if (oldOpcode == OpCode::Break && debugManager->stepController.stepType == STEP_DOCUMENT)
  242. {
  243. // That means we have delivered the stepping to the debugger, where we had the breakpoint
  244. // already, however it is possible that debugger can initiate the step_document. In that
  245. // case debugger did not break due to break. So we have break as a breakpoint reason.
  246. *pOriginalOpcode = OpCode::Break;
  247. }
  248. else if (OpCode::Break == *pOriginalOpcode)
  249. {
  250. debugManager->stepController.stepCompleteOnInlineBreakpoint = true;
  251. }
  252. }
  253. }
  254. },
  255. [&](bool)
  256. {
  257. DestroyLocation();
  258. });
  259. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchStepHandler: end: pHaltState=%p\n"), pHaltState);
  260. }
  261. void ProbeContainer::DispatchAsyncBreak(InterpreterHaltState* pHaltState)
  262. {
  263. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: start: this=%p, pHaltState=%p\n"), this, pHaltState);
  264. if (!this->pAsyncHaltCallback || !CanDispatchHalt(pHaltState))
  265. {
  266. return;
  267. }
  268. TryFinally([&]()
  269. {
  270. InitializeLocation(pHaltState, /* We don't need to match script context, stop at any available script function */ false);
  271. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"),
  272. pHaltState, pHaltState->IsValid());
  273. if (pHaltState->IsValid())
  274. {
  275. // Activate the current haltCallback with asyncStepController.
  276. debugManager->asyncBreakController.Activate(this->pAsyncHaltCallback);
  277. if (debugManager->asyncBreakController.IsAtStoppingLocation(pHaltState))
  278. {
  279. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: IsAtStoppingLocation: pHaltState=%p\n"), pHaltState);
  280. pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  281. debugManager->stepController.Deactivate(pHaltState);
  282. debugManager->asyncBreakController.DispatchAndReset(pHaltState);
  283. }
  284. }
  285. },
  286. [&](bool)
  287. {
  288. DestroyLocation();
  289. });
  290. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchAsyncBreak: end: pHaltState=%p\n"), pHaltState);
  291. }
  292. void ProbeContainer::DispatchInlineBreakpoint(InterpreterHaltState* pHaltState)
  293. {
  294. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState);
  295. if (!CanDispatchHalt(pHaltState))
  296. {
  297. return;
  298. }
  299. Assert(pHaltState->stopType == STOP_INLINEBREAKPOINT);
  300. TryFinally([&]()
  301. {
  302. InitializeLocation(pHaltState);
  303. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"),
  304. pHaltState, pHaltState->IsValid());
  305. Assert(pHaltState->IsValid());
  306. // The ByteCodeReader should be available at this point, but because of possibility of garbled frame, we shouldn't hit AV
  307. if (pHaltState->IsValid())
  308. {
  309. #if DBG
  310. pHaltState->GetFunction()->MustBeInDebugMode();
  311. #endif
  312. // an inline breakpoint is being dispatched deactivate other stopping controllers
  313. debugManager->stepController.Deactivate(pHaltState);
  314. debugManager->asyncBreakController.Deactivate();
  315. pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  316. haltCallbackProbe->DispatchHalt(pHaltState);
  317. }
  318. },
  319. [&](bool)
  320. {
  321. DestroyLocation();
  322. });
  323. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchInlineBreakpoint: end: pHaltState=%p\n"), pHaltState);
  324. }
  325. bool ProbeContainer::DispatchExceptionBreakpoint(InterpreterHaltState* pHaltState)
  326. {
  327. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState);
  328. bool fSuccess = false;
  329. if (!haltCallbackProbe || haltCallbackProbe->IsInClosedState() || debugManager->IsAtDispatchHalt())
  330. {
  331. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: not in break mode: pHaltState=%p\n"), pHaltState);
  332. // Will not be able to handle multiple break-hits.
  333. return fSuccess;
  334. }
  335. Assert(pHaltState->stopType == STOP_EXCEPTIONTHROW);
  336. jsExceptionObject = pHaltState->exceptionObject->GetThrownObject(nullptr);
  337. // Will store current offset of the bytecode block.
  338. int currentOffset = -1;
  339. TryFinally([&]()
  340. {
  341. InitializeLocation(pHaltState, false);
  342. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: initialized location: pHaltState=%p, IsInterpreterFrame=%d\n"),
  343. pHaltState, pHaltState->IsValid(), pHaltState->topFrame && pHaltState->topFrame->IsInterpreterFrame());
  344. // The ByteCodeReader should be available at this point, but because of possibility of garbled frame, we shouldn't hit AV
  345. if (pHaltState->IsValid() && pHaltState->GetFunction()->GetScriptContext()->IsScriptContextInDebugMode())
  346. {
  347. #if DBG
  348. pHaltState->GetFunction()->MustBeInDebugMode();
  349. #endif
  350. // For interpreter frames, change the current location pointer of bytecode block, as it might be pointing to the next statement on the body.
  351. // In order to generated proper binding of break on exception to the statement, the bytecode offset needed to be on the same span
  352. // of the statement.
  353. // For native frames the offset is always current.
  354. // Move back a single byte to ensure that it falls under on the same statement.
  355. if (pHaltState->topFrame->IsInterpreterFrame())
  356. {
  357. currentOffset = pHaltState->GetCurrentOffset();
  358. Assert(currentOffset > 0);
  359. pHaltState->SetCurrentOffset(currentOffset - 1);
  360. }
  361. // an inline breakpoint is being dispatched deactivate other stopping controllers
  362. debugManager->stepController.Deactivate(pHaltState);
  363. debugManager->asyncBreakController.Deactivate();
  364. pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  365. ScriptContext *pTopFuncContext = pHaltState->GetFunction()->GetScriptContext();
  366. // If the top function's context is different from the current context, that means current frame is not alive anymore and breaking here cannot not happen.
  367. // So in that case we will consider the top function's context and break on that context.
  368. if (pTopFuncContext != pScriptContext)
  369. {
  370. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: top function's context is different from the current context: pHaltState=%p, haltCallbackProbe=%p\n"),
  371. pHaltState, pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe);
  372. if (pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe)
  373. {
  374. pTopFuncContext->GetDebugContext()->GetProbeContainer()->haltCallbackProbe->DispatchHalt(pHaltState);
  375. fSuccess = true;
  376. }
  377. }
  378. else
  379. {
  380. haltCallbackProbe->DispatchHalt(pHaltState);
  381. fSuccess = true;
  382. }
  383. }
  384. },
  385. [&](bool)
  386. {
  387. // If the next statement has changed, we need to log that to exception object so that it will not try to advance to next statement again.
  388. pHaltState->exceptionObject->SetIgnoreAdvanceToNextStatement(IsNextStatementChanged);
  389. // Restore the current offset;
  390. if (currentOffset != -1 && pHaltState->topFrame->IsInterpreterFrame())
  391. {
  392. pHaltState->SetCurrentOffset(currentOffset);
  393. }
  394. DestroyLocation();
  395. });
  396. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchExceptionBreakpoint: end: pHaltState=%p, fSuccess=%d\n"), pHaltState, fSuccess);
  397. return fSuccess;
  398. }
  399. void ProbeContainer::DispatchDOMMutationBreakpoint()
  400. {
  401. InterpreterHaltState haltState(STOP_DOMMUTATIONBREAKPOINT, /*_executingFunction*/nullptr);
  402. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchDOMMutationBreakpoint: start: this=%p, pHaltState=%p\n"), this, haltState);
  403. if (!CanDispatchHalt(&haltState))
  404. {
  405. return;
  406. }
  407. int currentOffset = -1;
  408. TryFinally([&]()
  409. {
  410. InitializeLocation(&haltState);
  411. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchDOMMutationBreakpoint: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"),
  412. haltState, haltState.IsValid());
  413. if (haltState.IsValid())
  414. {
  415. if (haltState.topFrame->IsInterpreterFrame())
  416. {
  417. currentOffset = haltState.GetCurrentOffset();
  418. Assert(currentOffset > 0);
  419. haltState.SetCurrentOffset(currentOffset - 1);
  420. }
  421. debugManager->stepController.Deactivate(&haltState);
  422. debugManager->asyncBreakController.Deactivate();
  423. haltState.GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  424. Assert(haltState.GetFunction()->GetScriptContext() == pScriptContext);
  425. haltCallbackProbe->DispatchHalt(&haltState);
  426. }
  427. },
  428. [&](bool)
  429. {
  430. // Restore the current offset;
  431. if (currentOffset != -1 && haltState.topFrame->IsInterpreterFrame())
  432. {
  433. haltState.SetCurrentOffset(currentOffset);
  434. }
  435. DestroyLocation();
  436. });
  437. }
  438. void ProbeContainer::DispatchMutationBreakpoint(InterpreterHaltState* pHaltState)
  439. {
  440. Assert(pHaltState->stopType == STOP_MUTATIONBREAKPOINT);
  441. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchMutationBreakpoint: start: this=%p, pHaltState=%p\n"), this, pHaltState);
  442. if (!CanDispatchHalt(pHaltState))
  443. {
  444. return;
  445. }
  446. // will store Current offset of the bytecode block.
  447. int currentOffset = -1;
  448. TryFinally([&]()
  449. {
  450. InitializeLocation(pHaltState);
  451. OUTPUT_TRACE(Js::DebuggerPhase, _u("ProbeContainer::DispatchMutationBreakpoint: initialized location: pHaltState=%p, pHaltState->IsValid()=%d\n"),
  452. pHaltState, pHaltState->IsValid());
  453. if (pHaltState->IsValid())
  454. {
  455. // For interpreter frames, change the current location pointer of bytecode block, as it might be pointing to the next statement on the body.
  456. // In order to generated proper binding of mutation statement, the bytecode offset needed to be on the same span of the statement.
  457. // For native frames the offset is always current.
  458. // Move back a single byte to ensure that it falls under on the same statement.
  459. if (pHaltState->topFrame->IsInterpreterFrame())
  460. {
  461. currentOffset = pHaltState->GetCurrentOffset();
  462. Assert(currentOffset > 0);
  463. pHaltState->SetCurrentOffset(currentOffset - 1);
  464. }
  465. debugManager->stepController.Deactivate(pHaltState);
  466. debugManager->asyncBreakController.Deactivate();
  467. pHaltState->GetFunction()->CheckAndRegisterFuncToDiag(pScriptContext);
  468. Assert(pHaltState->GetFunction()->GetScriptContext() == pScriptContext);
  469. haltCallbackProbe->DispatchHalt(pHaltState);
  470. }
  471. },
  472. [&](bool)
  473. {
  474. // Restore the current offset;
  475. if (currentOffset != -1 && pHaltState->topFrame->IsInterpreterFrame())
  476. {
  477. pHaltState->SetCurrentOffset(currentOffset);
  478. }
  479. DestroyLocation();
  480. });
  481. }
  482. void ProbeContainer::DispatchProbeHandlers(InterpreterHaltState* pHaltState)
  483. {
  484. if (!CanDispatchHalt(pHaltState))
  485. {
  486. return;
  487. }
  488. TryFinally([&]()
  489. {
  490. InitializeLocation(pHaltState);
  491. if (pHaltState->IsValid())
  492. {
  493. Js::ProbeList * localPendingProbeList = this->pendingProbeList;
  494. diagProbeList->Map([pHaltState, localPendingProbeList](int index, Probe * probe)
  495. {
  496. if (probe->CanHalt(pHaltState))
  497. {
  498. localPendingProbeList->Add(probe);
  499. }
  500. });
  501. if (localPendingProbeList->Count() != 0)
  502. {
  503. localPendingProbeList->MapUntil([&](int index, Probe * probe)
  504. {
  505. if (haltCallbackProbe && !haltCallbackProbe->IsInClosedState())
  506. {
  507. debugManager->stepController.Deactivate(pHaltState);
  508. debugManager->asyncBreakController.Deactivate();
  509. haltCallbackProbe->DispatchHalt(pHaltState);
  510. }
  511. // If SetNextStatement happened between multiple BPs on same location, IP changed so rest of dispatch are not valid.
  512. return this->IsSetNextStatementCalled();
  513. });
  514. }
  515. }
  516. },
  517. [&](bool)
  518. {
  519. pendingProbeList->Clear();
  520. DestroyLocation();
  521. });
  522. }
  523. void ProbeContainer::UpdateStep(bool fDuringSetupDebugApp/*= false*/)
  524. {
  525. // This function indicate that when the page is being refreshed and the last action we have done was stepping.
  526. // so update the state of the current stepController.
  527. if (debugManager)
  528. {
  529. // Usually we need to be in debug mode to UpdateStep. But during setting up new engine to debug mode we have an
  530. // ordering issue and the new engine will enter debug mode after this. So allow non-debug mode if fDuringSetupDebugApp.
  531. AssertMsg(fDuringSetupDebugApp || (pScriptContext && pScriptContext->IsScriptContextInDebugMode()), "Why UpdateStep when we are not in debug mode?");
  532. debugManager->stepController.stepType = STEP_IN;
  533. }
  534. }
  535. void ProbeContainer::DeactivateStep()
  536. {
  537. if (debugManager)
  538. {
  539. debugManager->stepController.stepType = STEP_NONE;
  540. }
  541. }
  542. void ProbeContainer::InitializeInlineBreakEngine(HaltCallback* probe)
  543. {
  544. AssertMsg(!haltCallbackProbe || probe == haltCallbackProbe, "Overwrite of Inline bp probe with different probe");
  545. haltCallbackProbe = probe;
  546. }
  547. void ProbeContainer::UninstallInlineBreakpointProbe(HaltCallback* probe)
  548. {
  549. haltCallbackProbe = nullptr;
  550. }
  551. void ProbeContainer::InitializeDebuggerScriptOptionCallback(DebuggerOptionsCallback* debuggerOptionsCallback)
  552. {
  553. Assert(this->debuggerOptionsCallback == nullptr);
  554. this->debuggerOptionsCallback = debuggerOptionsCallback;
  555. }
  556. void ProbeContainer::UninstallDebuggerScriptOptionCallback()
  557. {
  558. this->debuggerOptionsCallback = nullptr;
  559. }
  560. void ProbeContainer::AddProbe(Probe* pProbe)
  561. {
  562. if (pProbe->Install(nullptr))
  563. {
  564. diagProbeList->Add(pProbe);
  565. }
  566. }
  567. void ProbeContainer::RemoveProbe(Probe* pProbe)
  568. {
  569. if (pProbe->Uninstall(nullptr))
  570. {
  571. diagProbeList->Remove(pProbe);
  572. }
  573. }
  574. void ProbeContainer::RemoveAllProbes()
  575. {
  576. #ifdef ENABLE_MUTATION_BREAKPOINT
  577. if (HasMutationBreakpoints())
  578. {
  579. ClearMutationBreakpoints();
  580. }
  581. #endif
  582. for (int i = 0; i < diagProbeList->Count(); i++)
  583. {
  584. diagProbeList->Item(i)->Uninstall(nullptr);
  585. }
  586. diagProbeList->Clear();
  587. }
  588. // Retrieves the offset of next statement in JavaScript user code for advancing from current statement
  589. // (normal flow-control is respected).
  590. // Returns true on success, false if it's not possible to get next statement for advance from current.
  591. bool ProbeContainer::GetNextUserStatementOffsetForAdvance(Js::FunctionBody* functionBody, ByteCodeReader* reader, int currentOffset, int* nextStatementOffset)
  592. {
  593. int originalCurrentOffset = currentOffset;
  594. while (GetNextUserStatementOffsetHelper(functionBody, currentOffset, FunctionBody::SAT_FromCurrentToNext, nextStatementOffset))
  595. {
  596. Js::DebuggerScope *debuggerScope = functionBody->GetDiagCatchScopeObjectAt(currentOffset);
  597. if (debuggerScope != nullptr && !debuggerScope->IsOffsetInScope(*nextStatementOffset))
  598. {
  599. // Our next statement is not within this catch block, So we cannot just jump to it, we need to return false so the stack unwind will happen.
  600. return false;
  601. }
  602. Assert(currentOffset < *nextStatementOffset);
  603. if (IsTmpRegCountIncreased(functionBody, reader, originalCurrentOffset, *nextStatementOffset, true /*restoreOffset*/))
  604. {
  605. currentOffset = *nextStatementOffset;
  606. }
  607. else
  608. {
  609. return true;
  610. }
  611. }
  612. return false;
  613. }
  614. // Retrieves the offset of beginning of next statement in JavaScript user code for explicit set next statement
  615. // (normal flow-control is not respected, just get start next statement).
  616. // Returns true on success, false if it's not possible to get next statement for advance from current.
  617. bool ProbeContainer::GetNextUserStatementOffsetForSetNext(Js::FunctionBody* functionBody, int currentOffset, int* nextStatementOffset)
  618. {
  619. return GetNextUserStatementOffsetHelper(functionBody, currentOffset, FunctionBody::SAT_NextStatementStart, nextStatementOffset);
  620. }
  621. // Retrieves the offset of beginning of next statement in JavaScript user code for scenario specified by adjType.
  622. // Returns true on success, false if it's not possible to get next statement for advance from current.
  623. bool ProbeContainer::GetNextUserStatementOffsetHelper(
  624. Js::FunctionBody* functionBody, int currentOffset, FunctionBody::StatementAdjustmentType adjType, int* nextStatementOffset)
  625. {
  626. Assert(functionBody);
  627. Assert(nextStatementOffset);
  628. FunctionBody::StatementMapList* pStatementMaps = functionBody->GetStatementMaps();
  629. if (pStatementMaps && pStatementMaps->Count() > 1)
  630. {
  631. for (int index = 0; index < pStatementMaps->Count() - 1; index++)
  632. {
  633. FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
  634. if (!pStatementMap->isSubexpression && pStatementMap->byteCodeSpan.Includes(currentOffset))
  635. {
  636. int nextMapIndex = index;
  637. FunctionBody::StatementMap* pNextStatementMap = Js::FunctionBody::GetNextNonSubexpressionStatementMap(pStatementMaps, ++nextMapIndex);
  638. if (!pNextStatementMap)
  639. {
  640. break;
  641. }
  642. // We are trying to find out the Branch opcode, between current and next statement. Skipping that would give use incorrect execution order.
  643. FunctionBody::StatementAdjustmentRecord adjRecord;
  644. if (pNextStatementMap->byteCodeSpan.begin > pStatementMap->byteCodeSpan.end &&
  645. functionBody->GetBranchOffsetWithin(pStatementMap->byteCodeSpan.end, pNextStatementMap->byteCodeSpan.begin, &adjRecord) &&
  646. (adjRecord.GetAdjustmentType() & adjType))
  647. {
  648. Assert(adjRecord.GetByteCodeOffset() > (uint)pStatementMap->byteCodeSpan.end);
  649. *nextStatementOffset = adjRecord.GetByteCodeOffset();
  650. }
  651. else
  652. {
  653. *nextStatementOffset = pNextStatementMap->byteCodeSpan.begin;
  654. }
  655. return true;
  656. }
  657. }
  658. }
  659. *nextStatementOffset = -1;
  660. return false;
  661. }
  662. bool ProbeContainer::FetchTmpRegCount(Js::FunctionBody * functionBody, Js::ByteCodeReader * reader, int atOffset, uint32 *pTmpRegCount, Js::OpCode *pOp)
  663. {
  664. Assert(pTmpRegCount);
  665. Assert(pOp);
  666. Js::LayoutSize layoutSize;
  667. reader->SetCurrentOffset(atOffset);
  668. *pOp = reader->ReadOp(layoutSize);
  669. if (*pOp == Js::OpCode::Break)
  670. {
  671. // User might have put breakpoint on the skipped or target statement, get the original opcode;
  672. if (functionBody->ProbeAtOffset(atOffset, pOp))
  673. {
  674. if (Js::OpCodeUtil::IsPrefixOpcode(*pOp))
  675. {
  676. *pOp = reader->ReadPrefixedOp(layoutSize, *pOp);
  677. }
  678. }
  679. }
  680. if (*pOp == Js::OpCode::EmitTmpRegCount)
  681. {
  682. switch (layoutSize)
  683. {
  684. case Js::SmallLayout:
  685. {
  686. const unaligned Js::OpLayoutReg1_Small * playout = reader->Reg1_Small();
  687. *pTmpRegCount = (uint32)playout->R0;
  688. }
  689. break;
  690. case Js::MediumLayout:
  691. {
  692. const unaligned Js::OpLayoutReg1_Medium * playout = reader->Reg1_Medium();
  693. *pTmpRegCount = (uint32)playout->R0;
  694. }
  695. break;
  696. case Js::LargeLayout:
  697. {
  698. const unaligned Js::OpLayoutReg1_Large * playout = reader->Reg1_Large();
  699. *pTmpRegCount = (uint32)playout->R0;
  700. }
  701. break;
  702. default:
  703. Assert(false);
  704. __assume(false);
  705. }
  706. return true;
  707. }
  708. return false;
  709. }
  710. // The logic below makes use of number of tmp (temp) registers of A and B.
  711. // Set next statement is not allowed.
  712. // if numberOfTmpReg(A) < numberOfTmpReg(B)
  713. // or if any statement between A and B has number of tmpReg more than the lowest found.
  714. //
  715. // Get the temp register count for the A
  716. // This is a base and will store the lowest tmp reg count we have got yet, while walking the skipped statements.
  717. bool ProbeContainer::IsTmpRegCountIncreased(Js::FunctionBody* functionBody, ByteCodeReader* reader, int currentOffset, int nextStmOffset, bool restoreOffset)
  718. {
  719. Js::FunctionBody::StatementMapList* pStatementMaps = functionBody->GetStatementMaps();
  720. Assert(pStatementMaps && pStatementMaps->Count() > 0);
  721. int direction = currentOffset < nextStmOffset ? 1 : -1;
  722. int startIndex = functionBody->GetEnclosingStatementIndexFromByteCode(currentOffset, true);
  723. uint32 tmpRegCountLowest = 0;
  724. // In the native code-gen (or interpreter which created from bailout points) the EmitTmpRegCount is not handled,
  725. // so lets calculate it by going through all statements backward from the current offset
  726. int index = startIndex;
  727. for (; index > 0; index--)
  728. {
  729. Js::FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
  730. Js::OpCode op;
  731. if (!pStatementMap->isSubexpression && FetchTmpRegCount(functionBody, reader, pStatementMap->byteCodeSpan.begin, &tmpRegCountLowest, &op))
  732. {
  733. break;
  734. }
  735. }
  736. // Reset to the current offset.
  737. reader->SetCurrentOffset(currentOffset);
  738. uint32 tmpRegCountOnNext = tmpRegCountLowest; // Will fetch the tmp reg count till the B and skipped statements.
  739. Assert(startIndex != -1);
  740. index = startIndex + direction;
  741. while (index > 0 && index < pStatementMaps->Count())
  742. {
  743. Js::FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
  744. if (pStatementMap->isSubexpression)
  745. {
  746. index += direction;
  747. continue;
  748. }
  749. if (direction == 1) // NOTE: Direction & corresponding condition
  750. {
  751. if (nextStmOffset < pStatementMap->byteCodeSpan.begin) // check only till nextstatement offset
  752. {
  753. break;
  754. }
  755. }
  756. Js::OpCode op;
  757. FetchTmpRegCount(functionBody, reader, pStatementMap->byteCodeSpan.begin, &tmpRegCountOnNext, &op);
  758. if (tmpRegCountOnNext < tmpRegCountLowest)
  759. {
  760. tmpRegCountLowest = tmpRegCountOnNext;
  761. }
  762. // On the reverse direction stop only when we find the tmpRegCount info for the setnext or below.
  763. if (direction == -1 && (op == Js::OpCode::EmitTmpRegCount))
  764. {
  765. if (nextStmOffset >= pStatementMap->byteCodeSpan.begin)
  766. {
  767. break;
  768. }
  769. }
  770. index += direction;
  771. }
  772. // On the reverse way if we have reached the first statement, then our tmpRegCountOnNext is 0.
  773. if (direction == -1 && index == 0)
  774. {
  775. tmpRegCountOnNext = 0;
  776. }
  777. if (restoreOffset)
  778. {
  779. // Restore back the original IP.
  780. reader->SetCurrentOffset(currentOffset);
  781. }
  782. return (tmpRegCountOnNext > tmpRegCountLowest);
  783. }
  784. bool ProbeContainer::AdvanceToNextUserStatement(Js::FunctionBody* functionBody, ByteCodeReader* reader)
  785. {
  786. // Move back a byte to make sure we are within the bounds of
  787. // our current statement (See DispatchExceptionBreakpoint)
  788. int currentOffset = reader->GetCurrentOffset() - 1;
  789. int nextStatementOffset;
  790. if (this->GetNextUserStatementOffsetForAdvance(functionBody, reader, currentOffset, &nextStatementOffset))
  791. {
  792. reader->SetCurrentOffset(nextStatementOffset);
  793. return true;
  794. }
  795. return false;
  796. }
  797. void ProbeContainer::SetNextStatementAt(int _bytecodeOffset)
  798. {
  799. Assert(_bytecodeOffset != debugManager->pCurrentInterpreterLocation->GetCurrentOffset());
  800. this->bytecodeOffset = _bytecodeOffset;
  801. Assert(IsNextStatementChanged == false);
  802. this->IsNextStatementChanged = true;
  803. }
  804. void ProbeContainer::AsyncActivate(HaltCallback* haltCallback)
  805. {
  806. OUTPUT_TRACE(Js::DebuggerPhase, _u("Async break activated\n"));
  807. InterlockedExchangePointer((PVOID*)&this->pAsyncHaltCallback, haltCallback);
  808. Assert(debugManager);
  809. debugManager->asyncBreakController.Activate(haltCallback);
  810. }
  811. void ProbeContainer::AsyncDeactivate()
  812. {
  813. InterlockedExchangePointer((PVOID*)&this->pAsyncHaltCallback, nullptr);
  814. Assert(debugManager);
  815. debugManager->asyncBreakController.Deactivate();
  816. }
  817. bool ProbeContainer::IsAsyncActivate() const
  818. {
  819. return this->pAsyncHaltCallback != nullptr;
  820. }
  821. void ProbeContainer::PrepDiagForEnterScript()
  822. {
  823. // This will be called from ParseScriptText.
  824. // This is to ensure the every script will call EnterScript back to host once, in-order to synchronize PDM with document.
  825. Assert(this->pScriptContext);
  826. if (this->pScriptContext->IsScriptContextInDebugMode())
  827. {
  828. isForcedToEnterScriptStart = true;
  829. }
  830. }
  831. void ProbeContainer::RegisterContextToDiag(DWORD_PTR context, ArenaAllocator *alloc)
  832. {
  833. Assert(this->pScriptContext->IsScriptContextInSourceRundownOrDebugMode());
  834. Assert(alloc);
  835. if (registeredFuncContextList == nullptr)
  836. {
  837. registeredFuncContextList = JsUtil::List<DWORD_PTR, ArenaAllocator>::New(alloc);
  838. }
  839. registeredFuncContextList->Add(context);
  840. }
  841. bool ProbeContainer::IsContextRegistered(DWORD_PTR context)
  842. {
  843. return registeredFuncContextList != nullptr && registeredFuncContextList->Contains(context);
  844. }
  845. FunctionBody * ProbeContainer::GetGlobalFunc(ScriptContext* scriptContext, DWORD_PTR secondaryHostSourceContext)
  846. {
  847. return scriptContext->FindFunction([&secondaryHostSourceContext] (FunctionBody* pFunc) {
  848. return ((pFunc->GetSecondaryHostSourceContext() == secondaryHostSourceContext) &&
  849. pFunc->GetIsGlobalFunc());
  850. });
  851. }
  852. bool ProbeContainer::HasAllowedForException(__in JavascriptExceptionObject* exceptionObject)
  853. {
  854. // We do not want to break on internal exception.
  855. if (isThrowInternal)
  856. {
  857. return false;
  858. }
  859. bool fIsFirstChance = false;
  860. bool fHasAllowed = false;
  861. bool fIsInNonUserCode = false;
  862. if (this->IsExceptionReportingEnabled() && (debugManager != nullptr))
  863. {
  864. fHasAllowed = !debugManager->pThreadContext->HasCatchHandler();
  865. if (!fHasAllowed)
  866. {
  867. if (IsFirstChanceExceptionEnabled())
  868. {
  869. fHasAllowed = fIsFirstChance = true;
  870. }
  871. // We must determine if the exception is in user code AND if it's first chance as some debuggers
  872. // ask for both and filter later.
  873. // first validate if the throwing function is NonUserCode function, if not then verify if the exception is being caught in nonuser code.
  874. if (exceptionObject && exceptionObject->GetFunctionBody() != nullptr && !exceptionObject->GetFunctionBody()->IsNonUserCode())
  875. {
  876. fIsInNonUserCode = IsNonUserCodeSupportEnabled() && !debugManager->pThreadContext->IsUserCode();
  877. }
  878. if (!fHasAllowed)
  879. {
  880. fHasAllowed = fIsInNonUserCode;
  881. }
  882. }
  883. }
  884. if (exceptionObject)
  885. {
  886. exceptionObject->SetIsFirstChance(fIsFirstChance);
  887. exceptionObject->SetIsExceptionCaughtInNonUserCode(fIsInNonUserCode);
  888. }
  889. return fHasAllowed;
  890. }
  891. bool ProbeContainer::IsExceptionReportingEnabled()
  892. {
  893. return this->debuggerOptionsCallback == nullptr || this->debuggerOptionsCallback->IsExceptionReportingEnabled();
  894. }
  895. bool ProbeContainer::IsFirstChanceExceptionEnabled()
  896. {
  897. return this->debuggerOptionsCallback != nullptr && this->debuggerOptionsCallback->IsFirstChanceExceptionEnabled();
  898. }
  899. // Mentions if the debugger has enabled the support to differentiate the exception kind.
  900. bool ProbeContainer::IsNonUserCodeSupportEnabled()
  901. {
  902. return this->debuggerOptionsCallback != nullptr && this->debuggerOptionsCallback->IsNonUserCodeSupportEnabled();
  903. }
  904. // Mentions if the debugger has enabled the support to display library stack frame.
  905. bool ProbeContainer::IsLibraryStackFrameSupportEnabled()
  906. {
  907. return CONFIG_FLAG(LibraryStackFrameDebugger) || (this->debuggerOptionsCallback != nullptr && this->debuggerOptionsCallback->IsLibraryStackFrameSupportEnabled());
  908. }
  909. void ProbeContainer::PinPropertyRecord(const Js::PropertyRecord *propertyRecord)
  910. {
  911. Assert(propertyRecord);
  912. this->pinnedPropertyRecords->Add(propertyRecord);
  913. }
  914. #ifdef ENABLE_MUTATION_BREAKPOINT
  915. bool ProbeContainer::HasMutationBreakpoints()
  916. {
  917. return mutationBreakpointList && !mutationBreakpointList->Empty();
  918. }
  919. void ProbeContainer::InsertMutationBreakpoint(MutationBreakpoint *mutationBreakpoint)
  920. {
  921. Assert(mutationBreakpoint);
  922. RecyclerWeakReference<Js::MutationBreakpoint>* weakBp = nullptr;
  923. pScriptContext->GetRecycler()->FindOrCreateWeakReferenceHandle(mutationBreakpoint, &weakBp);
  924. Assert(weakBp);
  925. // Make sure list is created prior to insertion
  926. InitMutationBreakpointListIfNeeded();
  927. if (mutationBreakpointList->Contains(weakBp))
  928. {
  929. return;
  930. }
  931. mutationBreakpointList->Add(weakBp);
  932. }
  933. void ProbeContainer::ClearMutationBreakpoints()
  934. {
  935. mutationBreakpointList->Map([=](uint i, RecyclerWeakReference<Js::MutationBreakpoint>* weakBp) {
  936. if (mutationBreakpointList->IsItemValid(i))
  937. {
  938. Js::MutationBreakpoint* mutationBreakpoint = weakBp->Get();
  939. if (mutationBreakpoint)
  940. {
  941. mutationBreakpoint->Reset();
  942. }
  943. }
  944. });
  945. mutationBreakpointList->ClearAndZero();
  946. }
  947. void ProbeContainer::InitMutationBreakpointListIfNeeded()
  948. {
  949. if (!mutationBreakpointList && Js::MutationBreakpoint::IsFeatureEnabled(pScriptContext))
  950. {
  951. Recycler *recycler = pScriptContext->GetRecycler();
  952. mutationBreakpointList.Root(RecyclerNew(recycler, MutationBreakpointList, recycler), recycler);
  953. }
  954. }
  955. void ProbeContainer::RemoveMutationBreakpointListIfNeeded()
  956. {
  957. if (mutationBreakpointList)
  958. {
  959. if (HasMutationBreakpoints())
  960. {
  961. ClearMutationBreakpoints();
  962. }
  963. else
  964. {
  965. mutationBreakpointList->ClearAndZero();
  966. }
  967. mutationBreakpointList.Unroot(pScriptContext->GetRecycler());
  968. }
  969. }
  970. #endif
  971. } // namespace Js.
  972. #endif