1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "debugger/DebugScript.h"
9 #include "mozilla/Assertions.h" // for AssertionConditionType
10 #include "mozilla/HashTable.h" // for HashMapEntry, HashTable<>::Ptr, HashMap
11 #include "mozilla/UniquePtr.h" // for UniquePtr
13 #include <utility> // for std::move
17 #include "debugger/DebugAPI.h" // for DebugAPI
18 #include "debugger/Debugger.h" // for JSBreakpointSite, Breakpoint
19 #include "gc/Cell.h" // for TenuredCell
20 #include "gc/FreeOp.h" // for JSFreeOp
21 #include "gc/GCEnum.h" // for MemoryUse, MemoryUse::BreakpointSite
22 #include "gc/Marking.h" // for IsAboutToBeFinalized
23 #include "gc/Zone.h" // for Zone
24 #include "gc/ZoneAllocator.h" // for AddCellMemory
25 #include "jit/BaselineJIT.h" // for BaselineScript
26 #include "vm/BytecodeIterator.h" // for AllBytecodesIterable
27 #include "vm/JSContext.h" // for JSContext
28 #include "vm/JSScript.h" // for JSScript, DebugScriptMap
29 #include "vm/NativeObject.h" // for NativeObject
30 #include "vm/Realm.h" // for Realm, AutoRealm
31 #include "vm/Runtime.h" // for ReportOutOfMemory
32 #include "vm/Stack.h" // for ActivationIterator, Activation
34 #include "gc/FreeOp-inl.h" // for JSFreeOp::free_
35 #include "gc/GC-inl.h" // for ZoneCellIter
36 #include "gc/Marking-inl.h" // for CheckGCThingAfterMovingGC
37 #include "gc/WeakMap-inl.h" // for WeakMap::remove
38 #include "vm/BytecodeIterator-inl.h" // for AllBytecodesIterable
39 #include "vm/JSContext-inl.h" // for JSContext::check
40 #include "vm/JSScript-inl.h" // for JSScript::hasBaselineScript
41 #include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
46 DebugScript
* DebugScript::get(JSScript
* script
) {
47 MOZ_ASSERT(script
->hasDebugScript());
48 DebugScriptMap
* map
= script
->zone()->debugScriptMap
.get();
50 DebugScriptMap::Ptr p
= map
->lookup(script
);
52 return p
->value().get();
56 DebugScript
* DebugScript::getOrCreate(JSContext
* cx
, JSScript
* script
) {
57 if (script
->hasDebugScript()) {
61 size_t nbytes
= allocSize(script
->length());
62 UniqueDebugScript
debug(
63 reinterpret_cast<DebugScript
*>(cx
->pod_calloc
<uint8_t>(nbytes
)));
68 /* Create zone's debugScriptMap if necessary. */
69 if (!script
->zone()->debugScriptMap
) {
70 auto map
= cx
->make_unique
<DebugScriptMap
>();
75 script
->zone()->debugScriptMap
= std::move(map
);
78 MOZ_ASSERT(script
->hasBytecode());
80 DebugScript
* borrowed
= debug
.get();
81 if (!script
->zone()->debugScriptMap
->putNew(script
, std::move(debug
))) {
82 ReportOutOfMemory(cx
);
86 // It is safe to set this: we can't fail after this point.
87 script
->setHasDebugScript(true);
88 AddCellMemory(script
, nbytes
, MemoryUse::ScriptDebugScript
);
91 * Ensure that any Interpret() instances running on this script have
92 * interrupts enabled. The interrupts must stay enabled until the
93 * debug state is destroyed.
95 for (ActivationIterator
iter(cx
); !iter
.done(); ++iter
) {
96 if (iter
->isInterpreter()) {
97 iter
->asInterpreter()->enableInterruptsIfRunning(script
);
105 JSBreakpointSite
* DebugScript::getBreakpointSite(JSScript
* script
,
107 uint32_t offset
= script
->pcToOffset(pc
);
108 return script
->hasDebugScript() ? get(script
)->breakpoints
[offset
] : nullptr;
112 JSBreakpointSite
* DebugScript::getOrCreateBreakpointSite(JSContext
* cx
,
115 AutoRealm
ar(cx
, script
);
117 DebugScript
* debug
= getOrCreate(cx
, script
);
122 JSBreakpointSite
*& site
= debug
->breakpoints
[script
->pcToOffset(pc
)];
125 site
= cx
->new_
<JSBreakpointSite
>(script
, pc
);
130 AddCellMemory(script
, sizeof(JSBreakpointSite
), MemoryUse::BreakpointSite
);
132 if (script
->hasBaselineScript()) {
133 script
->baselineScript()->toggleDebugTraps(script
, pc
);
141 void DebugScript::destroyBreakpointSite(JSFreeOp
* fop
, JSScript
* script
,
143 DebugScript
* debug
= get(script
);
144 JSBreakpointSite
*& site
= debug
->breakpoints
[script
->pcToOffset(pc
)];
146 MOZ_ASSERT(site
->isEmpty());
152 if (!debug
->needed()) {
153 DebugAPI::destroyDebugScript(fop
, script
);
156 if (script
->hasBaselineScript()) {
157 script
->baselineScript()->toggleDebugTraps(script
, pc
);
162 void DebugScript::clearBreakpointsIn(JSFreeOp
* fop
, JSScript
* script
,
163 Debugger
* dbg
, JSObject
* handler
) {
165 // Breakpoints hold wrappers in the script's compartment for the handler. Make
166 // sure we don't try to search for the unwrapped handler.
167 MOZ_ASSERT_IF(handler
, script
->compartment() == handler
->compartment());
169 if (!script
->hasDebugScript()) {
173 AllBytecodesIterable
iter(script
);
174 for (BytecodeLocation loc
: iter
) {
175 JSBreakpointSite
* site
= getBreakpointSite(script
, loc
.toRawBytecode());
178 for (Breakpoint
* bp
= site
->firstBreakpoint(); bp
; bp
= nextbp
) {
179 nextbp
= bp
->nextInSite();
180 if ((!dbg
|| bp
->debugger
== dbg
) &&
181 (!handler
|| bp
->getHandler() == handler
)) {
191 uint32_t DebugScript::getStepperCount(JSScript
* script
) {
192 return script
->hasDebugScript() ? get(script
)->stepperCount
: 0;
197 bool DebugScript::incrementStepperCount(JSContext
* cx
, JSScript
* script
) {
199 MOZ_ASSERT(cx
->realm()->isDebuggee());
201 AutoRealm
ar(cx
, script
);
203 DebugScript
* debug
= getOrCreate(cx
, script
);
208 debug
->stepperCount
++;
210 if (debug
->stepperCount
== 1) {
211 if (script
->hasBaselineScript()) {
212 script
->baselineScript()->toggleDebugTraps(script
, nullptr);
220 void DebugScript::decrementStepperCount(JSFreeOp
* fop
, JSScript
* script
) {
221 DebugScript
* debug
= get(script
);
223 MOZ_ASSERT(debug
->stepperCount
> 0);
225 debug
->stepperCount
--;
227 if (debug
->stepperCount
== 0) {
228 if (script
->hasBaselineScript()) {
229 script
->baselineScript()->toggleDebugTraps(script
, nullptr);
232 if (!debug
->needed()) {
233 DebugAPI::destroyDebugScript(fop
, script
);
239 bool DebugScript::incrementGeneratorObserverCount(JSContext
* cx
,
242 MOZ_ASSERT(cx
->realm()->isDebuggee());
244 AutoRealm
ar(cx
, script
);
246 DebugScript
* debug
= getOrCreate(cx
, script
);
251 debug
->generatorObserverCount
++;
253 // It is our caller's responsibility, before bumping the generator observer
254 // count, to make sure that the baseline code includes the necessary
255 // JSOp::AfterYield instrumentation by calling
256 // {ensure,update}ExecutionObservabilityOfScript.
257 MOZ_ASSERT_IF(script
->hasBaselineScript(),
258 script
->baselineScript()->hasDebugInstrumentation());
264 void DebugScript::decrementGeneratorObserverCount(JSFreeOp
* fop
,
266 DebugScript
* debug
= get(script
);
268 MOZ_ASSERT(debug
->generatorObserverCount
> 0);
270 debug
->generatorObserverCount
--;
272 if (!debug
->needed()) {
273 DebugAPI::destroyDebugScript(fop
, script
);
278 void DebugAPI::traceDebugScript(JSTracer
* trc
, JSScript
* script
) {
279 MOZ_ASSERT(script
->hasDebugScript());
280 DebugScript::get(script
)->trace(trc
, script
);
283 void DebugScript::trace(JSTracer
* trc
, JSScript
* owner
) {
284 size_t length
= owner
->length();
285 for (size_t i
= 0; i
< length
; i
++) {
286 JSBreakpointSite
* site
= breakpoints
[i
];
294 void DebugAPI::destroyDebugScript(JSFreeOp
* fop
, JSScript
* script
) {
295 if (script
->hasDebugScript()) {
296 DebugScriptMap
* map
= script
->zone()->debugScriptMap
.get();
298 DebugScriptMap::Ptr p
= map
->lookup(script
);
300 DebugScript
* debug
= p
->value().release();
302 script
->setHasDebugScript(false);
304 debug
->delete_(fop
, script
);
308 void DebugScript::delete_(JSFreeOp
* fop
, JSScript
* owner
) {
309 size_t length
= owner
->length();
310 for (size_t i
= 0; i
< length
; i
++) {
311 JSBreakpointSite
* site
= breakpoints
[i
];
317 fop
->free_(owner
, this, allocSize(owner
->length()),
318 MemoryUse::ScriptDebugScript
);
321 #ifdef JSGC_HASH_TABLE_CHECKS
323 void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript
* ds
) {
324 for (uint32_t i
= 0; i
< ds
->numSites
; i
++) {
325 JSBreakpointSite
* site
= ds
->breakpoints
[i
];
327 CheckGCThingAfterMovingGC(site
->script
.get());
331 #endif // JSGC_HASH_TABLE_CHECKS
334 bool DebugAPI::stepModeEnabledSlow(JSScript
* script
) {
335 return DebugScript::get(script
)->stepperCount
> 0;
339 bool DebugAPI::hasBreakpointsAtSlow(JSScript
* script
, jsbytecode
* pc
) {
340 JSBreakpointSite
* site
= DebugScript::getBreakpointSite(script
, pc
);