Bug 1832850 - Part 5: Move the allocateObject definition into gc/Nursery.h r=jandem
[gecko.git] / js / src / debugger / DebugScript.cpp
blob610784c228a34ebfe189cc94c9cd1235407bf60d
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
15 #include "debugger/DebugAPI.h" // for DebugAPI
16 #include "debugger/Debugger.h" // for JSBreakpointSite, Breakpoint
17 #include "gc/Cell.h" // for TenuredCell
18 #include "gc/GCContext.h" // for JS::GCContext
19 #include "gc/GCEnum.h" // for MemoryUse, MemoryUse::BreakpointSite
20 #include "gc/Marking.h" // for IsAboutToBeFinalized
21 #include "gc/Zone.h" // for Zone
22 #include "gc/ZoneAllocator.h" // for AddCellMemory
23 #include "jit/BaselineJIT.h" // for BaselineScript
24 #include "vm/BytecodeIterator.h" // for AllBytecodesIterable
25 #include "vm/JSContext.h" // for JSContext
26 #include "vm/JSScript.h" // for JSScript, DebugScriptMap
27 #include "vm/NativeObject.h" // for NativeObject
28 #include "vm/Realm.h" // for Realm, AutoRealm
29 #include "vm/Runtime.h" // for ReportOutOfMemory
30 #include "vm/Stack.h" // for ActivationIterator, Activation
32 #include "gc/GC-inl.h" // for ZoneCellIter
33 #include "gc/GCContext-inl.h" // for JS::GCContext::free_
34 #include "gc/Marking-inl.h" // for CheckGCThingAfterMovingGC
35 #include "gc/WeakMap-inl.h" // for WeakMap::remove
36 #include "vm/BytecodeIterator-inl.h" // for AllBytecodesIterable
37 #include "vm/JSContext-inl.h" // for JSContext::check
38 #include "vm/JSObject-inl.h" // for NewObjectWithGivenProto
39 #include "vm/JSScript-inl.h" // for JSScript::hasBaselineScript
40 #include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
42 namespace js {
44 const JSClass DebugScriptObject::class_ = {
45 "DebugScriptObject",
46 JSCLASS_HAS_RESERVED_SLOTS(SlotCount) | JSCLASS_BACKGROUND_FINALIZE,
47 &classOps_, JS_NULL_CLASS_SPEC};
49 const JSClassOps DebugScriptObject::classOps_ = {
50 nullptr, // addProperty
51 nullptr, // delProperty
52 nullptr, // enumerate
53 nullptr, // newEnumerate
54 nullptr, // resolve
55 nullptr, // mayResolve
56 DebugScriptObject::finalize, // finalize
57 nullptr, // call
58 nullptr, // construct
59 DebugScriptObject::trace, // trace
62 /* static */
63 DebugScriptObject* DebugScriptObject::create(JSContext* cx,
64 UniqueDebugScript debugScript,
65 size_t nbytes) {
66 auto* object = NewObjectWithGivenProto<DebugScriptObject>(cx, nullptr);
67 if (!object) {
68 return nullptr;
71 object->initReservedSlot(ScriptSlot, PrivateValue(debugScript.release()));
72 AddCellMemory(object, nbytes, MemoryUse::ScriptDebugScript);
74 return object;
77 DebugScript* DebugScriptObject::debugScript() const {
78 return maybePtrFromReservedSlot<DebugScript>(ScriptSlot);
81 /* static */
82 void DebugScriptObject::trace(JSTracer* trc, JSObject* obj) {
83 DebugScript* debugScript = obj->as<DebugScriptObject>().debugScript();
84 if (debugScript) {
85 debugScript->trace(trc);
89 /* static */
90 void DebugScriptObject::finalize(JS::GCContext* gcx, JSObject* obj) {
91 DebugScriptObject* object = &obj->as<DebugScriptObject>();
92 DebugScript* debugScript = object->debugScript();
93 if (debugScript) {
94 debugScript->delete_(gcx, object);
98 /* static */
99 DebugScript* DebugScript::get(JSScript* script) {
100 MOZ_ASSERT(script->hasDebugScript());
101 DebugScriptMap* map = script->zone()->debugScriptMap;
102 MOZ_ASSERT(map);
103 DebugScriptMap::Ptr p = map->lookupUnbarriered(script);
104 MOZ_ASSERT(p);
105 return p->value().get()->as<DebugScriptObject>().debugScript();
108 /* static */
109 DebugScript* DebugScript::getOrCreate(JSContext* cx, HandleScript script) {
110 cx->check(script);
112 if (script->hasDebugScript()) {
113 return get(script);
116 size_t nbytes = allocSize(script->length());
117 UniqueDebugScript debug(
118 reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
119 if (!debug) {
120 return nullptr;
123 debug->codeLength = script->length();
125 Rooted<DebugScriptObject*> object(
126 cx, DebugScriptObject::create(cx, std::move(debug), nbytes));
127 if (!object) {
128 return nullptr;
131 /* Create zone's debugScriptMap if necessary. */
132 Zone* zone = script->zone();
133 MOZ_ASSERT(cx->zone() == zone);
134 if (!zone->debugScriptMap) {
135 DebugScriptMap* map = cx->new_<DebugScriptMap>(cx);
136 if (!map) {
137 return nullptr;
140 zone->debugScriptMap = map;
143 MOZ_ASSERT(script->hasBytecode());
145 if (!zone->debugScriptMap->putNew(script.get(), object.get())) {
146 ReportOutOfMemory(cx);
147 return nullptr;
150 // It is safe to set this: we can't fail after this point.
151 script->setHasDebugScript(true);
154 * Ensure that any Interpret() instances running on this script have
155 * interrupts enabled. The interrupts must stay enabled until the
156 * debug state is destroyed.
158 for (ActivationIterator iter(cx); !iter.done(); ++iter) {
159 if (iter->isInterpreter()) {
160 iter->asInterpreter()->enableInterruptsIfRunning(script);
164 return object->debugScript();
167 /* static */
168 JSBreakpointSite* DebugScript::getBreakpointSite(JSScript* script,
169 jsbytecode* pc) {
170 uint32_t offset = script->pcToOffset(pc);
171 return script->hasDebugScript() ? get(script)->breakpoints[offset] : nullptr;
174 /* static */
175 JSBreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
176 HandleScript script,
177 jsbytecode* pc) {
178 AutoRealm ar(cx, script);
180 DebugScript* debug = getOrCreate(cx, script);
181 if (!debug) {
182 return nullptr;
185 JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
187 if (!site) {
188 site = cx->new_<JSBreakpointSite>(script, pc);
189 if (!site) {
190 return nullptr;
192 debug->numSites++;
193 AddCellMemory(script, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
195 if (script->hasBaselineScript()) {
196 script->baselineScript()->toggleDebugTraps(script, pc);
200 return site;
203 /* static */
204 void DebugScript::destroyBreakpointSite(JS::GCContext* gcx, JSScript* script,
205 jsbytecode* pc) {
206 DebugScript* debug = get(script);
207 JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
208 MOZ_ASSERT(site);
209 MOZ_ASSERT(site->isEmpty());
211 site->delete_(gcx);
212 site = nullptr;
214 debug->numSites--;
215 if (!debug->needed()) {
216 DebugAPI::removeDebugScript(gcx, script);
219 if (script->hasBaselineScript()) {
220 script->baselineScript()->toggleDebugTraps(script, pc);
224 /* static */
225 void DebugScript::clearBreakpointsIn(JS::GCContext* gcx, JSScript* script,
226 Debugger* dbg, JSObject* handler) {
227 MOZ_ASSERT(script);
228 // Breakpoints hold wrappers in the script's compartment for the handler. Make
229 // sure we don't try to search for the unwrapped handler.
230 MOZ_ASSERT_IF(handler, script->compartment() == handler->compartment());
232 if (!script->hasDebugScript()) {
233 return;
236 AllBytecodesIterable iter(script);
237 for (BytecodeLocation loc : iter) {
238 JSBreakpointSite* site = getBreakpointSite(script, loc.toRawBytecode());
239 if (site) {
240 Breakpoint* nextbp;
241 for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
242 nextbp = bp->nextInSite();
243 if ((!dbg || bp->debugger == dbg) &&
244 (!handler || bp->getHandler() == handler)) {
245 bp->remove(gcx);
252 #ifdef DEBUG
253 /* static */
254 uint32_t DebugScript::getStepperCount(JSScript* script) {
255 return script->hasDebugScript() ? get(script)->stepperCount : 0;
257 #endif // DEBUG
259 /* static */
260 bool DebugScript::incrementStepperCount(JSContext* cx, HandleScript script) {
261 cx->check(script);
262 MOZ_ASSERT(cx->realm()->isDebuggee());
264 AutoRealm ar(cx, script);
266 DebugScript* debug = getOrCreate(cx, script);
267 if (!debug) {
268 return false;
271 debug->stepperCount++;
273 if (debug->stepperCount == 1) {
274 if (script->hasBaselineScript()) {
275 script->baselineScript()->toggleDebugTraps(script, nullptr);
279 return true;
282 /* static */
283 void DebugScript::decrementStepperCount(JS::GCContext* gcx, JSScript* script) {
284 DebugScript* debug = get(script);
285 MOZ_ASSERT(debug);
286 MOZ_ASSERT(debug->stepperCount > 0);
288 debug->stepperCount--;
290 if (debug->stepperCount == 0) {
291 if (script->hasBaselineScript()) {
292 script->baselineScript()->toggleDebugTraps(script, nullptr);
295 if (!debug->needed()) {
296 DebugAPI::removeDebugScript(gcx, script);
301 /* static */
302 bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
303 HandleScript script) {
304 cx->check(script);
305 MOZ_ASSERT(cx->realm()->isDebuggee());
307 AutoRealm ar(cx, script);
309 DebugScript* debug = getOrCreate(cx, script);
310 if (!debug) {
311 return false;
314 debug->generatorObserverCount++;
316 // It is our caller's responsibility, before bumping the generator observer
317 // count, to make sure that the baseline code includes the necessary
318 // JSOp::AfterYield instrumentation by calling
319 // {ensure,update}ExecutionObservabilityOfScript.
320 MOZ_ASSERT_IF(script->hasBaselineScript(),
321 script->baselineScript()->hasDebugInstrumentation());
323 return true;
326 /* static */
327 void DebugScript::decrementGeneratorObserverCount(JS::GCContext* gcx,
328 JSScript* script) {
329 DebugScript* debug = get(script);
330 MOZ_ASSERT(debug);
331 MOZ_ASSERT(debug->generatorObserverCount > 0);
333 debug->generatorObserverCount--;
335 if (!debug->needed()) {
336 DebugAPI::removeDebugScript(gcx, script);
340 void DebugScript::trace(JSTracer* trc) {
341 for (size_t i = 0; i < codeLength; i++) {
342 JSBreakpointSite* site = breakpoints[i];
343 if (site) {
344 site->trace(trc);
349 /* static */
350 void DebugAPI::removeDebugScript(JS::GCContext* gcx, JSScript* script) {
351 if (script->hasDebugScript()) {
352 if (IsAboutToBeFinalizedUnbarriered(script)) {
353 // The script is dying and all breakpoint data will be cleaned up.
354 return;
357 DebugScriptMap* map = script->zone()->debugScriptMap;
358 MOZ_ASSERT(map);
359 DebugScriptMap::Ptr p = map->lookupUnbarriered(script);
360 MOZ_ASSERT(p);
361 map->remove(p);
362 script->setHasDebugScript(false);
364 // The DebugScript will be destroyed at the next GC when its owning
365 // DebugScriptObject dies.
369 void DebugScript::delete_(JS::GCContext* gcx, DebugScriptObject* owner) {
370 for (size_t i = 0; i < codeLength; i++) {
371 JSBreakpointSite* site = breakpoints[i];
372 if (site) {
373 site->delete_(gcx);
377 gcx->free_(owner, this, allocSize(codeLength), MemoryUse::ScriptDebugScript);
380 #ifdef JSGC_HASH_TABLE_CHECKS
381 /* static */
382 void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
383 for (uint32_t i = 0; i < ds->numSites; i++) {
384 JSBreakpointSite* site = ds->breakpoints[i];
385 if (site) {
386 CheckGCThingAfterMovingGC(site->script.get());
390 #endif // JSGC_HASH_TABLE_CHECKS
392 /* static */
393 bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
394 return DebugScript::get(script)->stepperCount > 0;
397 /* static */
398 bool DebugAPI::hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc) {
399 JSBreakpointSite* site = DebugScript::getBreakpointSite(script, pc);
400 return !!site;
403 /* static */
404 void DebugAPI::traceDebugScriptMap(JSTracer* trc, DebugScriptMap* map) {
405 map->trace(trc);
408 /* static */
409 void DebugAPI::deleteDebugScriptMap(DebugScriptMap* map) { js_delete(map); }
411 } // namespace js