Source code

Revision control

Other Tools

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/. */
6
7
#include "debugger/DebugScript.h"
8
9
#include "mozilla/Assertions.h" // for AssertionConditionType
10
#include "mozilla/HashTable.h" // for HashMapEntry, HashTable<>::Ptr, HashMap
11
#include "mozilla/Move.h" // for std::move
12
#include "mozilla/UniquePtr.h" // for UniquePtr
13
14
#include "jsapi.h"
15
16
#include "debugger/DebugAPI.h" // for DebugAPI
17
#include "debugger/Debugger.h" // for JSBreakpointSite, Breakpoint
18
#include "gc/Barrier.h" // for GCPtrNativeObject, WriteBarriered
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/JSContext.h" // for JSContext
27
#include "vm/JSScript.h" // for JSScript, DebugScriptMap
28
#include "vm/NativeObject.h" // for NativeObject
29
#include "vm/Realm.h" // for Realm, AutoRealm
30
#include "vm/Runtime.h" // for ReportOutOfMemory
31
#include "vm/Stack.h" // for ActivationIterator, Activation
32
33
#include "gc/FreeOp-inl.h" // for JSFreeOp::free_
34
#include "gc/GC-inl.h" // for ZoneCellIter
35
#include "gc/Marking-inl.h" // for CheckGCThingAfterMovingGC
36
#include "vm/JSContext-inl.h" // for JSContext::check
37
#include "vm/JSScript-inl.h" // for JSScript::hasBaselineScript
38
#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
39
40
namespace js {
41
42
/* static */
43
DebugScript* DebugScript::get(JSScript* script) {
44
MOZ_ASSERT(script->hasDebugScript());
45
DebugScriptMap* map = script->zone()->debugScriptMap.get();
46
MOZ_ASSERT(map);
47
DebugScriptMap::Ptr p = map->lookup(script);
48
MOZ_ASSERT(p);
49
return p->value().get();
50
}
51
52
/* static */
53
DebugScript* DebugScript::getOrCreate(JSContext* cx, JSScript* script) {
54
if (script->hasDebugScript()) {
55
return get(script);
56
}
57
58
size_t nbytes = allocSize(script->length());
59
UniqueDebugScript debug(
60
reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
61
if (!debug) {
62
return nullptr;
63
}
64
65
/* Create zone's debugScriptMap if necessary. */
66
if (!script->zone()->debugScriptMap) {
67
auto map = cx->make_unique<DebugScriptMap>();
68
if (!map) {
69
return nullptr;
70
}
71
72
script->zone()->debugScriptMap = std::move(map);
73
}
74
75
DebugScript* borrowed = debug.get();
76
if (!script->zone()->debugScriptMap->putNew(script, std::move(debug))) {
77
ReportOutOfMemory(cx);
78
return nullptr;
79
}
80
81
// It is safe to set this: we can't fail after this point.
82
script->setHasDebugScript(true);
83
AddCellMemory(script, nbytes, MemoryUse::ScriptDebugScript);
84
85
/*
86
* Ensure that any Interpret() instances running on this script have
87
* interrupts enabled. The interrupts must stay enabled until the
88
* debug state is destroyed.
89
*/
90
for (ActivationIterator iter(cx); !iter.done(); ++iter) {
91
if (iter->isInterpreter()) {
92
iter->asInterpreter()->enableInterruptsIfRunning(script);
93
}
94
}
95
96
return borrowed;
97
}
98
99
/* static */
100
JSBreakpointSite* DebugScript::getBreakpointSite(JSScript* script,
101
jsbytecode* pc) {
102
uint32_t offset = script->pcToOffset(pc);
103
return script->hasDebugScript() ? get(script)->breakpoints[offset] : nullptr;
104
}
105
106
/* static */
107
JSBreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
108
JSScript* script,
109
jsbytecode* pc) {
110
AutoRealm ar(cx, script);
111
112
DebugScript* debug = getOrCreate(cx, script);
113
if (!debug) {
114
return nullptr;
115
}
116
117
JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
118
119
if (!site) {
120
site = cx->new_<JSBreakpointSite>(script, pc);
121
if (!site) {
122
return nullptr;
123
}
124
debug->numSites++;
125
AddCellMemory(script, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
126
127
if (script->hasBaselineScript()) {
128
script->baselineScript()->toggleDebugTraps(script, pc);
129
}
130
}
131
132
return site;
133
}
134
135
/* static */
136
void DebugScript::destroyBreakpointSite(JSFreeOp* fop, JSScript* script,
137
jsbytecode* pc) {
138
DebugScript* debug = get(script);
139
JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
140
MOZ_ASSERT(site);
141
MOZ_ASSERT(site->isEmpty());
142
143
site->delete_(fop);
144
site = nullptr;
145
146
debug->numSites--;
147
if (!debug->needed()) {
148
DebugAPI::destroyDebugScript(fop, script);
149
}
150
151
if (script->hasBaselineScript()) {
152
script->baselineScript()->toggleDebugTraps(script, pc);
153
}
154
}
155
156
/* static */
157
void DebugScript::clearBreakpointsIn(JSFreeOp* fop, Realm* realm, Debugger* dbg,
158
JSObject* handler) {
159
for (auto script = realm->zone()->cellIter<JSScript>(); !script.done();
160
script.next()) {
161
if (script->realm() == realm && script->hasDebugScript()) {
162
clearBreakpointsIn(fop, script, dbg, handler);
163
}
164
}
165
}
166
167
/* static */
168
void DebugScript::clearBreakpointsIn(JSFreeOp* fop, JSScript* script,
169
Debugger* dbg, JSObject* handler) {
170
// Breakpoints hold wrappers in the script's compartment for the handler. Make
171
// sure we don't try to search for the unwrapped handler.
172
MOZ_ASSERT_IF(script && handler,
173
script->compartment() == handler->compartment());
174
175
if (!script->hasDebugScript()) {
176
return;
177
}
178
179
for (jsbytecode* pc = script->code(); pc < script->codeEnd(); pc++) {
180
JSBreakpointSite* site = getBreakpointSite(script, pc);
181
if (site) {
182
Breakpoint* nextbp;
183
for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
184
nextbp = bp->nextInSite();
185
if ((!dbg || bp->debugger == dbg) &&
186
(!handler || bp->getHandler() == handler)) {
187
bp->remove(fop);
188
}
189
}
190
}
191
}
192
}
193
194
#ifdef DEBUG
195
/* static */
196
uint32_t DebugScript::getStepperCount(JSScript* script) {
197
return script->hasDebugScript() ? get(script)->stepperCount : 0;
198
}
199
#endif // DEBUG
200
201
/* static */
202
bool DebugScript::incrementStepperCount(JSContext* cx, JSScript* script) {
203
cx->check(script);
204
MOZ_ASSERT(cx->realm()->isDebuggee());
205
206
AutoRealm ar(cx, script);
207
208
DebugScript* debug = getOrCreate(cx, script);
209
if (!debug) {
210
return false;
211
}
212
213
debug->stepperCount++;
214
215
if (debug->stepperCount == 1) {
216
if (script->hasBaselineScript()) {
217
script->baselineScript()->toggleDebugTraps(script, nullptr);
218
}
219
}
220
221
return true;
222
}
223
224
/* static */
225
void DebugScript::decrementStepperCount(JSFreeOp* fop, JSScript* script) {
226
DebugScript* debug = get(script);
227
MOZ_ASSERT(debug);
228
MOZ_ASSERT(debug->stepperCount > 0);
229
230
debug->stepperCount--;
231
232
if (debug->stepperCount == 0) {
233
if (script->hasBaselineScript()) {
234
script->baselineScript()->toggleDebugTraps(script, nullptr);
235
}
236
237
if (!debug->needed()) {
238
DebugAPI::destroyDebugScript(fop, script);
239
}
240
}
241
}
242
243
/* static */
244
bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
245
JSScript* script) {
246
cx->check(script);
247
MOZ_ASSERT(cx->realm()->isDebuggee());
248
249
AutoRealm ar(cx, script);
250
251
DebugScript* debug = getOrCreate(cx, script);
252
if (!debug) {
253
return false;
254
}
255
256
debug->generatorObserverCount++;
257
258
// It is our caller's responsibility, before bumping the generator observer
259
// count, to make sure that the baseline code includes the necessary
260
// JSOP_AFTERYIELD instrumentation by calling
261
// {ensure,update}ExecutionObservabilityOfScript.
262
MOZ_ASSERT_IF(script->hasBaselineScript(),
263
script->baselineScript()->hasDebugInstrumentation());
264
265
return true;
266
}
267
268
/* static */
269
void DebugScript::decrementGeneratorObserverCount(JSFreeOp* fop,
270
JSScript* script) {
271
DebugScript* debug = get(script);
272
MOZ_ASSERT(debug);
273
MOZ_ASSERT(debug->generatorObserverCount > 0);
274
275
debug->generatorObserverCount--;
276
277
if (!debug->needed()) {
278
DebugAPI::destroyDebugScript(fop, script);
279
}
280
}
281
282
/* static */
283
void DebugAPI::traceDebugScript(JSTracer* trc, JSScript* script) {
284
MOZ_ASSERT(script->hasDebugScript());
285
DebugScript::get(script)->trace(trc, script);
286
}
287
288
void DebugScript::trace(JSTracer* trc, JSScript* owner) {
289
size_t length = owner->length();
290
for (size_t i = 0; i < length; i++) {
291
JSBreakpointSite* site = breakpoints[i];
292
if (site) {
293
site->trace(trc);
294
}
295
}
296
}
297
298
/* static */
299
void DebugAPI::destroyDebugScript(JSFreeOp* fop, JSScript* script) {
300
if (script->hasDebugScript()) {
301
DebugScriptMap* map = script->zone()->debugScriptMap.get();
302
MOZ_ASSERT(map);
303
DebugScriptMap::Ptr p = map->lookup(script);
304
MOZ_ASSERT(p);
305
DebugScript* debug = p->value().release();
306
map->remove(p);
307
script->setHasDebugScript(false);
308
309
debug->delete_(fop, script);
310
}
311
}
312
313
void DebugScript::delete_(JSFreeOp* fop, JSScript* owner) {
314
size_t length = owner->length();
315
for (size_t i = 0; i < length; i++) {
316
JSBreakpointSite* site = breakpoints[i];
317
if (site) {
318
site->delete_(fop);
319
}
320
}
321
322
fop->free_(owner, this, allocSize(owner->length()),
323
MemoryUse::ScriptDebugScript);
324
}
325
326
#ifdef JSGC_HASH_TABLE_CHECKS
327
/* static */
328
void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
329
for (uint32_t i = 0; i < ds->numSites; i++) {
330
JSBreakpointSite* site = ds->breakpoints[i];
331
if (site) {
332
CheckGCThingAfterMovingGC(site->script.get());
333
}
334
}
335
}
336
#endif // JSGC_HASH_TABLE_CHECKS
337
338
/* static */
339
bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
340
return DebugScript::get(script)->stepperCount > 0;
341
}
342
343
/* static */
344
bool DebugAPI::hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc) {
345
JSBreakpointSite* site = DebugScript::getBreakpointSite(script, pc);
346
return !!site;
347
}
348
349
} // namespace js