ProbeContainer.cpp 45 KB

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