Bug 1681763 [wpt PR 26839] - WPT test: referrer on navigation from opaque origin...
[gecko.git] / js / src / debugger / DebugScript.cpp
blobcaa4f30f884b82879378a64455f8d9579ab646c1
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 "jsapi.h"
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
43 namespace js {
45 /* static */
46 DebugScript* DebugScript::get(JSScript* script) {
47 MOZ_ASSERT(script->hasDebugScript());
48 DebugScriptMap* map = script->zone()->debugScriptMap.get();
49 MOZ_ASSERT(map);
50 DebugScriptMap::Ptr p = map->lookup(script);
51 MOZ_ASSERT(p);
52 return p->value().get();
55 /* static */
56 DebugScript* DebugScript::getOrCreate(JSContext* cx, JSScript* script) {
57 if (script->hasDebugScript()) {
58 return get(script);
61 size_t nbytes = allocSize(script->length());
62 UniqueDebugScript debug(
63 reinterpret_cast<DebugScript*>(cx->pod_calloc<uint8_t>(nbytes)));
64 if (!debug) {
65 return nullptr;
68 /* Create zone's debugScriptMap if necessary. */
69 if (!script->zone()->debugScriptMap) {
70 auto map = cx->make_unique<DebugScriptMap>();
71 if (!map) {
72 return nullptr;
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);
83 return nullptr;
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);
101 return borrowed;
104 /* static */
105 JSBreakpointSite* DebugScript::getBreakpointSite(JSScript* script,
106 jsbytecode* pc) {
107 uint32_t offset = script->pcToOffset(pc);
108 return script->hasDebugScript() ? get(script)->breakpoints[offset] : nullptr;
111 /* static */
112 JSBreakpointSite* DebugScript::getOrCreateBreakpointSite(JSContext* cx,
113 JSScript* script,
114 jsbytecode* pc) {
115 AutoRealm ar(cx, script);
117 DebugScript* debug = getOrCreate(cx, script);
118 if (!debug) {
119 return nullptr;
122 JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
124 if (!site) {
125 site = cx->new_<JSBreakpointSite>(script, pc);
126 if (!site) {
127 return nullptr;
129 debug->numSites++;
130 AddCellMemory(script, sizeof(JSBreakpointSite), MemoryUse::BreakpointSite);
132 if (script->hasBaselineScript()) {
133 script->baselineScript()->toggleDebugTraps(script, pc);
137 return site;
140 /* static */
141 void DebugScript::destroyBreakpointSite(JSFreeOp* fop, JSScript* script,
142 jsbytecode* pc) {
143 DebugScript* debug = get(script);
144 JSBreakpointSite*& site = debug->breakpoints[script->pcToOffset(pc)];
145 MOZ_ASSERT(site);
146 MOZ_ASSERT(site->isEmpty());
148 site->delete_(fop);
149 site = nullptr;
151 debug->numSites--;
152 if (!debug->needed()) {
153 DebugAPI::destroyDebugScript(fop, script);
156 if (script->hasBaselineScript()) {
157 script->baselineScript()->toggleDebugTraps(script, pc);
161 /* static */
162 void DebugScript::clearBreakpointsIn(JSFreeOp* fop, JSScript* script,
163 Debugger* dbg, JSObject* handler) {
164 MOZ_ASSERT(script);
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()) {
170 return;
173 AllBytecodesIterable iter(script);
174 for (BytecodeLocation loc : iter) {
175 JSBreakpointSite* site = getBreakpointSite(script, loc.toRawBytecode());
176 if (site) {
177 Breakpoint* nextbp;
178 for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
179 nextbp = bp->nextInSite();
180 if ((!dbg || bp->debugger == dbg) &&
181 (!handler || bp->getHandler() == handler)) {
182 bp->remove(fop);
189 #ifdef DEBUG
190 /* static */
191 uint32_t DebugScript::getStepperCount(JSScript* script) {
192 return script->hasDebugScript() ? get(script)->stepperCount : 0;
194 #endif // DEBUG
196 /* static */
197 bool DebugScript::incrementStepperCount(JSContext* cx, JSScript* script) {
198 cx->check(script);
199 MOZ_ASSERT(cx->realm()->isDebuggee());
201 AutoRealm ar(cx, script);
203 DebugScript* debug = getOrCreate(cx, script);
204 if (!debug) {
205 return false;
208 debug->stepperCount++;
210 if (debug->stepperCount == 1) {
211 if (script->hasBaselineScript()) {
212 script->baselineScript()->toggleDebugTraps(script, nullptr);
216 return true;
219 /* static */
220 void DebugScript::decrementStepperCount(JSFreeOp* fop, JSScript* script) {
221 DebugScript* debug = get(script);
222 MOZ_ASSERT(debug);
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);
238 /* static */
239 bool DebugScript::incrementGeneratorObserverCount(JSContext* cx,
240 JSScript* script) {
241 cx->check(script);
242 MOZ_ASSERT(cx->realm()->isDebuggee());
244 AutoRealm ar(cx, script);
246 DebugScript* debug = getOrCreate(cx, script);
247 if (!debug) {
248 return false;
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());
260 return true;
263 /* static */
264 void DebugScript::decrementGeneratorObserverCount(JSFreeOp* fop,
265 JSScript* script) {
266 DebugScript* debug = get(script);
267 MOZ_ASSERT(debug);
268 MOZ_ASSERT(debug->generatorObserverCount > 0);
270 debug->generatorObserverCount--;
272 if (!debug->needed()) {
273 DebugAPI::destroyDebugScript(fop, script);
277 /* static */
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];
287 if (site) {
288 site->trace(trc);
293 /* static */
294 void DebugAPI::destroyDebugScript(JSFreeOp* fop, JSScript* script) {
295 if (script->hasDebugScript()) {
296 DebugScriptMap* map = script->zone()->debugScriptMap.get();
297 MOZ_ASSERT(map);
298 DebugScriptMap::Ptr p = map->lookup(script);
299 MOZ_ASSERT(p);
300 DebugScript* debug = p->value().release();
301 map->remove(p);
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];
312 if (site) {
313 site->delete_(fop);
317 fop->free_(owner, this, allocSize(owner->length()),
318 MemoryUse::ScriptDebugScript);
321 #ifdef JSGC_HASH_TABLE_CHECKS
322 /* static */
323 void DebugAPI::checkDebugScriptAfterMovingGC(DebugScript* ds) {
324 for (uint32_t i = 0; i < ds->numSites; i++) {
325 JSBreakpointSite* site = ds->breakpoints[i];
326 if (site) {
327 CheckGCThingAfterMovingGC(site->script.get());
331 #endif // JSGC_HASH_TABLE_CHECKS
333 /* static */
334 bool DebugAPI::stepModeEnabledSlow(JSScript* script) {
335 return DebugScript::get(script)->stepperCount > 0;
338 /* static */
339 bool DebugAPI::hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc) {
340 JSBreakpointSite* site = DebugScript::getBreakpointSite(script, pc);
341 return !!site;
344 } // namespace js