Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/debugger/./../../../../js/src/debugger/Debugger.cpp
Warning:line 4241, column 5
Value stored to 'dbg' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_debugger0.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/debugger -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/debugger -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src -I /root/firefox-clang/js/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-10-17-231001-351276-1 -x c++ Unified_cpp_js_src_debugger0.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "mozilla/Attributes.h" // for MOZ_STACK_CLASS, MOZ_RAII
6#include "mozilla/DebugOnly.h" // for DebugOnly
7#include "mozilla/DoublyLinkedList.h" // for DoublyLinkedList<>::Iterator
8#include "mozilla/HashTable.h" // for HashMapEntry
9#include "mozilla/Maybe.h" // for Maybe, Nothing, Some
10#include "mozilla/ScopeExit.h" // for MakeScopeExit, ScopeExit
11#include "mozilla/Sprintf.h" // for SprintfLiteral
12#include "mozilla/TimeStamp.h" // for TimeStamp
13#include "mozilla/UniquePtr.h" // for UniquePtr
14#include "mozilla/Variant.h" // for AsVariant, AsVariantTemporary
15#include "mozilla/Vector.h" // for Vector, Vector<>::ConstRange
16
17#include <algorithm> // for std::find, std::max
18#include <functional> // for function
19#include <stddef.h> // for size_t
20#include <stdint.h> // for uint32_t, uint64_t, int32_t
21#include <string.h> // for strlen, strcmp
22#include <type_traits> // for std::underlying_type_t
23#include <utility> // for std::move
24
25#include "jsapi.h" // for CallArgs, CallArgsFromVp
26#include "jstypes.h" // for JS_PUBLIC_API
27
28#include "builtin/Array.h" // for NewDenseFullyAllocatedArray
29#include "debugger/DebugAPI.h" // for ResumeMode, DebugAPI
30#include "debugger/DebuggerMemory.h" // for DebuggerMemory
31#include "debugger/DebugScript.h" // for DebugScript
32#include "debugger/Environment.h" // for DebuggerEnvironment
33
34#include "debugger/Debugger-inl.h"
35#ifdef MOZ_EXECUTION_TRACING1
36# include "debugger/ExecutionTracer.h" // for ExecutionTracer::onEnterFrame, ExecutionTracer::onLeaveFrame
37#endif
38#include "debugger/Frame.h" // for DebuggerFrame
39#include "debugger/NoExecute.h" // for EnterDebuggeeNoExecute
40#include "debugger/Object.h" // for DebuggerObject
41#include "debugger/Script.h" // for DebuggerScript
42#include "debugger/Source.h" // for DebuggerSource
43#include "frontend/CompilationStencil.h" // for CompilationStencil
44#include "frontend/FrontendContext.h" // for AutoReportFrontendContext
45#include "frontend/Parser.h" // for Parser
46#include "gc/GC.h" // for IterateScripts
47#include "gc/GCContext.h" // for JS::GCContext
48#include "gc/GCMarker.h" // for GCMarker
49#include "gc/GCRuntime.h" // for GCRuntime, AutoEnterIteration
50#include "gc/HashUtil.h" // for DependentAddPtr
51#include "gc/Marking.h" // for IsAboutToBeFinalized
52#include "gc/PublicIterators.h" // for RealmsIter, CompartmentsIter
53#include "gc/Statistics.h" // for Statistics::SliceData
54#include "gc/Tracer.h" // for TraceEdge
55#include "gc/Zone.h" // for Zone
56#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
57#include "jit/BaselineDebugModeOSR.h" // for RecompileOnStackBaselineScriptsForDebugMode
58#include "jit/BaselineJIT.h" // for FinishDiscardBaselineScript
59#include "jit/Invalidation.h" // for IonScriptKeyVector
60#include "jit/JitContext.h" // for JitContext
61#include "jit/JitOptions.h" // for fuzzingSafe
62#include "jit/JitScript.h" // for JitScript
63#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
64#include "jit/RematerializedFrame.h" // for RematerializedFrame
65#include "js/CallAndConstruct.h" // JS::IsCallable
66#include "js/Conversions.h" // for ToBoolean, ToUint32
67#include "js/Debug.h" // for Builder::Object, Builder
68#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
69#include "js/GCAPI.h" // for GarbageCollectionEvent
70#include "js/GCVariant.h" // for GCVariant
71#include "js/HeapAPI.h" // for ExposeObjectToActiveJS
72#include "js/Promise.h" // for AutoDebuggerJobQueueInterruption
73#include "js/PropertyAndElement.h" // for JS_GetProperty
74#include "js/Proxy.h" // for PropertyDescriptor
75#include "js/SourceText.h" // for SourceText
76#include "js/StableStringChars.h" // for AutoStableStringChars
77#include "js/UbiNode.h" // for Node, RootList, Edge
78#include "js/UbiNodeBreadthFirst.h" // for BreadthFirst
79#include "js/Wrapper.h" // for CheckedUnwrapStatic
80#include "util/Identifier.h" // for IsIdentifier
81#include "util/Text.h" // for DuplicateString, js_strlen
82#include "vm/ArrayObject.h" // for ArrayObject
83#include "vm/AsyncFunction.h" // for AsyncFunctionGeneratorObject
84#include "vm/AsyncIteration.h" // for AsyncGeneratorObject
85#include "vm/BytecodeUtil.h" // for JSDVG_IGNORE_STACK
86#include "vm/Compartment.h" // for CrossCompartmentKey
87#include "vm/EnvironmentObject.h" // for IsSyntacticEnvironment
88#include "vm/ErrorReporting.h" // for ReportErrorToGlobal
89#include "vm/GeneratorObject.h" // for AbstractGeneratorObject
90#include "vm/GlobalObject.h" // for GlobalObject
91#include "vm/Interpreter.h" // for Call, ReportIsNotFunction
92#include "vm/Iteration.h" // for CreateIterResultObject
93#include "vm/JSAtomUtils.h" // for Atomize, AtomizeUTF8Chars, ZoneHasRef, AtomToId, ClassName
94#include "vm/JSContext.h" // for JSContext
95#include "vm/JSFunction.h" // for JSFunction
96#include "vm/JSObject.h" // for JSObject, RequireObject,
97#include "vm/JSScript.h" // for BaseScript, ScriptSourceObject
98#include "vm/ObjectOperations.h" // for DefineDataProperty
99#include "vm/PlainObject.h" // for js::PlainObject
100#include "vm/PromiseObject.h" // for js::PromiseObject
101#include "vm/ProxyObject.h" // for ProxyObject, JSObject::is
102#include "vm/Realm.h" // for AutoRealm, Realm
103#include "vm/Runtime.h" // for ReportOutOfMemory, JSRuntime
104#include "vm/SavedFrame.h" // for SavedFrame
105#include "vm/SavedStacks.h" // for SavedStacks
106#include "vm/Scope.h" // for Scope
107#include "vm/StringType.h" // for JSString, PropertyName
108#include "vm/WrapperObject.h" // for CrossCompartmentWrapperObject
109#include "wasm/WasmDebug.h" // for DebugState
110#include "wasm/WasmInstance.h" // for Instance
111#include "wasm/WasmJS.h" // for WasmInstanceObject
112#include "wasm/WasmRealm.h" // for Realm
113#include "wasm/WasmStacks.h" // for ContStack
114#include "wasm/WasmTypeDecls.h" // for WasmInstanceObjectVector
115
116#include "debugger/DebugAPI-inl.h"
117#include "debugger/Environment-inl.h" // for DebuggerEnvironment::owner
118#include "debugger/Frame-inl.h" // for DebuggerFrame::hasGeneratorInfo
119#include "debugger/Object-inl.h" // for DebuggerObject::owner and isInstance.
120#include "debugger/Script-inl.h" // for DebuggerScript::getReferent
121#include "gc/GC-inl.h" // for ZoneCellIter
122#include "gc/Marking-inl.h" // for MaybeForwarded
123#include "gc/StableCellHasher-inl.h"
124#include "gc/WeakMap-inl.h" // for DebuggerWeakMap::trace
125#include "vm/Compartment-inl.h" // for Compartment::wrap
126#include "vm/GeckoProfiler-inl.h" // for AutoSuppressProfilerSampling
127#include "vm/JSAtomUtils-inl.h" // for AtomToId, ValueToId
128#include "vm/JSContext-inl.h" // for JSContext::check
129#include "vm/JSObject-inl.h" // for JSObject::isCallable
130#include "vm/JSScript-inl.h" // for JSScript::isDebuggee, JSScript
131#include "vm/NativeObject-inl.h" // for NativeObject::ensureDenseInitializedLength
132#include "vm/ObjectOperations-inl.h" // for GetProperty, HasProperty
133#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
134#include "vm/Stack-inl.h" // for AbstractFramePtr::script
135#include "wasm/WasmInstance-inl.h" // for Instance::codeMeta()
136
137namespace js {
138
139namespace frontend {
140class FullParseHandler;
141}
142
143namespace gc {
144class Cell;
145}
146
147namespace jit {
148class BaselineFrame;
149}
150
151} /* namespace js */
152
153using namespace js;
154
155using JS::AutoStableStringChars;
156using JS::CompileOptions;
157using JS::dbg::Builder;
158using mozilla::AsVariant;
159using mozilla::DebugOnly;
160using mozilla::MakeScopeExit;
161using mozilla::Maybe;
162using mozilla::Nothing;
163using mozilla::Some;
164using mozilla::TimeStamp;
165
166/*** Utils ******************************************************************/
167
168bool js::IsInterpretedNonSelfHostedFunction(JSFunction* fun) {
169 return fun->isInterpreted() && !fun->isSelfHostedBuiltin();
170}
171
172JSScript* js::GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun) {
173 MOZ_ASSERT(IsInterpretedNonSelfHostedFunction(fun))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsInterpretedNonSelfHostedFunction(fun))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(IsInterpretedNonSelfHostedFunction(fun)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("IsInterpretedNonSelfHostedFunction(fun)"
, "./../../../../js/src/debugger/Debugger.cpp", 173); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsInterpretedNonSelfHostedFunction(fun)" ")"
); do { MOZ_CrashSequence(__null, 173); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
174 AutoRealm ar(cx, fun);
175 return JSFunction::getOrCreateScript(cx, fun);
176}
177
178ArrayObject* js::GetFunctionParameterNamesArray(JSContext* cx,
179 HandleFunction fun) {
180 RootedValueVector names(cx);
181
182 // The default value for each argument is |undefined|.
183 if (!names.growBy(fun->nargs())) {
184 return nullptr;
185 }
186
187 if (IsInterpretedNonSelfHostedFunction(fun) && fun->nargs() > 0) {
188 RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
189 if (!script) {
190 return nullptr;
191 }
192
193 MOZ_ASSERT(fun->nargs() == script->numArgs())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fun->nargs() == script->numArgs())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(fun->nargs() == script->numArgs()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("fun->nargs() == script->numArgs()"
, "./../../../../js/src/debugger/Debugger.cpp", 193); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fun->nargs() == script->numArgs()" ")"
); do { MOZ_CrashSequence(__null, 193); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
194
195 PositionalFormalParameterIter fi(script);
196 for (size_t i = 0; i < fun->nargs(); i++, fi++) {
197 MOZ_ASSERT(fi.argumentSlot() == i)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(fi.argumentSlot() == i)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(fi.argumentSlot() == i))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("fi.argumentSlot() == i"
, "./../../../../js/src/debugger/Debugger.cpp", 197); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "fi.argumentSlot() == i" ")"); do { MOZ_CrashSequence
(__null, 197); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
198 if (JSAtom* atom = fi.name()) {
199 // Skip any internal, non-identifier names, like for example ".args".
200 if (IsIdentifier(atom)) {
201 cx->recordRef(atom);
202 names[i].setString(atom);
203 }
204 }
205 }
206 }
207
208 return NewDenseCopiedArray(cx, names.length(), names.begin());
209}
210
211bool js::ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id) {
212 if (!ToPropertyKey(cx, v, id)) {
213 return false;
214 }
215 if (!id.isAtom() || !IsIdentifier(id.toAtom())) {
216 RootedValue val(cx, v);
217 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK1, val,
218 nullptr, "not an identifier");
219 return false;
220 }
221 return true;
222}
223
224class js::AutoRestoreRealmDebugMode {
225 Realm* realm_;
226 uint32_t bits_;
227
228 public:
229 explicit AutoRestoreRealmDebugMode(Realm* realm)
230 : realm_(realm), bits_(realm->debugModeBits_) {
231 MOZ_ASSERT(realm_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(realm_)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(realm_))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("realm_", "./../../../../js/src/debugger/Debugger.cpp"
, 231); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realm_" ")")
; do { MOZ_CrashSequence(__null, 231); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
232 }
233
234 ~AutoRestoreRealmDebugMode() {
235 if (realm_) {
236 realm_->restoreDebugModeBitsOnOOM(bits_);
237 }
238 }
239
240 void release() { realm_ = nullptr; }
241};
242
243/* static */
244bool DebugAPI::slowPathCheckNoExecute(JSContext* cx, HandleScript script) {
245 MOZ_ASSERT(cx->realm()->isDebuggee())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cx->realm()->isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(cx->realm()->isDebuggee
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("cx->realm()->isDebuggee()", "./../../../../js/src/debugger/Debugger.cpp"
, 245); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cx->realm()->isDebuggee()"
")"); do { MOZ_CrashSequence(__null, 245); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
246 MOZ_ASSERT(cx->noExecuteDebuggerTop)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cx->noExecuteDebuggerTop)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(cx->noExecuteDebuggerTop)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("cx->noExecuteDebuggerTop"
, "./../../../../js/src/debugger/Debugger.cpp", 246); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "cx->noExecuteDebuggerTop" ")"); do { MOZ_CrashSequence
(__null, 246); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
247 return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
248}
249
250static void PropagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
251 HandleValue rval) {
252 // The Debugger's hooks may return a value that affects the completion
253 // value of the given frame. For example, a hook may return `{ return: 42 }`
254 // to terminate the frame and return `42` as the final frame result.
255 // To accomplish this, the debugger treats these return values as if
256 // execution of the JS function has been terminated without a pending
257 // exception, but with a special flag. When the error is handled by the
258 // interpreter or JIT, the special flag and the error state will be cleared
259 // and execution will continue from the end of the frame.
260 MOZ_ASSERT(!cx->isExceptionPending())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->isExceptionPending())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cx->isExceptionPending()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!cx->isExceptionPending()", "./../../../../js/src/debugger/Debugger.cpp"
, 260); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { MOZ_CrashSequence(__null, 260); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
261 cx->setPropagatingForcedReturn();
262 frame.setReturnValue(rval);
263}
264
265[[nodiscard]] static bool AdjustGeneratorResumptionValue(JSContext* cx,
266 AbstractFramePtr frame,
267 ResumeMode& resumeMode,
268 MutableHandleValue vp);
269
270[[nodiscard]] static bool ApplyFrameResumeMode(JSContext* cx,
271 AbstractFramePtr frame,
272 ResumeMode resumeMode,
273 HandleValue rv,
274 Handle<SavedFrame*> exnStack) {
275 RootedValue rval(cx, rv);
276
277 // The value passed in here is unwrapped and has no guarantees about what
278 // compartment it may be associated with, so we explicitly wrap it into the
279 // debuggee compartment.
280 if (!cx->compartment()->wrap(cx, &rval)) {
281 return false;
282 }
283
284 if (!AdjustGeneratorResumptionValue(cx, frame, resumeMode, &rval)) {
285 return false;
286 }
287
288 switch (resumeMode) {
289 case ResumeMode::Continue:
290 break;
291
292 case ResumeMode::Throw:
293 // If we have a stack from the original throw, use it instead of
294 // associating the throw with the current execution point.
295 if (exnStack) {
296 cx->setPendingException(rval, exnStack);
297 } else {
298 cx->setPendingException(rval, ShouldCaptureStack::Always);
299 }
300 return false;
301
302 case ResumeMode::Terminate:
303 cx->reportUncatchableException();
304 return false;
305
306 case ResumeMode::Return:
307 PropagateForcedReturn(cx, frame, rval);
308 return false;
309
310 default:
311 MOZ_CRASH("bad Debugger::onEnterFrame resume mode")do { do { } while (false); MOZ_ReportCrash("" "bad Debugger::onEnterFrame resume mode"
, "./../../../../js/src/debugger/Debugger.cpp", 311); AnnotateMozCrashReason
("MOZ_CRASH(" "bad Debugger::onEnterFrame resume mode" ")"); do
{ MOZ_CrashSequence(__null, 311); __attribute__((nomerge)) ::
abort(); } while (false); } while (false)
;
312 }
313
314 return true;
315}
316static bool ApplyFrameResumeMode(JSContext* cx, AbstractFramePtr frame,
317 ResumeMode resumeMode, HandleValue rval) {
318 Rooted<SavedFrame*> nullStack(cx);
319 return ApplyFrameResumeMode(cx, frame, resumeMode, rval, nullStack);
320}
321
322bool js::ValueToStableChars(JSContext* cx, const char* fnname,
323 HandleValue value,
324 AutoStableStringChars& stableChars) {
325 if (!value.isString()) {
326 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
327 JSMSG_NOT_EXPECTED_TYPE, fnname, "string",
328 InformalValueTypeName(value));
329 return false;
330 }
331 Rooted<JSLinearString*> linear(cx, value.toString()->ensureLinear(cx));
332 if (!linear) {
333 return false;
334 }
335 if (!stableChars.initTwoByte(cx, linear)) {
336 return false;
337 }
338 return true;
339}
340
341bool EvalOptions::setFilename(JSContext* cx, const char* filename) {
342 JS::UniqueChars copy;
343 if (filename) {
344 copy = DuplicateString(cx, filename);
345 if (!copy) {
346 return false;
347 }
348 }
349
350 filename_ = std::move(copy);
351 return true;
352}
353
354bool js::ParseEvalOptions(JSContext* cx, HandleValue value,
355 EvalOptions& options) {
356 if (!value.isObject()) {
357 return true;
358 }
359
360 RootedObject opts(cx, &value.toObject());
361
362 RootedValue v(cx);
363 if (!JS_GetProperty(cx, opts, "url", &v)) {
364 return false;
365 }
366 if (!v.isUndefined()) {
367 RootedString url_str(cx, ToString<CanGC>(cx, v));
368 if (!url_str) {
369 return false;
370 }
371 UniqueChars url_bytes = JS_EncodeStringToUTF8(cx, url_str);
372 if (!url_bytes) {
373 return false;
374 }
375 if (!options.setFilename(cx, url_bytes.get())) {
376 return false;
377 }
378 }
379
380 if (!JS_GetProperty(cx, opts, "lineNumber", &v)) {
381 return false;
382 }
383 if (!v.isUndefined()) {
384 uint32_t lineno;
385 if (!ToUint32(cx, v, &lineno)) {
386 return false;
387 }
388 options.setLineno(lineno);
389 }
390
391 if (!JS_GetProperty(cx, opts, "allowRedeclaringExistingLexicalBinding", &v)) {
392 return false;
393 }
394 if (v.isBoolean()) {
395 options.setAllowRedeclaringExistingLexicalBinding(v.toBoolean());
396 }
397
398 if (!JS_GetProperty(cx, opts, "hideFromDebugger", &v)) {
399 return false;
400 }
401 options.setHideFromDebugger(ToBoolean(v));
402
403 if (!JS_GetProperty(cx, opts, "bypassCSP", &v)) {
404 return false;
405 }
406 options.setBypassCSP(ToBoolean(v));
407
408 if (options.kind() == EvalOptions::EnvKind::GlobalWithExtraOuterBindings) {
409 if (!JS_GetProperty(cx, opts, "useInnerBindings", &v)) {
410 return false;
411 }
412 if (ToBoolean(v)) {
413 options.setUseInnerBindings();
414 }
415 }
416
417 // allowRedeclaringExistingLexicalBinding only applies to
418 // executeInGlobal and executeInGlobalWithBindings (without
419 // useInnerBindings). It isn't supported by Frame.eval and its
420 // variants, and combining it with useInnerBindings should throw.
421 if (options.allowRedeclaringExistingLexicalBinding() &&
422 options.kind() != EvalOptions::EnvKind::Global &&
423 options.kind() != EvalOptions::EnvKind::GlobalWithExtraOuterBindings) {
424 JS_ReportErrorASCII(
425 cx,
426 "allowRedeclaringExistingLexicalBinding is only supported by "
427 "executeInGlobal and executeInGlobalWithBindings without "
428 "useInnerBindings");
429 return false;
430 }
431
432 return true;
433}
434
435template <class R, class W, bool IKO>
436DebuggerWeakMap<R, W, IKO>::DebuggerWeakMap(JSContext* cx)
437 : Base(cx->zone()), compartment(cx->compartment()) {}
438
439/*** Breakpoints ************************************************************/
440
441bool BreakpointSite::isEmpty() const { return breakpoints.isEmpty(); }
442
443void BreakpointSite::trace(JSTracer* trc) {
444 for (auto p = breakpoints.begin(); p; p++) {
445 p->trace(trc);
446 }
447}
448
449void BreakpointSite::finalize(JS::GCContext* gcx) {
450 while (!breakpoints.isEmpty()) {
451 breakpoints.begin()->delete_(gcx);
452 }
453}
454
455Breakpoint* BreakpointSite::firstBreakpoint() const {
456 if (isEmpty()) {
457 return nullptr;
458 }
459 return &(*breakpoints.begin());
460}
461
462bool BreakpointSite::hasBreakpoint(Breakpoint* toFind) {
463 const BreakpointList::Iterator bp(toFind);
464 for (auto p = breakpoints.begin(); p; p++) {
465 if (p == bp) {
466 return true;
467 }
468 }
469 return false;
470}
471
472Breakpoint::Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
473 BreakpointSite* site, HandleObject handler)
474 : debugger(debugger),
475 wrappedDebugger(wrappedDebugger),
476 site(site),
477 handler(handler) {
478 MOZ_ASSERT(UncheckedUnwrap(wrappedDebugger) == debugger->object)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(UncheckedUnwrap(wrappedDebugger) == debugger->object
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(UncheckedUnwrap(wrappedDebugger) == debugger->object
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"UncheckedUnwrap(wrappedDebugger) == debugger->object", "./../../../../js/src/debugger/Debugger.cpp"
, 478); AnnotateMozCrashReason("MOZ_ASSERT" "(" "UncheckedUnwrap(wrappedDebugger) == debugger->object"
")"); do { MOZ_CrashSequence(__null, 478); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
479 MOZ_ASSERT(handler->compartment() == wrappedDebugger->compartment())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(handler->compartment() == wrappedDebugger->compartment
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(handler->compartment() == wrappedDebugger->compartment
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("handler->compartment() == wrappedDebugger->compartment()"
, "./../../../../js/src/debugger/Debugger.cpp", 479); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "handler->compartment() == wrappedDebugger->compartment()"
")"); do { MOZ_CrashSequence(__null, 479); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
480
481 debugger->breakpoints.pushBack(this);
482 site->breakpoints.pushBack(this);
483}
484
485void Breakpoint::trace(JSTracer* trc) {
486 MOZ_ASSERT_IF(trc->kind() != JS::TracerKind::Moving,do { if (trc->kind() != JS::TracerKind::Moving) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(!IsDeadProxyObject
(wrappedDebugger))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsDeadProxyObject(wrappedDebugger
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsDeadProxyObject(wrappedDebugger)", "./../../../../js/src/debugger/Debugger.cpp"
, 487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsDeadProxyObject(wrappedDebugger)"
")"); do { MOZ_CrashSequence(__null, 487); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
487 !IsDeadProxyObject(wrappedDebugger))do { if (trc->kind() != JS::TracerKind::Moving) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(!IsDeadProxyObject
(wrappedDebugger))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsDeadProxyObject(wrappedDebugger
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsDeadProxyObject(wrappedDebugger)", "./../../../../js/src/debugger/Debugger.cpp"
, 487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsDeadProxyObject(wrappedDebugger)"
")"); do { MOZ_CrashSequence(__null, 487); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
488 TraceEdge(trc, &wrappedDebugger, "breakpoint owner");
489
490 TraceEdge(trc, &handler, "breakpoint handler");
491}
492
493void Breakpoint::delete_(JS::GCContext* gcx) {
494 debugger->breakpoints.remove(this);
495 site->breakpoints.remove(this);
496 gc::Cell* cell = site->owningCell();
497 gcx->delete_(cell, this, MemoryUse::Breakpoint);
498}
499
500void Breakpoint::remove(JS::GCContext* gcx) {
501 BreakpointSite* savedSite = site;
502 delete_(gcx);
503
504 savedSite->destroyIfEmpty(gcx);
505}
506
507Breakpoint* Breakpoint::nextInDebugger() { return debuggerLink.mNext; }
508
509Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
510
511JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
512 : script(script), pc(pc) {
513 MOZ_ASSERT(!DebugAPI::hasBreakpointsAt(script, pc))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!DebugAPI::hasBreakpointsAt(script, pc))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!DebugAPI::hasBreakpointsAt(script, pc)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!DebugAPI::hasBreakpointsAt(script, pc)"
, "./../../../../js/src/debugger/Debugger.cpp", 513); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!DebugAPI::hasBreakpointsAt(script, pc)" ")"
); do { MOZ_CrashSequence(__null, 513); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
514}
515
516void JSBreakpointSite::remove(JS::GCContext* gcx) {
517 DebugScript::destroyBreakpointSite(gcx, script, pc);
518}
519
520void JSBreakpointSite::trace(JSTracer* trc) {
521 BreakpointSite::trace(trc);
522 TraceEdge(trc, &script, "breakpoint script");
523}
524
525void JSBreakpointSite::delete_(JS::GCContext* gcx) {
526 BreakpointSite::finalize(gcx);
527
528 gcx->delete_(script, this, MemoryUse::BreakpointSite);
529}
530
531gc::Cell* JSBreakpointSite::owningCell() { return script; }
532
533Realm* JSBreakpointSite::realm() const { return script->realm(); }
534
535WasmBreakpointSite::WasmBreakpointSite(WasmInstanceObject* instanceObject_,
536 uint32_t offset_)
537 : instanceObject(instanceObject_), offset(offset_) {
538 MOZ_ASSERT(instanceObject_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instanceObject_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(instanceObject_))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("instanceObject_"
, "./../../../../js/src/debugger/Debugger.cpp", 538); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instanceObject_" ")"); do { MOZ_CrashSequence
(__null, 538); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
539 MOZ_ASSERT(instanceObject_->instance().debugEnabled())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(instanceObject_->instance().debugEnabled())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(instanceObject_->instance().debugEnabled()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("instanceObject_->instance().debugEnabled()"
, "./../../../../js/src/debugger/Debugger.cpp", 539); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "instanceObject_->instance().debugEnabled()"
")"); do { MOZ_CrashSequence(__null, 539); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
540}
541
542void WasmBreakpointSite::trace(JSTracer* trc) {
543 BreakpointSite::trace(trc);
544 TraceEdge(trc, &instanceObject, "breakpoint Wasm instance");
545}
546
547void WasmBreakpointSite::remove(JS::GCContext* gcx) {
548 instanceObject->instance().destroyBreakpointSite(gcx, offset);
549}
550
551void WasmBreakpointSite::delete_(JS::GCContext* gcx) {
552 BreakpointSite::finalize(gcx);
553
554 gcx->delete_(instanceObject, this, MemoryUse::BreakpointSite);
555}
556
557gc::Cell* WasmBreakpointSite::owningCell() { return instanceObject; }
558
559Realm* WasmBreakpointSite::realm() const { return instanceObject->realm(); }
560
561/*** Debugger hook dispatch *************************************************/
562
563Debugger::Debugger(JSContext* cx, NativeObject* dbg)
564 : object(dbg),
565 debuggees(cx->zone()),
566 uncaughtExceptionHook(nullptr),
567 allowUnobservedWasm(false),
568 exclusiveDebuggerOnEval(false),
569 inspectNativeCallArguments(false),
570 collectCoverageInfo(false),
571 shouldAvoidSideEffects(false),
572 observedGCs(cx->zone()),
573 allocationsLog(cx),
574 trackingAllocationSites(false),
575 allocationSamplingProbability(1.0),
576 maxAllocationsLogLength(DEFAULT_MAX_LOG_LENGTH),
577 allocationsLogOverflowed(false),
578 frames(cx->zone()),
579 generatorFrames(cx),
580#ifdef ENABLE_WASM_JSPI1
581 wasmContFrames(cx->zone()),
582#endif
583 scripts(cx),
584 sources(cx),
585 objects(cx),
586 environments(cx),
587 wasmInstanceScripts(cx),
588 wasmInstanceSources(cx) {
589 cx->check(dbg);
590
591 cx->runtime()->debuggerList().insertBack(this);
592}
593
594template <typename ElementAccess>
595static void RemoveDebuggerEntry(
596 mozilla::DoublyLinkedList<Debugger, ElementAccess>& list, Debugger* dbg) {
597 // The "probably" here is because there could technically be multiple lists
598 // with this type signature and theoretically the debugger could be an entry
599 // in a different one. That is not actually possible however because there
600 // is only one list the debugger could be in.
601 if (list.ElementProbablyInList(dbg)) {
602 list.remove(dbg);
603 }
604}
605
606Debugger::~Debugger() {
607 MOZ_ASSERT(debuggees.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(debuggees.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(debuggees.empty()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("debuggees.empty()"
, "./../../../../js/src/debugger/Debugger.cpp", 607); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "debuggees.empty()" ")"); do { MOZ_CrashSequence
(__null, 607); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
608 allocationsLog.clear();
609
610 // Breakpoints should hold us alive, so any breakpoints remaining must be set
611 // in dying JSScripts. We should clean them up, but this never asserts. I'm
612 // not sure why.
613 MOZ_ASSERT(breakpoints.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(breakpoints.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(breakpoints.isEmpty()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("breakpoints.isEmpty()"
, "./../../../../js/src/debugger/Debugger.cpp", 613); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "breakpoints.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 613); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
614
615 // We don't have to worry about locking here since Debugger is not
616 // background finalized.
617 JSContext* cx = TlsContext.get();
618 RemoveDebuggerEntry(cx->runtime()->onNewGlobalObjectWatchers(), this);
619 RemoveDebuggerEntry(cx->runtime()->onGarbageCollectionWatchers(), this);
620}
621
622#ifdef DEBUG1
623/* static */
624bool Debugger::isChildJSObject(JSObject* obj) {
625 return obj->getClass() == &DebuggerFrame::class_ ||
626 obj->getClass() == &DebuggerScript::class_ ||
627 obj->getClass() == &DebuggerSource::class_ ||
628 obj->getClass() == &DebuggerObject::class_ ||
629 obj->getClass() == &DebuggerEnvironment::class_;
630}
631#endif
632
633bool Debugger::hasMemory() const {
634 return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE).isObject();
635}
636
637DebuggerMemory& Debugger::memory() const {
638 MOZ_ASSERT(hasMemory())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hasMemory())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hasMemory()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("hasMemory()", "./../../../../js/src/debugger/Debugger.cpp"
, 638); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hasMemory()"
")"); do { MOZ_CrashSequence(__null, 638); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
639 return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE)
640 .toObject()
641 .as<DebuggerMemory>();
642}
643
644/*** Debugger accessors *******************************************************/
645
646bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
647 MutableHandleValue vp) {
648 Rooted<DebuggerFrame*> result(cx);
649 if (!Debugger::getFrame(cx, iter, &result)) {
650 return false;
651 }
652 vp.setObject(*result);
653 return true;
654}
655
656bool Debugger::getFrame(JSContext* cx, MutableHandle<DebuggerFrame*> result) {
657 RootedObject proto(
658 cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
659 Rooted<NativeObject*> debugger(cx, object);
660
661 // Since there is no frame/generator data to associate with this frame, this
662 // will create a new, "terminated" Debugger.Frame object.
663 Rooted<DebuggerFrame*> frame(
664 cx, DebuggerFrame::create(cx, proto, debugger, nullptr, nullptr));
665 if (!frame) {
666 return false;
667 }
668
669 result.set(frame);
670 return true;
671}
672
673bool Debugger::getFrame(JSContext* cx, const FrameIter& iter,
674 MutableHandle<DebuggerFrame*> result) {
675 AbstractFramePtr referent = iter.abstractFramePtr();
676 MOZ_ASSERT_IF(referent.hasScript(), !referent.script()->selfHosted())do { if (referent.hasScript()) { do { static_assert( mozilla::
detail::AssertionConditionType<decltype(!referent.script()
->selfHosted())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!referent.script()->selfHosted
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!referent.script()->selfHosted()", "./../../../../js/src/debugger/Debugger.cpp"
, 676); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!referent.script()->selfHosted()"
")"); do { MOZ_CrashSequence(__null, 676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
677
678 // A generator's resume is finished at JSOp::AfterYield. Before that, the
679 // frame's pc is still the script start and its locals and expression stack
680 // haven't been restored.
681 MOZ_ASSERT(!iter.isResumingGenerator())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.isResumingGenerator())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.isResumingGenerator())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!iter.isResumingGenerator()"
, "./../../../../js/src/debugger/Debugger.cpp", 681); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!iter.isResumingGenerator()" ")"); do { MOZ_CrashSequence
(__null, 681); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
682
683 FrameMap::AddPtr p = frames.lookupForAdd(referent);
684 if (!p) {
685 Rooted<AbstractGeneratorObject*> genObj(cx);
686 if (referent.isGeneratorFrame()) {
687 if (referent.isFunctionFrame()) {
688 AutoRealm ar(cx, referent.callee());
689 genObj = GetGeneratorObjectForFrame(cx, referent);
690 } else {
691 MOZ_ASSERT(referent.isModuleFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(referent.isModuleFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(referent.isModuleFrame()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("referent.isModuleFrame()"
, "./../../../../js/src/debugger/Debugger.cpp", 691); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "referent.isModuleFrame()" ")"); do { MOZ_CrashSequence
(__null, 691); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
692 AutoRealm ar(cx, referent.script()->module());
693 genObj = GetGeneratorObjectForFrame(cx, referent);
694 }
695
696 // If this frame has a generator associated with it, but no on-stack
697 // Debugger.Frame object was found, there should not be a suspended
698 // Debugger.Frame either because otherwise slowPathOnResumeFrame would
699 // have already populated the "frames" map with a Debugger.Frame.
700 MOZ_ASSERT_IF(genObj, !generatorFrames.has(genObj))do { if (genObj) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(!generatorFrames.has(genObj))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!generatorFrames.has(genObj)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!generatorFrames.has(genObj)", "./../../../../js/src/debugger/Debugger.cpp"
, 700); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!generatorFrames.has(genObj)"
")"); do { MOZ_CrashSequence(__null, 700); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
701
702 // If the frame's generator is closed, there is no way to associate the
703 // generator with the frame successfully because there is no way to
704 // get the generator's callee script, and even if we could, having it
705 // there would in no way affect the behavior of the frame.
706 if (genObj && genObj->isClosed()) {
707 genObj = nullptr;
708 }
709
710 // If no AbstractGeneratorObject exists yet, we create a Debugger.Frame
711 // below anyway, and Debugger::onNewGenerator() will associate it
712 // with the AbstractGeneratorObject later when we hit JSOp::Generator.
713 }
714
715 // Create and populate the Debugger.Frame object.
716 RootedObject proto(
717 cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
718 Rooted<NativeObject*> debugger(cx, object);
719
720 Rooted<DebuggerFrame*> frame(
721 cx, DebuggerFrame::create(cx, proto, debugger, &iter, genObj));
722 if (!frame) {
723 return false;
724 }
725
726 auto terminateDebuggerFrameGuard = MakeScopeExit([&] {
727 terminateDebuggerFrame(cx->gcContext(), this, frame, referent);
728 });
729
730 if (genObj) {
731 DependentAddPtr<GeneratorWeakMap> genPtr(cx, generatorFrames, genObj);
732 if (!genPtr.add(cx, generatorFrames, genObj, frame)) {
733 return false;
734 }
735 }
736
737 if (!ensureExecutionObservabilityOfFrame(cx, referent)) {
738 return false;
739 }
740
741 if (!frames.add(p, referent, frame)) {
742 ReportOutOfMemory(cx);
743 return false;
744 }
745
746#ifdef ENABLE_WASM_JSPI1
747 if (frame->isWasmContFrame()) {
748 if (!wasmContFrames.append(referent)) {
749 // terminateDebuggerFrameGuard is still armed and will remove the
750 // entry from `frames` on return.
751 ReportOutOfMemory(cx);
752 return false;
753 }
754 }
755#endif
756
757 terminateDebuggerFrameGuard.release();
758 }
759
760 result.set(p->value());
761 return true;
762}
763
764bool Debugger::getFrame(JSContext* cx, Handle<AbstractGeneratorObject*> genObj,
765 MutableHandle<DebuggerFrame*> result) {
766 // To create a Debugger.Frame for a running generator, we'd also need a
767 // FrameIter for its stack frame. We could make this work by searching the
768 // stack for the generator's frame, but for the moment, we only need this
769 // function to handle generators we've found on promises' reaction records,
770 // which should always be suspended.
771 MOZ_ASSERT(genObj->isSuspended())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj->isSuspended())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(genObj->isSuspended()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("genObj->isSuspended()"
, "./../../../../js/src/debugger/Debugger.cpp", 771); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "genObj->isSuspended()" ")"); do { MOZ_CrashSequence
(__null, 771); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
772
773 // Do we have an existing Debugger.Frame for this generator?
774 DependentAddPtr<GeneratorWeakMap> p(cx, generatorFrames, genObj);
775 if (p) {
776 MOZ_ASSERT(&p->value()->unwrappedGenerator() == genObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(&p->value()->unwrappedGenerator() == genObj
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(&p->value()->unwrappedGenerator() == genObj
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"&p->value()->unwrappedGenerator() == genObj", "./../../../../js/src/debugger/Debugger.cpp"
, 776); AnnotateMozCrashReason("MOZ_ASSERT" "(" "&p->value()->unwrappedGenerator() == genObj"
")"); do { MOZ_CrashSequence(__null, 776); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
777 result.set(p->value());
778 return true;
779 }
780
781 // Create a new Debugger.Frame.
782 RootedObject proto(
783 cx, &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject());
784 Rooted<NativeObject*> debugger(cx, object);
785
786 result.set(DebuggerFrame::create(cx, proto, debugger, nullptr, genObj));
787 if (!result) {
788 return false;
789 }
790
791 if (!p.add(cx, generatorFrames, genObj, result)) {
792 terminateDebuggerFrame(cx->gcContext(), this, result, NullFramePtr());
793 return false;
794 }
795
796 return true;
797}
798
799static bool DebuggerExists(
800 GlobalObject* global, const std::function<bool(Debugger* dbg)>& predicate) {
801 // The GC analysis can't determine that the predicate can't GC, so let it know
802 // explicitly.
803 JS::AutoSuppressGCAnalysis nogc;
804
805 for (Realm::DebuggerVectorEntry& entry : global->getDebuggers(nogc)) {
806 // Callbacks should not create new references to the debugger, so don't
807 // use a barrier. This allows this method to be called during GC.
808 if (predicate(entry.dbg.unbarrieredGet())) {
809 return true;
810 }
811 }
812 return false;
813}
814
815/* static */
816bool Debugger::hasLiveHook(GlobalObject* global, Hook which) {
817 return DebuggerExists(global,
818 [=](Debugger* dbg) { return dbg->getHook(which); });
819}
820
821/* static */
822bool DebugAPI::debuggerObservesAllExecution(GlobalObject* global) {
823 return DebuggerExists(
824 global, [=](Debugger* dbg) { return dbg->observesAllExecution(); });
825}
826
827/* static */
828bool DebugAPI::debuggerObservesCoverage(GlobalObject* global) {
829 return DebuggerExists(global,
830 [=](Debugger* dbg) { return dbg->observesCoverage(); });
831}
832
833/* static */
834bool DebugAPI::debuggerObservesWasm(GlobalObject* global) {
835 return DebuggerExists(global,
836 [=](Debugger* dbg) { return dbg->observesWasm(); });
837}
838
839/* static */
840bool DebugAPI::debuggerObservesNativeCall(GlobalObject* global) {
841 return DebuggerExists(
842 global, [=](Debugger* dbg) { return dbg->observesNativeCalls(); });
843}
844
845/* static */
846bool DebugAPI::hasExceptionUnwindHook(GlobalObject* global) {
847 return Debugger::hasLiveHook(global, Debugger::OnExceptionUnwind);
848}
849
850/* static */
851bool DebugAPI::hasDebuggerStatementHook(GlobalObject* global) {
852 return Debugger::hasLiveHook(global, Debugger::OnDebuggerStatement);
853}
854
855template <typename HookIsEnabledFun /* bool (Debugger*) */>
856bool DebuggerList<HookIsEnabledFun>::init(JSContext* cx) {
857 // Determine which debuggers will receive this event, and in what order.
858 // Make a copy of the list, since the original is mutable and we will be
859 // calling into arbitrary JS.
860 Handle<GlobalObject*> global = cx->global();
861 JS::AutoAssertNoGC nogc;
862 for (Realm::DebuggerVectorEntry& entry : global->getDebuggers(nogc)) {
863 Debugger* dbg = entry.dbg;
864 if (dbg->isHookCallAllowed(cx) && hookIsEnabled(dbg)) {
865 if (!debuggers.append(ObjectValue(*dbg->toJSObject()))) {
866 return false;
867 }
868 }
869 }
870 return true;
871}
872
873template <typename HookIsEnabledFun /* bool (Debugger*) */>
874template <typename FireHookFun /* bool (Debugger*) */>
875bool DebuggerList<HookIsEnabledFun>::dispatchHook(JSContext* cx,
876 FireHookFun fireHook) {
877 // Preserve the debuggee's microtask event queue while we run the hooks, so
878 // the debugger's microtask checkpoints don't run from the debuggee's
879 // microtasks, and vice versa.
880 JS::AutoDebuggerJobQueueInterruption adjqi;
881 if (!adjqi.init(cx)) {
882 return false;
883 }
884
885 // Deliver the event to each debugger, checking again to make sure it
886 // should still be delivered.
887 Handle<GlobalObject*> global = cx->global();
888 for (Value* p = debuggers.begin(); p != debuggers.end(); p++) {
889 Debugger* dbg = Debugger::fromJSObject(&p->toObject());
890 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
891 if (dbg->debuggees.has(global) && hookIsEnabled(dbg)) {
892 bool result =
893 dbg->enterDebuggerHook(cx, [&]() -> bool { return fireHook(dbg); });
894 adjqi.runJobs();
895 if (!result) {
896 return false;
897 }
898 }
899 }
900 return true;
901}
902
903template <typename HookIsEnabledFun /* bool (Debugger*) */>
904template <typename FireHookFun /* bool (Debugger*) */>
905void DebuggerList<HookIsEnabledFun>::dispatchQuietHook(JSContext* cx,
906 FireHookFun fireHook) {
907 bool result =
908 dispatchHook(cx, [&](Debugger* dbg) -> bool { return fireHook(dbg); });
909
910 // dispatchHook may fail due to OOM. This OOM is not handlable at the
911 // callsites of dispatchQuietHook in the engine.
912 if (!result) {
913 cx->clearPendingException();
914 }
915}
916
917template <typename HookIsEnabledFun /* bool (Debugger*) */>
918template <typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue vp) */>
919bool DebuggerList<HookIsEnabledFun>::dispatchResumptionHook(
920 JSContext* cx, AbstractFramePtr frame, FireHookFun fireHook) {
921 ResumeMode resumeMode = ResumeMode::Continue;
922 RootedValue rval(cx);
923 return dispatchHook(cx,
924 [&](Debugger* dbg) -> bool {
925 return fireHook(dbg, resumeMode, &rval);
926 }) &&
927 ApplyFrameResumeMode(cx, frame, resumeMode, rval);
928}
929
930JSObject* Debugger::getHook(Hook hook) const {
931 MOZ_ASSERT(hook >= 0 && hook < HookCount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook >= 0 && hook < HookCount)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(hook >= 0 && hook < HookCount))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("hook >= 0 && hook < HookCount"
, "./../../../../js/src/debugger/Debugger.cpp", 931); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook >= 0 && hook < HookCount"
")"); do { MOZ_CrashSequence(__null, 931); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
932 const Value& v = object->getReservedSlot(JSSLOT_DEBUG_HOOK_START +
933 std::underlying_type_t<Hook>(hook));
934 return v.isUndefined() ? nullptr : &v.toObject();
935}
936
937bool Debugger::hasAnyLiveHooks() const {
938 // A onNewGlobalObject hook does not hold its Debugger live, so its behavior
939 // is nondeterministic. This behavior is not satisfying, but it is at least
940 // documented.
941 if (getHook(OnDebuggerStatement) || getHook(OnExceptionUnwind) ||
942 getHook(OnNewScript) || getHook(OnEnterFrame)) {
943 return true;
944 }
945
946 return false;
947}
948
949/* static */
950bool DebugAPI::slowPathOnEnterFrame(JSContext* cx, AbstractFramePtr frame) {
951#ifdef MOZ_EXECUTION_TRACING1
952 if (cx->hasExecutionTracer()) {
953 cx->getExecutionTracer().onEnterFrame(cx, frame);
954 }
955#endif
956 return Debugger::dispatchResumptionHook(
957 cx, frame,
958 [frame](Debugger* dbg) -> bool {
959 return dbg->observesFrame(frame) && dbg->observesEnterFrame();
960 },
961 [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
962 -> bool { return dbg->fireEnterFrame(cx, resumeMode, vp); });
963}
964
965/* static */
966bool DebugAPI::slowPathOnResumeFrame(JSContext* cx, AbstractFramePtr frame) {
967#ifdef MOZ_EXECUTION_TRACING1
968 if (cx->hasExecutionTracer()) {
969 cx->getExecutionTracer().onEnterFrame(cx, frame);
970 }
971#endif
972 // Don't count on this method to be called every time a generator is
973 // resumed! This is called only if the frame's debuggee bit is set,
974 // i.e. the script has breakpoints or the frame is stepping.
975 MOZ_ASSERT(frame.isGeneratorFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frame.isGeneratorFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame.isGeneratorFrame()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("frame.isGeneratorFrame()"
, "./../../../../js/src/debugger/Debugger.cpp", 975); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame.isGeneratorFrame()" ")"); do { MOZ_CrashSequence
(__null, 975); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
976 MOZ_ASSERT(frame.isDebuggee())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frame.isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame.isDebuggee()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("frame.isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 976); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame.isDebuggee()" ")"); do { MOZ_CrashSequence
(__null, 976); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
977
978 Rooted<AbstractGeneratorObject*> genObj(
979 cx, GetGeneratorObjectForFrame(cx, frame));
980 MOZ_ASSERT(genObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(genObj))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("genObj", "./../../../../js/src/debugger/Debugger.cpp"
, 980); AnnotateMozCrashReason("MOZ_ASSERT" "(" "genObj" ")")
; do { MOZ_CrashSequence(__null, 980); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
981
982 // If there is an OOM, we mark all of the Debugger.Frame objects terminated
983 // because we want to ensure that none of the frames are in a partially
984 // initialized state where they are in "generatorFrames" but not "frames".
985 auto terminateDebuggerFramesGuard = MakeScopeExit([&] {
986 Debugger::terminateDebuggerFrames(cx, frame);
987
988 MOZ_ASSERT(!DebugAPI::inFrameMaps(frame))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!DebugAPI::inFrameMaps(frame))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!DebugAPI::inFrameMaps(frame
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!DebugAPI::inFrameMaps(frame)", "./../../../../js/src/debugger/Debugger.cpp"
, 988); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!DebugAPI::inFrameMaps(frame)"
")"); do { MOZ_CrashSequence(__null, 988); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
989 });
990
991 // For each debugger, if there is an existing Debugger.Frame object for the
992 // resumed `frame`, update it with the new frame pointer and make sure the
993 // frame is observable.
994 FrameIter iter(cx);
995 MOZ_ASSERT(iter.abstractFramePtr() == frame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.abstractFramePtr() == frame)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.abstractFramePtr() == frame
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"iter.abstractFramePtr() == frame", "./../../../../js/src/debugger/Debugger.cpp"
, 995); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.abstractFramePtr() == frame"
")"); do { MOZ_CrashSequence(__null, 995); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
996 jsbytecode* pc = iter.pc();
997 MOZ_ASSERT(JSOp(*pc) == JSOp::AfterYield)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JSOp(*pc) == JSOp::AfterYield)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JSOp(*pc) == JSOp::AfterYield
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"JSOp(*pc) == JSOp::AfterYield", "./../../../../js/src/debugger/Debugger.cpp"
, 997); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JSOp(*pc) == JSOp::AfterYield"
")"); do { MOZ_CrashSequence(__null, 997); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
998 {
999 JS::AutoAssertNoGC nogc;
1000 for (Realm::DebuggerVectorEntry& entry :
1001 frame.global()->getDebuggers(nogc)) {
1002 Debugger* dbg = entry.dbg;
1003 if (Debugger::GeneratorWeakMap::Ptr generatorEntry =
1004 dbg->generatorFrames.lookup(genObj)) {
1005 DebuggerFrame* frameObj = generatorEntry->value();
1006 MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(&frameObj->unwrappedGenerator() == genObj)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(&frameObj->unwrappedGenerator() == genObj))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("&frameObj->unwrappedGenerator() == genObj"
, "./../../../../js/src/debugger/Debugger.cpp", 1006); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "&frameObj->unwrappedGenerator() == genObj"
")"); do { MOZ_CrashSequence(__null, 1006); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1007 if (!dbg->frames.putNew(frame, frameObj)) {
1008 ReportOutOfMemory(cx);
1009 return false;
1010 }
1011 if (!frameObj->resume(iter)) {
1012 return false;
1013 }
1014 }
1015 }
1016 }
1017
1018 terminateDebuggerFramesGuard.release();
1019
1020 if (!slowPathOnEnterFrame(cx, frame)) {
1021 return false;
1022 }
1023
1024 // Handle breakpoints/stepping for the JSOp::AfterYield op.
1025 if (DebugAPI::stepModeEnabled(frame.script())) {
1026 if (!DebugAPI::onSingleStep(cx)) {
1027 return false;
1028 }
1029 }
1030 if (DebugAPI::hasBreakpointsAt(frame.script(), pc)) {
1031 if (!DebugAPI::onTrap(cx)) {
1032 return false;
1033 }
1034 }
1035
1036 return true;
1037}
1038
1039/* static */
1040NativeResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx,
1041 const CallArgs& args,
1042 CallReason reason) {
1043 if (!cx->realm()->debuggerObservesNativeCall()) {
1044 return NativeResumeMode::Continue;
1045 }
1046
1047 DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
1048 return dbg->getHook(Debugger::OnNativeCall);
1049 });
1050
1051 if (!debuggerList.init(cx)) {
1052 return NativeResumeMode::Abort;
1053 }
1054
1055 if (debuggerList.empty()) {
1056 return NativeResumeMode::Continue;
1057 }
1058
1059 // The onNativeCall hook is fired when self hosted functions are called,
1060 // and any other self hosted function or C++ native that is directly called
1061 // by the self hosted function is considered to be part of the same
1062 // native call, except for the following 4 cases:
1063 //
1064 // * callContentFunction and constructContentFunction,
1065 // which uses CallReason::CallContent
1066 // * Function.prototype.call and Function.prototype.apply,
1067 // which uses CallReason::FunCall
1068 // * Getter call which uses CallReason::Getter
1069 // * Setter call which uses CallReason::Setter
1070 //
1071 // We check this only after checking that debuggerList has items in order
1072 // to avoid unnecessary calls to cx->currentScript(), which can be expensive
1073 // when the top frame is in jitcode.
1074 JSScript* script = cx->currentScript();
1075 if (script && script->selfHosted() && reason != CallReason::CallContent &&
1076 reason != CallReason::FunCall && reason != CallReason::Getter &&
1077 reason != CallReason::Setter) {
1078 return NativeResumeMode::Continue;
1079 }
1080
1081 RootedValue rval(cx);
1082 ResumeMode resumeMode = ResumeMode::Continue;
1083 bool result = debuggerList.dispatchHook(cx, [&](Debugger* dbg) -> bool {
1084 return dbg->fireNativeCall(cx, args, reason, resumeMode, &rval);
1085 });
1086 if (!result) {
1087 return NativeResumeMode::Abort;
1088 }
1089
1090 // Hook must follow normal native function conventions and not return
1091 // primitive values.
1092 if (resumeMode == ResumeMode::Return) {
1093 if (args.isConstructing() && !rval.isObject()) {
1094 JS_ReportErrorASCII(
1095 cx, "onNativeCall hook must return an object for constructor call");
1096 return NativeResumeMode::Abort;
1097 }
1098 }
1099
1100 // The value is not in any particular compartment, so it needs to be
1101 // explicitly wrapped into the debuggee compartment.
1102 if (!cx->compartment()->wrap(cx, &rval)) {
1103 return NativeResumeMode::Abort;
1104 }
1105
1106 switch (resumeMode) {
1107 case ResumeMode::Continue:
1108 break;
1109
1110 case ResumeMode::Throw:
1111 cx->setPendingException(rval, ShouldCaptureStack::Always);
1112 return NativeResumeMode::Abort;
1113
1114 case ResumeMode::Terminate:
1115 cx->reportUncatchableException();
1116 return NativeResumeMode::Abort;
1117
1118 case ResumeMode::Return:
1119 args.rval().set(rval);
1120 return NativeResumeMode::Override;
1121 }
1122
1123 return NativeResumeMode::Continue;
1124}
1125
1126/* static */
1127bool DebugAPI::slowPathShouldAvoidSideEffects(JSContext* cx) {
1128 return DebuggerExists(
1129 cx->global(), [=](Debugger* dbg) { return dbg->shouldAvoidSideEffects; });
1130}
1131
1132/*
1133 * RAII class to mark a generator as "running" temporarily while running
1134 * debugger code.
1135 *
1136 * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
1137 * or awaiting, its generator is in the "suspended" state. Letting script
1138 * observe this state, with the generator on stack yet also reenterable, would
1139 * be bad, so we mark it running while we fire events.
1140 */
1141class MOZ_RAII AutoSetGeneratorRunning {
1142 int32_t resumeIndex_;
1143 AsyncGeneratorObject::State asyncGenState_;
1144 Rooted<AbstractGeneratorObject*> genObj_;
1145
1146 public:
1147 AutoSetGeneratorRunning(JSContext* cx,
1148 Handle<AbstractGeneratorObject*> genObj)
1149 : resumeIndex_(0),
1150 asyncGenState_(static_cast<AsyncGeneratorObject::State>(0)),
1151 genObj_(cx, genObj) {
1152 if (genObj) {
1153 if (!genObj->isClosed() && !genObj->isBeforeInitialYield() &&
1154 genObj->isSuspended()) {
1155 // Yielding or awaiting.
1156 resumeIndex_ = genObj->resumeIndex();
1157 genObj->setRunning();
1158
1159 // Async generators have additionally bookkeeping which must be
1160 // adjusted when switching over to the running state.
1161 if (genObj->is<AsyncGeneratorObject>()) {
1162 auto* generator = &genObj->as<AsyncGeneratorObject>();
1163 asyncGenState_ = generator->state();
1164 generator->setExecuting();
1165 }
1166 } else {
1167 // Returning or throwing. The generator is already closed, if
1168 // it was ever exposed at all.
1169 genObj_ = nullptr;
1170 }
1171 }
1172 }
1173
1174 ~AutoSetGeneratorRunning() {
1175 if (genObj_) {
1176 MOZ_ASSERT(genObj_->isRunning())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj_->isRunning())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(genObj_->isRunning()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("genObj_->isRunning()"
, "./../../../../js/src/debugger/Debugger.cpp", 1176); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "genObj_->isRunning()" ")"); do { MOZ_CrashSequence
(__null, 1176); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1177 genObj_->setResumeIndex(resumeIndex_);
1178 if (genObj_->is<AsyncGeneratorObject>()) {
1179 genObj_->as<AsyncGeneratorObject>().setState(asyncGenState_);
1180 }
1181 }
1182 }
1183};
1184
1185/*
1186 * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
1187 * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
1188 * |cx->fp()|'s return value, and return a new success value.
1189 */
1190/* static */
1191bool DebugAPI::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
1192 const jsbytecode* pc, bool frameOk) {
1193#ifdef MOZ_EXECUTION_TRACING1
1194 if (cx->hasExecutionTracer()) {
1195 cx->getExecutionTracer().onLeaveFrame(cx, frame);
1196 }
1197#endif
1198 MOZ_ASSERT_IF(!frame.isWasmDebugFrame(), pc)do { if (!frame.isWasmDebugFrame()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(pc)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pc))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("pc", "./../../../../js/src/debugger/Debugger.cpp", 1198); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pc" ")"); do { MOZ_CrashSequence(__null, 1198
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); } } while (false)
;
1199
1200 mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
1201
1202 // These are updated below, but consulted by the cleanup code we register now,
1203 // so declare them here, initialized to quiescent values.
1204 Rooted<Completion> completion(cx);
1205 bool success = false;
1206
1207 auto frameMapsGuard = MakeScopeExit([&] {
1208 // Clean up all Debugger.Frame instances on exit. On suspending, pass the
1209 // flag that says to leave those frames `.live`. Note that if the completion
1210 // is a suspension but success is false, the generator gets closed, not
1211 // suspended.
1212 if (success && completion.get().suspending()) {
1213 Debugger::suspendGeneratorDebuggerFrames(cx, frame);
1214 } else {
1215 if (frame.isWasmDebugFrame()) {
1216 DebugEnvironments::onPopWasm(cx, frame);
1217 }
1218 Debugger::terminateDebuggerFrames(cx, frame);
1219 }
1220 });
1221
1222 // The onPop handler and associated clean up logic should not run multiple
1223 // times on the same frame. If slowPathOnLeaveFrame has already been
1224 // called, the frame will not be present in the Debugger frame maps.
1225 Rooted<Debugger::DebuggerFrameVector> frames(cx);
1226 if (!Debugger::getDebuggerFrames(frame, &frames)) {
1227 // There is at least one match Debugger.Frame we failed to process, so drop
1228 // the pending exception and raise an out-of-memory instead.
1229 if (!frameOk) {
1230 cx->clearPendingException();
1231 }
1232 ReportOutOfMemory(cx);
1233 return false;
1234 }
1235 if (frames.empty()) {
1236 return frameOk;
1237 }
1238
1239 // Convert current exception state into a Completion and clear exception off
1240 // of the JSContext.
1241 completion = Completion::fromJSFramePop(cx, frame, pc, frameOk);
1242
1243 ResumeMode resumeMode = ResumeMode::Continue;
1244 RootedValue rval(cx);
1245
1246 {
1247 // Preserve the debuggee's microtask event queue while we run the hooks, so
1248 // the debugger's microtask checkpoints don't run from the debuggee's
1249 // microtasks, and vice versa.
1250 JS::AutoDebuggerJobQueueInterruption adjqi;
1251 if (!adjqi.init(cx)) {
1252 return false;
1253 }
1254
1255 // This path can be hit via unwinding the stack due to over-recursion or
1256 // OOM. In those cases, don't fire the frames' onPop handlers, because
1257 // invoking JS will only trigger the same condition. See
1258 // slowPathOnExceptionUnwind.
1259 if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
1260 Rooted<AbstractGeneratorObject*> genObj(
1261 cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
1262 : nullptr);
1263
1264 // For each Debugger.Frame, fire its onPop handler, if any.
1265 for (size_t i = 0; i < frames.length(); i++) {
1266 Handle<DebuggerFrame*> frameobj = frames[i];
1267 Debugger* dbg = frameobj->owner();
1268 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
1269
1270 // Removing a global from a Debugger's debuggee set kills all of that
1271 // Debugger's D.Fs in that global. This means that one D.F's onPop can
1272 // kill the next D.F. So we have to check whether frameobj is still "on
1273 // the stack".
1274 if (frameobj->isOnStack(cx) && frameobj->onPopHandler()) {
1275 OnPopHandler* handler = frameobj->onPopHandler();
1276
1277 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
1278 ResumeMode nextResumeMode = ResumeMode::Continue;
1279 RootedValue nextValue(cx);
1280
1281 // Call the onPop handler.
1282 bool success;
1283 {
1284 // Mark the generator as running, to prevent reentrance.
1285 //
1286 // At certain points in a generator's lifetime,
1287 // GetGeneratorObjectForFrame can return null even when the
1288 // generator exists, but at those points the generator has not yet
1289 // been exposed to JavaScript, so reentrance isn't possible
1290 // anyway. So there's no harm done if this has no effect in that
1291 // case.
1292 AutoSetGeneratorRunning asgr(cx, genObj);
1293 success = handler->onPop(cx, frameobj, completion, nextResumeMode,
1294 &nextValue);
1295 }
1296
1297 return dbg->processParsedHandlerResult(cx, frame, pc, success,
1298 nextResumeMode, nextValue,
1299 resumeMode, &rval);
1300 });
1301 adjqi.runJobs();
1302
1303 if (!result) {
1304 return false;
1305 }
1306
1307 // At this point, we are back in the debuggee compartment, and
1308 // any error has been wrapped up as a completion value.
1309 MOZ_ASSERT(!cx->isExceptionPending())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->isExceptionPending())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cx->isExceptionPending()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!cx->isExceptionPending()", "./../../../../js/src/debugger/Debugger.cpp"
, 1309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { MOZ_CrashSequence(__null, 1309); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1310 }
1311 }
1312 }
1313 }
1314
1315 completion.get().updateFromHookResult(resumeMode, rval);
1316
1317 // Now that we've run all the handlers, extract the final resumption mode. */
1318 ResumeMode completionResumeMode;
1319 RootedValue completionValue(cx);
1320 Rooted<SavedFrame*> completionStack(cx);
1321 completion.get().toResumeMode(completionResumeMode, &completionValue,
1322 &completionStack);
1323
1324 // If we are returning the original value used to create the completion, then
1325 // we don't want to treat the resumption value as a Return completion, because
1326 // that would cause us to apply AdjustGeneratorResumptionValue to the
1327 // already-adjusted value that the generator actually returned.
1328 if (resumeMode == ResumeMode::Continue &&
1329 completionResumeMode == ResumeMode::Return) {
1330 completionResumeMode = ResumeMode::Continue;
1331 }
1332
1333 if (!ApplyFrameResumeMode(cx, frame, completionResumeMode, completionValue,
1334 completionStack)) {
1335 if (!cx->isPropagatingForcedReturn()) {
1336 // If this is an exception or termination, we just propagate that along.
1337 return false;
1338 }
1339
1340 // Since we are leaving the frame here, we can convert a forced return
1341 // into a normal return right away.
1342 cx->clearPropagatingForcedReturn();
1343 }
1344 success = true;
1345 return true;
1346}
1347
1348/* static */
1349bool DebugAPI::slowPathOnNewGenerator(JSContext* cx, AbstractFramePtr frame,
1350 Handle<AbstractGeneratorObject*> genObj) {
1351 // This is called from JSOp::Generator, after default parameter expressions
1352 // are evaluated and well after onEnterFrame, so Debugger.Frame objects for
1353 // `frame` may already have been exposed to debugger code. The
1354 // AbstractGeneratorObject for this generator call, though, has just been
1355 // created. It must be associated with any existing Debugger.Frames.
1356
1357 // Initializing frames with their associated generator is critical to the
1358 // functionality of the debugger, so if there is an OOM, we want to
1359 // cleanly terminate all of the frames.
1360 auto terminateDebuggerFramesGuard =
1361 MakeScopeExit([&] { Debugger::terminateDebuggerFrames(cx, frame); });
1362
1363 bool ok = true;
1364 gc::AutoSuppressGC nogc(cx);
1365 Debugger::forEachOnStackDebuggerFrame(
1366 frame, nogc, [&](Debugger* dbg, DebuggerFrame* frameObjPtr) {
1367 if (!ok) {
1368 return;
1369 }
1370
1371 Rooted<DebuggerFrame*> frameObj(cx, frameObjPtr);
1372
1373 AutoRealm ar(cx, frameObj);
1374
1375 if (!DebuggerFrame::setGeneratorInfo(cx, frameObj, genObj)) {
1376 // This leaves `genObj` and `frameObj` unassociated. It's OK
1377 // because we won't pause again with this generator on the stack:
1378 // the caller will immediately discard `genObj` and unwind `frame`.
1379 ok = false;
1380 return;
1381 }
1382
1383 DependentAddPtr<Debugger::GeneratorWeakMap> genPtr(
1384 cx, dbg->generatorFrames, genObj);
1385 if (!genPtr.add(cx, dbg->generatorFrames, genObj, frameObj)) {
1386 ok = false;
1387 }
1388 });
1389
1390 if (!ok) {
1391 return false;
1392 }
1393
1394 terminateDebuggerFramesGuard.release();
1395 return true;
1396}
1397
1398/* static */
1399bool DebugAPI::slowPathOnDebuggerStatement(JSContext* cx,
1400 AbstractFramePtr frame) {
1401 return Debugger::dispatchResumptionHook(
1402 cx, frame,
1403 [](Debugger* dbg) -> bool {
1404 return dbg->getHook(Debugger::OnDebuggerStatement);
1405 },
1406 [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
1407 -> bool { return dbg->fireDebuggerStatement(cx, resumeMode, vp); });
1408}
1409
1410/* static */
1411bool DebugAPI::slowPathOnExceptionUnwind(JSContext* cx,
1412 AbstractFramePtr frame) {
1413 // Invoking more JS on an over-recursed stack or after OOM is only going
1414 // to result in more of the same error.
1415 if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) {
1416 return true;
1417 }
1418
1419 // The Debugger API mustn't muck with frames from self-hosted scripts.
1420 if (frame.hasScript() && frame.script()->selfHosted()) {
1421 return true;
1422 }
1423
1424 DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
1425 return dbg->getHook(Debugger::OnExceptionUnwind);
1426 });
1427
1428 if (!debuggerList.init(cx)) {
1429 return false;
1430 }
1431
1432 if (debuggerList.empty()) {
1433 return true;
1434 }
1435
1436 // We save and restore the exception once up front to avoid having to do it
1437 // for each 'onExceptionUnwind' hook that has been registered, and we also
1438 // only do it if the debuggerList contains items in order to avoid extra work.
1439 RootedValue exc(cx);
1440 Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
1441 if (!cx->getPendingException(&exc)) {
1442 return false;
1443 }
1444 cx->clearPendingException();
1445
1446 bool result = debuggerList.dispatchResumptionHook(
1447 cx, frame,
1448 [&](Debugger* dbg, ResumeMode& resumeMode,
1449 MutableHandleValue vp) -> bool {
1450 return dbg->fireExceptionUnwind(cx, exc, resumeMode, vp);
1451 });
1452 if (!result) {
1453 return false;
1454 }
1455
1456 cx->setPendingException(exc, stack);
1457 return true;
1458}
1459
1460// TODO: Remove Remove this function when all properties/methods returning a
1461/// DebuggerEnvironment have been given a C++ interface (bug 1271649).
1462bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1463 MutableHandleValue rval) {
1464 if (!env) {
1465 rval.setNull();
1466 return true;
1467 }
1468
1469 Rooted<DebuggerEnvironment*> envobj(cx);
1470
1471 if (!wrapEnvironment(cx, env, &envobj)) {
1472 return false;
1473 }
1474
1475 rval.setObject(*envobj);
1476 return true;
1477}
1478
1479bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1480 MutableHandle<DebuggerEnvironment*> result) {
1481 MOZ_ASSERT(env)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(env)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(env))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("env", "./../../../../js/src/debugger/Debugger.cpp"
, 1481); AnnotateMozCrashReason("MOZ_ASSERT" "(" "env" ")"); do
{ MOZ_CrashSequence(__null, 1481); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1482
1483 // DebuggerEnv should only wrap a debug scope chain obtained (transitively)
1484 // from GetDebugEnvironmentFor(Frame|Function).
1485 MOZ_ASSERT(!IsSyntacticEnvironment(env))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsSyntacticEnvironment(env))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsSyntacticEnvironment(env)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!IsSyntacticEnvironment(env)", "./../../../../js/src/debugger/Debugger.cpp"
, 1485); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsSyntacticEnvironment(env)"
")"); do { MOZ_CrashSequence(__null, 1485); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1486
1487 DependentAddPtr<EnvironmentWeakMap> p(cx, environments, env);
1488 if (p) {
1489 result.set(&p->value()->as<DebuggerEnvironment>());
1490 } else {
1491 // Create a new Debugger.Environment for env.
1492 RootedObject proto(
1493 cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
1494 Rooted<NativeObject*> debugger(cx, object);
1495
1496 Rooted<DebuggerEnvironment*> envobj(
1497 cx, DebuggerEnvironment::create(cx, proto, env, debugger));
1498 if (!envobj) {
1499 return false;
1500 }
1501
1502 if (!p.add(cx, environments, env, envobj)) {
1503 // We need to destroy the edge to the referent, to avoid trying to trace
1504 // it during untimely collections.
1505 envobj->clearReferent();
1506 return false;
1507 }
1508
1509 result.set(envobj);
1510 }
1511
1512 return true;
1513}
1514
1515bool Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1516 cx->check(object.get());
1517
1518 if (vp.isObject()) {
1519 RootedObject obj(cx, &vp.toObject());
1520 Rooted<DebuggerObject*> dobj(cx);
1521
1522 if (!wrapDebuggeeObject(cx, obj, &dobj)) {
1523 return false;
1524 }
1525
1526 vp.setObject(*dobj);
1527 } else if (vp.isMagic()) {
1528 Rooted<PlainObject*> optObj(cx, NewPlainObject(cx));
1529 if (!optObj) {
1530 return false;
1531 }
1532
1533 // We handle three sentinel values: missing arguments
1534 // (JS_MISSING_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
1535 // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
1536 //
1537 // Other magic values should not have escaped.
1538 PropertyName* name;
1539 switch (vp.whyMagic()) {
1540 case JS_MISSING_ARGUMENTS:
1541 name = cx->names().missingArguments;
1542 break;
1543 case JS_OPTIMIZED_OUT:
1544 name = cx->names().optimizedOut;
1545 break;
1546 case JS_UNINITIALIZED_LEXICAL:
1547 name = cx->names().uninitialized;
1548 break;
1549 default:
1550 MOZ_CRASH("Unsupported magic value escaped to Debugger")do { do { } while (false); MOZ_ReportCrash("" "Unsupported magic value escaped to Debugger"
, "./../../../../js/src/debugger/Debugger.cpp", 1550); AnnotateMozCrashReason
("MOZ_CRASH(" "Unsupported magic value escaped to Debugger" ")"
); do { MOZ_CrashSequence(__null, 1550); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1551 }
1552
1553 RootedValue trueVal(cx, BooleanValue(true));
1554 if (!DefineDataProperty(cx, optObj, name, trueVal)) {
1555 return false;
1556 }
1557
1558 vp.setObject(*optObj);
1559 } else if (!cx->compartment()->wrap(cx, vp)) {
1560 vp.setUndefined();
1561 return false;
1562 }
1563
1564 return true;
1565}
1566
1567bool Debugger::wrapNullableDebuggeeObject(
1568 JSContext* cx, HandleObject obj, MutableHandle<DebuggerObject*> result) {
1569 if (!obj) {
1570 result.set(nullptr);
1571 return true;
1572 }
1573
1574 return wrapDebuggeeObject(cx, obj, result);
1575}
1576
1577bool Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1578 MutableHandle<DebuggerObject*> result) {
1579 MOZ_ASSERT(obj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(obj)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(obj))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("obj", "./../../../../js/src/debugger/Debugger.cpp"
, 1579); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj" ")"); do
{ MOZ_CrashSequence(__null, 1579); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1580
1581 DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
1582 if (p) {
1583 result.set(&p->value()->as<DebuggerObject>());
1584 } else {
1585 // Create a new Debugger.Object for obj.
1586 Rooted<NativeObject*> debugger(cx, object);
1587 RootedObject proto(
1588 cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
1589 Rooted<DebuggerObject*> dobj(
1590 cx, DebuggerObject::create(cx, proto, obj, debugger));
1591 if (!dobj) {
1592 return false;
1593 }
1594
1595 if (!p.add(cx, objects, obj, dobj)) {
1596 // We need to destroy the edge to the referent, to avoid trying to trace
1597 // it during untimely collections.
1598 dobj->clearReferent();
1599 return false;
1600 }
1601
1602 result.set(dobj);
1603 }
1604
1605 return true;
1606}
1607
1608static DebuggerObject* ToNativeDebuggerObject(JSContext* cx,
1609 MutableHandleObject obj) {
1610 if (!obj->is<DebuggerObject>()) {
1611 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1612 JSMSG_NOT_EXPECTED_TYPE, "Debugger",
1613 "Debugger.Object", obj->getClass()->name);
1614 return nullptr;
1615 }
1616
1617 return &obj->as<DebuggerObject>();
1618}
1619
1620bool Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj) {
1621 DebuggerObject* ndobj = ToNativeDebuggerObject(cx, obj);
1622 if (!ndobj) {
1623 return false;
1624 }
1625
1626 if (ndobj->owner() != Debugger::fromJSObject(object)) {
1627 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1628 JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
1629 return false;
1630 }
1631
1632 obj.set(ndobj->referent());
1633 return true;
1634}
1635
1636bool Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1637 cx->check(object.get(), vp);
1638 if (vp.isObject()) {
1639 RootedObject dobj(cx, &vp.toObject());
1640 if (!unwrapDebuggeeObject(cx, &dobj)) {
1641 return false;
1642 }
1643 vp.setObject(*dobj);
1644 }
1645 return true;
1646}
1647
1648static bool CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
1649 const char* methodname, const char* propname) {
1650 if (arg->compartment() != obj->compartment()) {
1651 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1652 JSMSG_DEBUG_COMPARTMENT_MISMATCH, methodname,
1653 propname);
1654 return false;
1655 }
1656 return true;
1657}
1658
1659static bool CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
1660 const char* methodname, const char* propname) {
1661 if (v.isObject()) {
1662 return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
1663 }
1664 return true;
1665}
1666
1667bool Debugger::unwrapPropertyDescriptor(
1668 JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc) {
1669 if (desc.hasValue()) {
1670 RootedValue value(cx, desc.value());
1671 if (!unwrapDebuggeeValue(cx, &value) ||
1672 !CheckArgCompartment(cx, obj, value, "defineProperty", "value")) {
1673 return false;
1674 }
1675 desc.setValue(value);
1676 }
1677
1678 if (desc.hasGetter()) {
1679 RootedObject get(cx, desc.getter());
1680 if (get) {
1681 if (!unwrapDebuggeeObject(cx, &get)) {
1682 return false;
1683 }
1684 if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get")) {
1685 return false;
1686 }
1687 }
1688 desc.setGetter(get);
1689 }
1690
1691 if (desc.hasSetter()) {
1692 RootedObject set(cx, desc.setter());
1693 if (set) {
1694 if (!unwrapDebuggeeObject(cx, &set)) {
1695 return false;
1696 }
1697 if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set")) {
1698 return false;
1699 }
1700 }
1701 desc.setSetter(set);
1702 }
1703
1704 return true;
1705}
1706
1707/*** Debuggee resumption values and debugger error handling *****************/
1708
1709static bool GetResumptionProperty(JSContext* cx, HandleObject obj,
1710 Handle<PropertyName*> name,
1711 ResumeMode namedMode, ResumeMode& resumeMode,
1712 MutableHandleValue vp, int* hits) {
1713 bool found;
1714 if (!HasProperty(cx, obj, name, &found)) {
1715 return false;
1716 }
1717 if (found) {
1718 ++*hits;
1719 resumeMode = namedMode;
1720 if (!GetProperty(cx, obj, obj, name, vp)) {
1721 return false;
1722 }
1723 }
1724 return true;
1725}
1726
1727bool js::ParseResumptionValue(JSContext* cx, HandleValue rval,
1728 ResumeMode& resumeMode, MutableHandleValue vp) {
1729 if (rval.isUndefined()) {
1730 resumeMode = ResumeMode::Continue;
1731 vp.setUndefined();
1732 return true;
1733 }
1734 if (rval.isNull()) {
1735 resumeMode = ResumeMode::Terminate;
1736 vp.setUndefined();
1737 return true;
1738 }
1739
1740 int hits = 0;
1741 if (rval.isObject()) {
1742 RootedObject obj(cx, &rval.toObject());
1743 if (!GetResumptionProperty(cx, obj, cx->names().return_, ResumeMode::Return,
1744 resumeMode, vp, &hits)) {
1745 return false;
1746 }
1747 if (!GetResumptionProperty(cx, obj, cx->names().throw_, ResumeMode::Throw,
1748 resumeMode, vp, &hits)) {
1749 return false;
1750 }
1751 }
1752
1753 if (hits != 1) {
1754 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1755 JSMSG_DEBUG_BAD_RESUMPTION);
1756 return false;
1757 }
1758 return true;
1759}
1760
1761static bool CheckResumptionValue(JSContext* cx, AbstractFramePtr frame,
1762 const jsbytecode* pc, ResumeMode resumeMode,
1763 MutableHandleValue vp) {
1764 // Only forced returns from a frame need to be validated because forced
1765 // throw values behave just like debuggee `throw` statements. Since
1766 // forced-return is all custom logic within SpiderMonkey itself, we need
1767 // our own custom validation for it to conform with what is expected.
1768 if (resumeMode != ResumeMode::Return || !frame) {
1769 return true;
1770 }
1771
1772 // This replicates the ECMA spec's behavior for [[Construct]] in derived
1773 // class constructors (section 9.2.2 of ECMA262-2020), where returning a
1774 // non-undefined primitive causes an exception tobe thrown.
1775 if (frame.debuggerNeedsCheckPrimitiveReturn() && vp.isPrimitive()) {
1776 if (!vp.isUndefined()) {
1777 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK0, vp,
1778 nullptr);
1779 return false;
1780 }
1781
1782 RootedValue thisv(cx);
1783 {
1784 AutoRealm ar(cx, frame.environmentChain());
1785 if (!GetThisValueForDebuggerFrameMaybeOptimizedOut(cx, frame, pc,
1786 &thisv)) {
1787 return false;
1788 }
1789 }
1790
1791 if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
1792 return ThrowUninitializedThis(cx);
1793 }
1794 MOZ_ASSERT(!thisv.isMagic())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!thisv.isMagic())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!thisv.isMagic()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!thisv.isMagic()"
, "./../../../../js/src/debugger/Debugger.cpp", 1794); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!thisv.isMagic()" ")"); do { MOZ_CrashSequence
(__null, 1794); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1795
1796 if (!cx->compartment()->wrap(cx, &thisv)) {
1797 return false;
1798 }
1799 vp.set(thisv);
1800 }
1801
1802 // Check for forcing return from a generator before the initial yield. This
1803 // is not supported because some engine-internal code assumes a call to a
1804 // generator will return a GeneratorObject; see bug 1477084.
1805 if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
1806 Rooted<AbstractGeneratorObject*> genObj(cx);
1807 {
1808 AutoRealm ar(cx, frame.callee());
1809 genObj = GetGeneratorObjectForFrame(cx, frame);
1810 }
1811
1812 if (!genObj || genObj->isBeforeInitialYield()) {
1813 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1814 JSMSG_DEBUG_FORCED_RETURN_DISALLOWED);
1815 return false;
1816 }
1817 }
1818
1819 return true;
1820}
1821
1822// Last-minute sanity adjustments to resumption.
1823//
1824// This is called last, as we leave the debugger. It must happen outside the
1825// control of the uncaughtExceptionHook, because this code assumes we won't
1826// change our minds and continue execution--we must not close the generator
1827// object unless we're really going to force-return.
1828[[nodiscard]] static bool AdjustGeneratorResumptionValue(
1829 JSContext* cx, AbstractFramePtr frame, ResumeMode& resumeMode,
1830 MutableHandleValue vp) {
1831 if (resumeMode != ResumeMode::Return && resumeMode != ResumeMode::Throw) {
1832 return true;
1833 }
1834
1835 if (!frame) {
1836 return true;
1837 }
1838 // Async modules need to be handled separately, as they do not have a callee.
1839 // frame.callee will throw if it is called on a moduleFrame.
1840 bool isAsyncModule = frame.isModuleFrame() && frame.script()->isAsync();
1841 if (!frame.isFunctionFrame() && !isAsyncModule) {
1842 return true;
1843 }
1844
1845 // Treat `{return: <value>}` like a `return` statement. Simulate what the
1846 // debuggee would do for an ordinary `return` statement, using a few bytecode
1847 // instructions. It's simpler to do the work manually than to count on that
1848 // bytecode sequence existing in the debuggee, somehow jump to it, and then
1849 // avoid re-entering the debugger from it.
1850 //
1851 // Similarly treat `{throw: <value>}` like a `throw` statement.
1852 //
1853 // Note: Async modules use the same handling as async functions.
1854 if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
1855 // Throw doesn't require any special processing for (async) generators.
1856 if (resumeMode == ResumeMode::Throw) {
1857 return true;
1858 }
1859
1860 // Forcing return from a (possibly async) generator.
1861 Rooted<AbstractGeneratorObject*> genObj(
1862 cx, GetGeneratorObjectForFrame(cx, frame));
1863
1864 // We already went through CheckResumptionValue, which would have replaced
1865 // this invalid resumption value with an error if we were trying to force
1866 // return before the initial yield.
1867 MOZ_RELEASE_ASSERT(genObj && !genObj->isBeforeInitialYield())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj && !genObj->isBeforeInitialYield()
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(genObj && !genObj->isBeforeInitialYield()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"genObj && !genObj->isBeforeInitialYield()", "./../../../../js/src/debugger/Debugger.cpp"
, 1867); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "genObj && !genObj->isBeforeInitialYield()"
")"); do { MOZ_CrashSequence(__null, 1867); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1868
1869 // 1. `return <value>` creates and returns a new object,
1870 // `{value: <value>, done: true}`.
1871 //
1872 // For non-async generators, the iterator result object is created in
1873 // bytecode, so we have to simulate that here. For async generators, our
1874 // C++ implementation of AsyncGeneratorResolve will do this. So don't do it
1875 // twice:
1876 if (!genObj->is<AsyncGeneratorObject>()) {
1877 PlainObject* pair = CreateIterResultObject(cx, vp, true);
1878 if (!pair) {
1879 return false;
1880 }
1881 vp.setObject(*pair);
1882 }
1883
1884 // 2. The generator must be closed.
1885 genObj->setClosed(cx);
1886
1887 // Async generators have additionally bookkeeping which must be adjusted
1888 // when switching over to the closed state.
1889 if (genObj->is<AsyncGeneratorObject>()) {
1890 genObj->as<AsyncGeneratorObject>().setCompleted();
1891 }
1892 } else if (isAsyncModule || frame.callee()->isAsync()) {
1893 if (AbstractGeneratorObject* genObj =
1894 GetGeneratorObjectForFrame(cx, frame)) {
1895 // Throw doesn't require any special processing for async functions when
1896 // the internal generator object is already present.
1897 if (resumeMode == ResumeMode::Throw) {
1898 return true;
1899 }
1900
1901 Rooted<AsyncFunctionGeneratorObject*> generator(
1902 cx, &genObj->as<AsyncFunctionGeneratorObject>());
1903
1904 // 1. `return <value>` fulfills and returns the async function's promise.
1905 Rooted<PromiseObject*> promise(cx, generator->promise());
1906 if (promise->state() == JS::PromiseState::Pending) {
1907 if (!AsyncFunctionResolve(cx, generator, vp)) {
1908 return false;
1909 }
1910 }
1911 vp.setObject(*promise);
1912
1913 // 2. The generator must be closed.
1914 generator->setClosed(cx);
1915 } else {
1916 // We're before entering the actual function code.
1917
1918 // 1. `throw <value>` creates a promise rejected with the value *vp.
1919 // 1. `return <value>` creates a promise resolved with the value *vp.
1920 JSObject* promise = resumeMode == ResumeMode::Throw
1921 ? PromiseObject::unforgeableReject(cx, vp)
1922 : PromiseObject::unforgeableResolve(cx, vp);
1923 if (!promise) {
1924 return false;
1925 }
1926 vp.setObject(*promise);
1927
1928 // 2. Return normally in both cases.
1929 resumeMode = ResumeMode::Return;
1930 }
1931 }
1932
1933 return true;
1934}
1935
1936bool Debugger::processParsedHandlerResult(JSContext* cx, AbstractFramePtr frame,
1937 const jsbytecode* pc, bool success,
1938 ResumeMode resumeMode,
1939 HandleValue value,
1940 ResumeMode& resultMode,
1941 MutableHandleValue vp) {
1942 RootedValue rootValue(cx, value);
1943 if (!success || !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
1944 RootedValue exceptionRv(cx);
1945 if (!callUncaughtExceptionHandler(cx, &exceptionRv) ||
1946 !ParseResumptionValue(cx, exceptionRv, resumeMode, &rootValue) ||
1947 !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
1948 return false;
1949 }
1950 }
1951
1952 // Since debugger hooks accumulate into the same final value handle, we
1953 // use that to throw if multiple hooks try to set a resumption value.
1954 if (resumeMode != ResumeMode::Continue) {
1955 if (resultMode != ResumeMode::Continue) {
1956 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1957 JSMSG_DEBUG_RESUMPTION_CONFLICT);
1958 return false;
1959 }
1960
1961 vp.set(rootValue);
1962 resultMode = resumeMode;
1963 }
1964
1965 return true;
1966}
1967
1968bool Debugger::processHandlerResult(JSContext* cx, bool success, HandleValue rv,
1969 AbstractFramePtr frame, jsbytecode* pc,
1970 ResumeMode& resultMode,
1971 MutableHandleValue vp) {
1972 ResumeMode resumeMode = ResumeMode::Continue;
1973 RootedValue value(cx);
1974 if (success) {
1975 success = ParseResumptionValue(cx, rv, resumeMode, &value);
1976 }
1977 return processParsedHandlerResult(cx, frame, pc, success, resumeMode, value,
1978 resultMode, vp);
1979}
1980
1981bool Debugger::prepareResumption(JSContext* cx, AbstractFramePtr frame,
1982 const jsbytecode* pc, ResumeMode& resumeMode,
1983 MutableHandleValue vp) {
1984 return unwrapDebuggeeValue(cx, vp) &&
1985 CheckResumptionValue(cx, frame, pc, resumeMode, vp);
1986}
1987
1988bool Debugger::callUncaughtExceptionHandler(JSContext* cx,
1989 MutableHandleValue vp) {
1990 // Uncaught exceptions arise from Debugger code, and so we must already be in
1991 // an NX section. This also establishes that we are already within the scope
1992 // of an AutoDebuggerJobQueueInterruption object.
1993 MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(EnterDebuggeeNoExecute::isLockedInStack(cx, *this))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(EnterDebuggeeNoExecute::isLockedInStack(cx, *this)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
, "./../../../../js/src/debugger/Debugger.cpp", 1993); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
")"); do { MOZ_CrashSequence(__null, 1993); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1994
1995 if (cx->isExceptionPending() && uncaughtExceptionHook) {
1996 RootedValue exc(cx);
1997 if (!cx->getPendingException(&exc)) {
1998 return false;
1999 }
2000 cx->clearPendingException();
2001
2002 RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
2003 if (js::Call(cx, fval, object, exc, vp)) {
2004 return true;
2005 }
2006 }
2007 return false;
2008}
2009
2010bool Debugger::handleUncaughtException(JSContext* cx) {
2011 RootedValue rv(cx);
2012
2013 return callUncaughtExceptionHandler(cx, &rv);
2014}
2015
2016void Debugger::reportUncaughtException(JSContext* cx) {
2017 // Uncaught exceptions arise from Debugger code, and so we must already be
2018 // in an NX section.
2019 MOZ_ASSERT(EnterDebuggeeNoExecute::isLockedInStack(cx, *this))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(EnterDebuggeeNoExecute::isLockedInStack(cx, *this))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(EnterDebuggeeNoExecute::isLockedInStack(cx, *this)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
, "./../../../../js/src/debugger/Debugger.cpp", 2019); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
")"); do { MOZ_CrashSequence(__null, 2019); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2020
2021 if (cx->isExceptionPending()) {
2022 // We want to report the pending exception, but we want to let the
2023 // embedding handle it however it wants to. So pretend like we're
2024 // starting a new script execution on our current compartment (which
2025 // is the debugger compartment, so reported errors won't get
2026 // reported to various onerror handlers in debuggees) and as part of
2027 // that "execution" simply throw our exception so the embedding can
2028 // deal.
2029 RootedValue exn(cx);
2030 if (cx->getPendingException(&exn)) {
2031 // Clear the exception, because ReportErrorToGlobal will assert that
2032 // we don't have one.
2033 cx->clearPendingException();
2034 ReportErrorToGlobal(cx, cx->global(), exn);
2035 }
2036
2037 // And if not, or if PrepareScriptEnvironmentAndInvoke somehow left an
2038 // exception on cx (which it totally shouldn't do), just give up.
2039 cx->clearPendingException();
2040 }
2041}
2042
2043/*** Debuggee completion values *********************************************/
2044
2045/* static */
2046Completion Completion::fromJSResult(JSContext* cx, bool ok, const Value& rv) {
2047 MOZ_ASSERT_IF(ok, !cx->isExceptionPending())do { if (ok) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(!cx->isExceptionPending())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cx->isExceptionPending()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!cx->isExceptionPending()", "./../../../../js/src/debugger/Debugger.cpp"
, 2047); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { MOZ_CrashSequence(__null, 2047); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
2048
2049 if (ok) {
2050 return Completion(Return(rv));
2051 }
2052
2053 if (!cx->isExceptionPending()) {
2054 return Completion(Terminate());
2055 }
2056
2057 RootedValue exception(cx);
2058 Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
2059 bool getSucceeded = cx->getPendingException(&exception);
2060 cx->clearPendingException();
2061 if (!getSucceeded) {
2062 return Completion(Terminate());
2063 }
2064
2065 return Completion(Throw(exception, stack));
2066}
2067
2068/* static */
2069Completion Completion::fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
2070 const jsbytecode* pc, bool ok) {
2071 // Only Wasm frames get a null pc.
2072 MOZ_ASSERT_IF(!frame.isWasmDebugFrame(), pc)do { if (!frame.isWasmDebugFrame()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(pc)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(pc))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("pc", "./../../../../js/src/debugger/Debugger.cpp", 2072); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "pc" ")"); do { MOZ_CrashSequence(__null, 2072
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false); } } while (false)
;
2073
2074 // If this isn't a generator suspension, then that's already handled above.
2075 if (!ok || !frame.isGeneratorFrame()) {
2076 return fromJSResult(cx, ok, frame.returnValue());
2077 }
2078
2079 // A generator is being suspended or returning.
2080
2081 // Since generators are never wasm, we can assume pc is not nullptr, and
2082 // that analyzing bytecode is meaningful.
2083 MOZ_ASSERT(!frame.isWasmDebugFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!frame.isWasmDebugFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!frame.isWasmDebugFrame())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!frame.isWasmDebugFrame()"
, "./../../../../js/src/debugger/Debugger.cpp", 2083); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!frame.isWasmDebugFrame()" ")"); do { MOZ_CrashSequence
(__null, 2083); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2084
2085 // If we're leaving successfully at a yield opcode, we're probably
2086 // suspending; the `isClosed()` check detects a debugger forced return from
2087 // an `onStep` handler, which looks almost the same.
2088 //
2089 // GetGeneratorObjectForFrame can return nullptr even when a generator
2090 // object does exist, if the frame is paused between the Generator and
2091 // SetAliasedVar opcodes.
2092 Rooted<AbstractGeneratorObject*> generatorObj(
2093 cx, GetGeneratorObjectForFrame(cx, frame));
2094
2095 if (generatorObj && !generatorObj->isClosed()) {
2096 switch (JSOp(*pc)) {
2097 case JSOp::InitialYield:
2098 return Completion(InitialYield(generatorObj));
2099
2100 case JSOp::Yield:
2101 return Completion(Yield(generatorObj, frame.returnValue()));
2102
2103 case JSOp::Await:
2104 return Completion(Await(generatorObj, frame.returnValue()));
2105
2106 default:
2107 break;
2108 }
2109 }
2110
2111 return Completion(Return(frame.returnValue()));
2112}
2113
2114void Completion::trace(JSTracer* trc) {
2115 variant.match([=](auto& var) { var.trace(trc); });
2116}
2117
2118struct MOZ_STACK_CLASS Completion::BuildValueMatcher {
2119 JSContext* cx;
2120 Debugger* dbg;
2121 MutableHandleValue result;
2122
2123 BuildValueMatcher(JSContext* cx, Debugger* dbg, MutableHandleValue result)
2124 : cx(cx), dbg(dbg), result(result) {
2125 cx->check(dbg->toJSObject());
2126 }
2127
2128 bool operator()(const Completion::Return& ret) {
2129 Rooted<NativeObject*> obj(cx, newObject());
2130 RootedValue retval(cx, ret.value);
2131 if (!obj || !wrap(&retval) || !add(obj, cx->names().return_, retval)) {
2132 return false;
2133 }
2134 result.setObject(*obj);
2135 return true;
2136 }
2137
2138 bool operator()(const Completion::Throw& thr) {
2139 Rooted<NativeObject*> obj(cx, newObject());
2140 RootedValue exc(cx, thr.exception);
2141 if (!obj || !wrap(&exc) || !add(obj, cx->names().throw_, exc)) {
2142 return false;
2143 }
2144 if (thr.stack) {
2145 RootedValue stack(cx, ObjectValue(*thr.stack));
2146 if (!wrapStack(&stack) || !add(obj, cx->names().stack, stack)) {
2147 return false;
2148 }
2149 }
2150 result.setObject(*obj);
2151 return true;
2152 }
2153
2154 bool operator()(const Completion::Terminate& term) {
2155 result.setNull();
2156 return true;
2157 }
2158
2159 bool operator()(const Completion::InitialYield& initialYield) {
2160 Rooted<NativeObject*> obj(cx, newObject());
2161 RootedValue gen(cx, ObjectValue(*initialYield.generatorObject));
2162 if (!obj || !wrap(&gen) || !add(obj, cx->names().return_, gen) ||
2163 !add(obj, cx->names().yield, TrueHandleValue) ||
2164 !add(obj, cx->names().initial, TrueHandleValue)) {
2165 return false;
2166 }
2167 result.setObject(*obj);
2168 return true;
2169 }
2170
2171 bool operator()(const Completion::Yield& yield) {
2172 Rooted<NativeObject*> obj(cx, newObject());
2173 RootedValue iteratorResult(cx, yield.iteratorResult);
2174 if (!obj || !wrap(&iteratorResult) ||
2175 !add(obj, cx->names().return_, iteratorResult) ||
2176 !add(obj, cx->names().yield, TrueHandleValue)) {
2177 return false;
2178 }
2179 result.setObject(*obj);
2180 return true;
2181 }
2182
2183 bool operator()(const Completion::Await& await) {
2184 Rooted<NativeObject*> obj(cx, newObject());
2185 RootedValue awaitee(cx, await.awaitee);
2186 if (!obj || !wrap(&awaitee) || !add(obj, cx->names().return_, awaitee) ||
2187 !add(obj, cx->names().await, TrueHandleValue)) {
2188 return false;
2189 }
2190 result.setObject(*obj);
2191 return true;
2192 }
2193
2194 private:
2195 NativeObject* newObject() const { return NewPlainObject(cx); }
2196
2197 bool add(Handle<NativeObject*> obj, PropertyName* name,
2198 HandleValue value) const {
2199 return NativeDefineDataProperty(cx, obj, name, value, JSPROP_ENUMERATE);
2200 }
2201
2202 bool wrap(MutableHandleValue v) const {
2203 return dbg->wrapDebuggeeValue(cx, v);
2204 }
2205
2206 // Saved stacks are wrapped for direct consumption by debugger code.
2207 bool wrapStack(MutableHandleValue stack) const {
2208 return cx->compartment()->wrap(cx, stack);
2209 }
2210};
2211
2212bool Completion::buildCompletionValue(JSContext* cx, Debugger* dbg,
2213 MutableHandleValue result) const {
2214 return variant.match(BuildValueMatcher(cx, dbg, result));
2215}
2216
2217void Completion::updateFromHookResult(ResumeMode resumeMode,
2218 HandleValue value) {
2219 switch (resumeMode) {
2220 case ResumeMode::Continue:
2221 // No change to how we'll resume.
2222 break;
2223
2224 case ResumeMode::Throw:
2225 // Since this is a new exception, the stack for the old one may not apply.
2226 // If we extend resumption values to specify stacks, we could revisit
2227 // this.
2228 variant = Variant(Throw(value, nullptr));
2229 break;
2230
2231 case ResumeMode::Terminate:
2232 variant = Variant(Terminate());
2233 break;
2234
2235 case ResumeMode::Return:
2236 variant = Variant(Return(value));
2237 break;
2238
2239 default:
2240 MOZ_CRASH("invalid resumeMode value")do { do { } while (false); MOZ_ReportCrash("" "invalid resumeMode value"
, "./../../../../js/src/debugger/Debugger.cpp", 2240); AnnotateMozCrashReason
("MOZ_CRASH(" "invalid resumeMode value" ")"); do { MOZ_CrashSequence
(__null, 2240); __attribute__((nomerge)) ::abort(); } while (
false); } while (false)
;
2241 }
2242}
2243
2244struct MOZ_STACK_CLASS Completion::ToResumeModeMatcher {
2245 MutableHandleValue value;
2246 MutableHandle<SavedFrame*> exnStack;
2247 ToResumeModeMatcher(MutableHandleValue value,
2248 MutableHandle<SavedFrame*> exnStack)
2249 : value(value), exnStack(exnStack) {}
2250
2251 ResumeMode operator()(const Return& ret) {
2252 value.set(ret.value);
2253 return ResumeMode::Return;
2254 }
2255
2256 ResumeMode operator()(const Throw& thr) {
2257 value.set(thr.exception);
2258 exnStack.set(thr.stack);
2259 return ResumeMode::Throw;
2260 }
2261
2262 ResumeMode operator()(const Terminate& term) {
2263 value.setUndefined();
2264 return ResumeMode::Terminate;
2265 }
2266
2267 ResumeMode operator()(const InitialYield& initialYield) {
2268 value.setObject(*initialYield.generatorObject);
2269 return ResumeMode::Return;
2270 }
2271
2272 ResumeMode operator()(const Yield& yield) {
2273 value.set(yield.iteratorResult);
2274 return ResumeMode::Return;
2275 }
2276
2277 ResumeMode operator()(const Await& await) {
2278 value.set(await.awaitee);
2279 return ResumeMode::Return;
2280 }
2281};
2282
2283void Completion::toResumeMode(ResumeMode& resumeMode, MutableHandleValue value,
2284 MutableHandle<SavedFrame*> exnStack) const {
2285 resumeMode = variant.match(ToResumeModeMatcher(value, exnStack));
2286}
2287
2288/*** Firing debugger hooks **************************************************/
2289
2290static bool CallMethodIfPresent(JSContext* cx, HandleObject obj,
2291 const char* name, size_t argc, Value* argv,
2292 MutableHandleValue rval) {
2293 rval.setUndefined();
2294 JSAtom* atom = Atomize(cx, name, strlen(name));
2295 if (!atom) {
2296 return false;
2297 }
2298
2299 RootedId id(cx, AtomToId(atom));
2300 RootedValue fval(cx);
2301 if (!GetProperty(cx, obj, obj, id, &fval)) {
2302 return false;
2303 }
2304
2305 if (!IsCallable(fval)) {
2306 return true;
2307 }
2308
2309 InvokeArgs args(cx);
2310 if (!args.init(cx, argc)) {
2311 return false;
2312 }
2313
2314 for (size_t i = 0; i < argc; i++) {
2315 args[i].set(argv[i]);
2316 }
2317
2318 rval.setObject(*obj); // overwritten by successful Call
2319 return js::Call(cx, fval, rval, args, rval);
2320}
2321
2322bool Debugger::fireDebuggerStatement(JSContext* cx, ResumeMode& resumeMode,
2323 MutableHandleValue vp) {
2324 RootedObject hook(cx, getHook(OnDebuggerStatement));
2325 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2325); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2325); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2326 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2326); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2326); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2327
2328 ScriptFrameIter iter(cx);
2329 RootedValue scriptFrame(cx);
2330 if (!getFrame(cx, iter, &scriptFrame)) {
2331 return false;
2332 }
2333
2334 RootedValue fval(cx, ObjectValue(*hook));
2335 RootedValue rv(cx);
2336 bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
2337 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2338 resumeMode, vp);
2339}
2340
2341bool Debugger::fireExceptionUnwind(JSContext* cx, HandleValue exc,
2342 ResumeMode& resumeMode,
2343 MutableHandleValue vp) {
2344 RootedObject hook(cx, getHook(OnExceptionUnwind));
2345 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2345); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2345); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2346 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2346); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2346); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2347
2348 RootedValue scriptFrame(cx);
2349 RootedValue wrappedExc(cx, exc);
2350
2351 FrameIter iter(cx);
2352 if (!getFrame(cx, iter, &scriptFrame) ||
2353 !wrapDebuggeeValue(cx, &wrappedExc)) {
2354 return false;
2355 }
2356
2357 RootedValue fval(cx, ObjectValue(*hook));
2358 RootedValue rv(cx);
2359 bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
2360 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2361 resumeMode, vp);
2362}
2363
2364bool Debugger::fireEnterFrame(JSContext* cx, ResumeMode& resumeMode,
2365 MutableHandleValue vp) {
2366 RootedObject hook(cx, getHook(OnEnterFrame));
2367 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2367); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2367); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2368 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2368); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2368); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2369
2370 RootedValue scriptFrame(cx);
2371
2372 FrameIter iter(cx);
2373
2374#if DEBUG1
2375 // Assert that the hook won't be able to re-enter the generator.
2376 if (iter.hasScript() && JSOp(*iter.pc()) == JSOp::AfterYield) {
2377 AutoRealm ar(cx, iter.script());
2378 auto* genObj = GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
2379 MOZ_ASSERT(genObj->isRunning())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj->isRunning())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(genObj->isRunning()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("genObj->isRunning()"
, "./../../../../js/src/debugger/Debugger.cpp", 2379); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "genObj->isRunning()" ")"); do { MOZ_CrashSequence
(__null, 2379); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2380 }
2381#endif
2382
2383 if (!getFrame(cx, iter, &scriptFrame)) {
2384 return false;
2385 }
2386
2387 RootedValue fval(cx, ObjectValue(*hook));
2388 RootedValue rv(cx);
2389 bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
2390
2391 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2392 resumeMode, vp);
2393}
2394
2395bool Debugger::fireNativeCall(JSContext* cx, const CallArgs& args,
2396 CallReason reason, ResumeMode& resumeMode,
2397 MutableHandleValue vp) {
2398 RootedObject hook(cx, getHook(OnNativeCall));
2399 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2399); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2400 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2400); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2400); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2401
2402 RootedValue fval(cx, ObjectValue(*hook));
2403 RootedValue calleeval(cx, args.calleev());
2404 if (!wrapDebuggeeValue(cx, &calleeval)) {
2405 return false;
2406 }
2407
2408 JSAtom* reasonAtom = nullptr;
2409 switch (reason) {
2410 case CallReason::Call:
2411 reasonAtom = cx->names().call;
2412 break;
2413 case CallReason::CallContent:
2414 reasonAtom = cx->names().call;
2415 break;
2416 case CallReason::FunCall:
2417 reasonAtom = cx->names().call;
2418 break;
2419 case CallReason::Getter:
2420 reasonAtom = cx->names().get;
2421 break;
2422 case CallReason::Setter:
2423 reasonAtom = cx->names().set;
2424 break;
2425 }
2426 MOZ_ASSERT(ZoneHasRef(cx->zone(), reasonAtom))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ZoneHasRef(cx->zone(), reasonAtom))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(ZoneHasRef(cx->zone(), reasonAtom)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("ZoneHasRef(cx->zone(), reasonAtom)"
, "./../../../../js/src/debugger/Debugger.cpp", 2426); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ZoneHasRef(cx->zone(), reasonAtom)" ")"
); do { MOZ_CrashSequence(__null, 2426); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2427
2428 RootedValue reasonval(cx, StringValue(reasonAtom));
2429
2430 bool ok = false;
2431 RootedValue rv(cx);
2432 if (inspectNativeCallArguments) {
2433 RootedValue thisVal(cx, args.thisv());
2434 // Ignore anything that may make wrapDebuggeeValue to throw
2435 if (thisVal.isMagic() && thisVal.whyMagic() != JS_MISSING_ARGUMENTS &&
2436 thisVal.whyMagic() != JS_UNINITIALIZED_LEXICAL) {
2437 thisVal.setMagic(JS_OPTIMIZED_OUT);
2438 }
2439 if (!wrapDebuggeeValue(cx, &thisVal)) {
2440 return false;
2441 }
2442
2443 unsigned arrsize = args.length();
2444 Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, arrsize));
2445 if (!arrobj) {
2446 return false;
2447 }
2448 arrobj->ensureDenseInitializedLength(0, arrsize);
2449 for (unsigned i = 0; i < arrsize; i++) {
2450 RootedValue v(cx, args.get(i));
2451 if (!wrapDebuggeeValue(cx, &v)) {
2452 return false;
2453 }
2454 arrobj->setDenseElement(i, v);
2455 }
2456 RootedValue arrayval(cx, ObjectValue(*arrobj));
2457 if (!wrapDebuggeeValue(cx, &arrayval)) {
2458 return false;
2459 }
2460
2461 FixedInvokeArgs<4> iargs(cx);
2462 iargs[0].set(calleeval);
2463 iargs[1].set(reasonval);
2464 iargs[2].set(thisVal);
2465 iargs[3].set(arrayval);
2466
2467 RootedValue thisv(cx, ObjectOrNullValue(object));
2468 ok = js::Call(cx, fval, thisv, iargs, &rv);
2469 } else {
2470 ok = js::Call(cx, fval, object, calleeval, reasonval, &rv);
2471 }
2472
2473 return processHandlerResult(cx, ok, rv, NullFramePtr(), nullptr, resumeMode,
2474 vp);
2475}
2476
2477bool Debugger::fireNewScript(JSContext* cx,
2478 Handle<DebuggerScriptReferent> scriptReferent) {
2479 RootedObject hook(cx, getHook(OnNewScript));
2480 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2480); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2480); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2481 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2481); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2481); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2482
2483 JSObject* dsobj = wrapVariantReferent(cx, scriptReferent);
2484 if (!dsobj) {
2485 return false;
2486 }
2487
2488 RootedValue fval(cx, ObjectValue(*hook));
2489 RootedValue dsval(cx, ObjectValue(*dsobj));
2490 RootedValue rv(cx);
2491 return js::Call(cx, fval, object, dsval, &rv) || handleUncaughtException(cx);
2492}
2493
2494bool Debugger::fireOnGarbageCollectionHook(
2495 JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData) {
2496 MOZ_ASSERT(observedGC(gcData->majorGCNumber()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(observedGC(gcData->majorGCNumber()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(observedGC(gcData->majorGCNumber())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("observedGC(gcData->majorGCNumber())"
, "./../../../../js/src/debugger/Debugger.cpp", 2496); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "observedGC(gcData->majorGCNumber())" ")"
); do { MOZ_CrashSequence(__null, 2496); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2497 observedGCs.remove(gcData->majorGCNumber());
2498
2499 RootedObject hook(cx, getHook(OnGarbageCollection));
2500 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2500); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2500); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2501 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2501); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2501); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2502
2503 JSObject* dataObj = gcData->toJSObject(cx);
2504 if (!dataObj) {
2505 return false;
2506 }
2507
2508 RootedValue fval(cx, ObjectValue(*hook));
2509 RootedValue dataVal(cx, ObjectValue(*dataObj));
2510 RootedValue rv(cx);
2511 return js::Call(cx, fval, object, dataVal, &rv) ||
2512 handleUncaughtException(cx);
2513}
2514
2515template <typename HookIsEnabledFun /* bool (Debugger*) */,
2516 typename FireHookFun /* bool (Debugger*) */>
2517/* static */
2518void Debugger::dispatchQuietHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
2519 FireHookFun fireHook) {
2520 DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
2521
2522 if (!debuggerList.init(cx)) {
2523 // init may fail due to OOM. This OOM is not handlable at the
2524 // callsites of dispatchQuietHook in the engine.
2525 cx->clearPendingException();
2526 return;
2527 }
2528
2529 debuggerList.dispatchQuietHook(cx, fireHook);
2530}
2531
2532template <typename HookIsEnabledFun /* bool (Debugger*) */, typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue vp) */>
2533/* static */
2534bool Debugger::dispatchResumptionHook(JSContext* cx, AbstractFramePtr frame,
2535 HookIsEnabledFun hookIsEnabled,
2536 FireHookFun fireHook) {
2537 DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
2538
2539 if (!debuggerList.init(cx)) {
2540 return false;
2541 }
2542
2543 return debuggerList.dispatchResumptionHook(cx, frame, fireHook);
2544}
2545
2546// Maximum length for source URLs that can be remembered.
2547static const size_t SourceURLMaxLength = 1024;
2548
2549// Maximum number of source URLs that can be remembered in a realm.
2550static const size_t SourceURLRealmLimit = 100;
2551
2552static bool RememberSourceURL(JSContext* cx, HandleScript script) {
2553 cx->check(script);
2554
2555 // Sources introduced dynamically are not remembered.
2556 if (script->sourceObject()->unwrappedIntroductionScript()) {
2557 return true;
2558 }
2559
2560 const char* filename = script->filename();
2561 if (!filename ||
2562 strnlen(filename, SourceURLMaxLength + 1) > SourceURLMaxLength) {
2563 return true;
2564 }
2565
2566 Rooted<ArrayObject*> holder(cx, script->global().getSourceURLsHolder());
2567 if (!holder) {
2568 holder = NewDenseEmptyArray(cx);
2569 if (!holder) {
2570 return false;
2571 }
2572 script->global().setSourceURLsHolder(holder);
2573 }
2574
2575 if (holder->length() >= SourceURLRealmLimit) {
2576 return true;
2577 }
2578
2579 RootedString filenameString(cx,
2580 AtomizeUTF8Chars(cx, filename, strlen(filename)));
2581 if (!filenameString) {
2582 return false;
2583 }
2584
2585 // The source URLs holder never escapes to script, so we can treat it as a
2586 // newborn array for the purpose of adding elements.
2587 return NewbornArrayPush(cx, holder, StringValue(filenameString));
2588}
2589
2590void DebugAPI::onNewScript(JSContext* cx, HandleScript script) {
2591 if (!script->realm()->isDebuggee()) {
2592 // Remember the URLs associated with scripts in non-system realms,
2593 // in case the debugger is attached later.
2594 if (!script->realm()->isSystem()) {
2595 if (!RememberSourceURL(cx, script)) {
2596 cx->clearPendingException();
2597 }
2598 }
2599 return;
2600 }
2601
2602 Debugger::dispatchQuietHook(
2603 cx,
2604 [script](Debugger* dbg) -> bool {
2605 return dbg->observesNewScript() && dbg->observesScript(script);
2606 },
2607 [&](Debugger* dbg) -> bool {
2608 BaseScript* base = script.get();
2609 Rooted<DebuggerScriptReferent> scriptReferent(cx, base);
2610 return dbg->fireNewScript(cx, scriptReferent);
2611 });
2612}
2613
2614#ifdef ENABLE_WASM_JSPI1
2615// Returns true if `addr` falls within `resumeBase` or any child stack in the
2616// chain that will be freed by ContStack::freeSuspended(resumeBase).
2617static bool ContStackChainHasAddress(wasm::ContStack* resumeBase,
2618 uintptr_t addr) {
2619 if (resumeBase->hasStackAddress(addr)) {
2620 return true;
2621 }
2622 // For nested continuations the intermediate and innermost stacks are still
2623 // active (handlers_ set). Starting from the resume target's handlers_ we
2624 // traverse the same chain that freeSuspended() walks.
2625 wasm::ContStack* resumeTargetStack = resumeBase->resumeTargetStack();
2626 if (resumeTargetStack) {
2627 for (wasm::Handlers* h = resumeTargetStack->handlers(); h;
2628 h = h->self->handlers()) {
2629 if (h->child && h->child->hasStackAddress(addr)) {
2630 return true;
2631 }
2632 }
2633 }
2634 return false;
2635}
2636
2637/* static */
2638void DebugAPI::onLeaveWasmCont(JSContext* cx, wasm::ContStack* resumeBase) {
2639 JS::GCContext* gcx = cx->gcContext();
2640 size_t terminatedFrames = 0;
2641 JSRuntime* rt = cx->runtime();
2642 for (Debugger* dbg = rt->debuggerList().getFirst(); dbg;
2643 dbg = dbg->getNext()) {
2644 for (auto iter = dbg->frames.modIter(); !iter.done(); iter.next()) {
2645 DebuggerFrame* frameObj = iter.get().value();
2646 JS::Value slot =
2647 frameObj->getReservedSlot(DebuggerFrame::WASM_CONT_FRAME_PTR_SLOT);
2648 if (slot.isUndefined()) {
2649 continue;
2650 }
2651 AbstractFramePtr fp = AbstractFramePtr::fromRaw(slot.toPrivate());
2652 if (!fp.isWasmDebugFrame()) {
2653 continue;
2654 }
2655 uintptr_t addr = reinterpret_cast<uintptr_t>(fp.asWasmDebugFrame());
2656 if (!ContStackChainHasAddress(resumeBase, addr)) {
2657 continue;
2658 }
2659 Debugger::terminateDebuggerFrame(gcx, dbg, frameObj, fp, &iter, nullptr);
2660 terminatedFrames++;
2661 }
2662 }
2663
2664 // Also purge the liveEnvs/missingEnvs entries holding frame pointers into
2665 // the stacks being freed: a discarded continuation never unwinds, so
2666 // DebugEnvironments::onPopWasm never runs for its DebugFrames.
2667 //
2668 // onDiscardWasmCont cannot reuse onPopWasm's lookup into missingEnvs:
2669 // we run from ContObject::finalize, and that lookup needs three barriered
2670 // reads of possibly-dying cells: Instance::object(), the
2671 // WeakHeapPtr<WasmFunctionScope*> in the instance's function scope map, and
2672 // the WeakHeapPtr<DebugEnvironmentProxy*> value of the entry. It scans both
2673 // maps by raw frame address instead.
2674 //
2675 // Such entries are only created through DebuggerFrame, so a frame with one
2676 // was in dbg->frames and got terminated above. Unless the dying-instance
2677 // pass in DebugAPI::sweepAll terminated it first, in which case traceWeak
2678 // dropped the entry too (an entry keeps its WasmInstanceObject alive through
2679 // WasmInstanceScope). Nothing terminated means nothing to purge.
2680 if (terminatedFrames > 0) {
2681 DebugEnvironments::onDiscardWasmCont(rt, [&](uintptr_t addr) {
2682 return ContStackChainHasAddress(resumeBase, addr);
2683 });
2684 }
2685}
2686#endif // ENABLE_WASM_JSPI
2687
2688void DebugAPI::slowPathOnNewWasmInstance(
2689 JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
2690 Debugger::dispatchQuietHook(
2691 cx,
2692 [wasmInstance](Debugger* dbg) -> bool {
2693 return dbg->observesNewScript() &&
2694 dbg->observesGlobal(&wasmInstance->global());
2695 },
2696 [&](Debugger* dbg) -> bool {
2697 Rooted<DebuggerScriptReferent> scriptReferent(cx, wasmInstance.get());
2698 return dbg->fireNewScript(cx, scriptReferent);
2699 });
2700}
2701
2702/* static */
2703bool DebugAPI::onTrap(JSContext* cx) {
2704 FrameIter iter(cx);
2705
2706 // Callers must suppress breakpoints while the frame is in the
2707 // generator-resume prologue.
2708 MOZ_ASSERT(!iter.isResumingGenerator())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.isResumingGenerator())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.isResumingGenerator())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!iter.isResumingGenerator()"
, "./../../../../js/src/debugger/Debugger.cpp", 2708); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!iter.isResumingGenerator()" ")"); do { MOZ_CrashSequence
(__null, 2708); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2709
2710 JS::AutoSaveExceptionState savedExc(cx);
2711 Rooted<GlobalObject*> global(cx);
2712 BreakpointSite* site;
2713 bool isJS; // true when iter.hasScript(), false when iter.isWasm()
2714 jsbytecode* pc; // valid when isJS == true
2715 uint32_t bytecodeOffset; // valid when isJS == false
2716 if (iter.hasScript()) {
2717 RootedScript script(cx, iter.script());
2718 MOZ_ASSERT(script->isDebuggee())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(script->isDebuggee()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("script->isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 2718); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->isDebuggee()" ")"); do { MOZ_CrashSequence
(__null, 2718); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2719 global.set(&script->global());
2720 isJS = true;
2721 pc = iter.pc();
2722 bytecodeOffset = 0;
2723 site = DebugScript::getBreakpointSite(script, pc);
2724 } else {
2725 MOZ_ASSERT(iter.isWasm())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.isWasm())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.isWasm()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("iter.isWasm()",
"./../../../../js/src/debugger/Debugger.cpp", 2725); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "iter.isWasm()" ")"); do { MOZ_CrashSequence
(__null, 2725); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2726 global.set(&iter.wasmInstance()->object()->global());
2727 isJS = false;
2728 pc = nullptr;
2729 bytecodeOffset = iter.wasmBytecodeOffset();
2730 site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
2731 }
2732
2733 // Build list of breakpoint handlers.
2734 //
2735 // This does not need to be rooted: since the JSScript/WasmInstance is on the
2736 // stack, the Breakpoints will not be GC'd. However, they may be deleted, and
2737 // we check for that case below.
2738 Vector<Breakpoint*> triggered(cx);
2739 for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
2740 if (!triggered.append(bp)) {
2741 return false;
2742 }
2743 }
2744
2745 ResumeMode resumeMode = ResumeMode::Continue;
2746 RootedValue rval(cx);
2747
2748 if (triggered.length() > 0) {
2749 // Preserve the debuggee's microtask event queue while we run the hooks, so
2750 // the debugger's microtask checkpoints don't run from the debuggee's
2751 // microtasks, and vice versa.
2752 JS::AutoDebuggerJobQueueInterruption adjqi;
2753 if (!adjqi.init(cx)) {
2754 return false;
2755 }
2756
2757 for (Breakpoint* bp : triggered) {
2758 // Handlers can clear breakpoints. Check that bp still exists.
2759 if (!site || !site->hasBreakpoint(bp)) {
2760 continue;
2761 }
2762
2763 // We have to check whether dbg is debugging this global here: a
2764 // breakpoint handler can disable other Debuggers or remove debuggees.
2765 Debugger* dbg = bp->debugger;
2766 if (dbg->debuggees.has(global)) {
2767 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
2768
2769 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
2770 RootedValue scriptFrame(cx);
2771 if (!dbg->getFrame(cx, iter, &scriptFrame)) {
2772 return false;
2773 }
2774
2775 // Re-wrap the breakpoint's handler for the Debugger's compartment.
2776 // When the handler and the Debugger are in the same compartment (the
2777 // usual case), this actually unwraps it, but there's no requirement
2778 // that they be in the same compartment, so we can't be sure.
2779 Rooted<JSObject*> handler(cx, bp->handler);
2780 if (!cx->compartment()->wrap(cx, &handler)) {
2781 return false;
2782 }
2783
2784 RootedValue rv(cx);
2785 bool ok = CallMethodIfPresent(cx, handler, "hit", 1,
2786 scriptFrame.address(), &rv);
2787
2788 return dbg->processHandlerResult(cx, ok, rv, iter.abstractFramePtr(),
2789 iter.pc(), resumeMode, &rval);
2790 });
2791 adjqi.runJobs();
2792
2793 if (!result) {
2794 return false;
2795 }
2796
2797 // Calling JS code invalidates site. Reload it.
2798 if (isJS) {
2799 site = DebugScript::getBreakpointSite(iter.script(), pc);
2800 } else {
2801 site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
2802 }
2803 }
2804 }
2805 }
2806
2807 if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
2808 savedExc.drop();
2809 return false;
2810 }
2811 return true;
2812}
2813
2814/* static */
2815bool DebugAPI::onSingleStep(JSContext* cx) {
2816 FrameIter iter(cx);
2817
2818 // Callers must suppress stepping while the frame is in the generator-resume
2819 // prologue.
2820 MOZ_ASSERT(!iter.isResumingGenerator())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!iter.isResumingGenerator())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!iter.isResumingGenerator())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!iter.isResumingGenerator()"
, "./../../../../js/src/debugger/Debugger.cpp", 2820); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!iter.isResumingGenerator()" ")"); do { MOZ_CrashSequence
(__null, 2820); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2821
2822 // We may be stepping over a JSOp::Exception, that pushes the context's
2823 // pending exception for a 'catch' clause to handle. Don't let the onStep
2824 // handlers mess with that (other than by returning a resumption value).
2825 JS::AutoSaveExceptionState savedExc(cx);
2826
2827 // Build list of Debugger.Frame instances referring to this frame with
2828 // onStep handlers.
2829 Rooted<Debugger::DebuggerFrameVector> frames(cx);
2830 if (!Debugger::getDebuggerFrames(iter.abstractFramePtr(), &frames)) {
2831 ReportOutOfMemory(cx);
2832 return false;
2833 }
2834
2835#ifdef DEBUG1
2836 // Validate the single-step count on this frame's script, to ensure that
2837 // we're not receiving traps we didn't ask for. Even when frames is
2838 // non-empty (and thus we know this trap was requested), do the check
2839 // anyway, to make sure the count has the correct non-zero value.
2840 //
2841 // The converse --- ensuring that we do receive traps when we should --- can
2842 // be done with unit tests.
2843 if (iter.hasScript()) {
2844 uint32_t liveStepperCount = 0;
2845 uint32_t suspendedStepperCount = 0;
2846 JSScript* trappingScript = iter.script();
2847 JS::AutoAssertNoGC nogc;
2848 for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
2849 Debugger* dbg = entry.dbg;
2850 for (auto iter = dbg->frames.iter(); !iter.done(); iter.next()) {
2851 AbstractFramePtr frame = iter.get().key();
2852 NativeObject* frameobj = iter.get().value();
2853 if (frame.isWasmDebugFrame()) {
2854 continue;
2855 }
2856 if (frame.script() == trappingScript &&
2857 !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
2858 .isUndefined()) {
2859 liveStepperCount++;
2860 }
2861 }
2862
2863 // Also count hooks set on suspended generator frames.
2864 for (auto iter = dbg->generatorFrames.iter(); !iter.done(); iter.next()) {
2865 AbstractGeneratorObject& genObj = *iter.get().key();
2866 DebuggerFrame& frameObj = *iter.get().value();
2867 MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(&frameObj.unwrappedGenerator() == &genObj)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(&frameObj.unwrappedGenerator() == &genObj)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("&frameObj.unwrappedGenerator() == &genObj"
, "./../../../../js/src/debugger/Debugger.cpp", 2867); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "&frameObj.unwrappedGenerator() == &genObj"
")"); do { MOZ_CrashSequence(__null, 2867); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2868
2869 // Live Debugger.Frames were already counted in dbg->frames loop.
2870 if (frameObj.isOnStack(cx)) {
2871 continue;
2872 }
2873
2874 // A closed generator no longer has a callee so it will not be able to
2875 // compare with the trappingScript.
2876 if (genObj.isClosed()) {
2877 continue;
2878 }
2879
2880 // If a frame isn't live, but it has an entry in generatorFrames,
2881 // it had better be suspended.
2882 MOZ_ASSERT(genObj.isSuspended())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(genObj.isSuspended())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(genObj.isSuspended()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("genObj.isSuspended()"
, "./../../../../js/src/debugger/Debugger.cpp", 2882); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "genObj.isSuspended()" ")"); do { MOZ_CrashSequence
(__null, 2882); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2883
2884 if (genObj.script() == trappingScript &&
2885 !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
2886 .isUndefined()) {
2887 suspendedStepperCount++;
2888 }
2889 }
2890 }
2891
2892 MOZ_ASSERT(liveStepperCount + suspendedStepperCount ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(liveStepperCount + suspendedStepperCount == DebugScript
::getStepperCount(trappingScript))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(liveStepperCount + suspendedStepperCount
== DebugScript::getStepperCount(trappingScript)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
, "./../../../../js/src/debugger/Debugger.cpp", 2893); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
")"); do { MOZ_CrashSequence(__null, 2893); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2893 DebugScript::getStepperCount(trappingScript))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(liveStepperCount + suspendedStepperCount == DebugScript
::getStepperCount(trappingScript))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(liveStepperCount + suspendedStepperCount
== DebugScript::getStepperCount(trappingScript)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
, "./../../../../js/src/debugger/Debugger.cpp", 2893); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
")"); do { MOZ_CrashSequence(__null, 2893); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2894 }
2895#endif
2896
2897 RootedValue rval(cx);
2898 ResumeMode resumeMode = ResumeMode::Continue;
2899
2900 if (frames.length() > 0) {
2901 // Preserve the debuggee's microtask event queue while we run the hooks, so
2902 // the debugger's microtask checkpoints don't run from the debuggee's
2903 // microtasks, and vice versa.
2904 JS::AutoDebuggerJobQueueInterruption adjqi;
2905 if (!adjqi.init(cx)) {
2906 return false;
2907 }
2908
2909 // Call onStep for frames that have the handler set.
2910 for (size_t i = 0; i < frames.length(); i++) {
2911 Handle<DebuggerFrame*> frame = frames[i];
2912 OnStepHandler* handler = frame->onStepHandler();
2913 if (!handler) {
2914 continue;
2915 }
2916
2917 Debugger* dbg = frame->owner();
2918 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
2919
2920 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
2921 ResumeMode nextResumeMode = ResumeMode::Continue;
2922 RootedValue nextValue(cx);
2923
2924 bool success = handler->onStep(cx, frame, nextResumeMode, &nextValue);
2925 return dbg->processParsedHandlerResult(
2926 cx, iter.abstractFramePtr(), iter.pc(), success, nextResumeMode,
2927 nextValue, resumeMode, &rval);
2928 });
2929 adjqi.runJobs();
2930
2931 if (!result) {
2932 return false;
2933 }
2934 }
2935 }
2936
2937 if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
2938 savedExc.drop();
2939 return false;
2940 }
2941 return true;
2942}
2943
2944bool Debugger::fireNewGlobalObject(JSContext* cx,
2945 Handle<GlobalObject*> global) {
2946 RootedObject hook(cx, getHook(OnNewGlobalObject));
2947 MOZ_ASSERT(hook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(hook))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("hook", "./../../../../js/src/debugger/Debugger.cpp"
, 2947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { MOZ_CrashSequence(__null, 2947); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2948 MOZ_ASSERT(hook->isCallable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hook->isCallable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hook->isCallable()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("hook->isCallable()"
, "./../../../../js/src/debugger/Debugger.cpp", 2948); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hook->isCallable()" ")"); do { MOZ_CrashSequence
(__null, 2948); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2949
2950 RootedValue wrappedGlobal(cx, ObjectValue(*global));
2951 if (!wrapDebuggeeValue(cx, &wrappedGlobal)) {
2952 return false;
2953 }
2954
2955 // onNewGlobalObject is infallible, and thus is only allowed to return
2956 // undefined as a resumption value. If it returns anything else, we throw.
2957 // And if that happens, or if the hook itself throws, we invoke the
2958 // uncaughtExceptionHook so that we never leave an exception pending on the
2959 // cx. This allows JS_NewGlobalObject to avoid handling failures from
2960 // debugger hooks.
2961 RootedValue rv(cx);
2962 RootedValue fval(cx, ObjectValue(*hook));
2963 bool ok = js::Call(cx, fval, object, wrappedGlobal, &rv);
2964 if (ok && !rv.isUndefined()) {
2965 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2966 JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2967 ok = false;
2968 }
2969
2970 return ok || handleUncaughtException(cx);
2971}
2972
2973void DebugAPI::slowPathOnNewGlobalObject(JSContext* cx,
2974 Handle<GlobalObject*> global) {
2975 MOZ_ASSERT(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!cx->runtime()->onNewGlobalObjectWatchers().isEmpty
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()"
, "./../../../../js/src/debugger/Debugger.cpp", 2975); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()"
")"); do { MOZ_CrashSequence(__null, 2975); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2976 if (global->realm()->creationOptions().invisibleToDebugger()) {
2977 return;
2978 }
2979
2980 // Make a copy of the runtime's onNewGlobalObjectWatchers before running the
2981 // handlers. Since one Debugger's handler can disable another's, the list
2982 // can be mutated while we're walking it.
2983 RootedObjectVector watchers(cx);
2984 for (auto& dbg : cx->runtime()->onNewGlobalObjectWatchers()) {
2985 MOZ_ASSERT(dbg.observesNewGlobalObject())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg.observesNewGlobalObject())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dbg.observesNewGlobalObject(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("dbg.observesNewGlobalObject()", "./../../../../js/src/debugger/Debugger.cpp"
, 2985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbg.observesNewGlobalObject()"
")"); do { MOZ_CrashSequence(__null, 2985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2986 JSObject* obj = dbg.object;
2987 JS::ExposeObjectToActiveJS(obj);
2988 if (!watchers.append(obj)) {
2989 if (cx->isExceptionPending()) {
2990 cx->clearPendingException();
2991 }
2992 return;
2993 }
2994 }
2995
2996 // Preserve the debuggee's microtask event queue while we run the hooks, so
2997 // the debugger's microtask checkpoints don't run from the debuggee's
2998 // microtasks, and vice versa.
2999 JS::AutoDebuggerJobQueueInterruption adjqi;
3000 if (!adjqi.init(cx)) {
3001 cx->clearPendingException();
3002 return;
3003 }
3004
3005 for (size_t i = 0; i < watchers.length(); i++) {
3006 Debugger* dbg = Debugger::fromJSObject(watchers[i]);
3007 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
3008
3009 if (dbg->observesNewGlobalObject()) {
3010 bool result = dbg->enterDebuggerHook(
3011 cx, [&]() -> bool { return dbg->fireNewGlobalObject(cx, global); });
3012 adjqi.runJobs();
3013
3014 if (!result) {
3015 // Like other quiet hooks using dispatchQuietHook, this hook
3016 // silently ignores all errors that propagate out of it and aren't
3017 // already handled by the hook error reporting.
3018 cx->clearPendingException();
3019 break;
3020 }
3021 }
3022 }
3023 MOZ_ASSERT(!cx->isExceptionPending())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->isExceptionPending())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cx->isExceptionPending()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!cx->isExceptionPending()", "./../../../../js/src/debugger/Debugger.cpp"
, 3023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { MOZ_CrashSequence(__null, 3023); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3024}
3025
3026/* static */
3027void DebugAPI::slowPathOnGeneratorClosed(JSContext* cx,
3028 AbstractGeneratorObject* genObj) {
3029 JS::AutoAssertNoGC nogc;
3030 for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
3031 Debugger* dbg = entry.dbg;
3032 if (Debugger::GeneratorWeakMap::Ptr frameEntry =
3033 dbg->generatorFrames.lookup(genObj)) {
3034 DebuggerFrame* frameObj = frameEntry->value();
3035 frameObj->onGeneratorClosed(cx->gcContext());
3036 }
3037 }
3038}
3039
3040/* static */
3041void DebugAPI::slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
3042 Realm::DebuggerVector& dbgs,
3043 const JS::AutoRequireNoGC& nogc) {
3044 for (Realm::DebuggerVector::Range r = dbgs.all(); !r.empty(); r.popFront()) {
3045 if (!r.front().dbg.unbarrieredGet()->debuggeeIsBeingCollected(
3046 majorGCNumber)) {
3047#ifdef DEBUG1
3048 fprintf(stderrstderr,
3049 "OOM while notifying observing Debuggers of a GC: The "
3050 "onGarbageCollection\n"
3051 "hook will not be fired for this GC for some Debuggers!\n");
3052#endif
3053 return;
3054 }
3055 }
3056}
3057
3058/* static */
3059Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
3060 JS::AutoAssertNoGC nogc;
3061 Realm::DebuggerVector& dbgs = global->getDebuggers(nogc);
3062 if (dbgs.empty()) {
3063 return Nothing();
3064 }
3065
3066 DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
3067
3068 double probability = 0;
3069 bool foundAnyDebuggers = false;
3070 for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
3071 // The set of debuggers had better not change while we're iterating,
3072 // such that the vector gets reallocated.
3073 MOZ_ASSERT(dbgs.begin() == begin)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbgs.begin() == begin)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dbgs.begin() == begin))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("dbgs.begin() == begin"
, "./../../../../js/src/debugger/Debugger.cpp", 3073); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbgs.begin() == begin" ")"); do { MOZ_CrashSequence
(__null, 3073); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3074 // Use unbarrieredGet() to prevent triggering read barrier while collecting,
3075 // this is safe as long as dbgp does not escape.
3076 Debugger* dbgp = p->dbg.unbarrieredGet();
3077
3078 if (dbgp->trackingAllocationSites) {
3079 foundAnyDebuggers = true;
3080 probability = std::max(dbgp->allocationSamplingProbability, probability);
3081 }
3082 }
3083
3084 return foundAnyDebuggers ? Some(probability) : Nothing();
3085}
3086
3087/* static */
3088bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
3089 Handle<SavedFrame*> frame,
3090 mozilla::TimeStamp when,
3091 Realm::DebuggerVector& dbgs,
3092 const gc::AutoSuppressGC& nogc) {
3093 MOZ_ASSERT(!dbgs.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!dbgs.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!dbgs.empty()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!dbgs.empty()",
"./../../../../js/src/debugger/Debugger.cpp", 3093); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!dbgs.empty()" ")"); do { MOZ_CrashSequence
(__null, 3093); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3094 mozilla::DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
3095
3096 // GC is suppressed so we can iterate over the debuggers; appendAllocationSite
3097 // calls Compartment::wrap, and thus could GC.
3098
3099 for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
3100 // The set of debuggers had better not change while we're iterating,
3101 // such that the vector gets reallocated.
3102 MOZ_ASSERT(dbgs.begin() == begin)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbgs.begin() == begin)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dbgs.begin() == begin))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("dbgs.begin() == begin"
, "./../../../../js/src/debugger/Debugger.cpp", 3102); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbgs.begin() == begin" ")"); do { MOZ_CrashSequence
(__null, 3102); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3103
3104 if (p->dbg->trackingAllocationSites &&
3105 !p->dbg->appendAllocationSite(cx, obj, frame, when)) {
3106 return false;
3107 }
3108 }
3109
3110 return true;
3111}
3112
3113bool Debugger::isDebuggeeUnbarriered(const Realm* realm) const {
3114 MOZ_ASSERT(realm)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(realm)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(realm))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("realm", "./../../../../js/src/debugger/Debugger.cpp"
, 3114); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realm" ")")
; do { MOZ_CrashSequence(__null, 3114); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3115 return realm->isDebuggee() &&
3116 debuggees.has(realm->unsafeUnbarrieredMaybeGlobal());
3117}
3118
3119bool Debugger::appendAllocationSite(JSContext* cx, HandleObject obj,
3120 Handle<SavedFrame*> frame,
3121 mozilla::TimeStamp when) {
3122 MOZ_ASSERT(trackingAllocationSites)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(trackingAllocationSites)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(trackingAllocationSites))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("trackingAllocationSites"
, "./../../../../js/src/debugger/Debugger.cpp", 3122); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "trackingAllocationSites" ")"); do { MOZ_CrashSequence
(__null, 3122); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3123
3124 AutoRealm ar(cx, object);
3125 RootedObject wrappedFrame(cx, frame);
3126 if (!cx->compartment()->wrap(cx, &wrappedFrame)) {
3127 return false;
3128 }
3129
3130 auto className = obj->getClass()->name;
3131 auto size =
3132 JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
3133 auto inNursery = gc::IsInsideNursery(obj);
3134
3135 if (!allocationsLog.emplaceBack(wrappedFrame, when, className, size,
3136 inNursery)) {
3137 ReportOutOfMemory(cx);
3138 return false;
3139 }
3140
3141 if (allocationsLog.length() > maxAllocationsLogLength) {
3142 allocationsLog.popFront();
3143 MOZ_ASSERT(allocationsLog.length() == maxAllocationsLogLength)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(allocationsLog.length() == maxAllocationsLogLength)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(allocationsLog.length() == maxAllocationsLogLength))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("allocationsLog.length() == maxAllocationsLogLength"
, "./../../../../js/src/debugger/Debugger.cpp", 3143); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "allocationsLog.length() == maxAllocationsLogLength"
")"); do { MOZ_CrashSequence(__null, 3143); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3144 allocationsLogOverflowed = true;
3145 }
3146
3147 return true;
3148}
3149
3150/*** Debugger code invalidation for observing execution *********************/
3151
3152class MOZ_RAII ExecutionObservableRealms
3153 : public DebugAPI::ExecutionObservableSet {
3154 HashSet<Realm*> realms_;
3155 HashSet<Zone*> zones_;
3156
3157 public:
3158 explicit ExecutionObservableRealms(JSContext* cx) : realms_(cx), zones_(cx) {}
3159
3160 bool add(Realm* realm) {
3161 return realms_.put(realm) && zones_.put(realm->zone());
3162 }
3163
3164 using RealmIterator = HashSet<Realm*>::Iterator;
3165 const HashSet<Realm*>* realms() const { return &realms_; }
3166
3167 const HashSet<Zone*>* zones() const override { return &zones_; }
3168 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3169 return script->hasBaselineScript() && realms_.has(script->realm());
3170 }
3171 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3172 // AbstractFramePtr can't refer to non-remateralized Ion frames or
3173 // non-debuggee wasm frames, so if iter refers to one such, we know we
3174 // don't match.
3175 return iter.hasUsableAbstractFramePtr() && realms_.has(iter.realm());
3176 }
3177};
3178
3179// Given a particular AbstractFramePtr F that has become observable, this
3180// represents the stack frames that need to be bailed out or marked as
3181// debuggees, and the scripts that need to be recompiled, taking inlining into
3182// account.
3183class MOZ_RAII ExecutionObservableFrame
3184 : public DebugAPI::ExecutionObservableSet {
3185 AbstractFramePtr frame_;
3186
3187 public:
3188 explicit ExecutionObservableFrame(AbstractFramePtr frame) : frame_(frame) {}
3189
3190 Zone* singleZone() const override {
3191 // We never inline across realms, let alone across zones, so
3192 // frames_'s script's zone is the only one of interest.
3193 return frame_.script()->zone();
3194 }
3195
3196 JSScript* singleScriptForZoneInvalidation() const override {
3197 MOZ_CRASH(do { do { } while (false); MOZ_ReportCrash("" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
, "./../../../../js/src/debugger/Debugger.cpp", 3198); AnnotateMozCrashReason
("MOZ_CRASH(" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
")"); do { MOZ_CrashSequence(__null, 3198); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
3198 "ExecutionObservableFrame shouldn't need zone-wide invalidation.")do { do { } while (false); MOZ_ReportCrash("" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
, "./../../../../js/src/debugger/Debugger.cpp", 3198); AnnotateMozCrashReason
("MOZ_CRASH(" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
")"); do { MOZ_CrashSequence(__null, 3198); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3199 return nullptr;
3200 }
3201
3202 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3203 // Normally, *this represents exactly one script: the one frame_ is
3204 // running.
3205 //
3206 // However, debug-mode OSR uses *this for both invalidating Ion frames,
3207 // and recompiling the Baseline scripts that those Ion frames will bail
3208 // out into. Suppose frame_ is an inline frame, executing a copy of its
3209 // JSScript, S_inner, that has been inlined into the IonScript of some
3210 // other JSScript, S_outer. We must match S_outer, to decide which Ion
3211 // frame to invalidate; and we must match S_inner, to decide which
3212 // Baseline script to recompile.
3213 //
3214 // Note that this does not, by design, invalidate *all* inliners of
3215 // frame_.script(), as only frame_ is made observable, not
3216 // frame_.script().
3217 if (!script->hasBaselineScript()) {
3218 return false;
3219 }
3220
3221 if (frame_.hasScript() && script == frame_.script()) {
3222 return true;
3223 }
3224
3225 return frame_.isRematerializedFrame() &&
3226 script == frame_.asRematerializedFrame()->outerScript();
3227 }
3228
3229 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3230 // AbstractFramePtr can't refer to non-remateralized Ion frames or
3231 // non-debuggee wasm frames, so if iter refers to one such, we know we
3232 // don't match.
3233 //
3234 // We never use this 'has' overload for frame invalidation, only for
3235 // frame debuggee marking; so this overload doesn't need a parallel to
3236 // the just-so inlining logic above.
3237 return iter.hasUsableAbstractFramePtr() &&
3238 iter.abstractFramePtr() == frame_;
3239 }
3240};
3241
3242class MOZ_RAII ExecutionObservableScript
3243 : public DebugAPI::ExecutionObservableSet {
3244 RootedScript script_;
3245
3246 public:
3247 ExecutionObservableScript(JSContext* cx, JSScript* script)
3248 : script_(cx, script) {}
3249
3250 Zone* singleZone() const override { return script_->zone(); }
3251 JSScript* singleScriptForZoneInvalidation() const override { return script_; }
3252 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3253 return script->hasBaselineScript() && script == script_;
3254 }
3255 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3256 // AbstractFramePtr can't refer to non-remateralized Ion frames, and
3257 // while a non-rematerialized Ion frame may indeed be running script_,
3258 // we cannot mark them as debuggees until they bail out.
3259 //
3260 // Upon bailing out, any newly constructed Baseline frames that came
3261 // from Ion frames with scripts that are isDebuggee() is marked as
3262 // debuggee. This is correct in that the only other way a frame may be
3263 // marked as debuggee is via Debugger.Frame reflection, which would
3264 // have rematerialized any Ion frames.
3265 //
3266 // Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
3267 // iter refers to one such, we know we don't match.
3268 return iter.hasUsableAbstractFramePtr() && !iter.isWasm() &&
3269 iter.abstractFramePtr().script() == script_;
3270 }
3271};
3272
3273/* static */
3274bool Debugger::updateExecutionObservabilityOfFrames(
3275 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
3276 IsObserving observing) {
3277 AutoSuppressProfilerSampling suppressProfilerSampling(cx);
3278
3279 if (!jit::RecompileOnStackBaselineScriptsForDebugMode(cx, obs, observing)) {
3280 return false;
3281 }
3282
3283 AbstractFramePtr oldestEnabledFrame;
3284 for (AllFramesIter iter(cx); !iter.done(); ++iter) {
3285 if (obs.shouldMarkAsDebuggee(iter)) {
3286 if (observing) {
3287 if (!iter.abstractFramePtr().isDebuggee()) {
3288 oldestEnabledFrame = iter.abstractFramePtr();
3289 oldestEnabledFrame.setIsDebuggee();
3290 }
3291 if (iter.abstractFramePtr().isWasmDebugFrame()) {
3292 iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
3293 }
3294 } else {
3295#ifdef DEBUG1
3296 // Debugger.Frame lifetimes are managed by the debug epilogue,
3297 // so in general it's unsafe to unmark a frame if it has a
3298 // Debugger.Frame associated with it.
3299 MOZ_ASSERT(!DebugAPI::inFrameMaps(iter.abstractFramePtr()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!DebugAPI::inFrameMaps(iter.abstractFramePtr()))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!DebugAPI::inFrameMaps(iter.abstractFramePtr())))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!DebugAPI::inFrameMaps(iter.abstractFramePtr())"
, "./../../../../js/src/debugger/Debugger.cpp", 3299); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!DebugAPI::inFrameMaps(iter.abstractFramePtr())"
")"); do { MOZ_CrashSequence(__null, 3299); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3300#endif
3301 iter.abstractFramePtr().unsetIsDebuggee();
3302 }
3303 }
3304 }
3305
3306 // See comment in unsetPrevUpToDateUntil.
3307 if (oldestEnabledFrame) {
3308 AutoRealm ar(cx, oldestEnabledFrame.environmentChain());
3309 DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
3310 }
3311
3312 return true;
3313}
3314
3315static inline void MarkJitScriptActiveIfObservable(
3316 JSScript* script, const DebugAPI::ExecutionObservableSet& obs) {
3317 if (obs.shouldRecompileOrInvalidate(script)) {
3318 script->jitScript()->icScript()->setActive();
3319 }
3320}
3321
3322static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
3323 JSScript* script,
3324 jit::IonScriptKeyVector& invalid,
3325 Vector<JSScript*>& scripts) {
3326 // Enter the script's realm as AddPendingInvalidation attempts to
3327 // cancel off-thread compilations, whose books are kept on the
3328 // script's realm.
3329 MOZ_ASSERT(script->zone() == zone)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->zone() == zone)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(script->zone() == zone)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("script->zone() == zone"
, "./../../../../js/src/debugger/Debugger.cpp", 3329); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->zone() == zone" ")"); do { MOZ_CrashSequence
(__null, 3329); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3330 AutoRealm ar(cx, script);
3331 AddPendingInvalidation(invalid, script);
3332 return scripts.append(script);
3333}
3334
3335static bool UpdateExecutionObservabilityOfScriptsInZone(
3336 JSContext* cx, Zone* zone, const DebugAPI::ExecutionObservableSet& obs,
3337 Debugger::IsObserving observing) {
3338 using namespace js::jit;
3339
3340 AutoSuppressProfilerSampling suppressProfilerSampling(cx);
3341
3342 CancelOffThreadBaselineCompile(zone);
3343
3344 JS::GCContext* gcx = cx->gcContext();
3345
3346 Vector<JSScript*> scripts(cx);
3347
3348 // Iterate through observable scripts, invalidating their Ion scripts and
3349 // appending them to a vector for discarding their baseline scripts later.
3350 {
3351 IonScriptKeyVector invalid;
3352 if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
3353 if (obs.shouldRecompileOrInvalidate(script)) {
3354 if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
3355 return false;
3356 }
3357 }
3358 } else {
3359 for (auto base = zone->cellIter<BaseScript>(); !base.done();
3360 base.next()) {
3361 if (!base->hasJitScript()) {
3362 continue;
3363 }
3364 JSScript* script = base->asJSScript();
3365 if (obs.shouldRecompileOrInvalidate(script)) {
3366 if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
3367 return false;
3368 }
3369 }
3370 }
3371 }
3372 Invalidate(cx, invalid);
3373 }
3374
3375 for (size_t i = 0; i < scripts.length(); i++) {
3376 MOZ_ASSERT(!scripts[i]->jitScript()->icScript()->active())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!scripts[i]->jitScript()->icScript()->active
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!scripts[i]->jitScript()->icScript()->active
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!scripts[i]->jitScript()->icScript()->active()", "./../../../../js/src/debugger/Debugger.cpp"
, 3376); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scripts[i]->jitScript()->icScript()->active()"
")"); do { MOZ_CrashSequence(__null, 3376); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3377 }
3378
3379 // Code below this point must be infallible to ensure the active bit of
3380 // BaselineScripts is in a consistent state.
3381 //
3382 // Mark active baseline scripts in the observable set so that they don't
3383 // get discarded. They will be recompiled.
3384 for (JitActivationIterator actIter(cx); !actIter.done(); ++actIter) {
3385 if (actIter->compartment()->zone() != zone) {
3386 continue;
3387 }
3388
3389 for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
3390 const JSJitFrameIter& frame = iter.frame();
3391 switch (frame.type()) {
3392 case FrameType::BaselineJS:
3393 MarkJitScriptActiveIfObservable(frame.script(), obs);
3394 break;
3395 case FrameType::IonJS:
3396 MarkJitScriptActiveIfObservable(frame.script(), obs);
3397 for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
3398 ++inlineIter) {
3399 MarkJitScriptActiveIfObservable(inlineIter.script(), obs);
3400 }
3401 break;
3402 default:;
3403 }
3404 }
3405 }
3406
3407 // Iterate through the scripts again and finish discarding
3408 // BaselineScripts. This must be done as a separate phase as we can only
3409 // discard the BaselineScript on scripts that have no IonScript.
3410 for (size_t i = 0; i < scripts.length(); i++) {
3411 if (!scripts[i]->jitScript()->icScript()->active()) {
3412 FinishDiscardBaselineScript(gcx, scripts[i]);
3413 }
3414 scripts[i]->jitScript()->icScript()->resetActive();
3415 }
3416
3417 // Iterate through all wasm instances to find ones that need to be updated.
3418 for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
3419 for (auto iter = r->wasm.instances().iter(); !iter.done(); iter.next()) {
3420 wasm::Instance* instance = iter.get();
3421 if (!instance->debugEnabled()) {
3422 continue;
3423 }
3424
3425 bool enableTrap = observing == Debugger::Observing;
3426 instance->debug().ensureEnterFrameTrapsState(cx, instance, enableTrap);
3427 }
3428 }
3429
3430 return true;
3431}
3432
3433/* static */
3434bool Debugger::updateExecutionObservabilityOfScripts(
3435 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
3436 IsObserving observing) {
3437 if (Zone* zone = obs.singleZone()) {
3438 return UpdateExecutionObservabilityOfScriptsInZone(cx, zone, obs,
3439 observing);
3440 }
3441
3442 for (auto iter = obs.zones()->iter(); !iter.done(); iter.next()) {
3443 if (!UpdateExecutionObservabilityOfScriptsInZone(cx, iter.get(), obs,
3444 observing)) {
3445 return false;
3446 }
3447 }
3448
3449 return true;
3450}
3451
3452template <typename FrameFn>
3453/* static */
3454void Debugger::forEachOnStackDebuggerFrame(AbstractFramePtr frame,
3455 const JS::AutoRequireNoGC& nogc,
3456 FrameFn fn) {
3457 for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
3458 Debugger* dbg = entry.dbg;
3459 if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
3460 fn(dbg, frameEntry->value());
3461 }
3462 }
3463}
3464
3465template <typename FrameFn>
3466/* static */
3467void Debugger::forEachOnStackOrSuspendedGeneratorDebuggerFrame(
3468 JSContext* cx, AbstractFramePtr frame, const JS::AutoRequireNoGC& nogc,
3469 FrameFn fn) {
3470 Rooted<AbstractGeneratorObject*> genObj(
3471 cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
3472 : nullptr);
3473
3474 for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
3475 Debugger* dbg = entry.dbg;
3476
3477 DebuggerFrame* frameObj = nullptr;
3478 if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
3479 frameObj = frameEntry->value();
3480 } else if (GeneratorWeakMap::Ptr frameEntry =
3481 dbg->generatorFrames.lookup(genObj)) {
3482 frameObj = frameEntry->value();
3483 }
3484
3485 if (frameObj) {
3486 fn(dbg, frameObj);
3487 }
3488 }
3489}
3490
3491/* static */
3492bool Debugger::getDebuggerFrames(AbstractFramePtr frame,
3493 MutableHandle<DebuggerFrameVector> frames) {
3494 bool hadOOM = false;
3495 JS::AutoAssertNoGC nogc;
3496 forEachOnStackDebuggerFrame(frame, nogc,
3497 [&](Debugger*, DebuggerFrame* frameobj) {
3498 if (!hadOOM && !frames.append(frameobj)) {
3499 hadOOM = true;
3500 }
3501 });
3502 return !hadOOM;
3503}
3504
3505/* static */
3506bool Debugger::updateExecutionObservability(
3507 JSContext* cx, DebugAPI::ExecutionObservableSet& obs,
3508 IsObserving observing) {
3509 if (!obs.singleZone() && obs.zones()->empty()) {
3510 return true;
3511 }
3512
3513 // Invalidate scripts first so we can set the needsArgsObj flag on scripts
3514 // before patching frames.
3515 return updateExecutionObservabilityOfScripts(cx, obs, observing) &&
3516 updateExecutionObservabilityOfFrames(cx, obs, observing);
3517}
3518
3519/* static */
3520bool Debugger::ensureExecutionObservabilityOfScript(JSContext* cx,
3521 JSScript* script) {
3522 if (script->isDebuggee()) {
3523 return true;
3524 }
3525 ExecutionObservableScript obs(cx, script);
3526 return updateExecutionObservability(cx, obs, Observing);
3527}
3528
3529/* static */
3530bool DebugAPI::ensureExecutionObservabilityOfOsrFrame(
3531 JSContext* cx, AbstractFramePtr osrSourceFrame) {
3532 MOZ_ASSERT(osrSourceFrame.isDebuggee())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(osrSourceFrame.isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(osrSourceFrame.isDebuggee())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("osrSourceFrame.isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 3532); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "osrSourceFrame.isDebuggee()" ")"); do { MOZ_CrashSequence
(__null, 3532); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3533 if (osrSourceFrame.script()->hasBaselineScript() &&
3534 osrSourceFrame.script()->baselineScript()->hasDebugInstrumentation()) {
3535 return true;
3536 }
3537 ExecutionObservableFrame obs(osrSourceFrame);
3538 return Debugger::updateExecutionObservabilityOfFrames(cx, obs, Observing);
3539}
3540
3541/* static */
3542bool Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx,
3543 AbstractFramePtr frame) {
3544 MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),do { if (frame.hasScript() && frame.script()->isDebuggee
()) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(frame.isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame.isDebuggee()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("frame.isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 3545); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame.isDebuggee()" ")"); do { MOZ_CrashSequence
(__null, 3545); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
3545 frame.isDebuggee())do { if (frame.hasScript() && frame.script()->isDebuggee
()) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(frame.isDebuggee())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame.isDebuggee()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("frame.isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 3545); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame.isDebuggee()" ")"); do { MOZ_CrashSequence
(__null, 3545); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
3546 MOZ_ASSERT_IF(frame.isWasmDebugFrame(), frame.wasmInstance()->debugEnabled())do { if (frame.isWasmDebugFrame()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(frame.wasmInstance
()->debugEnabled())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame.wasmInstance()->debugEnabled
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("frame.wasmInstance()->debugEnabled()", "./../../../../js/src/debugger/Debugger.cpp"
, 3546); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.wasmInstance()->debugEnabled()"
")"); do { MOZ_CrashSequence(__null, 3546); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3547 if (frame.isDebuggee()) {
3548 return true;
3549 }
3550 ExecutionObservableFrame obs(frame);
3551 return updateExecutionObservabilityOfFrames(cx, obs, Observing);
3552}
3553
3554/* static */
3555bool Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx,
3556 Realm* realm) {
3557 if (realm->debuggerObservesAllExecution()) {
3558 return true;
3559 }
3560 ExecutionObservableRealms obs(cx);
3561 if (!obs.add(realm)) {
3562 return false;
3563 }
3564 realm->updateDebuggerObservesAllExecution();
3565 return updateExecutionObservability(cx, obs, Observing);
3566}
3567
3568/* static */
3569bool Debugger::hookObservesAllExecution(Hook which) {
3570 return which == OnEnterFrame;
3571}
3572
3573Debugger::IsObserving Debugger::observesAllExecution() const {
3574 if (!!getHook(OnEnterFrame)) {
3575 return Observing;
3576 }
3577 return NotObserving;
3578}
3579
3580Debugger::IsObserving Debugger::observesWasm() const {
3581 if (!allowUnobservedWasm) {
3582 return Observing;
3583 }
3584 return NotObserving;
3585}
3586
3587Debugger::IsObserving Debugger::observesCoverage() const {
3588 if (collectCoverageInfo) {
3589 return Observing;
3590 }
3591 return NotObserving;
3592}
3593
3594Debugger::IsObserving Debugger::observesNativeCalls() const {
3595 if (getHook(Debugger::OnNativeCall)) {
3596 return Observing;
3597 }
3598 return NotObserving;
3599}
3600
3601bool Debugger::isExclusiveDebuggerOnEval() const {
3602 return exclusiveDebuggerOnEval;
3603}
3604
3605// Toggle whether this Debugger's debuggees observe all execution. This is
3606// called when a hook that observes all execution is set or unset. See
3607// hookObservesAllExecution.
3608bool Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx,
3609 IsObserving observing) {
3610 ExecutionObservableRealms obs(cx);
3611
3612 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3613 GlobalObject* global = iter.get();
3614 JS::Realm* realm = global->realm();
3615
3616 if (realm->debuggerObservesAllExecution() == observing) {
3617 continue;
3618 }
3619
3620 // It's expensive to eagerly invalidate and recompile a realm,
3621 // so add the realm to the set only if we are observing.
3622 if (observing && !obs.add(realm)) {
3623 return false;
3624 }
3625 }
3626
3627 if (!updateExecutionObservability(cx, obs, observing)) {
3628 return false;
3629 }
3630
3631 for (auto iter = obs.realms()->iter(); !iter.done(); iter.next()) {
3632 iter.get()->updateDebuggerObservesAllExecution();
3633 }
3634
3635 return true;
3636}
3637
3638bool Debugger::updateObservesCoverageOnDebuggees(JSContext* cx,
3639 IsObserving observing) {
3640 ExecutionObservableRealms obs(cx);
3641
3642 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3643 GlobalObject* global = iter.get();
3644 Realm* realm = global->realm();
3645
3646 if (realm->debuggerObservesCoverage() == observing) {
3647 continue;
3648 }
3649
3650 // Invalidate and recompile a realm to add or remove PCCounts
3651 // increments. We have to eagerly invalidate, as otherwise we might have
3652 // dangling pointers to freed PCCounts.
3653 if (!obs.add(realm)) {
3654 return false;
3655 }
3656 }
3657
3658 // If any frame on the stack belongs to the debuggee, then we cannot update
3659 // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
3660 // to recompile it with/without ScriptCount support.
3661 for (AllFramesIter iter(cx); !iter.done(); ++iter) {
3662 if (obs.shouldMarkAsDebuggee(iter)) {
3663 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3664 JSMSG_DEBUG_NOT_IDLE);
3665 return false;
3666 }
3667 }
3668
3669 if (!updateExecutionObservability(cx, obs, observing)) {
3670 return false;
3671 }
3672
3673 // All realms can safely be toggled, and all scripts will be recompiled.
3674 // Thus we can update each realm accordingly.
3675 for (auto iter = obs.realms()->iter(); !iter.done(); iter.next()) {
3676 iter.get()->updateDebuggerObservesCoverage();
3677 }
3678
3679 return true;
3680}
3681
3682void Debugger::updateObservesWasmOnDebuggees(IsObserving observing) {
3683 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3684 GlobalObject* global = iter.get();
3685 Realm* realm = global->realm();
3686
3687 if (realm->debuggerObservesWasm() == observing) {
3688 continue;
3689 }
3690
3691 realm->updateDebuggerObservesWasm();
3692 }
3693}
3694
3695void Debugger::updateObservesNativeCallOnDebuggees(IsObserving observing) {
3696 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3697 GlobalObject* global = iter.get();
3698 Realm* realm = global->realm();
3699
3700 if (realm->debuggerObservesNativeCall() == observing) {
3701 continue;
3702 }
3703
3704 realm->updateDebuggerObservesNativeCall();
3705 }
3706}
3707
3708/*** Allocations Tracking ***************************************************/
3709
3710/* static */
3711bool Debugger::cannotTrackAllocations(const GlobalObject& global) {
3712 auto existingCallback = global.realm()->getAllocationMetadataBuilder();
3713 return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
3714}
3715
3716/* static */
3717bool DebugAPI::isObservedByDebuggerTrackingAllocations(
3718 const GlobalObject& debuggee) {
3719 JS::AutoAssertNoGC nogc;
3720 for (Realm::DebuggerVectorEntry& entry : debuggee.getDebuggers(nogc)) {
3721 // Use unbarrieredGet() to prevent triggering read barrier while
3722 // collecting, this is safe as long as dbg does not escape.
3723 Debugger* dbg = entry.dbg.unbarrieredGet();
3724 if (dbg->trackingAllocationSites) {
3725 return true;
3726 }
3727 }
3728
3729 return false;
3730}
3731
3732/* static */
3733void Debugger::addAllocationsTracking(JSContext* cx,
3734 Handle<GlobalObject*> debuggee) {
3735 // Precondition: the given global object is being observed by at least one
3736 // Debugger that is tracking allocations.
3737 MOZ_ASSERT(DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee)"
, "./../../../../js/src/debugger/Debugger.cpp", 3737); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee)"
")"); do { MOZ_CrashSequence(__null, 3737); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3738
3739 // Precondition: There is no existing callback installed.
3740 MOZ_ASSERT(!cannotTrackAllocations(*debuggee))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cannotTrackAllocations(*debuggee))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cannotTrackAllocations(*debuggee
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!cannotTrackAllocations(*debuggee)", "./../../../../js/src/debugger/Debugger.cpp"
, 3740); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cannotTrackAllocations(*debuggee)"
")"); do { MOZ_CrashSequence(__null, 3740); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3741
3742 debuggee->realm()->setAllocationMetadataBuilder(
3743 &SavedStacks::metadataBuilder);
3744 debuggee->realm()->chooseAllocationSamplingProbability();
3745}
3746
3747/* static */
3748bool Debugger::checkCanAddAllocationsTracking(JSContext* cx,
3749 Handle<GlobalObject*> debuggee) {
3750 if (Debugger::cannotTrackAllocations(*debuggee)) {
3751 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3752 JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
3753 return false;
3754 }
3755
3756 return true;
3757}
3758
3759/* static */
3760void Debugger::removeAllocationsTracking(GlobalObject& global) {
3761 // If there are still Debuggers that are observing allocations, we cannot
3762 // remove the metadata callback yet. Recompute the sampling probability
3763 // based on the remaining debuggers' needs.
3764 if (DebugAPI::isObservedByDebuggerTrackingAllocations(global)) {
3765 global.realm()->chooseAllocationSamplingProbability();
3766 return;
3767 }
3768
3769 if (!global.realm()->runtimeFromMainThread()->recordAllocationCallback) {
3770 // Something like the Gecko Profiler could request from the the JS runtime
3771 // to record allocations. If it is recording allocations, then do not
3772 // destroy the allocation metadata builder at this time.
3773 global.realm()->forgetAllocationMetadataBuilder();
3774 }
3775}
3776
3777bool Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx) {
3778 MOZ_ASSERT(trackingAllocationSites)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(trackingAllocationSites)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(trackingAllocationSites))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("trackingAllocationSites"
, "./../../../../js/src/debugger/Debugger.cpp", 3778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "trackingAllocationSites" ")"); do { MOZ_CrashSequence
(__null, 3778); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3779
3780 // We don't want to end up in a state where we added allocations
3781 // tracking to some of our debuggees, but failed to do so for
3782 // others. Before attempting to start tracking allocations in *any* of
3783 // our debuggees, ensure that we will be able to track allocations for
3784 // *all* of our debuggees.
3785 Rooted<GlobalObject*> g(cx);
3786 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3787 g = iter.get().get();
3788 if (!checkCanAddAllocationsTracking(cx, g)) {
3789 return false;
3790 }
3791 }
3792
3793 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3794 // This should always succeed, since we already checked for the
3795 // error case above.
3796 g = iter.get().get();
3797 Debugger::addAllocationsTracking(cx, g);
3798 }
3799
3800 return true;
3801}
3802
3803void Debugger::removeAllocationsTrackingForAllDebuggees() {
3804 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
3805 Debugger::removeAllocationsTracking(*iter.get().get());
3806 }
3807
3808 allocationsLog.clear();
3809}
3810
3811/*** Debugger JSObjects *****************************************************/
3812
3813template <typename F>
3814inline void Debugger::forEachWeakMap(const F& f) {
3815 f(generatorFrames);
3816 f(objects);
3817 f(environments);
3818 f(scripts);
3819 f(sources);
3820 f(wasmInstanceScripts);
3821 f(wasmInstanceSources);
3822}
3823
3824void Debugger::traceCrossCompartmentEdges(JSTracer* trc) {
3825 forEachWeakMap(
3826 [trc](auto& weakMap) { weakMap.traceCrossCompartmentEdges(trc); });
3827}
3828
3829/*
3830 * Ordinarily, WeakMap keys and values are marked because at some point it was
3831 * discovered that the WeakMap was live; that is, some object containing the
3832 * WeakMap was marked during mark phase.
3833 *
3834 * However, during zone GC, we have to do something about cross-compartment
3835 * edges in non-GC'd compartments. Since the source may be live, we
3836 * conservatively assume it is and mark the edge.
3837 *
3838 * Each Debugger object keeps five cross-compartment WeakMaps: objects, scripts,
3839 * lazy scripts, script source objects, and environments. They have the property
3840 * that all their values are in the same compartment as the Debugger object,
3841 * but we have to mark the keys and the private pointer in the wrapper object.
3842 *
3843 * We must scan all Debugger objects regardless of whether they *currently* have
3844 * any debuggees in a compartment being GC'd, because the WeakMap entries
3845 * persist even when debuggees are removed.
3846 *
3847 * This happens during the initial mark phase, not iterative marking, because
3848 * all the edges being reported here are strong references.
3849 *
3850 * This method is also used during compacting GC to update cross compartment
3851 * pointers into zones that are being compacted.
3852 */
3853/* static */
3854void DebugAPI::traceCrossCompartmentEdges(JSTracer* trc) {
3855 MOZ_ASSERT(JS::RuntimeHeapIsMajorCollecting())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(JS::RuntimeHeapIsMajorCollecting())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(JS::RuntimeHeapIsMajorCollecting
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("JS::RuntimeHeapIsMajorCollecting()", "./../../../../js/src/debugger/Debugger.cpp"
, 3855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JS::RuntimeHeapIsMajorCollecting()"
")"); do { MOZ_CrashSequence(__null, 3855); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3856
3857 JSRuntime* rt = trc->runtime();
3858 gc::State state = rt->gc.state();
3859
3860 for (Debugger* dbg : rt->debuggerList()) {
3861 Zone* zone = MaybeForwarded(dbg->object.get())->zone();
3862 if (!zone->isCollecting() || state == gc::State::Compact) {
3863 dbg->traceCrossCompartmentEdges(trc);
3864 }
3865 }
3866}
3867
3868#ifdef DEBUG1
3869
3870static bool RuntimeHasDebugger(JSRuntime* rt, Debugger* dbg) {
3871 for (Debugger* d : rt->debuggerList()) {
3872 if (d == dbg) {
3873 return true;
3874 }
3875 }
3876 return false;
3877}
3878
3879/* static */
3880bool DebugAPI::edgeIsInDebuggerWeakmap(JSRuntime* rt, JSObject* src,
3881 JS::GCCellPtr dst) {
3882 if (!Debugger::isChildJSObject(src)) {
3883 return false;
3884 }
3885
3886 if (src->is<DebuggerFrame>()) {
3887 DebuggerFrame* frame = &src->as<DebuggerFrame>();
3888 Debugger* dbg = frame->owner();
3889 MOZ_ASSERT(RuntimeHasDebugger(rt, dbg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(RuntimeHasDebugger(rt, dbg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(RuntimeHasDebugger(rt, dbg))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("RuntimeHasDebugger(rt, dbg)"
, "./../../../../js/src/debugger/Debugger.cpp", 3889); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)" ")"); do { MOZ_CrashSequence
(__null, 3889); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3890
3891 if (dst.is<BaseScript>()) {
3892 // The generatorFrames map is not keyed on the associated JSScript. Get
3893 // the key from the source object and check everything matches.
3894 AbstractGeneratorObject* genObj = &frame->unwrappedGenerator();
3895 return frame->generatorScript() == &dst.as<BaseScript>() &&
3896 dbg->generatorFrames.hasEntry(genObj, frame);
3897 }
3898 return dst.is<JSObject>() &&
3899 dst.as<JSObject>().is<AbstractGeneratorObject>() &&
3900 dbg->generatorFrames.hasEntry(
3901 &dst.as<JSObject>().as<AbstractGeneratorObject>(), frame);
3902 }
3903 if (src->is<DebuggerObject>()) {
3904 DebuggerObject* dobj = &src->as<DebuggerObject>();
3905 Debugger* dbg = dobj->owner();
3906 MOZ_ASSERT(RuntimeHasDebugger(rt, dbg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(RuntimeHasDebugger(rt, dbg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(RuntimeHasDebugger(rt, dbg))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("RuntimeHasDebugger(rt, dbg)"
, "./../../../../js/src/debugger/Debugger.cpp", 3906); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)" ")"); do { MOZ_CrashSequence
(__null, 3906); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3907 return dst.is<JSObject>() &&
3908 dbg->objects.hasEntry(&dst.as<JSObject>(), dobj);
3909 }
3910 if (src->is<DebuggerEnvironment>()) {
3911 DebuggerEnvironment* denv = &src->as<DebuggerEnvironment>();
3912 Debugger* dbg = denv->owner();
3913 MOZ_ASSERT(RuntimeHasDebugger(rt, dbg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(RuntimeHasDebugger(rt, dbg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(RuntimeHasDebugger(rt, dbg))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("RuntimeHasDebugger(rt, dbg)"
, "./../../../../js/src/debugger/Debugger.cpp", 3913); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)" ")"); do { MOZ_CrashSequence
(__null, 3913); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3914 return dst.is<JSObject>() &&
3915 dbg->environments.hasEntry(&dst.as<JSObject>(), denv);
3916 }
3917 if (src->is<DebuggerScript>()) {
3918 DebuggerScript* dscript = &src->as<DebuggerScript>();
3919 Debugger* dbg = dscript->owner();
3920 MOZ_ASSERT(RuntimeHasDebugger(rt, dbg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(RuntimeHasDebugger(rt, dbg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(RuntimeHasDebugger(rt, dbg))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("RuntimeHasDebugger(rt, dbg)"
, "./../../../../js/src/debugger/Debugger.cpp", 3920); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)" ")"); do { MOZ_CrashSequence
(__null, 3920); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3921
3922 return src->as<DebuggerScript>().getReferent().match(
3923 [=](BaseScript* script) {
3924 return dst.is<BaseScript>() && script == &dst.as<BaseScript>() &&
3925 dbg->scripts.hasEntry(script, dscript);
3926 },
3927 [=](WasmInstanceObject* instance) {
3928 return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
3929 dbg->wasmInstanceScripts.hasEntry(instance, dscript);
3930 });
3931 }
3932 if (src->is<DebuggerSource>()) {
3933 DebuggerSource* dsource = &src->as<DebuggerSource>();
3934 Debugger* dbg = dsource->owner();
3935 MOZ_ASSERT(RuntimeHasDebugger(rt, dbg))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(RuntimeHasDebugger(rt, dbg))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(RuntimeHasDebugger(rt, dbg))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("RuntimeHasDebugger(rt, dbg)"
, "./../../../../js/src/debugger/Debugger.cpp", 3935); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)" ")"); do { MOZ_CrashSequence
(__null, 3935); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3936
3937 return src->as<DebuggerSource>().getReferent().match(
3938 [=](ScriptSourceObject* sso) {
3939 return dst.is<JSObject>() && sso == &dst.as<JSObject>() &&
3940 dbg->sources.hasEntry(sso, dsource);
3941 },
3942 [=](WasmInstanceObject* instance) {
3943 return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
3944 dbg->wasmInstanceSources.hasEntry(instance, dsource);
3945 });
3946 }
3947 MOZ_ASSERT_UNREACHABLE("Unhandled cross-compartment edge")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unhandled cross-compartment edge" ")", "./../../../../js/src/debugger/Debugger.cpp"
, 3947); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unhandled cross-compartment edge"
")"); do { MOZ_CrashSequence(__null, 3947); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3948}
3949
3950#endif
3951
3952/* See comments in DebugAPI.h. */
3953void DebugAPI::traceFramesWithLiveHooks(JSTracer* tracer) {
3954 JSRuntime* rt = tracer->runtime();
3955
3956 // Note that we must loop over all Debuggers here, not just those known to be
3957 // reachable from JavaScript. The existence of hooks set on a Debugger.Frame
3958 // for a live stack frame makes the Debuger.Frame (and hence its Debugger)
3959 // reachable.
3960 for (Debugger* dbg : rt->debuggerList()) {
3961 // Callback tracers set their own traversal boundaries, but otherwise we're
3962 // only interested in Debugger.Frames participating in the collection.
3963 if (!dbg->zone()->isGCMarking() && !tracer->isCallbackTracer()) {
3964 continue;
3965 }
3966
3967#ifdef ENABLE_WASM_JSPI1
3968 JSContext* cx = tracer->runtime()->mainContextFromOwnThread();
3969#endif
3970 for (auto iter = dbg->frames.iter(); !iter.done(); iter.next()) {
3971 HeapPtr<DebuggerFrame*>& frameobj = iter.get().value();
3972 MOZ_ASSERT(frameobj->isOnStackOrSuspendedWasmStack())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameobj->isOnStackOrSuspendedWasmStack())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(frameobj->isOnStackOrSuspendedWasmStack()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("frameobj->isOnStackOrSuspendedWasmStack()"
, "./../../../../js/src/debugger/Debugger.cpp", 3972); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameobj->isOnStackOrSuspendedWasmStack()"
")"); do { MOZ_CrashSequence(__null, 3972); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3973#ifdef ENABLE_WASM_JSPI1
3974 // Skip suspended wasm stack-switching frames: they are rooted from their
3975 // ContObject via traceWasmContFrame, so rooting them here too would trip
3976 // checkNoRuntimeRoots at shutdown. Active ones stay on a live stack and
3977 // are rooted here.
3978 if (!frameobj->isOnStack(cx)) {
3979 continue;
3980 }
3981#endif
3982 if (frameobj->hasAnyHooks()) {
3983 TraceEdge(tracer, &frameobj, "Debugger.Frame with live hooks");
3984 }
3985 }
3986 }
3987}
3988
3989#ifdef ENABLE_WASM_JSPI1
3990void DebugAPI::traceWasmContFrame(JSTracer* tracer, JSObject* src,
3991 wasm::DebugFrame* debugFrame,
3992 wasm::Instance* instance) {
3993 // Generic tracers (compacting, CompartmentCheck) must skip this inferred
3994 // cross-compartment edge, as in slowPathTraceGeneratorFrame; the caller only
3995 // invokes us while marking.
3996 MOZ_ASSERT(tracer->isMarkingTracer())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(tracer->isMarkingTracer())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(tracer->isMarkingTracer()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"tracer->isMarkingTracer()", "./../../../../js/src/debugger/Debugger.cpp"
, 3996); AnnotateMozCrashReason("MOZ_ASSERT" "(" "tracer->isMarkingTracer()"
")"); do { MOZ_CrashSequence(__null, 3996); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3997
3998 JS::AutoAssertNoGC nogc;
3999 AbstractFramePtr fp(debugFrame);
4000 // Resolve debuggers via the frame's realm, not rt->debuggerList(): frames on
4001 // one chain may span realms, and debuggerList is main-thread-only while this
4002 // can run on parallel marking threads.
4003 for (Realm::DebuggerVectorEntry& entry :
4004 instance->realm()->getDebuggers(nogc)) {
4005 Debugger* dbg = entry.dbg.unbarrieredGet();
4006 // readonlyThreadsafeLookup returns the same result as lookup(); it only
4007 // omits lookup()'s single-threaded ReentrancyGuard. We need that here
4008 // because parallel marking threads may run this concurrently, which is
4009 // safe: nothing mutates `frames` during marking. Every mutator runs on the
4010 // main thread, which is paused for parallel marking, and the sole GC-phase
4011 // mutator (DebugAPI::sweepAll) runs only after marking.
4012 auto p = dbg->frames.readonlyThreadsafeLookup(fp);
4013 if (!p) {
4014 continue;
4015 }
4016 HeapPtr<DebuggerFrame*>& frameobj = p->value();
4017 if (frameobj->hasAnyHooks()) {
4018 // Cross-compartment edge (ContObject in the debuggee, Debugger.Frame in
4019 // the debugger), so it can't use plain TraceEdge.
4020 TraceCrossCompartmentEdge(tracer, src, &frameobj,
4021 "wasm cont Debugger.Frame with live hooks");
4022 }
4023 }
4024}
4025#endif
4026
4027void DebugAPI::slowPathTraceGeneratorFrame(JSTracer* tracer,
4028 AbstractGeneratorObject* generator) {
4029 MOZ_ASSERT(generator->realm()->isDebuggee())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(generator->realm()->isDebuggee())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(generator->realm()->isDebuggee()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("generator->realm()->isDebuggee()"
, "./../../../../js/src/debugger/Debugger.cpp", 4029); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "generator->realm()->isDebuggee()" ")"
); do { MOZ_CrashSequence(__null, 4029); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4030
4031 // Ignore generic tracers.
4032 //
4033 // There are two kinds of generic tracers we need to bar: MovingTracers used
4034 // by compacting GC; and CompartmentCheckTracers.
4035 //
4036 // MovingTracers are used by the compacting GC to update pointers to objects
4037 // that have been moved: the MovingTracer checks each outgoing pointer to see
4038 // if it refers to a forwarding pointer, and if so, updates the pointer stored
4039 // in the object.
4040 //
4041 // Generator objects are background finalized, so the compacting GC assumes it
4042 // can update their pointers in the background as well. Since we treat
4043 // generator objects as having an owning edge to their Debugger.Frame objects,
4044 // a helper thread trying to update a generator object will end up calling
4045 // this function. However, it is verboten to do weak map lookups (e.g., in
4046 // Debugger::generatorFrames) off the main thread, since StableCellHasher
4047 // must consult the Zone to find the key's unique id.
4048 //
4049 // Fortunately, it's not necessary for compacting GC to worry about that edge
4050 // in the first place: the edge isn't a literal pointer stored on the
4051 // generator object, it's only inferred from the realm's debuggee status and
4052 // its Debuggers' generatorFrames weak maps. Those get relocated when the
4053 // Debugger itself is visited, so compacting GC can just ignore this edge.
4054 //
4055 // CompartmentCheckTracers walk the graph and verify that all
4056 // cross-compartment edges are recorded in the cross-compartment wrapper
4057 // tables. But edges between Debugger.Foo objects and their referents are not
4058 // in the CCW tables, so a CrossCompartmentCheckTracers also calls
4059 // DebugAPI::edgeIsInDebuggerWeakmap to see if a given cross-compartment edge
4060 // is accounted for there. However, edgeIsInDebuggerWeakmap only handles
4061 // debugger -> debuggee edges, so it won't recognize the edge we're
4062 // potentially traversing here, from a generator object to its Debugger.Frame.
4063 //
4064 // But since the purpose of this function is to retrieve such edges, if they
4065 // exist, from the very tables that edgeIsInDebuggerWeakmap would consult,
4066 // we're at no risk of reporting edges that they do not cover. So we can
4067 // safely hide the edges from CompartmentCheckTracers.
4068 //
4069 // We can't quite recognize MovingTracers and CompartmentCheckTracers
4070 // precisely, but they're both generic tracers, so we just show them all the
4071 // door. This means the generator -> Debugger.Frame edge is going to be
4072 // invisible to some traversals. We'll cope with that when it's a problem.
4073 if (!tracer->isMarkingTracer()) {
4074 return;
4075 }
4076
4077 mozilla::Maybe<AutoLockGC> lock;
4078 GCMarker* marker = GCMarker::fromTracer(tracer);
4079 if (marker->isParallelMarking()) {
4080 // Synchronise access to generatorFrames.
4081 lock.emplace(marker->runtime());
4082 }
4083
4084 JS::AutoAssertNoGC nogc;
4085 for (Realm::DebuggerVectorEntry& entry :
4086 generator->realm()->getDebuggers(nogc)) {
4087 Debugger* dbg = entry.dbg.unbarrieredGet();
4088
4089 if (Debugger::GeneratorWeakMap::Ptr entry =
4090 dbg->generatorFrames.lookupUnbarriered(generator)) {
4091 const PreBarriered<DebuggerFrame*>& frameObj = entry->value();
4092 if (frameObj->hasAnyHooks()) {
4093 // See comment above.
4094 TraceCrossCompartmentEdge(tracer, generator, &frameObj,
4095 "Debugger.Frame with hooks for generator");
4096 }
4097 }
4098 }
4099}
4100
4101/* static */
4102void DebugAPI::traceAllForMovingGC(JSTracer* trc) {
4103 JSRuntime* rt = trc->runtime();
4104 for (Debugger* dbg : rt->debuggerList()) {
4105 dbg->traceForMovingGC(trc);
4106 }
4107}
4108
4109/*
4110 * Trace all debugger-owned GC things unconditionally. This is used during
4111 * compacting GC and in minor GC: the minor GC cannot apply the weak constraints
4112 * of the full GC because it visits only part of the heap.
4113 */
4114void Debugger::traceForMovingGC(JSTracer* trc) {
4115 trace(trc);
4116
4117 for (auto iter = debuggees.modIter(); !iter.done(); iter.next()) {
4118 TraceEdge(trc, &iter.getMutable(), "Global Object");
4119 }
4120}
4121
4122/* static */
4123void Debugger::traceObject(JSTracer* trc, JSObject* obj) {
4124 if (Debugger* dbg = Debugger::fromJSObject(obj)) {
4125 dbg->trace(trc);
4126 }
4127}
4128
4129void Debugger::trace(JSTracer* trc) {
4130 TraceEdge(trc, &object, "Debugger Object");
4131
4132 TraceEdge(trc, &uncaughtExceptionHook, "hooks");
4133
4134 // Mark Debugger.Frame objects. Since the Debugger is reachable, JS could call
4135 // getNewestFrame and then walk the stack, so these are all reachable from JS.
4136 //
4137 // Note that if a Debugger.Frame has hooks set, it must be retained even if
4138 // its Debugger is unreachable, since JS could observe that its hooks did not
4139 // fire. That case is handled by DebugAPI::traceFrames.
4140 //
4141 // (We have weakly-referenced Debugger.Frame objects as well, for suspended
4142 // generator frames; these are traced via generatorFrames just below.)
4143 for (auto iter = frames.iter(); !iter.done(); iter.next()) {
4144 HeapPtr<DebuggerFrame*>& frameobj = iter.get().value();
4145 TraceEdge(trc, &frameobj, "live Debugger.Frame");
4146 MOZ_ASSERT(frameobj->isOnStackOrSuspendedWasmStack())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameobj->isOnStackOrSuspendedWasmStack())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(frameobj->isOnStackOrSuspendedWasmStack()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("frameobj->isOnStackOrSuspendedWasmStack()"
, "./../../../../js/src/debugger/Debugger.cpp", 4146); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameobj->isOnStackOrSuspendedWasmStack()"
")"); do { MOZ_CrashSequence(__null, 4146); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4147 }
4148
4149 allocationsLog.trace(trc);
4150
4151 forEachWeakMap([trc](auto& weakMap) { weakMap.trace(trc); });
4152}
4153
4154/* static */
4155void DebugAPI::traceFromRealm(JSTracer* trc, Realm* realm) {
4156 JS::AutoAssertNoGC nogc;
4157 for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
4158 TraceEdge(trc, &entry.debuggerLink, "realm debugger");
4159 }
4160}
4161
4162/* static */
4163void DebugAPI::sweepAll(JS::GCContext* gcx) {
4164 JSRuntime* rt = gcx->runtime();
4165
4166 Debugger* next;
4167 for (Debugger* dbg = rt->debuggerList().getFirst(); dbg; dbg = next) {
4168 next = dbg->getNext();
4169
4170 // Debugger.Frames for generator calls bump the JSScript's
4171 // generatorObserverCount, so the JIT will instrument the code to notify
4172 // Debugger when the generator is resumed. When a Debugger.Frame gets GC'd,
4173 // generatorObserverCount needs to be decremented. It's much easier to do
4174 // this when we know that all parties involved - the Debugger.Frame, the
4175 // generator object, and the JSScript - have not yet been finalized.
4176 //
4177 // Since DebugAPI::sweepAll is called after everything is marked, but before
4178 // anything has been finalized, this is the perfect place to drop the count.
4179 if (dbg->zone()->isGCSweeping()) {
4180 for (auto iter = dbg->generatorFrames.modIter(); !iter.done();
4181 iter.next()) {
4182 DebuggerFrame* frameObj = iter.get().value();
4183 if (IsAboutToBeFinalizedUnbarriered(frameObj)) {
4184 // If the DebuggerFrame is being finalized, that means either:
4185 // 1) It is not present in "frames".
4186 // 2) The Debugger itself is also being finalized.
4187 //
4188 // In the first case, passing the frame is not necessary because there
4189 // isn't a frame entry to clear, and in the second case,
4190 // removeDebuggeeGlobal below will iterate and remove the entries
4191 // anyway, so things will be cleaned up properly.
4192 Debugger::terminateDebuggerFrame(gcx, dbg, frameObj, NullFramePtr(),
4193 nullptr, &iter);
4194 }
4195 }
4196
4197#ifdef ENABLE_WASM_JSPI1
4198 // Wasm continuation frames whose wasm instance is dying must be
4199 // terminated here, before finalization. Without this,
4200 // ContObject::finalize would call onLeaveWasmCont ->
4201 // DebuggerFrame::terminate, which would call
4202 // IsAboutToBeFinalizedUnbarriered on the instance object - forbidden
4203 // during finalization. Terminating those frames now removes them from
4204 // dbg->frames so that onLeaveWasmCont will skip them.
4205 for (size_t i = 0; i < dbg->wasmContFrames.length();) {
4206 AbstractFramePtr fp = dbg->wasmContFrames[i];
4207 wasm::Instance* inst = fp.asWasmDebugFrame()->instance();
4208 if (!IsAboutToBeFinalizedUnbarriered(inst->objectUnbarriered())) {
4209 i++;
4210 continue;
4211 }
4212 auto p = dbg->frames.lookup(fp);
4213 MOZ_ASSERT(p)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(p)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(p))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("p", "./../../../../js/src/debugger/Debugger.cpp", 4213); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "p" ")"); do { MOZ_CrashSequence(__null, 4213
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
4214 // terminateDebuggerFrame erases fp from wasmContFrames, shifting any
4215 // later entries down into index i, so we don't advance i here. Assert
4216 // it removed exactly the entry at i so the loop makes progress and
4217 // cannot spin forever.
4218 mozilla::DebugOnly<size_t> lengthBefore = dbg->wasmContFrames.length();
4219 Debugger::terminateDebuggerFrame(gcx, dbg, p->value(), fp, nullptr,
4220 nullptr);
4221 MOZ_ASSERT(dbg->wasmContFrames.length() == lengthBefore - 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg->wasmContFrames.length() == lengthBefore - 1)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(dbg->wasmContFrames.length() == lengthBefore - 1)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("dbg->wasmContFrames.length() == lengthBefore - 1"
, "./../../../../js/src/debugger/Debugger.cpp", 4221); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbg->wasmContFrames.length() == lengthBefore - 1"
")"); do { MOZ_CrashSequence(__null, 4221); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4222 }
4223#endif
4224 }
4225
4226 // Detach dying debuggers and debuggees from each other. Since this
4227 // requires access to both objects it must be done before either
4228 // object is finalized.
4229 bool debuggerDying = IsAboutToBeFinalized(dbg->object);
4230 for (auto iter = dbg->debuggees.modIter(); !iter.done(); iter.next()) {
4231 GlobalObject* global = iter.get().unbarrieredGet();
4232 if (debuggerDying || IsAboutToBeFinalizedUnbarriered(global)) {
4233 dbg->removeDebuggeeGlobal(gcx, global, &iter, Debugger::FromSweep::Yes);
4234 }
4235 }
4236
4237 if (debuggerDying) {
4238 gcx->delete_(dbg->object, dbg, MemoryUse::Debugger);
4239 }
4240
4241 dbg = next;
Value stored to 'dbg' is never read
4242 }
4243}
4244
4245static inline bool SweepZonesInSameGroup(Zone* a, Zone* b) {
4246 // Ensure two zones are swept in the same sweep group by adding an edge
4247 // between them in each direction.
4248 return a->addSweepGroupEdgeTo(b) && b->addSweepGroupEdgeTo(a);
4249}
4250
4251/* static */
4252bool DebugAPI::findSweepGroupEdges(JSRuntime* rt) {
4253 // Ensure that debuggers and their debuggees are finalized in the same group
4254 // by adding edges in both directions for debuggee zones. These are weak
4255 // references that are not in the cross compartment wrapper map.
4256
4257 for (Debugger* dbg : rt->debuggerList()) {
4258 Zone* debuggerZone = dbg->object->zone();
4259 if (!debuggerZone->isGCMarking()) {
4260 continue;
4261 }
4262
4263 for (auto iter = dbg->debuggeeZones.iter(); !iter.done(); iter.next()) {
4264 Zone* debuggeeZone = iter.get();
4265 if (!debuggeeZone->isGCMarking()) {
4266 continue;
4267 }
4268
4269 if (!SweepZonesInSameGroup(debuggerZone, debuggeeZone)) {
4270 return false;
4271 }
4272 }
4273 }
4274
4275 return true;
4276}
4277
4278template <class UnbarrieredKey, class Wrapper, bool InvisibleKeysOk>
4279bool DebuggerWeakMap<UnbarrieredKey, Wrapper,
4280 InvisibleKeysOk>::findSweepGroupEdges(Zone* atomsZone) {
4281 Zone* debuggerZone = zone();
4282 MOZ_ASSERT(debuggerZone->isGCMarking())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(debuggerZone->isGCMarking())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(debuggerZone->isGCMarking
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("debuggerZone->isGCMarking()", "./../../../../js/src/debugger/Debugger.cpp"
, 4282); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debuggerZone->isGCMarking()"
")"); do { MOZ_CrashSequence(__null, 4282); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4283 for (auto iter = this->iter(); !iter.done(); iter.next()) {
4284 MOZ_ASSERT(iter.get().value()->zone() == debuggerZone)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.get().value()->zone() == debuggerZone)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(iter.get().value()->zone() == debuggerZone))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("iter.get().value()->zone() == debuggerZone"
, "./../../../../js/src/debugger/Debugger.cpp", 4284); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "iter.get().value()->zone() == debuggerZone"
")"); do { MOZ_CrashSequence(__null, 4284); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4285
4286 Zone* keyZone = iter.get().key()->zone();
4287 if (keyZone->isGCMarking() &&
4288 !SweepZonesInSameGroup(debuggerZone, keyZone)) {
4289 return false;
4290 }
4291 }
4292
4293 // Add in edges for delegates, if relevant for the key type.
4294 return Base::findSweepGroupEdges(atomsZone);
4295}
4296
4297const JSClassOps DebuggerInstanceObject::classOps_ = {
4298 .trace = Debugger::traceObject,
4299};
4300
4301const JSClass DebuggerInstanceObject::class_ = {
4302 "Debugger",
4303 JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_COUNT),
4304 &classOps_,
4305};
4306
4307static_assert(Debugger::JSSLOT_DEBUG_PROTO_START == 0,
4308 "DebuggerPrototypeObject only needs slots for the proto objects");
4309
4310const JSClass DebuggerPrototypeObject::class_ = {
4311 "DebuggerPrototype",
4312 JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_PROTO_STOP),
4313};
4314
4315static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args,
4316 const char* fnname) {
4317 JSObject* thisobj = RequireObject(cx, args.thisv());
4318 if (!thisobj) {
4319 return nullptr;
4320 }
4321 if (!thisobj->is<DebuggerInstanceObject>()) {
4322 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4323 JSMSG_INCOMPATIBLE_PROTO, "Debugger", fnname,
4324 thisobj->getClass()->name);
4325 return nullptr;
4326 }
4327
4328 Debugger* dbg = Debugger::fromJSObject(thisobj);
4329 MOZ_ASSERT(dbg)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(dbg))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("dbg", "./../../../../js/src/debugger/Debugger.cpp"
, 4329); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbg" ")"); do
{ MOZ_CrashSequence(__null, 4329); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4330 return dbg;
4331}
4332
4333struct MOZ_STACK_CLASS Debugger::CallData {
4334 JSContext* cx;
4335 const CallArgs& args;
4336
4337 Debugger* dbg;
4338
4339 CallData(JSContext* cx, const CallArgs& args, Debugger* dbg)
4340 : cx(cx), args(args), dbg(dbg) {}
4341
4342 bool getOnDebuggerStatement();
4343 bool setOnDebuggerStatement();
4344 bool getOnExceptionUnwind();
4345 bool setOnExceptionUnwind();
4346 bool getOnNewScript();
4347 bool setOnNewScript();
4348 bool getOnEnterFrame();
4349 bool setOnEnterFrame();
4350 bool getOnNativeCall();
4351 bool setOnNativeCall();
4352 bool getShouldAvoidSideEffects();
4353 bool setShouldAvoidSideEffects();
4354 bool getOnNewGlobalObject();
4355 bool setOnNewGlobalObject();
4356 bool getUncaughtExceptionHook();
4357 bool setUncaughtExceptionHook();
4358 bool getAllowUnobservedWasm();
4359 bool setAllowUnobservedWasm();
4360 bool getExclusiveDebuggerOnEval();
4361 bool setExclusiveDebuggerOnEval();
4362 bool getInspectNativeCallArguments();
4363 bool setInspectNativeCallArguments();
4364 bool getCollectCoverageInfo();
4365 bool setCollectCoverageInfo();
4366 bool getMemory();
4367 bool addDebuggee();
4368 bool addAllGlobalsAsDebuggees();
4369 bool removeDebuggee();
4370 bool removeAllDebuggees();
4371 bool hasDebuggee();
4372 bool getDebuggees();
4373 bool getNewestFrame();
4374 bool clearAllBreakpoints();
4375 bool findScripts();
4376 bool findSources();
4377 bool findAllGlobals();
4378 bool findSourceURLs();
4379 bool makeGlobalObjectReference();
4380 bool adoptDebuggeeValue();
4381 bool adoptFrame();
4382 bool adoptSource();
4383 bool enableAsyncStack();
4384 bool disableAsyncStack();
4385 bool enableUnlimitedStacksCapturing();
4386 bool disableUnlimitedStacksCapturing();
4387
4388 using Method = bool (CallData::*)();
4389
4390 template <Method MyMethod>
4391 static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
4392};
4393
4394template <Debugger::CallData::Method MyMethod>
4395/* static */
4396bool Debugger::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
4397 CallArgs args = CallArgsFromVp(argc, vp);
4398
4399 Debugger* dbg = Debugger_fromThisValue(cx, args, "method");
4400 if (!dbg) {
4401 return false;
4402 }
4403
4404 CallData data(cx, args, dbg);
4405 return (data.*MyMethod)();
4406}
4407
4408/* static */
4409bool Debugger::getHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
4410 Hook which) {
4411 MOZ_ASSERT(which >= 0 && which < HookCount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(which >= 0 && which < HookCount)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(which >= 0 && which < HookCount))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("which >= 0 && which < HookCount"
, "./../../../../js/src/debugger/Debugger.cpp", 4411); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "which >= 0 && which < HookCount"
")"); do { MOZ_CrashSequence(__null, 4411); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4412 args.rval().set(dbg.object->getReservedSlot(
4413 JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which)));
4414 return true;
4415}
4416
4417/* static */
4418bool Debugger::setHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
4419 Hook which) {
4420 MOZ_ASSERT(which >= 0 && which < HookCount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(which >= 0 && which < HookCount)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(which >= 0 && which < HookCount))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("which >= 0 && which < HookCount"
, "./../../../../js/src/debugger/Debugger.cpp", 4420); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "which >= 0 && which < HookCount"
")"); do { MOZ_CrashSequence(__null, 4420); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4421 if (!args.requireAtLeast(cx, "Debugger.setHook", 1)) {
4422 return false;
4423 }
4424 if (args[0].isObject()) {
4425 if (!args[0].toObject().isCallable()) {
4426 return ReportIsNotFunction(cx, args[0], args.length() - 1);
4427 }
4428 } else if (!args[0].isUndefined()) {
4429 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4430 JSMSG_NOT_CALLABLE_OR_UNDEFINED);
4431 return false;
4432 }
4433
4434 // Disallow simultaneous activation of OnEnterFrame and code coverage support;
4435 // as they both use the execution observer flag. See Bug 1608891.
4436 if (dbg.collectCoverageInfo && which == Hook::OnEnterFrame) {
4437 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4438 JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
4439 return false;
4440 }
4441
4442 uint32_t slot = JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which);
4443 RootedValue oldHook(cx, dbg.object->getReservedSlot(slot));
4444 dbg.object->setReservedSlot(slot, args[0]);
4445 if (hookObservesAllExecution(which)) {
4446 if (!dbg.updateObservesAllExecutionOnDebuggees(
4447 cx, dbg.observesAllExecution())) {
4448 dbg.object->setReservedSlot(slot, oldHook);
4449 return false;
4450 }
4451 }
4452
4453 Rooted<DebuggerDebuggeeLink*> debuggeeLink(cx, dbg.getDebuggeeLink());
4454 if (dbg.hasAnyLiveHooks()) {
4455 debuggeeLink->setLinkSlot(dbg);
4456 } else {
4457 debuggeeLink->clearLinkSlot();
4458 }
4459
4460 args.rval().setUndefined();
4461 return true;
4462}
4463
4464/* static */
4465bool Debugger::getGarbageCollectionHook(JSContext* cx, const CallArgs& args,
4466 Debugger& dbg) {
4467 return getHookImpl(cx, args, dbg, OnGarbageCollection);
4468}
4469
4470/* static */
4471bool Debugger::setGarbageCollectionHook(JSContext* cx, const CallArgs& args,
4472 Debugger& dbg) {
4473 Rooted<JSObject*> oldHook(cx, dbg.getHook(OnGarbageCollection));
4474
4475 if (!setHookImpl(cx, args, dbg, OnGarbageCollection)) {
4476 // We want to maintain the invariant that the hook is always set when the
4477 // Debugger is in the runtime's list, and vice-versa, so if we return early
4478 // and don't adjust the watcher list below, we need to be sure that the
4479 // hook didn't change.
4480 MOZ_ASSERT(dbg.getHook(OnGarbageCollection) == oldHook)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg.getHook(OnGarbageCollection) == oldHook)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(dbg.getHook(OnGarbageCollection) == oldHook))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("dbg.getHook(OnGarbageCollection) == oldHook"
, "./../../../../js/src/debugger/Debugger.cpp", 4480); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbg.getHook(OnGarbageCollection) == oldHook"
")"); do { MOZ_CrashSequence(__null, 4480); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4481 return false;
4482 }
4483
4484 // Add or remove ourselves from the runtime's list of Debuggers that care
4485 // about garbage collection.
4486 JSObject* newHook = dbg.getHook(OnGarbageCollection);
4487 if (!oldHook && newHook) {
4488 cx->runtime()->onGarbageCollectionWatchers().pushBack(&dbg);
4489 } else if (oldHook && !newHook) {
4490 cx->runtime()->onGarbageCollectionWatchers().remove(&dbg);
4491 }
4492
4493 return true;
4494}
4495
4496bool Debugger::CallData::getOnDebuggerStatement() {
4497 return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
4498}
4499
4500bool Debugger::CallData::setOnDebuggerStatement() {
4501 return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
4502}
4503
4504bool Debugger::CallData::getOnExceptionUnwind() {
4505 return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
4506}
4507
4508bool Debugger::CallData::setOnExceptionUnwind() {
4509 return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
4510}
4511
4512bool Debugger::CallData::getOnNewScript() {
4513 return getHookImpl(cx, args, *dbg, OnNewScript);
4514}
4515
4516bool Debugger::CallData::setOnNewScript() {
4517 return setHookImpl(cx, args, *dbg, OnNewScript);
4518}
4519
4520bool Debugger::CallData::getOnEnterFrame() {
4521 return getHookImpl(cx, args, *dbg, OnEnterFrame);
4522}
4523
4524bool Debugger::CallData::setOnEnterFrame() {
4525 return setHookImpl(cx, args, *dbg, OnEnterFrame);
4526}
4527
4528bool Debugger::CallData::getOnNativeCall() {
4529 return getHookImpl(cx, args, *dbg, OnNativeCall);
4530}
4531
4532bool Debugger::CallData::setOnNativeCall() {
4533 RootedObject oldHook(cx, dbg->getHook(OnNativeCall));
4534
4535 if (!setHookImpl(cx, args, *dbg, OnNativeCall)) {
4536 return false;
4537 }
4538
4539 JSObject* newHook = dbg->getHook(OnNativeCall);
4540 if (!oldHook && newHook) {
4541 dbg->updateObservesNativeCallOnDebuggees(Observing);
4542 } else if (oldHook && !newHook) {
4543 dbg->updateObservesNativeCallOnDebuggees(NotObserving);
4544 }
4545
4546 return true;
4547}
4548
4549bool Debugger::CallData::getShouldAvoidSideEffects() {
4550 args.rval().setBoolean(dbg->shouldAvoidSideEffects);
4551 return true;
4552}
4553
4554bool Debugger::CallData::setShouldAvoidSideEffects() {
4555 if (!args.requireAtLeast(cx, "Debugger.set shouldAvoidSideEffects", 1)) {
4556 return false;
4557 }
4558
4559 dbg->shouldAvoidSideEffects = ToBoolean(args[0]);
4560
4561 args.rval().setUndefined();
4562 return true;
4563}
4564
4565bool Debugger::CallData::getOnNewGlobalObject() {
4566 return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
4567}
4568
4569bool Debugger::CallData::setOnNewGlobalObject() {
4570 RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
4571
4572 if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject)) {
4573 return false;
4574 }
4575
4576 // Add or remove ourselves from the runtime's list of Debuggers that care
4577 // about new globals.
4578 JSObject* newHook = dbg->getHook(OnNewGlobalObject);
4579 if (!oldHook && newHook) {
4580 cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
4581 } else if (oldHook && !newHook) {
4582 cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
4583 }
4584
4585 return true;
4586}
4587
4588bool Debugger::CallData::getUncaughtExceptionHook() {
4589 args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
4590 return true;
4591}
4592
4593bool Debugger::CallData::setUncaughtExceptionHook() {
4594 if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1)) {
4595 return false;
4596 }
4597 if (!args[0].isNull() &&
4598 (!args[0].isObject() || !args[0].toObject().isCallable())) {
4599 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4600 JSMSG_ASSIGN_FUNCTION_OR_NULL,
4601 "uncaughtExceptionHook");
4602 return false;
4603 }
4604 dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
4605 args.rval().setUndefined();
4606 return true;
4607}
4608
4609bool Debugger::CallData::getAllowUnobservedWasm() {
4610 args.rval().setBoolean(dbg->allowUnobservedWasm);
4611 return true;
4612}
4613
4614bool Debugger::CallData::setAllowUnobservedWasm() {
4615 if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedWasm", 1)) {
4616 return false;
4617 }
4618 dbg->allowUnobservedWasm = ToBoolean(args[0]);
4619
4620 for (auto iter = dbg->debuggees.iter(); !iter.done(); iter.next()) {
4621 GlobalObject* global = iter.get();
4622 Realm* realm = global->realm();
4623 realm->updateDebuggerObservesWasm();
4624 }
4625
4626 args.rval().setUndefined();
4627 return true;
4628}
4629
4630bool Debugger::CallData::getExclusiveDebuggerOnEval() {
4631 args.rval().setBoolean(dbg->exclusiveDebuggerOnEval);
4632 return true;
4633}
4634
4635bool Debugger::CallData::setExclusiveDebuggerOnEval() {
4636 if (!args.requireAtLeast(cx, "Debugger.set exclusiveDebuggerOnEval", 1)) {
4637 return false;
4638 }
4639 dbg->exclusiveDebuggerOnEval = ToBoolean(args[0]);
4640
4641 args.rval().setUndefined();
4642 return true;
4643}
4644
4645bool Debugger::CallData::getInspectNativeCallArguments() {
4646 args.rval().setBoolean(dbg->inspectNativeCallArguments);
4647 return true;
4648}
4649
4650bool Debugger::CallData::setInspectNativeCallArguments() {
4651 if (!args.requireAtLeast(cx, "Debugger.set inspectNativeCallArguments", 1)) {
4652 return false;
4653 }
4654 dbg->inspectNativeCallArguments = ToBoolean(args[0]);
4655
4656 args.rval().setUndefined();
4657 return true;
4658}
4659
4660bool Debugger::CallData::getCollectCoverageInfo() {
4661 args.rval().setBoolean(dbg->collectCoverageInfo);
4662 return true;
4663}
4664
4665bool Debugger::CallData::setCollectCoverageInfo() {
4666 if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1)) {
4667 return false;
4668 }
4669
4670 // Disallow simultaneous activation of OnEnterFrame and code coverage support;
4671 // as they both use the execution observer flag. See Bug 1608891.
4672 uint32_t slot = JSSLOT_DEBUG_HOOK_START +
4673 std::underlying_type_t<Hook>(Hook::OnEnterFrame);
4674 if (!dbg->object->getReservedSlot(slot).isUndefined()) {
4675 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4676 JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
4677 return false;
4678 }
4679
4680 if (cx->realm()->isTracingExecution()) {
4681 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4682 JSMSG_DEBUG_EXCLUSIVE_EXECUTION_TRACE_COVERAGE);
4683 return false;
4684 }
4685
4686 bool oldFlag = dbg->collectCoverageInfo;
4687 bool newFlag = ToBoolean(args[0]);
4688 dbg->collectCoverageInfo = newFlag;
4689
4690 IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
4691 if (!dbg->updateObservesCoverageOnDebuggees(cx, observing)) {
4692 // Revert code coverage setting if we fail to update the observing flag.
4693 dbg->collectCoverageInfo = oldFlag;
4694 return false;
4695 }
4696
4697 args.rval().setUndefined();
4698 return true;
4699}
4700
4701bool Debugger::CallData::getMemory() {
4702 Value memoryValue =
4703 dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
4704
4705 if (!memoryValue.isObject()) {
4706 RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
4707 if (!memory) {
4708 return false;
4709 }
4710 memoryValue = ObjectValue(*memory);
4711 }
4712
4713 args.rval().set(memoryValue);
4714 return true;
4715}
4716
4717/*
4718 * Given a value used to designate a global (there's quite a variety; see the
4719 * docs), return the actual designee.
4720 *
4721 * Note that this does not check whether the designee is marked "invisible to
4722 * Debugger" or not; different callers need to handle invisible-to-Debugger
4723 * globals in different ways.
4724 */
4725GlobalObject* Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v) {
4726 if (!v.isObject()) {
4727 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4728 JSMSG_UNEXPECTED_TYPE, "argument",
4729 "not a global object");
4730 return nullptr;
4731 }
4732
4733 RootedObject obj(cx, &v.toObject());
4734
4735 // If it's a Debugger.Object belonging to this debugger, dereference that.
4736 if (obj->getClass() == &DebuggerObject::class_) {
4737 RootedValue rv(cx, v);
4738 if (!unwrapDebuggeeValue(cx, &rv)) {
4739 return nullptr;
4740 }
4741 obj = &rv.toObject();
4742 }
4743
4744 // If we have a cross-compartment wrapper, dereference as far as is secure.
4745 //
4746 // Since we're dealing with globals, we may have a WindowProxy here. So we
4747 // have to make sure to do a dynamic unwrap, and we want to unwrap the
4748 // WindowProxy too, if we have one.
4749 obj = CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
4750 if (!obj) {
4751 ReportAccessDenied(cx);
4752 return nullptr;
4753 }
4754
4755 if (JS_IsDeadWrapper(obj)) {
4756 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
4757 return nullptr;
4758 }
4759
4760 // If that didn't produce a global object, it's an error.
4761 if (!obj->is<GlobalObject>()) {
4762 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4763 JSMSG_UNEXPECTED_TYPE, "argument",
4764 "not a global object");
4765 return nullptr;
4766 }
4767
4768 return &obj->as<GlobalObject>();
4769}
4770
4771bool Debugger::CallData::addDebuggee() {
4772 if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1)) {
4773 return false;
4774 }
4775 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
4776 if (!global) {
4777 return false;
4778 }
4779
4780 if (!dbg->addDebuggeeGlobal(cx, global)) {
4781 return false;
4782 }
4783
4784 RootedValue v(cx, ObjectValue(*global));
4785 if (!dbg->wrapDebuggeeValue(cx, &v)) {
4786 return false;
4787 }
4788 args.rval().set(v);
4789 return true;
4790}
4791
4792bool Debugger::CallData::addAllGlobalsAsDebuggees() {
4793 for (CompartmentsIter comp(cx->runtime()); !comp.done(); comp.next()) {
4794 if (comp == dbg->object->compartment()) {
4795 continue;
4796 }
4797 for (RealmsInCompartmentIter r(comp); !r.done(); r.next()) {
4798 if (r->creationOptions().invisibleToDebugger()) {
4799 continue;
4800 }
4801 if (!r->hasInitializedGlobal()) {
4802 continue;
4803 }
4804 r->compartment()->gcState.scheduledForDestruction = false;
4805 Rooted<GlobalObject*> global(cx, r->maybeGlobal());
4806 MOZ_ASSERT(global)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(global)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(global))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("global", "./../../../../js/src/debugger/Debugger.cpp"
, 4806); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { MOZ_CrashSequence(__null, 4806); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4807 if (!dbg->addDebuggeeGlobal(cx, global)) {
4808 return false;
4809 }
4810 }
4811 }
4812
4813 args.rval().setUndefined();
4814 return true;
4815}
4816
4817bool Debugger::CallData::removeDebuggee() {
4818 if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1)) {
4819 return false;
4820 }
4821 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
4822 if (!global) {
4823 return false;
4824 }
4825
4826 ExecutionObservableRealms obs(cx);
4827
4828 if (dbg->debuggees.has(global)) {
4829 dbg->removeDebuggeeGlobal(cx->gcContext(), global, nullptr, FromSweep::No);
4830
4831 // Only update the realm if there are no Debuggers left, as it's
4832 // expensive to check if no other Debugger has a live script or frame
4833 // hook on any of the current on-stack debuggee frames.
4834 if (!global->hasDebuggers() && !obs.add(global->realm())) {
4835 return false;
4836 }
4837 if (!updateExecutionObservability(cx, obs, NotObserving)) {
4838 return false;
4839 }
4840 }
4841
4842 args.rval().setUndefined();
4843 return true;
4844}
4845
4846bool Debugger::CallData::removeAllDebuggees() {
4847 ExecutionObservableRealms obs(cx);
4848
4849 for (auto iter = dbg->debuggees.modIter(); !iter.done(); iter.next()) {
4850 Rooted<GlobalObject*> global(cx, iter.get());
4851 dbg->removeDebuggeeGlobal(cx->gcContext(), global, &iter, FromSweep::No);
4852
4853 // See note about adding to the observable set in removeDebuggee.
4854 if (!global->hasDebuggers() && !obs.add(global->realm())) {
4855 return false;
4856 }
4857 }
4858
4859 if (!updateExecutionObservability(cx, obs, NotObserving)) {
4860 return false;
4861 }
4862
4863 args.rval().setUndefined();
4864 return true;
4865}
4866
4867bool Debugger::CallData::hasDebuggee() {
4868 if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1)) {
4869 return false;
4870 }
4871 GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
4872 if (!global) {
4873 return false;
4874 }
4875 args.rval().setBoolean(!!dbg->debuggees.lookup(global));
4876 return true;
4877}
4878
4879bool Debugger::CallData::getDebuggees() {
4880 // Obtain the list of debuggees before wrapping each debuggee, as a GC could
4881 // update the debuggees set while we are iterating it.
4882 unsigned count = dbg->debuggees.count();
4883 RootedValueVector debuggees(cx);
4884 if (!debuggees.resize(count)) {
4885 return false;
4886 }
4887 unsigned i = 0;
4888 {
4889 JS::AutoCheckCannotGC nogc;
4890 for (auto iter = dbg->debuggees.iter(); !iter.done(); iter.next()) {
4891 debuggees[i++].setObject(*iter.get().get());
4892 }
4893 }
4894
4895 Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
4896 if (!arrobj) {
4897 return false;
4898 }
4899 arrobj->ensureDenseInitializedLength(0, count);
4900 for (i = 0; i < count; i++) {
4901 RootedValue v(cx, debuggees[i]);
4902 if (!dbg->wrapDebuggeeValue(cx, &v)) {
4903 return false;
4904 }
4905 arrobj->setDenseElement(i, v);
4906 }
4907
4908 args.rval().setObject(*arrobj);
4909 return true;
4910}
4911
4912bool Debugger::CallData::getNewestFrame() {
4913 // Note: we use FrameIter (not AllFramesIter) because debugger-frame iteration
4914 // must follow evalInFramePrev links. This preserves the debugger-visible
4915 // frame chain: for a debugger eval frame, `frame.older` must be the frame
4916 // we're evaluating in.
4917 for (FrameIter iter(cx); !iter.done(); ++iter) {
4918 if (dbg->observesFrame(iter)) {
4919 // Ensure that Ion frames are rematerialized. Only rematerialized
4920 // Ion frames may be used as AbstractFramePtrs.
4921 if (iter.isIon() && !iter.ensureHasRematerializedFrame(cx)) {
4922 return false;
4923 }
4924 return dbg->getFrame(cx, iter, args.rval());
4925 }
4926 }
4927 args.rval().setNull();
4928 return true;
4929}
4930
4931bool Debugger::CallData::clearAllBreakpoints() {
4932 JS::GCContext* gcx = cx->gcContext();
4933 Breakpoint* nextbp;
4934 for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = nextbp) {
4935 nextbp = bp->nextInDebugger();
4936
4937 bp->remove(gcx);
4938 }
4939 MOZ_ASSERT(!dbg->firstBreakpoint())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!dbg->firstBreakpoint())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!dbg->firstBreakpoint()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!dbg->firstBreakpoint()"
, "./../../../../js/src/debugger/Debugger.cpp", 4939); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!dbg->firstBreakpoint()" ")"); do { MOZ_CrashSequence
(__null, 4939); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4940
4941 return true;
4942}
4943
4944/* static */
4945bool Debugger::construct(JSContext* cx, unsigned argc, Value* vp) {
4946 CallArgs args = CallArgsFromVp(argc, vp);
4947
4948 // Check that the arguments, if any, are cross-compartment wrappers.
4949 for (unsigned i = 0; i < args.length(); i++) {
4950 JSObject* argobj = RequireObject(cx, args[i]);
4951 if (!argobj) {
4952 return false;
4953 }
4954 if (!argobj->is<CrossCompartmentWrapperObject>()) {
4955 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4956 JSMSG_DEBUG_CCW_REQUIRED, "Debugger");
4957 return false;
4958 }
4959 }
4960
4961 // Get Debugger.prototype.
4962 RootedValue v(cx);
4963 RootedObject callee(cx, &args.callee());
4964 if (!GetProperty(cx, callee, callee, cx->names().prototype, &v)) {
4965 return false;
4966 }
4967 Rooted<NativeObject*> proto(cx, &v.toObject().as<NativeObject>());
4968 MOZ_ASSERT(proto->is<DebuggerPrototypeObject>())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(proto->is<DebuggerPrototypeObject>())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(proto->is<DebuggerPrototypeObject>()))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("proto->is<DebuggerPrototypeObject>()"
, "./../../../../js/src/debugger/Debugger.cpp", 4968); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "proto->is<DebuggerPrototypeObject>()"
")"); do { MOZ_CrashSequence(__null, 4968); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4969
4970 // Make the new Debugger object. Each one has a reference to
4971 // Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
4972 // rest of the reserved slots are for hooks; they default to undefined.
4973 Rooted<DebuggerInstanceObject*> obj(
4974 cx, NewObjectWithGivenProto<DebuggerInstanceObject>(
4975 cx, proto, {.newKind = TenuredObject}));
4976 if (!obj) {
4977 return false;
4978 }
4979 for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP;
4980 slot++) {
4981 obj->setReservedSlot(slot, proto->getReservedSlot(slot));
4982 }
4983 obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
4984
4985 Rooted<NativeObject*> livenessLink(
4986 cx, NewObjectWithGivenProto<DebuggerDebuggeeLink>(cx, nullptr));
4987 if (!livenessLink) {
4988 return false;
4989 }
4990 obj->setReservedSlot(JSSLOT_DEBUG_DEBUGGEE_LINK, ObjectValue(*livenessLink));
4991
4992 Debugger* debugger;
4993 {
4994 // Construct the underlying C++ object.
4995 auto dbg = cx->make_unique<Debugger>(cx, obj.get());
4996 if (!dbg) {
4997 return false;
4998 }
4999
5000 // The object owns the released pointer.
5001 debugger = dbg.release();
5002 InitReservedSlot(obj, JSSLOT_DEBUG_DEBUGGER, debugger, MemoryUse::Debugger);
5003 }
5004
5005 // Add the initial debuggees, if any.
5006 for (unsigned i = 0; i < args.length(); i++) {
5007 JSObject& wrappedObj =
5008 args[i].toObject().as<ProxyObject>().private_().toObject();
5009 Rooted<GlobalObject*> debuggee(cx, &wrappedObj.nonCCWGlobal());
5010 if (!debugger->addDebuggeeGlobal(cx, debuggee)) {
5011 return false;
5012 }
5013 }
5014
5015 args.rval().setObject(*obj);
5016 return true;
5017}
5018
5019bool Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global) {
5020 if (debuggees.has(global)) {
5021 return true;
5022 }
5023
5024 // Callers should generally be unable to get a reference to a debugger-
5025 // invisible global in order to pass it to addDebuggee. But this is possible
5026 // with certain testing aides we expose in the shell, so just make addDebuggee
5027 // throw in that case.
5028 Realm* debuggeeRealm = global->realm();
5029 if (debuggeeRealm->creationOptions().invisibleToDebugger()) {
5030 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5031 JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
5032 return false;
5033 }
5034
5035 // Debugger and debuggee must be in different compartments.
5036 if (debuggeeRealm->compartment() == object->compartment()) {
5037 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5038 JSMSG_DEBUG_SAME_COMPARTMENT);
5039 return false;
5040 }
5041
5042 // Check for cycles. If global's realm is reachable from this Debugger
5043 // object's realm by following debuggee-to-debugger links, then adding
5044 // global would create a cycle. (Typically nobody is debugging the
5045 // debugger, in which case we zip through this code without looping.)
5046 Vector<Realm*> visited(cx);
5047 if (!visited.append(object->realm())) {
5048 return false;
5049 }
5050 for (size_t i = 0; i < visited.length(); i++) {
5051 Realm* realm = visited[i];
5052 if (realm == debuggeeRealm) {
5053 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
5054 return false;
5055 }
5056
5057 // Find all realms containing debuggers debugging realm's global object.
5058 // Add those realms to visited.
5059 if (realm->isDebuggee()) {
5060 JS::AutoAssertNoGC nogc;
5061 for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
5062 Realm* next = entry.dbg->object->realm();
5063 if (std::find(visited.begin(), visited.end(), next) == visited.end()) {
5064 if (!visited.append(next)) {
5065 return false;
5066 }
5067 }
5068 }
5069 }
5070 }
5071
5072 // Check whether we can add allocations tracking.
5073 if (!Debugger::checkCanAddAllocationsTracking(cx, global)) {
5074 return false;
5075 }
5076
5077 // For global to become this js::Debugger's debuggee:
5078 //
5079 // 1. this js::Debugger must be in global->getDebuggers(),
5080 // 2. global must be in this->debuggees,
5081 // 3. the debuggee's zone must be in this->debuggeeZones,
5082 // 4. if we are tracking allocations, the SavedStacksMetadataBuilder must be
5083 // installed for this realm, and
5084 // 5. Realm::isDebuggee()'s bit must be set.
5085 //
5086 // All five indications must be kept consistent.
5087
5088 AutoRealm ar(cx, global);
5089 Zone* zone = global->zone();
5090
5091 RootedObject debuggeeLink(cx, getDebuggeeLink());
5092 if (!cx->compartment()->wrap(cx, &debuggeeLink)) {
5093 return false;
5094 }
5095
5096 // There can be no GC past this point.
5097 JS::AutoAssertNoGC nogc;
5098
5099 // (1)
5100 auto& globalDebuggers = global->getDebuggers(nogc);
5101 if (!globalDebuggers.emplaceBack(this, debuggeeLink)) {
5102 ReportOutOfMemory(cx);
5103 return false;
5104 }
5105 auto globalDebuggersGuard = MakeScopeExit([&] { globalDebuggers.popBack(); });
5106
5107 // (2)
5108 if (!debuggees.put(global)) {
5109 ReportOutOfMemory(cx);
5110 return false;
5111 }
5112 auto debuggeesGuard = MakeScopeExit([&] { debuggees.remove(global); });
5113
5114 bool addingZoneRelation = !debuggeeZones.has(zone);
5115
5116 // (3)
5117 if (addingZoneRelation && !debuggeeZones.put(zone)) {
5118 ReportOutOfMemory(cx);
5119 return false;
5120 }
5121 auto debuggeeZonesGuard = MakeScopeExit([&] {
5122 if (addingZoneRelation) {
5123 debuggeeZones.remove(zone);
5124 }
5125 });
5126
5127 // (4)
5128 if (trackingAllocationSites) {
5129 Debugger::addAllocationsTracking(cx, global);
5130 }
5131
5132 auto allocationsTrackingGuard = MakeScopeExit([&] {
5133 if (trackingAllocationSites) {
5134 Debugger::removeAllocationsTracking(*global);
5135 }
5136 });
5137
5138 // (5)
5139 AutoRestoreRealmDebugMode debugModeGuard(debuggeeRealm);
5140 debuggeeRealm->setIsDebuggee();
5141 debuggeeRealm->updateDebuggerObservesWasm();
5142 debuggeeRealm->updateDebuggerObservesCoverage();
5143 if (observesAllExecution() &&
5144 !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm)) {
5145 return false;
5146 }
5147
5148 globalDebuggersGuard.release();
5149 debuggeesGuard.release();
5150 debuggeeZonesGuard.release();
5151 allocationsTrackingGuard.release();
5152 debugModeGuard.release();
5153 return true;
5154}
5155
5156void Debugger::recomputeDebuggeeZoneSet() {
5157 AutoEnterOOMUnsafeRegion oomUnsafe;
5158 debuggeeZones.clear();
5159 for (auto iter = debuggees.iter(); !iter.done(); iter.next()) {
5160 if (!debuggeeZones.put(iter.get().unbarrieredGet()->zone())) {
5161 oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
5162 }
5163 }
5164}
5165
5166template <typename T, typename AP>
5167static T* findDebuggerInVector(Debugger* dbg, Vector<T, 0, AP>* vec) {
5168 T* p;
5169 for (p = vec->begin(); p != vec->end(); p++) {
5170 if (p->dbg == dbg) {
5171 break;
5172 }
5173 }
5174 MOZ_ASSERT(p != vec->end())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(p != vec->end())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(p != vec->end()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("p != vec->end()"
, "./../../../../js/src/debugger/Debugger.cpp", 5174); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "p != vec->end()" ")"); do { MOZ_CrashSequence
(__null, 5174); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5175 return p;
5176}
5177
5178void Debugger::removeDebuggeeGlobal(JS::GCContext* gcx, GlobalObject* global,
5179 WeakGlobalObjectSet::ModIterator* debugIter,
5180 FromSweep fromSweep) {
5181 // The caller might have found global by enumerating this->debuggees; if
5182 // so, remove from the current iterator rather than calling HashSet::remove
5183 // below, to avoid invalidating the live iterator.
5184 MOZ_ASSERT(debuggees.has(global))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(debuggees.has(global))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(debuggees.has(global)))), 0)
)) { do { } while (false); MOZ_ReportAssertionFailure("debuggees.has(global)"
, "./../../../../js/src/debugger/Debugger.cpp", 5184); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "debuggees.has(global)" ")"); do { MOZ_CrashSequence
(__null, 5184); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5185 MOZ_ASSERT(debuggeeZones.has(global->zone()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(debuggeeZones.has(global->zone()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(debuggeeZones.has(global->
zone())))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("debuggeeZones.has(global->zone())", "./../../../../js/src/debugger/Debugger.cpp"
, 5185); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debuggeeZones.has(global->zone())"
")"); do { MOZ_CrashSequence(__null, 5185); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5186 MOZ_ASSERT_IF(debugIter, debugIter->get().unbarrieredGet() == global)do { if (debugIter) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(debugIter->get().unbarrieredGet() == global)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(debugIter->get().unbarrieredGet() == global))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("debugIter->get().unbarrieredGet() == global"
, "./../../../../js/src/debugger/Debugger.cpp", 5186); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "debugIter->get().unbarrieredGet() == global"
")"); do { MOZ_CrashSequence(__null, 5186); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5187
5188 // Clear this global's generators from generatorFrames as well.
5189 //
5190 // This method can be called either from script (dbg.removeDebuggee) or during
5191 // GC sweeping, because the Debugger, debuggee global, or both are being GC'd.
5192 //
5193 // When called from script, it's okay to iterate over generatorFrames and
5194 // touch its keys and values (even when an incremental GC is in progress).
5195 // When called from GC, it's not okay; the keys and values may be dying. But
5196 // in that case, we can actually just skip the loop entirely! If the Debugger
5197 // is going away, it doesn't care about the state of its generatorFrames
5198 // table, and the Debugger.Frame finalizer will fix up the generator observer
5199 // counts.
5200 if (fromSweep == FromSweep::No) {
5201 for (auto iter = generatorFrames.modIter(); !iter.done(); iter.next()) {
5202 AbstractGeneratorObject& genObj = *iter.get().key();
5203 if (&genObj.global() == global) {
5204 terminateDebuggerFrame(gcx, this, iter.get().value(), NullFramePtr(),
5205 nullptr, &iter);
5206 }
5207 }
5208 }
5209
5210 for (auto iter = frames.modIter(); !iter.done(); iter.next()) {
5211 AbstractFramePtr frame = iter.get().key();
5212 if (frame.hasGlobal(global)) {
5213 terminateDebuggerFrame(gcx, this, iter.get().value(), frame, &iter);
5214 }
5215 }
5216
5217 JS::AutoAssertNoGC nogc;
5218 auto& globalDebuggersVector = global->getDebuggers(nogc);
5219
5220 // The relation must be removed from up to three places:
5221 // globalDebuggersVector and debuggees for sure, and possibly the
5222 // compartment's debuggee set.
5223 //
5224 // The debuggee zone set is recomputed on demand. This avoids refcounting
5225 // and in practice we have relatively few debuggees that tend to all be in
5226 // the same zone. If after recomputing the debuggee zone set, this global's
5227 // zone is not in the set, then we must remove ourselves from the zone's
5228 // vector of observing debuggers.
5229 globalDebuggersVector.erase(
5230 findDebuggerInVector(this, &globalDebuggersVector));
5231
5232 if (debugIter) {
5233 debugIter->remove();
5234 } else {
5235 debuggees.remove(global);
5236 }
5237
5238 recomputeDebuggeeZoneSet();
5239
5240 // Remove all breakpoints for the debuggee.
5241 Breakpoint* nextbp;
5242 for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
5243 nextbp = bp->nextInDebugger();
5244
5245 if (bp->site->realm() == global->realm()) {
5246 bp->remove(gcx);
5247 }
5248 }
5249 MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint())do { if (debuggees.empty()) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(!firstBreakpoint())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!firstBreakpoint()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!firstBreakpoint()", "./../../../../js/src/debugger/Debugger.cpp"
, 5249); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!firstBreakpoint()"
")"); do { MOZ_CrashSequence(__null, 5249); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5250
5251 // If we are tracking allocation sites, we need to remove the object
5252 // metadata callback from this global's realm.
5253 if (trackingAllocationSites) {
5254 Debugger::removeAllocationsTracking(*global);
5255 }
5256
5257 if (!global->realm()->hasDebuggers() &&
5258 !global->realm()->isTracingExecution()) {
5259 global->realm()->unsetIsDebuggee();
5260 } else {
5261 global->realm()->updateDebuggerObservesAllExecution();
5262 global->realm()->updateDebuggerObservesWasm();
5263 global->realm()->updateDebuggerObservesCoverage();
5264 }
5265}
5266
5267class MOZ_STACK_CLASS Debugger::QueryBase {
5268 protected:
5269 QueryBase(JSContext* cx, Debugger* dbg)
5270 : cx(cx),
5271 debugger(dbg),
5272 iterMarker(&cx->runtime()->gc),
5273 realms(cx->zone()) {}
5274
5275 // The context in which we should do our work.
5276 JSContext* cx;
5277
5278 // The debugger for which we conduct queries.
5279 Debugger* debugger;
5280
5281 // Require the set of realms to stay fixed while the query is alive.
5282 gc::AutoEnterIteration iterMarker;
5283
5284 using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
5285
5286 // A script must be in one of these realms to match the query.
5287 RealmSet realms;
5288
5289 // Indicates whether OOM has occurred while matching.
5290 bool oom = false;
5291
5292 bool addRealm(Realm* realm) { return realms.put(realm); }
5293
5294 // Arrange for this query to match only scripts that run in |global|.
5295 bool matchSingleGlobal(GlobalObject* global) {
5296 MOZ_ASSERT(realms.count() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(realms.count() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(realms.count() == 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("realms.count() == 0"
, "./../../../../js/src/debugger/Debugger.cpp", 5296); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "realms.count() == 0" ")"); do { MOZ_CrashSequence
(__null, 5296); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5297 if (!addRealm(global->realm())) {
5298 ReportOutOfMemory(cx);
5299 return false;
5300 }
5301 return true;
5302 }
5303
5304 // Arrange for this ScriptQuery to match all scripts running in debuggee
5305 // globals.
5306 bool matchAllDebuggeeGlobals() {
5307 MOZ_ASSERT(realms.count() == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(realms.count() == 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(realms.count() == 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("realms.count() == 0"
, "./../../../../js/src/debugger/Debugger.cpp", 5307); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "realms.count() == 0" ")"); do { MOZ_CrashSequence
(__null, 5307); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5308 // Build our realm set from the debugger's set of debuggee globals.
5309 for (auto iter = debugger->debuggees.iter(); !iter.done(); iter.next()) {
5310 if (!addRealm(iter.get()->realm())) {
5311 ReportOutOfMemory(cx);
5312 return false;
5313 }
5314 }
5315 return true;
5316 }
5317};
5318
5319/*
5320 * A class for parsing 'findScripts' query arguments and searching for
5321 * scripts that match the criteria they represent.
5322 */
5323class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase {
5324 public:
5325 /* Construct a ScriptQuery to use matching scripts for |dbg|. */
5326 ScriptQuery(JSContext* cx, Debugger* dbg)
5327 : QueryBase(cx, dbg),
5328 url(cx),
5329 displayURLString(cx),
5330 source(cx, AsVariant(static_cast<ScriptSourceObject*>(nullptr))),
5331 scriptVector(cx, BaseScriptVector(cx)),
5332 partialMatchVector(cx, BaseScriptVector(cx)),
5333 wasmInstanceVector(cx, WasmInstanceObjectVector(cx)) {}
5334
5335 /*
5336 * Parse the query object |query|, and prepare to match only the scripts
5337 * it specifies.
5338 */
5339 bool parseQuery(HandleObject query) {
5340 // Check for a 'global' property, which limits the results to those
5341 // scripts scoped to a particular global object.
5342 RootedValue global(cx);
5343 if (!GetProperty(cx, query, query, cx->names().global, &global)) {
5344 return false;
5345 }
5346 if (global.isUndefined()) {
5347 if (!matchAllDebuggeeGlobals()) {
5348 return false;
5349 }
5350 } else {
5351 GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
5352 if (!globalObject) {
5353 return false;
5354 }
5355
5356 // If the given global isn't a debuggee, just leave the set of
5357 // acceptable globals empty; we'll return no scripts.
5358 if (debugger->debuggees.has(globalObject)) {
5359 if (!matchSingleGlobal(globalObject)) {
5360 return false;
5361 }
5362 }
5363 }
5364
5365 // Check for a 'url' property.
5366 if (!GetProperty(cx, query, query, cx->names().url, &url)) {
5367 return false;
5368 }
5369 if (!url.isUndefined() && !url.isString()) {
5370 JS_ReportErrorNumberASCII(
5371 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5372 "query object's 'url' property", "neither undefined nor a string");
5373 return false;
5374 }
5375
5376 // Check for a 'source' property
5377 RootedValue debuggerSource(cx);
5378 if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource)) {
5379 return false;
5380 }
5381 if (!debuggerSource.isUndefined()) {
5382 if (!debuggerSource.isObject() ||
5383 !debuggerSource.toObject().is<DebuggerSource>()) {
5384 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5385 JSMSG_UNEXPECTED_TYPE,
5386 "query object's 'source' property",
5387 "not undefined nor a Debugger.Source object");
5388 return false;
5389 }
5390
5391 DebuggerSource& debuggerSourceObj =
5392 debuggerSource.toObject().as<DebuggerSource>();
5393
5394 // If it does have an owner, it should match the Debugger we're
5395 // calling findScripts on. It would work fine even if it didn't,
5396 // but mixing Debugger.Sources is probably a sign of confusion.
5397 if (debuggerSourceObj.owner() != debugger) {
5398 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5399 JSMSG_DEBUG_WRONG_OWNER, "Debugger.Source");
5400 return false;
5401 }
5402
5403 hasSource = true;
5404 source = debuggerSourceObj.getReferent();
5405 }
5406
5407 // Check for a 'displayURL' property.
5408 RootedValue displayURL(cx);
5409 if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL)) {
5410 return false;
5411 }
5412 if (!displayURL.isUndefined() && !displayURL.isString()) {
5413 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5414 JSMSG_UNEXPECTED_TYPE,
5415 "query object's 'displayURL' property",
5416 "neither undefined nor a string");
5417 return false;
5418 }
5419
5420 if (displayURL.isString()) {
5421 displayURLString = displayURL.toString()->ensureLinear(cx);
5422 if (!displayURLString) {
5423 return false;
5424 }
5425 }
5426
5427 // Check for a 'line' property.
5428 RootedValue lineProperty(cx);
5429 if (!GetProperty(cx, query, query, cx->names().line, &lineProperty)) {
5430 return false;
5431 }
5432 if (lineProperty.isUndefined()) {
5433 hasLine = false;
5434 } else if (lineProperty.isNumber()) {
5435 if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
5436 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5437 JSMSG_QUERY_LINE_WITHOUT_URL,
5438 "'line' property");
5439 return false;
5440 }
5441 if (!parsePositiveInteger(lineProperty, line, JSMSG_DEBUG_BAD_LINE)) {
5442 return false;
5443 }
5444 hasLine = true;
5445 lineEnd = line;
5446 } else {
5447 JS_ReportErrorNumberASCII(
5448 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5449 "query object's 'line' property", "neither undefined nor an integer");
5450 return false;
5451 }
5452
5453 // Check for a 'start' property.
5454 RootedValue startProperty(cx);
5455 if (!GetProperty(cx, query, query, cx->names().start, &startProperty)) {
5456 return false;
5457 }
5458 if (startProperty.isObject()) {
5459 Rooted<JSObject*> startObject(cx, &startProperty.toObject());
5460 if (!parseLineColumnObject(startObject, "start", line, columnStart)) {
5461 return false;
5462 }
5463 hasLine = true;
5464 } else if (!startProperty.isUndefined()) {
5465 JS_ReportErrorNumberASCII(
5466 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5467 "query object's 'start' property", "neither undefined nor an object");
5468 return false;
5469 }
5470
5471 // Check for a 'end' property.
5472 RootedValue endProperty(cx);
5473 if (!GetProperty(cx, query, query, cx->names().end, &endProperty)) {
5474 return false;
5475 }
5476 if (endProperty.isObject()) {
5477 Rooted<JSObject*> endObject(cx, &endProperty.toObject());
5478 if (!parseLineColumnObject(endObject, "end", lineEnd, columnEnd)) {
5479 return false;
5480 }
5481 } else if (!endProperty.isUndefined()) {
5482 JS_ReportErrorNumberASCII(
5483 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5484 "query object's 'end' property", "neither undefined nor an object");
5485 return false;
5486 }
5487
5488 if (startProperty.isUndefined() ^ endProperty.isUndefined()) {
5489 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5490 JSMSG_QUERY_USE_START_AND_END_TOGETHER);
5491 return false;
5492 }
5493
5494 if (!startProperty.isUndefined()) {
5495 // endProperty is also not undefined here
5496 if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
5497 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5498 JSMSG_QUERY_LINE_WITHOUT_URL,
5499 "'start' and 'end' properties");
5500 return false;
5501 }
5502 }
5503
5504 if (hasLine && lineEnd < line) {
5505 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5506 JSMSG_QUERY_START_LINE_IS_AFTER_END);
5507 return false;
5508 }
5509
5510 // Check for an 'innermost' property.
5511 PropertyName* innermostName = cx->names().innermost;
5512 RootedValue innermostProperty(cx);
5513 if (!GetProperty(cx, query, query, innermostName, &innermostProperty)) {
5514 return false;
5515 }
5516 innermost = ToBoolean(innermostProperty);
5517 if (innermost) {
5518 // Technically, we need only check hasLine, but this is clearer.
5519 if ((displayURL.isUndefined() && url.isUndefined() && !hasSource) ||
5520 !hasLine) {
5521 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5522 JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
5523 return false;
5524 }
5525 }
5526
5527 return true;
5528 }
5529
5530 /* Set up this ScriptQuery appropriately for a missing query argument. */
5531 bool omittedQuery() {
5532 url.setUndefined();
5533 hasLine = false;
5534 innermost = false;
5535 displayURLString = nullptr;
5536 return matchAllDebuggeeGlobals();
5537 }
5538
5539 /*
5540 * Search all relevant realms and the stack for scripts matching
5541 * this query, and append the matching scripts to |scriptVector|.
5542 */
5543 bool findScripts() {
5544 if (!prepareQuery()) {
5545 return false;
5546 }
5547
5548 Realm* singletonRealm = nullptr;
5549 if (realms.count() == 1) {
5550 auto iter = realms.iter();
5551 singletonRealm = iter.get();
5552 }
5553
5554 // Search each realm for debuggee scripts.
5555 MOZ_ASSERT(scriptVector.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scriptVector.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(scriptVector.empty()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("scriptVector.empty()"
, "./../../../../js/src/debugger/Debugger.cpp", 5555); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "scriptVector.empty()" ")"); do { MOZ_CrashSequence
(__null, 5555); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5556 MOZ_ASSERT(partialMatchVector.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(partialMatchVector.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(partialMatchVector.empty()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("partialMatchVector.empty()"
, "./../../../../js/src/debugger/Debugger.cpp", 5556); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "partialMatchVector.empty()" ")"); do { MOZ_CrashSequence
(__null, 5556); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5557 oom = false;
5558 IterateScripts(cx, singletonRealm, this, considerScript);
5559 if (oom) {
5560 ReportOutOfMemory(cx);
5561 return false;
5562 }
5563
5564 // If we are filtering by line number, the lazy BaseScripts were not checked
5565 // yet since they do not implement `GetScriptLineExtent`. Instead we revisit
5566 // each result script and delazify its children and add any matching ones to
5567 // the results list.
5568 MOZ_ASSERT(hasLine || partialMatchVector.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(hasLine || partialMatchVector.empty())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(hasLine || partialMatchVector.empty()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("hasLine || partialMatchVector.empty()"
, "./../../../../js/src/debugger/Debugger.cpp", 5568); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "hasLine || partialMatchVector.empty()" ")"
); do { MOZ_CrashSequence(__null, 5568); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5569 Rooted<BaseScript*> script(cx);
5570 RootedFunction fun(cx);
5571 while (!partialMatchVector.empty()) {
5572 script = partialMatchVector.popCopy();
5573
5574 // As a performance optimization, we can skip scripts that are definitely
5575 // out-of-bounds for the target line. This was checked before adding to
5576 // the partialMatchVector, but the bound may have improved since then.
5577 if (script->extent().sourceEnd <= sourceOffsetLowerBound) {
5578 continue;
5579 }
5580
5581 MOZ_ASSERT(script->isFunction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->isFunction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(script->isFunction()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("script->isFunction()"
, "./../../../../js/src/debugger/Debugger.cpp", 5581); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->isFunction()" ")"); do { MOZ_CrashSequence
(__null, 5581); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5582 MOZ_ASSERT(script->isReadyForDelazification())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->isReadyForDelazification())>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(script->isReadyForDelazification()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("script->isReadyForDelazification()"
, "./../../../../js/src/debugger/Debugger.cpp", 5582); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->isReadyForDelazification()" ")"
); do { MOZ_CrashSequence(__null, 5582); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5583
5584 fun = script->function();
5585
5586 // Ignore any delazification placeholder functions. These should not be
5587 // exposed to debugger in any way.
5588 if (fun->isGhost()) {
5589 continue;
5590 }
5591
5592 // Delazify script.
5593 JSScript* compiledScript = GetOrCreateFunctionScript(cx, fun);
5594 if (!compiledScript) {
5595 return false;
5596 }
5597
5598 // If target line isn't in script, we are done with it.
5599 if (!scriptIsLineMatch(compiledScript)) {
5600 continue;
5601 }
5602
5603 // Add script to results now that we've completed checks.
5604 if (!scriptVector.append(compiledScript)) {
5605 return false;
5606 }
5607
5608 // If script was a leaf we are done with it. This is an optional
5609 // optimization to avoid inspecting the `gcthings` list below.
5610 if (!script->hasInnerFunctions()) {
5611 continue;
5612 }
5613
5614 // Now add inner scripts to `partialMatchVector` work list to determine if
5615 // they are matches. Note that out IterateScripts callback ignored them
5616 // already since they did not have a compiled parent at the time.
5617 for (JS::GCCellPtr thing : script->gcthings()) {
5618 if (!thing.is<JSObject>() || !thing.as<JSObject>().is<JSFunction>()) {
5619 continue;
5620 }
5621 JSFunction* fun = &thing.as<JSObject>().as<JSFunction>();
5622 if (!fun->hasBaseScript()) {
5623 continue;
5624 }
5625 BaseScript* inner = fun->baseScript();
5626 MOZ_ASSERT(inner)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inner)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(inner))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("inner", "./../../../../js/src/debugger/Debugger.cpp"
, 5626); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inner" ")")
; do { MOZ_CrashSequence(__null, 5626); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5627 if (!inner) {
5628 // If the function doesn't have script, ignore it.
5629 continue;
5630 }
5631
5632 if (!scriptIsPartialLineMatch(inner)) {
5633 continue;
5634 }
5635
5636 // Add the matching inner script to the back of the results queue
5637 // where it will be processed recursively.
5638 if (!partialMatchVector.append(inner)) {
5639 return false;
5640 }
5641 }
5642 }
5643
5644 // If this is an 'innermost' query, we want to filter the results again to
5645 // only return the innermost script for each realm. To do this we build a
5646 // hashmap to track innermost and then recreate the `scriptVector` with the
5647 // results that remain in the hashmap.
5648 if (innermost) {
5649 using RealmToScriptMap =
5650 GCHashMap<Realm*, BaseScript*, DefaultHasher<Realm*>>;
5651
5652 Rooted<RealmToScriptMap> innermostForRealm(cx, cx);
5653
5654 // Visit each candidate script and find innermost in each realm.
5655 for (BaseScript* script : scriptVector) {
5656 Realm* realm = script->realm();
5657 RealmToScriptMap::AddPtr p = innermostForRealm.lookupForAdd(realm);
5658 if (p) {
5659 // Is our newly found script deeper than the last one we found?
5660 BaseScript* incumbent = p->value();
5661 if (script->asJSScript()->innermostScope()->chainLength() >
5662 incumbent->asJSScript()->innermostScope()->chainLength()) {
5663 p->value() = script;
5664 }
5665 } else {
5666 // This is the first matching script we've encountered for this
5667 // realm, so it is thus the innermost such script.
5668 if (!innermostForRealm.add(p, realm, script)) {
5669 return false;
5670 }
5671 }
5672 }
5673
5674 // Reset the results vector.
5675 scriptVector.clear();
5676
5677 // Re-add only the innermost scripts to the results.
5678 for (auto iter = innermostForRealm.iter(); !iter.done(); iter.next()) {
5679 if (!scriptVector.append(iter.get().value())) {
5680 return false;
5681 }
5682 }
5683 }
5684
5685 // TODO: Until such time that wasm modules are real ES6 modules,
5686 // unconditionally consider all wasm toplevel instance scripts.
5687 for (auto iter = debugger->allDebuggees(); !iter.done(); iter.next()) {
5688 for (auto instIter = iter.get()->realm()->wasm.instances().iter();
5689 !instIter.done(); instIter.next()) {
5690 wasm::Instance* instance = instIter.get();
5691 if (instance->codeMeta().isSelfHostedModule()) {
5692 continue;
5693 }
5694 consider(instance->object());
5695 if (oom) {
5696 ReportOutOfMemory(cx);
5697 return false;
5698 }
5699 }
5700 }
5701
5702 return true;
5703 }
5704
5705 Handle<BaseScriptVector> foundScripts() const { return scriptVector; }
5706
5707 Handle<WasmInstanceObjectVector> foundWasmInstances() const {
5708 return wasmInstanceVector;
5709 }
5710
5711 private:
5712 static const uint32_t LINE_CONSTRAINT_NOT_PROVIDED = 0;
5713
5714 /* If this is a string, matching scripts have urls equal to it. */
5715 RootedValue url;
5716
5717 /* url as a C string. */
5718 UniqueChars urlCString;
5719
5720 /* If this is a string, matching scripts' sources have displayURLs equal to
5721 * it. */
5722 Rooted<JSLinearString*> displayURLString;
5723
5724 /*
5725 * If this is a source referent, matching scripts will have sources equal
5726 * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
5727 * very badly with Rooted's LIFO invariant.
5728 */
5729 bool hasSource = false;
5730 Rooted<DebuggerSourceReferent> source;
5731
5732 /* True if the query contained a 'line' or 'start' property. */
5733 bool hasLine = false;
5734
5735 /* The start line of the target range, inclusive. A script's lines must
5736 * overlap the target line range or it will be filtered out by the query. */
5737 uint32_t line = LINE_CONSTRAINT_NOT_PROVIDED;
5738
5739 /* The end line of the target range, inclusive. A script's lines must overlap
5740 * the target line range or it will be filtered out by the query. */
5741 uint32_t lineEnd = LINE_CONSTRAINT_NOT_PROVIDED;
5742
5743 Maybe<JS::LimitedColumnNumberOneOrigin> columnStart;
5744
5745 Maybe<JS::LimitedColumnNumberOneOrigin> columnEnd;
5746
5747 // As a performance optimization (and to avoid delazifying as many scripts),
5748 // we would like to know the source offset of the target range start line.
5749 //
5750 // Since we do not have a simple way to compute this precisely, we instead
5751 // track a lower-bound of the offset value. As we collect SourceExtent
5752 // examples with (line,column) <-> sourceStart mappings, we can improve the
5753 // bound. The target range start line is within the range
5754 // [sourceOffsetLowerBound, Inf).
5755 //
5756 // NOTE: Using a SourceExtent for updating the bound happens independently of
5757 // if the script matches the target range start line or not in the end.
5758 mutable uint32_t sourceOffsetLowerBound = 0;
5759
5760 /* True if the query has an 'innermost' property whose value is true. */
5761 bool innermost = false;
5762
5763 /*
5764 * Accumulate the scripts in an Rooted<BaseScriptVector> instead of creating
5765 * the JS array as we go, because we mustn't allocate JS objects or GC while
5766 * we use the CellIter.
5767 */
5768 Rooted<BaseScriptVector> scriptVector;
5769
5770 /*
5771 * While in the CellIter we may find BaseScripts that need to be compiled
5772 * before the query can be fully checked. Since we cannot compile while under
5773 * CellIter we accumulate them here instead.
5774 *
5775 * This occurs when matching line numbers since `GetScriptLineExtent` cannot
5776 * be computed without bytecode existing.
5777 */
5778 Rooted<BaseScriptVector> partialMatchVector;
5779
5780 /*
5781 * Like above, but for wasm modules.
5782 */
5783 Rooted<WasmInstanceObjectVector> wasmInstanceVector;
5784
5785 /*
5786 * Given that parseQuery or omittedQuery has been called, prepare to match
5787 * scripts. Set urlCString and displayURLChars as appropriate.
5788 */
5789 bool prepareQuery() {
5790 // Compute urlCString and displayURLChars, if a url or displayURL was
5791 // given respectively.
5792 if (url.isString()) {
5793 Rooted<JSString*> str(cx, url.toString());
5794 urlCString = JS_EncodeStringToUTF8(cx, str);
5795 if (!urlCString) {
5796 return false;
5797 }
5798 }
5799
5800 return true;
5801 }
5802
5803 template <size_t N>
5804 bool parseLineColumnObject(
5805 Handle<JSObject*> obj, const char (&propName)[N], uint32_t& lineOut,
5806 Maybe<JS::LimitedColumnNumberOneOrigin>& columnOut) {
5807 RootedValue lineProp(cx);
5808 if (!GetProperty(cx, obj, obj, cx->names().line, &lineProp)) {
5809 return false;
5810 }
5811 if (!lineProp.isNumber()) {
5812 static const char propMessageFormat[] =
5813 "query object's '%s.line' property";
5814 char propMessage[N - 1 /* propName's terminating null */
5815 + sizeof(propMessageFormat) - 2 /* '%s' is replaced */];
5816 DebugOnly<size_t> checkLen =
5817 SprintfLiteral(propMessage, propMessageFormat, propName);
5818 MOZ_ASSERT(checkLen == sizeof(propMessage) - 1 /* terminating null */)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checkLen == sizeof(propMessage) - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checkLen == sizeof(propMessage
) - 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("checkLen == sizeof(propMessage) - 1", "./../../../../js/src/debugger/Debugger.cpp"
, 5818); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checkLen == sizeof(propMessage) - 1"
")"); do { MOZ_CrashSequence(__null, 5818); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5819 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5820 JSMSG_UNEXPECTED_TYPE, propMessage,
5821 "not a number");
5822 return false;
5823 }
5824 if (!parsePositiveInteger(lineProp, lineOut, JSMSG_DEBUG_BAD_LINE)) {
5825 return false;
5826 }
5827
5828 RootedValue columnProp(cx);
5829 if (!GetProperty(cx, obj, obj, cx->names().column, &columnProp)) {
5830 return false;
5831 }
5832 if (!columnProp.isUndefined()) {
5833 if (!columnProp.isNumber()) {
5834 static const char propMessageFormat[] =
5835 "query object's '%s.column' property";
5836 char propMessage[N - 1 /* propName's terminating null */
5837 + sizeof(propMessageFormat) -
5838 2 /* '%s' is replaced */];
5839 DebugOnly<size_t> checkLen =
5840 SprintfLiteral(propMessage, propMessageFormat, propName);
5841 MOZ_ASSERT(checkLen == sizeof(propMessage) - 1 /* terminating null */)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(checkLen == sizeof(propMessage) - 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(checkLen == sizeof(propMessage
) - 1))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("checkLen == sizeof(propMessage) - 1", "./../../../../js/src/debugger/Debugger.cpp"
, 5841); AnnotateMozCrashReason("MOZ_ASSERT" "(" "checkLen == sizeof(propMessage) - 1"
")"); do { MOZ_CrashSequence(__null, 5841); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5842 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5843 JSMSG_UNEXPECTED_TYPE, propMessage,
5844 "not a number");
5845 return false;
5846 }
5847 uint32_t uintColumn = 0;
5848 if (!parsePositiveInteger(columnProp, uintColumn,
5849 JSMSG_BAD_COLUMN_NUMBER)) {
5850 return false;
5851 }
5852 if (uintColumn > JS::LimitedColumnNumberOneOrigin::Limit) {
5853 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5854 JSMSG_BAD_COLUMN_NUMBER);
5855 return false;
5856 }
5857 columnOut.emplace(JS::LimitedColumnNumberOneOrigin(uintColumn));
5858 }
5859 return true;
5860 }
5861
5862 bool parsePositiveInteger(Handle<Value> numberProp, uint32_t& result,
5863 JSErrNum errorNumber) {
5864 double doubleVal = numberProp.toNumber();
5865 uint32_t uintVal = (uint32_t)doubleVal;
5866 if (doubleVal <= 0 || uintVal != doubleVal) {
5867 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, errorNumber);
5868 return false;
5869 }
5870 result = uintVal;
5871 return true;
5872 }
5873
5874 void updateSourceOffsetLowerBound(const SourceExtent& extent) {
5875 // We trying to find the offset of (target-range-start-line, 0), so ignore
5876 // any scripts within the target range.
5877 MOZ_ASSERT(line != LINE_CONSTRAINT_NOT_PROVIDED &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd
!= LINE_CONSTRAINT_NOT_PROVIDED)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line != LINE_CONSTRAINT_NOT_PROVIDED
&& lineEnd != LINE_CONSTRAINT_NOT_PROVIDED))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
, "./../../../../js/src/debugger/Debugger.cpp", 5878); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
")"); do { MOZ_CrashSequence(__null, 5878); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5878 lineEnd != LINE_CONSTRAINT_NOT_PROVIDED)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd
!= LINE_CONSTRAINT_NOT_PROVIDED)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line != LINE_CONSTRAINT_NOT_PROVIDED
&& lineEnd != LINE_CONSTRAINT_NOT_PROVIDED))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
, "./../../../../js/src/debugger/Debugger.cpp", 5878); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
")"); do { MOZ_CrashSequence(__null, 5878); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5879 MOZ_ASSERT(extent.lineno <= lineEnd)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(extent.lineno <= lineEnd)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(extent.lineno <= lineEnd)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("extent.lineno <= lineEnd"
, "./../../../../js/src/debugger/Debugger.cpp", 5879); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "extent.lineno <= lineEnd" ")"); do { MOZ_CrashSequence
(__null, 5879); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5880 if (extent.lineno >= line) {
5881 return;
5882 }
5883
5884 // The extent.sourceStart position is now definitely *before* the target
5885 // range start line, so update sourceOffsetLowerBound if extent.sourceStart
5886 // is a tighter bound.
5887 if (extent.sourceStart > sourceOffsetLowerBound) {
5888 sourceOffsetLowerBound = extent.sourceStart;
5889 }
5890 }
5891
5892 // A partial match is a script that starts before the target range ends, but
5893 // may or may not end before the target range starts. We can also return false
5894 // if we can prove the script ends before the target range starts.
5895 bool scriptIsPartialLineMatch(BaseScript* script) {
5896 const SourceExtent& extent = script->extent();
5897
5898 // We only know for sure that the script is outside the target line range
5899 // if the start of script is after the target end line, because we don't
5900 // know how many lines the script has yet.
5901 MOZ_ASSERT(line != LINE_CONSTRAINT_NOT_PROVIDED &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd
!= LINE_CONSTRAINT_NOT_PROVIDED)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line != LINE_CONSTRAINT_NOT_PROVIDED
&& lineEnd != LINE_CONSTRAINT_NOT_PROVIDED))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
, "./../../../../js/src/debugger/Debugger.cpp", 5902); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
")"); do { MOZ_CrashSequence(__null, 5902); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
5902 lineEnd != LINE_CONSTRAINT_NOT_PROVIDED)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd
!= LINE_CONSTRAINT_NOT_PROVIDED)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line != LINE_CONSTRAINT_NOT_PROVIDED
&& lineEnd != LINE_CONSTRAINT_NOT_PROVIDED))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
, "./../../../../js/src/debugger/Debugger.cpp", 5902); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line != LINE_CONSTRAINT_NOT_PROVIDED && lineEnd != LINE_CONSTRAINT_NOT_PROVIDED"
")"); do { MOZ_CrashSequence(__null, 5902); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5903 MOZ_ASSERT(line <= lineEnd)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line <= lineEnd)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line <= lineEnd))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("line <= lineEnd"
, "./../../../../js/src/debugger/Debugger.cpp", 5903); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line <= lineEnd" ")"); do { MOZ_CrashSequence
(__null, 5903); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5904 if (extent.lineno > lineEnd) {
5905 return false;
5906 }
5907 if (columnEnd.isSome() && script->lineno() == lineEnd &&
5908 script->column() > columnEnd.value()) {
5909 return false;
5910 }
5911
5912 // Use the implicit (line, column) <-> sourceStart mapping from the
5913 // SourceExtent to update our bounds on possible matches. We call this
5914 // without knowing if the script is a match or not.
5915 updateSourceOffsetLowerBound(script->extent());
5916
5917 // As an optional performance optimization, we rule out any script that ends
5918 // before the lower-bound on where target range start line exists.
5919 return extent.sourceEnd > sourceOffsetLowerBound;
5920 }
5921
5922 // True if any part of script source overlaps the target range.
5923 bool scriptIsLineMatch(JSScript* script) {
5924 MOZ_ASSERT(scriptIsPartialLineMatch(script))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(scriptIsPartialLineMatch(script))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(scriptIsPartialLineMatch(script
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("scriptIsPartialLineMatch(script)", "./../../../../js/src/debugger/Debugger.cpp"
, 5924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scriptIsPartialLineMatch(script)"
")"); do { MOZ_CrashSequence(__null, 5924); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5925
5926 JS::LimitedColumnNumberOneOrigin scriptEndColumn;
5927 uint32_t lineCount = GetScriptLineExtent(script, &scriptEndColumn);
5928 if (columnStart.isSome() && script->lineno() + lineCount - 1 == line) {
5929 if (scriptEndColumn <= columnStart.value()) {
5930 return false;
5931 }
5932 }
5933 return (script->lineno() + lineCount > line);
5934 }
5935
5936 static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
5937 const JS::AutoRequireNoGC& nogc) {
5938 ScriptQuery* self = static_cast<ScriptQuery*>(data);
5939 self->consider(script, nogc);
5940 }
5941
5942 template <typename T>
5943 [[nodiscard]] bool commonFilter(T script, const JS::AutoRequireNoGC& nogc) {
5944 if (urlCString) {
5945 bool gotFilename = false;
5946 if (script->filename() &&
5947 strcmp(script->filename(), urlCString.get()) == 0) {
5948 gotFilename = true;
5949 }
5950
5951 bool gotSourceURL = false;
5952 if (!gotFilename && script->scriptSource()->introducerFilename() &&
5953 strcmp(script->scriptSource()->introducerFilename(),
5954 urlCString.get()) == 0) {
5955 gotSourceURL = true;
5956 }
5957 if (!gotFilename && !gotSourceURL) {
5958 return false;
5959 }
5960 }
5961 if (displayURLString) {
5962 if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL()) {
5963 return false;
5964 }
5965
5966 const char16_t* s = script->scriptSource()->displayURL();
5967 if (CompareChars(s, js_strlen(s), displayURLString) != 0) {
5968 return false;
5969 }
5970 }
5971 if (hasSource && !(source.is<ScriptSourceObject*>() &&
5972 source.as<ScriptSourceObject*>()->source() ==
5973 script->scriptSource())) {
5974 return false;
5975 }
5976 return true;
5977 }
5978
5979 /*
5980 * If |script| matches this query, append it to |scriptVector|. Set |oom| if
5981 * an out of memory condition occurred.
5982 */
5983 void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
5984 if (oom || script->selfHosted()) {
5985 return;
5986 }
5987
5988 Realm* realm = script->realm();
5989 if (!realms.has(realm)) {
5990 return;
5991 }
5992
5993 if (!commonFilter(script, nogc)) {
5994 return;
5995 }
5996
5997 bool partial = false;
5998
5999 if (hasLine) {
6000 if (!scriptIsPartialLineMatch(script)) {
6001 return;
6002 }
6003
6004 if (script->hasBytecode()) {
6005 // Check if line is within script (or any of its inner scripts).
6006 if (!scriptIsLineMatch(script->asJSScript())) {
6007 return;
6008 }
6009 } else {
6010 // GetScriptLineExtent is not available on lazy scripts so instead to
6011 // the partial match list for be compiled and reprocessed later. We only
6012 // add scripts that are ready for delazification and they may in turn
6013 // process their inner functions.
6014 if (!script->isReadyForDelazification()) {
6015 return;
6016 }
6017 partial = true;
6018 }
6019 }
6020
6021 // If innermost filter is required, we collect everything that matches the
6022 // line number and filter at the end of `findScripts`.
6023 MOZ_ASSERT_IF(innermost, hasLine)do { if (innermost) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(hasLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(hasLine))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("hasLine", "./../../../../js/src/debugger/Debugger.cpp"
, 6023); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hasLine" ")"
); do { MOZ_CrashSequence(__null, 6023); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6024
6025 Rooted<BaseScriptVector>& vec = partial ? partialMatchVector : scriptVector;
6026 if (!vec.append(script)) {
6027 oom = true;
6028 }
6029 }
6030
6031 /*
6032 * If |instanceObject| matches this query, append it to |wasmInstanceVector|.
6033 * Set |oom| if an out of memory condition occurred.
6034 */
6035 void consider(WasmInstanceObject* instanceObject) {
6036 if (oom) {
6037 return;
6038 }
6039
6040 if (hasSource && source != AsVariant(instanceObject)) {
6041 return;
6042 }
6043
6044 if (!wasmInstanceVector.append(instanceObject)) {
6045 oom = true;
6046 }
6047 }
6048};
6049
6050bool Debugger::CallData::findScripts() {
6051 ScriptQuery query(cx, dbg);
6052
6053 if (args.length() >= 1) {
6054 RootedObject queryObject(cx, RequireObject(cx, args[0]));
6055 if (!queryObject || !query.parseQuery(queryObject)) {
6056 return false;
6057 }
6058 } else {
6059 if (!query.omittedQuery()) {
6060 return false;
6061 }
6062 }
6063
6064 if (!query.findScripts()) {
6065 return false;
6066 }
6067
6068 Handle<BaseScriptVector> scripts(query.foundScripts());
6069 Handle<WasmInstanceObjectVector> wasmInstances(query.foundWasmInstances());
6070
6071 size_t resultLength = scripts.length() + wasmInstances.length();
6072 Rooted<ArrayObject*> result(cx,
6073 NewDenseFullyAllocatedArray(cx, resultLength));
6074 if (!result) {
6075 return false;
6076 }
6077
6078 result->ensureDenseInitializedLength(0, resultLength);
6079
6080 for (size_t i = 0; i < scripts.length(); i++) {
6081 JSObject* scriptObject = dbg->wrapScript(cx, scripts[i]);
6082 if (!scriptObject) {
6083 return false;
6084 }
6085 result->setDenseElement(i, ObjectValue(*scriptObject));
6086 }
6087
6088 size_t wasmStart = scripts.length();
6089 for (size_t i = 0; i < wasmInstances.length(); i++) {
6090 JSObject* scriptObject = dbg->wrapWasmScript(cx, wasmInstances[i]);
6091 if (!scriptObject) {
6092 return false;
6093 }
6094 result->setDenseElement(wasmStart + i, ObjectValue(*scriptObject));
6095 }
6096
6097 args.rval().setObject(*result);
6098 return true;
6099}
6100
6101/*
6102 * A class for searching sources for 'findSources'.
6103 */
6104class MOZ_STACK_CLASS Debugger::SourceQuery : public Debugger::QueryBase {
6105 public:
6106 using SourceSet = JS::GCHashSet<JSObject*, js::StableCellHasher<JSObject*>,
6107 ZoneAllocPolicy>;
6108
6109 SourceQuery(JSContext* cx, Debugger* dbg)
6110 : QueryBase(cx, dbg), sources(cx, SourceSet(cx->zone())) {}
6111
6112 bool findSources() {
6113 if (!matchAllDebuggeeGlobals()) {
6114 return false;
6115 }
6116
6117 Realm* singletonRealm = nullptr;
6118 if (realms.count() == 1) {
6119 auto iter = realms.iter();
6120 singletonRealm = iter.get();
6121 }
6122
6123 // Search each realm for debuggee scripts.
6124 MOZ_ASSERT(sources.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sources.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(sources.empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("sources.empty()"
, "./../../../../js/src/debugger/Debugger.cpp", 6124); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "sources.empty()" ")"); do { MOZ_CrashSequence
(__null, 6124); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6125 oom = false;
6126 IterateScripts(cx, singletonRealm, this, considerScript);
6127 if (oom) {
6128 ReportOutOfMemory(cx);
6129 return false;
6130 }
6131
6132 // IterateScripts only finds sources reachable via a live BaseScript.
6133 // For JS modules, onTopLevelEvaluationFinished clears ScriptSlot so the
6134 // BaseScript can be GC'd while the ScriptSourceObject is kept alive by
6135 // CyclicModuleFields. Collect those SSOs here.
6136 for (auto iter = debugger->allDebuggees(); !iter.done(); iter.next()) {
6137 auto siter = ObjectRealm::get(iter.get()).moduleScriptSources.iter();
6138 for (; !siter.done(); siter.next()) {
6139 if (ScriptSourceObject* sso = siter.get().get()) {
6140 if (!sources.put(sso)) {
6141 ReportOutOfMemory(cx);
6142 return false;
6143 }
6144 }
6145 }
6146 }
6147
6148 // TODO: Until such time that wasm modules are real ES6 modules,
6149 // unconditionally consider all wasm toplevel instance scripts.
6150 for (auto iter = debugger->allDebuggees(); !iter.done(); iter.next()) {
6151 for (auto instIter = iter.get()->realm()->wasm.instances().iter();
6152 !instIter.done(); instIter.next()) {
6153 wasm::Instance* instance = instIter.get();
6154 if (instance->codeMeta().isSelfHostedModule()) {
6155 continue;
6156 }
6157 consider(instance->object());
6158 if (oom) {
6159 ReportOutOfMemory(cx);
6160 return false;
6161 }
6162 }
6163 }
6164
6165 return true;
6166 }
6167
6168 Handle<SourceSet> foundSources() const { return sources; }
6169
6170 private:
6171 Rooted<SourceSet> sources;
6172
6173 static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
6174 const JS::AutoRequireNoGC& nogc) {
6175 SourceQuery* self = static_cast<SourceQuery*>(data);
6176 self->consider(script, nogc);
6177 }
6178
6179 void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
6180 if (oom || script->selfHosted()) {
6181 return;
6182 }
6183
6184 Realm* realm = script->realm();
6185 if (!realms.has(realm)) {
6186 return;
6187 }
6188
6189 ScriptSourceObject* source = script->sourceObject();
6190 if (!sources.put(source)) {
6191 oom = true;
6192 }
6193 }
6194
6195 void consider(WasmInstanceObject* instanceObject) {
6196 if (oom) {
6197 return;
6198 }
6199
6200 if (!sources.put(instanceObject)) {
6201 oom = true;
6202 }
6203 }
6204};
6205
6206static inline DebuggerSourceReferent AsSourceReferent(JSObject* obj) {
6207 if (obj->is<ScriptSourceObject>()) {
6208 return AsVariant(&obj->as<ScriptSourceObject>());
6209 }
6210 return AsVariant(&obj->as<WasmInstanceObject>());
6211}
6212
6213bool Debugger::CallData::findSources() {
6214 SourceQuery query(cx, dbg);
6215 if (!query.findSources()) {
6216 return false;
6217 }
6218
6219 Handle<SourceQuery::SourceSet> sources(query.foundSources());
6220
6221 size_t resultLength = sources.count();
6222 Rooted<ArrayObject*> result(cx,
6223 NewDenseFullyAllocatedArray(cx, resultLength));
6224 if (!result) {
6225 return false;
6226 }
6227
6228 result->ensureDenseInitializedLength(0, resultLength);
6229
6230 size_t i = 0;
6231 for (auto iter = sources.get().iter(); !iter.done(); iter.next()) {
6232 Rooted<DebuggerSourceReferent> sourceReferent(cx,
6233 AsSourceReferent(iter.get()));
6234 RootedObject sourceObject(cx, dbg->wrapVariantReferent(cx, sourceReferent));
6235 if (!sourceObject) {
6236 return false;
6237 }
6238 result->setDenseElement(i, ObjectValue(*sourceObject));
6239 i++;
6240 }
6241
6242 args.rval().setObject(*result);
6243 return true;
6244}
6245
6246bool Debugger::CallData::findAllGlobals() {
6247 RootedObjectVector globals(cx);
6248
6249 {
6250 // Accumulate the list of globals before wrapping them, because
6251 // wrapping can GC and collect realms from under us, while iterating.
6252 JS::AutoCheckCannotGC nogc;
6253
6254 for (RealmsIter r(cx->runtime()); !r.done(); r.next()) {
6255 if (r->creationOptions().invisibleToDebugger()) {
6256 continue;
6257 }
6258
6259 if (!r->hasInitializedGlobal()) {
6260 continue;
6261 }
6262
6263 if (JS::RealmBehaviorsRef(r).isNonLive()) {
6264 continue;
6265 }
6266
6267 r->compartment()->gcState.scheduledForDestruction = false;
6268
6269 GlobalObject* global = r->maybeGlobal();
6270
6271 // We pulled |global| out of nowhere, so it's possible that it was
6272 // marked gray by XPConnect. Since we're now exposing it to JS code,
6273 // we need to mark it black.
6274 JS::ExposeObjectToActiveJS(global);
6275 if (!globals.append(global)) {
6276 return false;
6277 }
6278 }
6279 }
6280
6281 RootedObject result(cx, NewDenseEmptyArray(cx));
6282 if (!result) {
6283 return false;
6284 }
6285
6286 for (size_t i = 0; i < globals.length(); i++) {
6287 RootedValue globalValue(cx, ObjectValue(*globals[i]));
6288 if (!dbg->wrapDebuggeeValue(cx, &globalValue)) {
6289 return false;
6290 }
6291 if (!NewbornArrayPush(cx, result, globalValue)) {
6292 return false;
6293 }
6294 }
6295
6296 args.rval().setObject(*result);
6297 return true;
6298}
6299
6300bool Debugger::CallData::findSourceURLs() {
6301 RootedObject result(cx, NewDenseEmptyArray(cx));
6302 if (!result) {
6303 return false;
6304 }
6305
6306 for (auto iter = dbg->allDebuggees(); !iter.done(); iter.next()) {
6307 RootedObject holder(cx, iter.get()->getSourceURLsHolder());
6308 if (holder) {
6309 RootedValue v(cx);
6310 for (size_t i = 0; i < holder->as<ArrayObject>().length(); i++) {
6311 v = holder->as<ArrayObject>().getDenseElement(i);
6312
6313 // The value is an atom and doesn't need wrapping, but the holder may be
6314 // in another zone and the atom must be marked when we create a
6315 // reference in this zone.
6316 MOZ_ASSERT(v.isString() && v.toString()->isAtom())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(v.isString() && v.toString()->isAtom())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(v.isString() && v.toString()->isAtom())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("v.isString() && v.toString()->isAtom()"
, "./../../../../js/src/debugger/Debugger.cpp", 6316); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "v.isString() && v.toString()->isAtom()"
")"); do { MOZ_CrashSequence(__null, 6316); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6317
6318 if (!cx->wrapOrRecordRefToValue(&v) ||
6319 !NewbornArrayPush(cx, result, v)) {
6320 return false;
6321 }
6322 }
6323 }
6324 }
6325
6326 args.rval().setObject(*result);
6327 return true;
6328}
6329
6330bool Debugger::CallData::makeGlobalObjectReference() {
6331 if (!args.requireAtLeast(cx, "Debugger.makeGlobalObjectReference", 1)) {
6332 return false;
6333 }
6334
6335 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
6336 if (!global) {
6337 return false;
6338 }
6339
6340 // If we create a D.O referring to a global in an invisible realm,
6341 // then from it we can reach function objects, scripts, environments, etc.,
6342 // none of which we're ever supposed to see.
6343 if (global->realm()->creationOptions().invisibleToDebugger()) {
6344 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
6345 JSMSG_DEBUG_INVISIBLE_COMPARTMENT);
6346 return false;
6347 }
6348
6349 args.rval().setObject(*global);
6350 return dbg->wrapDebuggeeValue(cx, args.rval());
6351}
6352
6353bool Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp) {
6354 CallArgs args = CallArgsFromVp(argc, vp);
6355
6356 if (!args.requireAtLeast(cx, "Debugger.isCompilableUnit", 1)) {
6357 return false;
6358 }
6359
6360 if (!args[0].isString()) {
6361 JS_ReportErrorNumberASCII(
6362 cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
6363 "Debugger.isCompilableUnit", "string", InformalValueTypeName(args[0]));
6364 return false;
6365 }
6366
6367 JSString* str = args[0].toString();
6368 size_t length = str->length();
6369
6370 AutoStableStringChars chars(cx);
6371 if (!chars.initTwoByte(cx, str)) {
6372 return false;
6373 }
6374
6375 bool result = true;
6376
6377 AutoReportFrontendContext fc(cx,
6378 AutoReportFrontendContext::Warning::Suppress);
6379 CompileOptions options(cx);
6380 Rooted<frontend::CompilationInput> input(cx,
6381 frontend::CompilationInput(options));
6382 if (!input.get().initForGlobal(&fc)) {
6383 return false;
6384 }
6385
6386 LifoAllocScope allocScope(&cx->tempLifoAlloc());
6387 frontend::NoScopeBindingCache scopeCache;
6388 frontend::CompilationState compilationState(&fc, allocScope, input.get());
6389 if (!compilationState.init(&fc, &scopeCache)) {
6390 return false;
6391 }
6392
6393 frontend::Parser<frontend::FullParseHandler, char16_t> parser(
6394 &fc, options, chars.twoByteChars(), length, compilationState,
6395 /* syntaxParser = */ nullptr);
6396 if (!parser.checkOptions() || parser.parse().isErr()) {
6397 // We ran into an error. If it was because we ran out of memory we report
6398 // it in the usual way.
6399 if (fc.hadOutOfMemory()) {
6400 return false;
6401 }
6402
6403 // If it was because we ran out of source, we return false so our caller
6404 // knows to try to collect more [source].
6405 if (parser.isUnexpectedEOF()) {
6406 result = false;
6407 }
6408
6409 fc.clearAutoReport();
6410 }
6411
6412 args.rval().setBoolean(result);
6413 return true;
6414}
6415
6416bool Debugger::CallData::adoptDebuggeeValue() {
6417 if (!args.requireAtLeast(cx, "Debugger.adoptDebuggeeValue", 1)) {
6418 return false;
6419 }
6420
6421 RootedValue v(cx, args[0]);
6422 if (v.isObject()) {
6423 RootedObject obj(cx, &v.toObject());
6424 DebuggerObject* ndobj = ToNativeDebuggerObject(cx, &obj);
6425 if (!ndobj) {
6426 return false;
6427 }
6428
6429 obj.set(ndobj->referent());
6430 v = ObjectValue(*obj);
6431
6432 if (!dbg->wrapDebuggeeValue(cx, &v)) {
6433 return false;
6434 }
6435 }
6436
6437 args.rval().set(v);
6438 return true;
6439}
6440
6441class DebuggerAdoptSourceMatcher {
6442 JSContext* cx_;
6443 Debugger* dbg_;
6444
6445 public:
6446 explicit DebuggerAdoptSourceMatcher(JSContext* cx, Debugger* dbg)
6447 : cx_(cx), dbg_(dbg) {}
6448
6449 using ReturnType = DebuggerSource*;
6450
6451 ReturnType match(Handle<ScriptSourceObject*> source) {
6452 if (source->compartment() == cx_->compartment()) {
6453 JS_ReportErrorASCII(cx_,
6454 "Source is in the same compartment as this debugger");
6455 return nullptr;
6456 }
6457 return dbg_->wrapSource(cx_, source);
6458 }
6459 ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
6460 if (wasmInstance->compartment() == cx_->compartment()) {
6461 JS_ReportErrorASCII(
6462 cx_, "WasmInstance is in the same compartment as this debugger");
6463 return nullptr;
6464 }
6465 return dbg_->wrapWasmSource(cx_, wasmInstance);
6466 }
6467};
6468
6469bool Debugger::CallData::adoptFrame() {
6470 if (!args.requireAtLeast(cx, "Debugger.adoptFrame", 1)) {
6471 return false;
6472 }
6473
6474 RootedObject obj(cx, RequireObject(cx, args[0]));
6475 if (!obj) {
6476 return false;
6477 }
6478
6479 obj = UncheckedUnwrap(obj);
6480 if (!obj->is<DebuggerFrame>()) {
6481 JS_ReportErrorASCII(cx, "Argument is not a Debugger.Frame");
6482 return false;
6483 }
6484
6485 RootedValue objVal(cx, ObjectValue(*obj));
6486 Rooted<DebuggerFrame*> frameObj(cx, DebuggerFrame::check(cx, objVal));
6487 if (!frameObj) {
6488 return false;
6489 }
6490
6491 Rooted<DebuggerFrame*> adoptedFrame(cx);
6492 if (frameObj->isOnStack(cx)) {
6493 FrameIter iter = frameObj->getFrameIter(cx);
6494 if (!dbg->observesFrame(iter)) {
6495 JS_ReportErrorASCII(cx, "Debugger.Frame's global is not a debuggee");
6496 return false;
6497 }
6498 if (!dbg->getFrame(cx, iter, &adoptedFrame)) {
6499 return false;
6500 }
6501 } else if (frameObj->isSuspendedGeneratorFrame()) {
6502 Rooted<AbstractGeneratorObject*> gen(cx, &frameObj->unwrappedGenerator());
6503 if (!dbg->observesGlobal(&gen->global())) {
6504 JS_ReportErrorASCII(cx, "Debugger.Frame's global is not a debuggee");
6505 return false;
6506 }
6507
6508 if (!dbg->getFrame(cx, gen, &adoptedFrame)) {
6509 return false;
6510 }
6511 } else {
6512 if (!dbg->getFrame(cx, &adoptedFrame)) {
6513 return false;
6514 }
6515 }
6516
6517 args.rval().setObject(*adoptedFrame);
6518 return true;
6519}
6520
6521bool Debugger::CallData::adoptSource() {
6522 if (!args.requireAtLeast(cx, "Debugger.adoptSource", 1)) {
6523 return false;
6524 }
6525
6526 RootedObject obj(cx, RequireObject(cx, args[0]));
6527 if (!obj) {
6528 return false;
6529 }
6530
6531 obj = UncheckedUnwrap(obj);
6532 if (!obj->is<DebuggerSource>()) {
6533 JS_ReportErrorASCII(cx, "Argument is not a Debugger.Source");
6534 return false;
6535 }
6536
6537 Rooted<DebuggerSource*> sourceObj(cx, &obj->as<DebuggerSource>());
6538 if (!sourceObj->getReferentRawObject()) {
6539 JS_ReportErrorASCII(cx, "Argument is Debugger.Source.prototype");
6540 return false;
6541 }
6542
6543 Rooted<DebuggerSourceReferent> referent(cx, sourceObj->getReferent());
6544
6545 DebuggerAdoptSourceMatcher matcher(cx, dbg);
6546 DebuggerSource* res = referent.match(matcher);
6547 if (!res) {
6548 return false;
6549 }
6550
6551 args.rval().setObject(*res);
6552 return true;
6553}
6554
6555bool Debugger::CallData::enableAsyncStack() {
6556 if (!args.requireAtLeast(cx, "Debugger.enableAsyncStack", 1)) {
6557 return false;
6558 }
6559 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
6560 if (!global) {
6561 return false;
6562 }
6563
6564 global->realm()->isAsyncStackCapturingEnabled = true;
6565
6566 args.rval().setUndefined();
6567 return true;
6568}
6569
6570bool Debugger::CallData::disableAsyncStack() {
6571 if (!args.requireAtLeast(cx, "Debugger.disableAsyncStack", 1)) {
6572 return false;
6573 }
6574 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
6575 if (!global) {
6576 return false;
6577 }
6578
6579 global->realm()->isAsyncStackCapturingEnabled = false;
6580
6581 args.rval().setUndefined();
6582 return true;
6583}
6584
6585bool Debugger::CallData::enableUnlimitedStacksCapturing() {
6586 if (!args.requireAtLeast(cx, "Debugger.enableUnlimitedStacksCapturing", 1)) {
6587 return false;
6588 }
6589 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
6590 if (!global) {
6591 return false;
6592 }
6593
6594 global->realm()->isUnlimitedStacksCapturingEnabled = true;
6595
6596 args.rval().setUndefined();
6597 return true;
6598}
6599
6600bool Debugger::CallData::disableUnlimitedStacksCapturing() {
6601 if (!args.requireAtLeast(cx, "Debugger.disableUnlimitedStacksCapturing", 1)) {
6602 return false;
6603 }
6604 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
6605 if (!global) {
6606 return false;
6607 }
6608
6609 global->realm()->isUnlimitedStacksCapturingEnabled = false;
6610
6611 args.rval().setUndefined();
6612 return true;
6613}
6614
6615const JSPropertySpec Debugger::properties[] = {
6616 JS_DEBUG_PSGS("onDebuggerStatement", getOnDebuggerStatement,JSPropertySpec::nativeAccessors("onDebuggerStatement", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnDebuggerStatement
>, nullptr, CallData::ToNative<&CallData::setOnDebuggerStatement
>, nullptr)
6617 setOnDebuggerStatement)JSPropertySpec::nativeAccessors("onDebuggerStatement", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnDebuggerStatement
>, nullptr, CallData::ToNative<&CallData::setOnDebuggerStatement
>, nullptr)
,
6618 JS_DEBUG_PSGS("onExceptionUnwind", getOnExceptionUnwind,JSPropertySpec::nativeAccessors("onExceptionUnwind", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnExceptionUnwind
>, nullptr, CallData::ToNative<&CallData::setOnExceptionUnwind
>, nullptr)
6619 setOnExceptionUnwind)JSPropertySpec::nativeAccessors("onExceptionUnwind", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnExceptionUnwind
>, nullptr, CallData::ToNative<&CallData::setOnExceptionUnwind
>, nullptr)
,
6620 JS_DEBUG_PSGS("onNewScript", getOnNewScript, setOnNewScript)JSPropertySpec::nativeAccessors("onNewScript", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnNewScript
>, nullptr, CallData::ToNative<&CallData::setOnNewScript
>, nullptr)
,
6621 JS_DEBUG_PSGS("onEnterFrame", getOnEnterFrame, setOnEnterFrame)JSPropertySpec::nativeAccessors("onEnterFrame", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnEnterFrame
>, nullptr, CallData::ToNative<&CallData::setOnEnterFrame
>, nullptr)
,
6622 JS_DEBUG_PSGS("onNativeCall", getOnNativeCall, setOnNativeCall)JSPropertySpec::nativeAccessors("onNativeCall", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnNativeCall
>, nullptr, CallData::ToNative<&CallData::setOnNativeCall
>, nullptr)
,
6623 JS_DEBUG_PSGS("shouldAvoidSideEffects", getShouldAvoidSideEffects,JSPropertySpec::nativeAccessors("shouldAvoidSideEffects", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getShouldAvoidSideEffects
>, nullptr, CallData::ToNative<&CallData::setShouldAvoidSideEffects
>, nullptr)
6624 setShouldAvoidSideEffects)JSPropertySpec::nativeAccessors("shouldAvoidSideEffects", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getShouldAvoidSideEffects
>, nullptr, CallData::ToNative<&CallData::setShouldAvoidSideEffects
>, nullptr)
,
6625 JS_DEBUG_PSGS("onNewGlobalObject", getOnNewGlobalObject,JSPropertySpec::nativeAccessors("onNewGlobalObject", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnNewGlobalObject
>, nullptr, CallData::ToNative<&CallData::setOnNewGlobalObject
>, nullptr)
6626 setOnNewGlobalObject)JSPropertySpec::nativeAccessors("onNewGlobalObject", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getOnNewGlobalObject
>, nullptr, CallData::ToNative<&CallData::setOnNewGlobalObject
>, nullptr)
,
6627 JS_DEBUG_PSGS("uncaughtExceptionHook", getUncaughtExceptionHook,JSPropertySpec::nativeAccessors("uncaughtExceptionHook", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getUncaughtExceptionHook
>, nullptr, CallData::ToNative<&CallData::setUncaughtExceptionHook
>, nullptr)
6628 setUncaughtExceptionHook)JSPropertySpec::nativeAccessors("uncaughtExceptionHook", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getUncaughtExceptionHook
>, nullptr, CallData::ToNative<&CallData::setUncaughtExceptionHook
>, nullptr)
,
6629 JS_DEBUG_PSGS("allowUnobservedWasm", getAllowUnobservedWasm,JSPropertySpec::nativeAccessors("allowUnobservedWasm", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getAllowUnobservedWasm
>, nullptr, CallData::ToNative<&CallData::setAllowUnobservedWasm
>, nullptr)
6630 setAllowUnobservedWasm)JSPropertySpec::nativeAccessors("allowUnobservedWasm", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getAllowUnobservedWasm
>, nullptr, CallData::ToNative<&CallData::setAllowUnobservedWasm
>, nullptr)
,
6631 JS_DEBUG_PSGS("collectCoverageInfo", getCollectCoverageInfo,JSPropertySpec::nativeAccessors("collectCoverageInfo", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getCollectCoverageInfo
>, nullptr, CallData::ToNative<&CallData::setCollectCoverageInfo
>, nullptr)
6632 setCollectCoverageInfo)JSPropertySpec::nativeAccessors("collectCoverageInfo", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getCollectCoverageInfo
>, nullptr, CallData::ToNative<&CallData::setCollectCoverageInfo
>, nullptr)
,
6633 JS_DEBUG_PSGS("exclusiveDebuggerOnEval", getExclusiveDebuggerOnEval,JSPropertySpec::nativeAccessors("exclusiveDebuggerOnEval", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getExclusiveDebuggerOnEval
>, nullptr, CallData::ToNative<&CallData::setExclusiveDebuggerOnEval
>, nullptr)
6634 setExclusiveDebuggerOnEval)JSPropertySpec::nativeAccessors("exclusiveDebuggerOnEval", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getExclusiveDebuggerOnEval
>, nullptr, CallData::ToNative<&CallData::setExclusiveDebuggerOnEval
>, nullptr)
,
6635 JS_DEBUG_PSGS("inspectNativeCallArguments", getInspectNativeCallArguments,JSPropertySpec::nativeAccessors("inspectNativeCallArguments",
CheckAccessorAttrs<0>(), CallData::ToNative<&CallData
::getInspectNativeCallArguments>, nullptr, CallData::ToNative
<&CallData::setInspectNativeCallArguments>, nullptr
)
6636 setInspectNativeCallArguments)JSPropertySpec::nativeAccessors("inspectNativeCallArguments",
CheckAccessorAttrs<0>(), CallData::ToNative<&CallData
::getInspectNativeCallArguments>, nullptr, CallData::ToNative
<&CallData::setInspectNativeCallArguments>, nullptr
)
,
6637 JS_DEBUG_PSG("memory", getMemory)JSPropertySpec::nativeAccessors("memory", CheckAccessorAttrs<
0>(), CallData::ToNative<&CallData::getMemory>, nullptr
)
,
6638 JS_STRING_SYM_PS(toStringTag, "Debugger", JSPROP_READONLY)JSPropertySpec::stringValue(::JS::SymbolCode::toStringTag, JSPROP_READONLY
, "Debugger")
,
6639 JS_PS_ENDJSPropertySpec::sentinel(),
6640};
6641
6642const JSFunctionSpec Debugger::methods[] = {
6643 JS_DEBUG_FN("addDebuggee", addDebuggee, 1){JSFunctionSpec::Name("addDebuggee"), {CallData::ToNative<
&CallData::addDebuggee>, nullptr}, 1, 0, nullptr}
,
6644 JS_DEBUG_FN("addAllGlobalsAsDebuggees", addAllGlobalsAsDebuggees, 0){JSFunctionSpec::Name("addAllGlobalsAsDebuggees"), {CallData::
ToNative<&CallData::addAllGlobalsAsDebuggees>, nullptr
}, 0, 0, nullptr}
,
6645 JS_DEBUG_FN("removeDebuggee", removeDebuggee, 1){JSFunctionSpec::Name("removeDebuggee"), {CallData::ToNative<
&CallData::removeDebuggee>, nullptr}, 1, 0, nullptr}
,
6646 JS_DEBUG_FN("removeAllDebuggees", removeAllDebuggees, 0){JSFunctionSpec::Name("removeAllDebuggees"), {CallData::ToNative
<&CallData::removeAllDebuggees>, nullptr}, 0, 0, nullptr
}
,
6647 JS_DEBUG_FN("hasDebuggee", hasDebuggee, 1){JSFunctionSpec::Name("hasDebuggee"), {CallData::ToNative<
&CallData::hasDebuggee>, nullptr}, 1, 0, nullptr}
,
6648 JS_DEBUG_FN("getDebuggees", getDebuggees, 0){JSFunctionSpec::Name("getDebuggees"), {CallData::ToNative<
&CallData::getDebuggees>, nullptr}, 0, 0, nullptr}
,
6649 JS_DEBUG_FN("getNewestFrame", getNewestFrame, 0){JSFunctionSpec::Name("getNewestFrame"), {CallData::ToNative<
&CallData::getNewestFrame>, nullptr}, 0, 0, nullptr}
,
6650 JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0){JSFunctionSpec::Name("clearAllBreakpoints"), {CallData::ToNative
<&CallData::clearAllBreakpoints>, nullptr}, 0, 0, nullptr
}
,
6651 JS_DEBUG_FN("findScripts", findScripts, 1){JSFunctionSpec::Name("findScripts"), {CallData::ToNative<
&CallData::findScripts>, nullptr}, 1, 0, nullptr}
,
6652 JS_DEBUG_FN("findSources", findSources, 1){JSFunctionSpec::Name("findSources"), {CallData::ToNative<
&CallData::findSources>, nullptr}, 1, 0, nullptr}
,
6653 JS_DEBUG_FN("findAllGlobals", findAllGlobals, 0){JSFunctionSpec::Name("findAllGlobals"), {CallData::ToNative<
&CallData::findAllGlobals>, nullptr}, 0, 0, nullptr}
,
6654 JS_DEBUG_FN("findSourceURLs", findSourceURLs, 0){JSFunctionSpec::Name("findSourceURLs"), {CallData::ToNative<
&CallData::findSourceURLs>, nullptr}, 0, 0, nullptr}
,
6655 JS_DEBUG_FN("makeGlobalObjectReference", makeGlobalObjectReference, 1){JSFunctionSpec::Name("makeGlobalObjectReference"), {CallData
::ToNative<&CallData::makeGlobalObjectReference>, nullptr
}, 1, 0, nullptr}
,
6656 JS_DEBUG_FN("adoptDebuggeeValue", adoptDebuggeeValue, 1){JSFunctionSpec::Name("adoptDebuggeeValue"), {CallData::ToNative
<&CallData::adoptDebuggeeValue>, nullptr}, 1, 0, nullptr
}
,
6657 JS_DEBUG_FN("adoptFrame", adoptFrame, 1){JSFunctionSpec::Name("adoptFrame"), {CallData::ToNative<&
CallData::adoptFrame>, nullptr}, 1, 0, nullptr}
,
6658 JS_DEBUG_FN("adoptSource", adoptSource, 1){JSFunctionSpec::Name("adoptSource"), {CallData::ToNative<
&CallData::adoptSource>, nullptr}, 1, 0, nullptr}
,
6659 JS_DEBUG_FN("enableAsyncStack", enableAsyncStack, 1){JSFunctionSpec::Name("enableAsyncStack"), {CallData::ToNative
<&CallData::enableAsyncStack>, nullptr}, 1, 0, nullptr
}
,
6660 JS_DEBUG_FN("disableAsyncStack", disableAsyncStack, 1){JSFunctionSpec::Name("disableAsyncStack"), {CallData::ToNative
<&CallData::disableAsyncStack>, nullptr}, 1, 0, nullptr
}
,
6661 JS_DEBUG_FN("enableUnlimitedStacksCapturing",{JSFunctionSpec::Name("enableUnlimitedStacksCapturing"), {CallData
::ToNative<&CallData::enableUnlimitedStacksCapturing>
, nullptr}, 1, 0, nullptr}
6662 enableUnlimitedStacksCapturing, 1){JSFunctionSpec::Name("enableUnlimitedStacksCapturing"), {CallData
::ToNative<&CallData::enableUnlimitedStacksCapturing>
, nullptr}, 1, 0, nullptr}
,
6663 JS_DEBUG_FN("disableUnlimitedStacksCapturing",{JSFunctionSpec::Name("disableUnlimitedStacksCapturing"), {CallData
::ToNative<&CallData::disableUnlimitedStacksCapturing>
, nullptr}, 1, 0, nullptr}
6664 disableUnlimitedStacksCapturing, 1){JSFunctionSpec::Name("disableUnlimitedStacksCapturing"), {CallData
::ToNative<&CallData::disableUnlimitedStacksCapturing>
, nullptr}, 1, 0, nullptr}
,
6665 JS_FS_END{JSFunctionSpec::Name(nullptr), {nullptr, nullptr}, 0, 0, nullptr
}
,
6666};
6667
6668const JSFunctionSpec Debugger::static_methods[]{
6669 JS_FN("isCompilableUnit", Debugger::isCompilableUnit, 1, 0){JSFunctionSpec::Name("isCompilableUnit"), {Debugger::isCompilableUnit
, nullptr}, 1, 0, nullptr}
,
6670 JS_FS_END{JSFunctionSpec::Name(nullptr), {nullptr, nullptr}, 0, 0, nullptr
}
,
6671};
6672
6673DebuggerScript* Debugger::newDebuggerScript(
6674 JSContext* cx, Handle<DebuggerScriptReferent> referent) {
6675 cx->check(object.get());
6676
6677 RootedObject proto(
6678 cx, &object->getReservedSlot(JSSLOT_DEBUG_SCRIPT_PROTO).toObject());
6679 MOZ_ASSERT(proto)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(proto)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(proto))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("proto", "./../../../../js/src/debugger/Debugger.cpp"
, 6679); AnnotateMozCrashReason("MOZ_ASSERT" "(" "proto" ")")
; do { MOZ_CrashSequence(__null, 6679); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6680 Rooted<NativeObject*> debugger(cx, object);
6681
6682 return DebuggerScript::create(cx, proto, referent, debugger);
6683}
6684
6685template <typename ReferentType, typename Map>
6686typename Map::WrapperType* Debugger::wrapVariantReferent(
6687 JSContext* cx, Map& map,
6688 Handle<typename Map::WrapperType::ReferentVariant> referent) {
6689 cx->check(object);
6690
6691 Handle<ReferentType*> untaggedReferent =
6692 referent.template as<ReferentType*>();
6693 MOZ_ASSERT(cx->compartment() != untaggedReferent->compartment())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(cx->compartment() != untaggedReferent->compartment
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(cx->compartment() != untaggedReferent->compartment
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("cx->compartment() != untaggedReferent->compartment()"
, "./../../../../js/src/debugger/Debugger.cpp", 6693); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "cx->compartment() != untaggedReferent->compartment()"
")"); do { MOZ_CrashSequence(__null, 6693); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6694
6695 DependentAddPtr<Map> p(cx, map, untaggedReferent);
6696 if (!p) {
6697 typename Map::WrapperType* wrapper = newVariantWrapper(cx, referent);
6698 if (!wrapper) {
6699 return nullptr;
6700 }
6701
6702 if (!p.add(cx, map, untaggedReferent, wrapper)) {
6703 // We need to destroy the edge to the referent, to avoid trying to trace
6704 // it during untimely collections.
6705 wrapper->clearReferent();
6706 return nullptr;
6707 }
6708 }
6709
6710 return &p->value()->template as<typename Map::WrapperType>();
6711}
6712
6713DebuggerScript* Debugger::wrapVariantReferent(
6714 JSContext* cx, Handle<DebuggerScriptReferent> referent) {
6715 if (referent.is<BaseScript*>()) {
6716 return wrapVariantReferent<BaseScript>(cx, scripts, referent);
6717 }
6718
6719 return wrapVariantReferent<WasmInstanceObject>(cx, wasmInstanceScripts,
6720 referent);
6721}
6722
6723DebuggerScript* Debugger::wrapScript(JSContext* cx,
6724 Handle<BaseScript*> script) {
6725 Rooted<DebuggerScriptReferent> referent(cx,
6726 DebuggerScriptReferent(script.get()));
6727 return wrapVariantReferent(cx, referent);
6728}
6729
6730DebuggerScript* Debugger::wrapWasmScript(
6731 JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
6732 Rooted<DebuggerScriptReferent> referent(cx, wasmInstance.get());
6733 return wrapVariantReferent(cx, referent);
6734}
6735
6736DebuggerSource* Debugger::newDebuggerSource(
6737 JSContext* cx, Handle<DebuggerSourceReferent> referent) {
6738 cx->check(object.get());
6739
6740 RootedObject proto(
6741 cx, &object->getReservedSlot(JSSLOT_DEBUG_SOURCE_PROTO).toObject());
6742 MOZ_ASSERT(proto)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(proto)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(proto))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("proto", "./../../../../js/src/debugger/Debugger.cpp"
, 6742); AnnotateMozCrashReason("MOZ_ASSERT" "(" "proto" ")")
; do { MOZ_CrashSequence(__null, 6742); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6743 Rooted<NativeObject*> debugger(cx, object);
6744 return DebuggerSource::create(cx, proto, referent, debugger);
6745}
6746
6747DebuggerSource* Debugger::wrapVariantReferent(
6748 JSContext* cx, Handle<DebuggerSourceReferent> referent) {
6749 DebuggerSource* obj;
6750 if (referent.is<ScriptSourceObject*>()) {
6751 obj = wrapVariantReferent<ScriptSourceObject>(cx, sources, referent);
6752 } else {
6753 obj = wrapVariantReferent<WasmInstanceObject>(cx, wasmInstanceSources,
6754 referent);
6755 }
6756 MOZ_ASSERT_IF(obj, obj->getReferent() == referent)do { if (obj) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(obj->getReferent() == referent)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(obj->getReferent() == referent))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("obj->getReferent() == referent"
, "./../../../../js/src/debugger/Debugger.cpp", 6756); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "obj->getReferent() == referent" ")"); do
{ MOZ_CrashSequence(__null, 6756); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
6757 return obj;
6758}
6759
6760DebuggerSource* Debugger::wrapSource(JSContext* cx,
6761 Handle<ScriptSourceObject*> source) {
6762 Rooted<DebuggerSourceReferent> referent(cx, source.get());
6763 return wrapVariantReferent(cx, referent);
6764}
6765
6766DebuggerSource* Debugger::wrapWasmSource(
6767 JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
6768 Rooted<DebuggerSourceReferent> referent(cx, wasmInstance.get());
6769 return wrapVariantReferent(cx, referent);
6770}
6771
6772bool Debugger::observesFrame(AbstractFramePtr frame) const {
6773 if (frame.isWasmDebugFrame()) {
6774 return observesWasm(frame.wasmInstance());
6775 }
6776
6777 return observesScript(frame.script());
6778}
6779
6780bool Debugger::observesFrame(const FrameIter& iter) const {
6781 // Skip frames not yet fully initialized during their prologue.
6782 if (iter.isInterp() && iter.isFunctionFrame()) {
6783 const Value& thisVal = iter.interpFrame()->thisArgument();
6784 if (thisVal.isMagic() && thisVal.whyMagic() == JS_IS_CONSTRUCTING) {
6785 return false;
6786 }
6787 }
6788 if (iter.isWasm()) {
6789 // Skip frame of wasm instances we cannot observe.
6790 if (!iter.wasmDebugEnabled()) {
6791 return false;
6792 }
6793 return observesWasm(iter.wasmInstance());
6794 }
6795 return observesScript(iter.script());
6796}
6797
6798bool Debugger::observesScript(JSScript* script) const {
6799 // Don't ever observe self-hosted scripts: the Debugger API can break
6800 // self-hosted invariants.
6801 return observesGlobal(&script->global()) && !script->selfHosted();
6802}
6803
6804bool Debugger::observesWasm(wasm::Instance* instance) const {
6805 if (!instance->debugEnabled()) {
6806 return false;
6807 }
6808 return observesGlobal(&instance->object()->global());
6809}
6810
6811/* static */
6812void Debugger::replaceFrameGuts(JSContext* cx, AbstractFramePtr from,
6813 AbstractFramePtr to, ScriptFrameIter& iter) {
6814 MOZ_ASSERT(from != to)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(from != to)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(from != to))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("from != to", "./../../../../js/src/debugger/Debugger.cpp"
, 6814); AnnotateMozCrashReason("MOZ_ASSERT" "(" "from != to"
")"); do { MOZ_CrashSequence(__null, 6814); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6815 AutoEnterOOMUnsafeRegion unsafe;
6816
6817 // Rekey missingScopes to maintain Debugger.Environment identity and
6818 // forward liveScopes to point to the new frame.
6819 DebugEnvironments::forwardLiveFrame(cx, from, to);
6820
6821 // Forward live Debugger.Frame objects.
6822 Rooted<DebuggerFrameVector> frames(cx);
6823 if (!getDebuggerFrames(from, &frames)) {
6824 unsafe.crash("replaceFrameGuts");
6825 }
6826
6827 for (size_t i = 0; i < frames.length(); i++) {
6828 Handle<DebuggerFrame*> frameobj = frames[i];
6829 Debugger* dbg = frameobj->owner();
6830
6831 // Update frame object's ScriptFrameIter::data pointer.
6832 if (!frameobj->replaceFrameIterData(cx, iter)) {
6833 unsafe.crash("replaceFrameGuts");
6834 }
6835
6836 // Add the frame object with |to| as key.
6837 if (!dbg->frames.putNew(to, frameobj)) {
6838 unsafe.crash("replaceFrameGuts");
6839 }
6840
6841 // Remove the old frame entry after all fallible operations are completed
6842 // so that an OOM will be able to clean up properly.
6843 dbg->frames.remove(from);
6844 }
6845
6846 MOZ_ASSERT(!DebugAPI::inFrameMaps(from))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!DebugAPI::inFrameMaps(from))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!DebugAPI::inFrameMaps(from)
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!DebugAPI::inFrameMaps(from)", "./../../../../js/src/debugger/Debugger.cpp"
, 6846); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!DebugAPI::inFrameMaps(from)"
")"); do { MOZ_CrashSequence(__null, 6846); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6847 MOZ_ASSERT_IF(!frames.empty(), DebugAPI::inFrameMaps(to))do { if (!frames.empty()) { do { static_assert( mozilla::detail
::AssertionConditionType<decltype(DebugAPI::inFrameMaps(to
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(DebugAPI::inFrameMaps(to)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("DebugAPI::inFrameMaps(to)", "./../../../../js/src/debugger/Debugger.cpp"
, 6847); AnnotateMozCrashReason("MOZ_ASSERT" "(" "DebugAPI::inFrameMaps(to)"
")"); do { MOZ_CrashSequence(__null, 6847); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6848}
6849
6850/* static */
6851bool DebugAPI::inFrameMaps(AbstractFramePtr frame) {
6852 bool foundAny = false;
6853 JS::AutoAssertNoGC nogc;
6854 Debugger::forEachOnStackDebuggerFrame(
6855 frame, nogc,
6856 [&](Debugger*, DebuggerFrame* frameobj) { foundAny = true; });
6857 return foundAny;
6858}
6859
6860/* static */
6861void Debugger::suspendGeneratorDebuggerFrames(JSContext* cx,
6862 AbstractFramePtr frame) {
6863 JS::GCContext* gcx = cx->gcContext();
6864 JS::AutoAssertNoGC nogc;
6865 forEachOnStackDebuggerFrame(
6866 frame, nogc, [&](Debugger* dbg, DebuggerFrame* dbgFrame) {
6867 dbg->frames.remove(frame);
6868
6869#if DEBUG1
6870 MOZ_ASSERT(dbgFrame->hasGeneratorInfo())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbgFrame->hasGeneratorInfo())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dbgFrame->hasGeneratorInfo
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("dbgFrame->hasGeneratorInfo()", "./../../../../js/src/debugger/Debugger.cpp"
, 6870); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbgFrame->hasGeneratorInfo()"
")"); do { MOZ_CrashSequence(__null, 6870); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6871 AbstractGeneratorObject& genObj = dbgFrame->unwrappedGenerator();
6872 GeneratorWeakMap::Ptr p = dbg->generatorFrames.lookup(&genObj);
6873 MOZ_ASSERT(p)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(p)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(p))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("p", "./../../../../js/src/debugger/Debugger.cpp", 6873); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "p" ")"); do { MOZ_CrashSequence(__null, 6873
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
6874 MOZ_ASSERT(p->value() == dbgFrame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(p->value() == dbgFrame)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(p->value() == dbgFrame)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("p->value() == dbgFrame"
, "./../../../../js/src/debugger/Debugger.cpp", 6874); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "p->value() == dbgFrame" ")"); do { MOZ_CrashSequence
(__null, 6874); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6875#endif
6876
6877 dbgFrame->suspendGeneratorFrame(gcx);
6878 });
6879}
6880
6881/* static */
6882void Debugger::terminateDebuggerFrames(JSContext* cx, AbstractFramePtr frame) {
6883 JS::GCContext* gcx = cx->gcContext();
6884
6885 JS::AutoAssertNoGC nogc;
6886 forEachOnStackOrSuspendedGeneratorDebuggerFrame(
6887 cx, frame, nogc, [&](Debugger* dbg, DebuggerFrame* dbgFrame) {
6888 Debugger::terminateDebuggerFrame(gcx, dbg, dbgFrame, frame);
6889 });
6890
6891 // If this is an eval frame, then from the debugger's perspective the
6892 // script is about to be destroyed. Remove any breakpoints in it.
6893 if (frame.isEvalFrame()) {
6894 RootedScript script(cx, frame.script());
6895 DebugScript::clearBreakpointsIn(cx->gcContext(), script, nullptr, nullptr);
6896 }
6897}
6898
6899/* static */
6900void Debugger::terminateDebuggerFrame(
6901 JS::GCContext* gcx, Debugger* dbg, DebuggerFrame* dbgFrame,
6902 AbstractFramePtr frame, FrameMap::ModIterator* maybeFramesIter,
6903 GeneratorWeakMap::ModIterator* maybeGeneratorFramesIter) {
6904 // If we were not passed the frame, either we are destroying a frame early
6905 // on before it was inserted into the "frames" list, or else we are
6906 // terminating a frame from "generatorFrames" and the "frames" entries will
6907 // be cleaned up later on with a second call to this function.
6908 MOZ_ASSERT_IF(!frame, !maybeFramesIter)do { if (!frame) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(!maybeFramesIter)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!maybeFramesIter))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!maybeFramesIter"
, "./../../../../js/src/debugger/Debugger.cpp", 6908); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!maybeFramesIter" ")"); do { MOZ_CrashSequence
(__null, 6908); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false); } } while (false)
;
6909 MOZ_ASSERT_IF(!frame, dbgFrame->hasGeneratorInfo())do { if (!frame) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(dbgFrame->hasGeneratorInfo())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(dbgFrame->hasGeneratorInfo
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("dbgFrame->hasGeneratorInfo()", "./../../../../js/src/debugger/Debugger.cpp"
, 6909); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbgFrame->hasGeneratorInfo()"
")"); do { MOZ_CrashSequence(__null, 6909); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6910 MOZ_ASSERT_IF(!dbgFrame->hasGeneratorInfo(), !maybeGeneratorFramesIter)do { if (!dbgFrame->hasGeneratorInfo()) { do { static_assert
( mozilla::detail::AssertionConditionType<decltype(!maybeGeneratorFramesIter
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!maybeGeneratorFramesIter))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("!maybeGeneratorFramesIter", "./../../../../js/src/debugger/Debugger.cpp"
, 6910); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!maybeGeneratorFramesIter"
")"); do { MOZ_CrashSequence(__null, 6910); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
6911
6912 if (frame) {
6913 if (maybeFramesIter) {
6914 maybeFramesIter->remove();
6915 } else {
6916 dbg->frames.remove(frame);
6917 }
6918#ifdef ENABLE_WASM_JSPI1
6919 dbg->wasmContFrames.eraseIf(
6920 [&frame](const AbstractFramePtr& fp) { return fp == frame; });
6921#endif
6922 }
6923
6924 if (dbgFrame->hasGeneratorInfo()) {
6925 if (maybeGeneratorFramesIter) {
6926 maybeGeneratorFramesIter->remove();
6927 } else {
6928 dbg->generatorFrames.remove(&dbgFrame->unwrappedGenerator());
6929 }
6930 }
6931
6932 dbgFrame->terminate(gcx, frame);
6933}
6934
6935DebuggerDebuggeeLink* Debugger::getDebuggeeLink() {
6936 return &object->getReservedSlot(JSSLOT_DEBUG_DEBUGGEE_LINK)
6937 .toObject()
6938 .as<DebuggerDebuggeeLink>();
6939}
6940
6941void DebuggerDebuggeeLink::setLinkSlot(Debugger& dbg) {
6942 setReservedSlot(DEBUGGER_LINK_SLOT, ObjectValue(*dbg.toJSObject()));
6943}
6944
6945void DebuggerDebuggeeLink::clearLinkSlot() {
6946 setReservedSlot(DEBUGGER_LINK_SLOT, UndefinedValue());
6947}
6948
6949const JSClass DebuggerDebuggeeLink::class_ = {
6950 "DebuggerDebuggeeLink",
6951 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
6952};
6953
6954/* static */
6955void DebugAPI::handleBaselineOsr(JSContext* cx, InterpreterFrame* from,
6956 jit::BaselineFrame* to) {
6957 ScriptFrameIter iter(cx);
6958 MOZ_ASSERT(iter.abstractFramePtr() == to)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(iter.abstractFramePtr() == to)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(iter.abstractFramePtr() == to
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"iter.abstractFramePtr() == to", "./../../../../js/src/debugger/Debugger.cpp"
, 6958); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.abstractFramePtr() == to"
")"); do { MOZ_CrashSequence(__null, 6958); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6959 Debugger::replaceFrameGuts(cx, from, to, iter);
6960}
6961
6962/* static */
6963void DebugAPI::handleIonBailout(JSContext* cx, jit::RematerializedFrame* from,
6964 jit::BaselineFrame* to) {
6965 // When we return to a bailed-out Ion real frame, we must update all
6966 // Debugger.Frames that refer to its inline frames. However, since we
6967 // can't pop individual inline frames off the stack (we can only pop the
6968 // real frame that contains them all, as a unit), we cannot assume that
6969 // the frame we're dealing with is the top frame. Advance the iterator
6970 // across any inlined frames younger than |to|, the baseline frame
6971 // reconstructed during bailout from the Ion frame corresponding to
6972 // |from|.
6973 ScriptFrameIter iter(cx);
6974 while (iter.abstractFramePtr() != to) {
6975 ++iter;
6976 }
6977 Debugger::replaceFrameGuts(cx, from, to, iter);
6978}
6979
6980/* static */
6981void DebugAPI::handleUnrecoverableIonBailoutError(
6982 JSContext* cx, jit::RematerializedFrame* frame) {
6983 // Ion bailout can fail due to overrecursion. In such cases we cannot
6984 // honor any further Debugger hooks on the frame, and need to ensure that
6985 // its Debugger.Frame entry is cleaned up.
6986 Debugger::terminateDebuggerFrames(cx, frame);
6987}
6988
6989/*** JS::dbg::Builder *******************************************************/
6990
6991Builder::Builder(JSContext* cx, js::Debugger* debugger)
6992 : debuggerObject(cx, debugger->toJSObject().get()), debugger(debugger) {}
6993
6994#if DEBUG1
6995void Builder::assertBuilt(JSObject* obj) {
6996 // We can't use assertSameCompartment here, because that is always keyed to
6997 // some JSContext's current compartment, whereas BuiltThings can be
6998 // constructed and assigned to without respect to any particular context;
6999 // the only constraint is that they should be in their debugger's compartment.
7000 MOZ_ASSERT_IF(obj, debuggerObject->compartment() == obj->compartment())do { if (obj) { do { static_assert( mozilla::detail::AssertionConditionType
<decltype(debuggerObject->compartment() == obj->compartment
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(debuggerObject->compartment() == obj->compartment
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("debuggerObject->compartment() == obj->compartment()",
"./../../../../js/src/debugger/Debugger.cpp", 7000); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "debuggerObject->compartment() == obj->compartment()"
")"); do { MOZ_CrashSequence(__null, 7000); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
7001}
7002#endif
7003
7004bool Builder::Object::definePropertyToTrusted(JSContext* cx, const char* name,
7005 JS::MutableHandleValue trusted) {
7006 // We should have checked for false Objects before calling this.
7007 MOZ_ASSERT(value)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(value)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(value))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("value", "./../../../../js/src/debugger/Debugger.cpp"
, 7007); AnnotateMozCrashReason("MOZ_ASSERT" "(" "value" ")")
; do { MOZ_CrashSequence(__null, 7007); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7008
7009 JSAtom* atom = Atomize(cx, name, strlen(name));
7010 if (!atom) {
7011 return false;
7012 }
7013 RootedId id(cx, AtomToId(atom));
7014
7015 return DefineDataProperty(cx, value, id, trusted);
7016}
7017
7018bool Builder::Object::defineProperty(JSContext* cx, const char* name,
7019 JS::HandleValue propval_) {
7020 AutoRealm ar(cx, debuggerObject());
7021
7022 RootedValue propval(cx, propval_);
7023 if (!debugger()->wrapDebuggeeValue(cx, &propval)) {
7024 return false;
7025 }
7026
7027 return definePropertyToTrusted(cx, name, &propval);
7028}
7029
7030bool Builder::Object::defineProperty(JSContext* cx, const char* name,
7031 JS::HandleObject propval_) {
7032 RootedValue propval(cx, ObjectOrNullValue(propval_));
7033 return defineProperty(cx, name, propval);
7034}
7035
7036bool Builder::Object::defineProperty(JSContext* cx, const char* name,
7037 Builder::Object& propval_) {
7038 AutoRealm ar(cx, debuggerObject());
7039
7040 RootedValue propval(cx, ObjectOrNullValue(propval_.value));
7041 return definePropertyToTrusted(cx, name, &propval);
7042}
7043
7044Builder::Object Builder::newObject(JSContext* cx) {
7045 AutoRealm ar(cx, debuggerObject);
7046
7047 Rooted<PlainObject*> obj(cx, NewPlainObject(cx));
7048
7049 // If the allocation failed, this will return a false Object, as the spec
7050 // promises.
7051 return Object(cx, *this, obj);
7052}
7053
7054/*** Glue *******************************************************************/
7055
7056extern JS_PUBLIC_API bool JS_DefineDebuggerObject(JSContext* cx,
7057 HandleObject obj) {
7058 Rooted<NativeObject*> debugCtor(cx), debugProto(cx), frameProto(cx),
7059 scriptProto(cx), sourceProto(cx), objectProto(cx), envProto(cx),
7060 memoryProto(cx), privateNameProto(cx);
7061 RootedObject debuggeeWouldRunProto(cx);
7062 RootedValue debuggeeWouldRunCtor(cx);
7063 Handle<GlobalObject*> global = obj.as<GlobalObject>();
7064
7065 debugProto = InitClass(cx, global, &DebuggerPrototypeObject::class_, nullptr,
7066 "Debugger", Debugger::construct, 1,
7067 Debugger::properties, Debugger::methods, nullptr,
7068 Debugger::static_methods, debugCtor.address());
7069 if (!debugProto) {
7070 return false;
7071 }
7072
7073 frameProto = DebuggerFrame::initClass(cx, global, debugCtor);
7074 if (!frameProto) {
7075 return false;
7076 }
7077
7078 scriptProto = DebuggerScript::initClass(cx, global, debugCtor);
7079 if (!scriptProto) {
7080 return false;
7081 }
7082
7083 sourceProto = DebuggerSource::initClass(cx, global, debugCtor);
7084 if (!sourceProto) {
7085 return false;
7086 }
7087
7088 objectProto = DebuggerObject::initClass(cx, global, debugCtor);
7089 if (!objectProto) {
7090 return false;
7091 }
7092
7093 envProto = DebuggerEnvironment::initClass(cx, global, debugCtor);
7094 if (!envProto) {
7095 return false;
7096 }
7097
7098 memoryProto = InitClass(
7099 cx, debugCtor, nullptr, nullptr, "Memory", DebuggerMemory::construct, 0,
7100 DebuggerMemory::properties, DebuggerMemory::methods, nullptr, nullptr);
7101 if (!memoryProto) {
7102 return false;
7103 }
7104
7105 privateNameProto = DebuggerPrivateName::initClass(cx, global, debugCtor);
7106 if (!privateNameProto) {
7107 return false;
7108 }
7109
7110 debuggeeWouldRunProto = GlobalObject::getOrCreateCustomErrorPrototype(
7111 cx, global, JSEXN_DEBUGGEEWOULDRUN);
7112 if (!debuggeeWouldRunProto) {
7113 return false;
7114 }
7115 debuggeeWouldRunCtor =
7116 ObjectValue(global->getConstructor(JSProto_DebuggeeWouldRun));
7117 RootedId debuggeeWouldRunId(
7118 cx, NameToId(ClassName(JSProto_DebuggeeWouldRun, cx)));
7119 if (!DefineDataProperty(cx, debugCtor, debuggeeWouldRunId,
7120 debuggeeWouldRunCtor, 0)) {
7121 return false;
7122 }
7123
7124 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_FRAME_PROTO,
7125 ObjectValue(*frameProto));
7126 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_OBJECT_PROTO,
7127 ObjectValue(*objectProto));
7128 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SCRIPT_PROTO,
7129 ObjectValue(*scriptProto));
7130 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_SOURCE_PROTO,
7131 ObjectValue(*sourceProto));
7132 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_ENV_PROTO,
7133 ObjectValue(*envProto));
7134 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_MEMORY_PROTO,
7135 ObjectValue(*memoryProto));
7136 debugProto->setReservedSlot(Debugger::JSSLOT_DEBUG_PRIVATE_NAME_PROTO,
7137 ObjectValue(*privateNameProto));
7138 return true;
7139}
7140
7141extern JS_PUBLIC_API const char* JS_GetLastOOMStackTrace(JSContext* cx) {
7142 return cx->getOOMStackTrace();
7143}
7144
7145JS_PUBLIC_API bool JS::dbg::IsDebugger(JSObject& obj) {
7146 /* We only care about debugger objects, so CheckedUnwrapStatic is OK. */
7147 JSObject* unwrapped = CheckedUnwrapStatic(&obj);
7148 if (!unwrapped || !unwrapped->is<DebuggerInstanceObject>()) {
7149 return false;
7150 }
7151 MOZ_ASSERT(js::Debugger::fromJSObject(unwrapped))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(js::Debugger::fromJSObject(unwrapped))>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(js::Debugger::fromJSObject(unwrapped)))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("js::Debugger::fromJSObject(unwrapped)"
, "./../../../../js/src/debugger/Debugger.cpp", 7151); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "js::Debugger::fromJSObject(unwrapped)" ")"
); do { MOZ_CrashSequence(__null, 7151); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7152 return true;
7153}
7154
7155JS_PUBLIC_API bool JS::dbg::GetDebuggeeGlobals(
7156 JSContext* cx, JSObject& dbgObj, MutableHandleObjectVector vector) {
7157 MOZ_ASSERT(IsDebugger(dbgObj))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsDebugger(dbgObj))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsDebugger(dbgObj)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("IsDebugger(dbgObj)"
, "./../../../../js/src/debugger/Debugger.cpp", 7157); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsDebugger(dbgObj)" ")"); do { MOZ_CrashSequence
(__null, 7157); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7158 /* Since we know we have a debugger object, CheckedUnwrapStatic is fine. */
7159 js::Debugger* dbg = js::Debugger::fromJSObject(CheckedUnwrapStatic(&dbgObj));
7160
7161 if (!vector.reserve(vector.length() + dbg->debuggees.count())) {
7162 JS_ReportOutOfMemory(cx);
7163 return false;
7164 }
7165
7166 for (auto iter = dbg->allDebuggees(); !iter.done(); iter.next()) {
7167 vector.infallibleAppend(static_cast<JSObject*>(iter.get()));
7168 }
7169
7170 return true;
7171}
7172
7173#ifdef DEBUG1
7174/* static */
7175bool Debugger::isDebuggerCrossCompartmentEdge(JSObject* obj,
7176 const gc::Cell* target) {
7177 MOZ_ASSERT(target)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(target)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(target))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("target", "./../../../../js/src/debugger/Debugger.cpp"
, 7177); AnnotateMozCrashReason("MOZ_ASSERT" "(" "target" ")"
); do { MOZ_CrashSequence(__null, 7177); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7178
7179 const gc::Cell* referent = nullptr;
7180 if (obj->is<DebuggerScript>()) {
7181 referent = obj->as<DebuggerScript>().getReferentCell();
7182 } else if (obj->is<DebuggerSource>()) {
7183 referent = obj->as<DebuggerSource>().getReferentRawObject();
7184 } else if (obj->is<DebuggerObject>()) {
7185 referent = obj->as<DebuggerObject>().referent();
7186 } else if (obj->is<DebuggerEnvironment>()) {
7187 referent = obj->as<DebuggerEnvironment>().referent();
7188 }
7189
7190 return referent == target;
7191}
7192
7193static void CheckDebuggeeThingRealm(Realm* realm, bool invisibleOk) {
7194 MOZ_ASSERT_IF(!invisibleOk, !realm->creationOptions().invisibleToDebugger())do { if (!invisibleOk) { do { static_assert( mozilla::detail::
AssertionConditionType<decltype(!realm->creationOptions
().invisibleToDebugger())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!realm->creationOptions()
.invisibleToDebugger()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!realm->creationOptions().invisibleToDebugger()", "./../../../../js/src/debugger/Debugger.cpp"
, 7194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!realm->creationOptions().invisibleToDebugger()"
")"); do { MOZ_CrashSequence(__null, 7194); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
7195}
7196
7197void js::CheckDebuggeeThing(BaseScript* script, bool invisibleOk) {
7198 CheckDebuggeeThingRealm(script->realm(), invisibleOk);
7199}
7200
7201void js::CheckDebuggeeThing(JSObject* obj, bool invisibleOk) {
7202 if (Realm* realm = JS::GetObjectRealmOrNull(obj)) {
7203 CheckDebuggeeThingRealm(realm, invisibleOk);
7204 }
7205}
7206#endif // DEBUG
7207
7208/*** JS::dbg::GarbageCollectionEvent ****************************************/
7209
7210namespace JS {
7211namespace dbg {
7212
7213/* static */ GarbageCollectionEvent::Ptr GarbageCollectionEvent::Create(
7214 JSRuntime* rt, ::js::gcstats::Statistics& stats, uint64_t gcNumber) {
7215 auto data = MakeUnique<GarbageCollectionEvent>(gcNumber);
7216 if (!data) {
7217 return nullptr;
7218 }
7219
7220 data->nonincrementalReason = stats.nonincrementalReason();
7221
7222 for (auto& slice : stats.slices()) {
7223 if (!data->reason) {
7224 // There is only one GC reason for the whole cycle, but for legacy
7225 // reasons this data is stored and replicated on each slice. Each
7226 // slice used to have its own GCReason, but now they are all the
7227 // same.
7228 data->reason = ExplainGCReason(slice.reason);
7229 MOZ_ASSERT(data->reason)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(data->reason)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(data->reason))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("data->reason"
, "./../../../../js/src/debugger/Debugger.cpp", 7229); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "data->reason" ")"); do { MOZ_CrashSequence
(__null, 7229); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7230 }
7231
7232 if (!data->collections.growBy(1)) {
7233 return nullptr;
7234 }
7235
7236 data->collections.back().startTimestamp = slice.start;
7237 data->collections.back().endTimestamp = slice.end;
7238 }
7239
7240 return data;
7241}
7242
7243static bool DefineStringProperty(JSContext* cx, HandleObject obj,
7244 PropertyName* propName, const char* strVal) {
7245 RootedValue val(cx, UndefinedValue());
7246 if (strVal) {
7247 JSAtom* atomized = Atomize(cx, strVal, strlen(strVal));
7248 if (!atomized) {
7249 return false;
7250 }
7251 val = StringValue(atomized);
7252 }
7253 return DefineDataProperty(cx, obj, propName, val);
7254}
7255
7256JSObject* GarbageCollectionEvent::toJSObject(JSContext* cx) const {
7257 RootedObject obj(cx, NewPlainObject(cx));
7258 RootedValue gcCycleNumberVal(cx, NumberValue(majorGCNumber_));
7259 if (!obj ||
7260 !DefineStringProperty(cx, obj, cx->names().nonincrementalReason,
7261 nonincrementalReason) ||
7262 !DefineStringProperty(cx, obj, cx->names().reason, reason) ||
7263 !DefineDataProperty(cx, obj, cx->names().gcCycleNumber,
7264 gcCycleNumberVal)) {
7265 return nullptr;
7266 }
7267
7268 Rooted<ArrayObject*> slicesArray(cx, NewDenseEmptyArray(cx));
7269 if (!slicesArray) {
7270 return nullptr;
7271 }
7272
7273 TimeStamp originTime = TimeStamp::ProcessCreation();
7274
7275 size_t idx = 0;
7276 for (auto range = collections.all(); !range.empty(); range.popFront()) {
7277 Rooted<PlainObject*> collectionObj(cx, NewPlainObject(cx));
7278 if (!collectionObj) {
7279 return nullptr;
7280 }
7281
7282 RootedValue start(cx), end(cx);
7283 start = NumberValue(
7284 (range.front().startTimestamp - originTime).ToMilliseconds());
7285 end =
7286 NumberValue((range.front().endTimestamp - originTime).ToMilliseconds());
7287 if (!DefineDataProperty(cx, collectionObj, cx->names().startTimestamp,
7288 start) ||
7289 !DefineDataProperty(cx, collectionObj, cx->names().endTimestamp, end)) {
7290 return nullptr;
7291 }
7292
7293 RootedValue collectionVal(cx, ObjectValue(*collectionObj));
7294 if (!DefineDataElement(cx, slicesArray, idx++, collectionVal)) {
7295 return nullptr;
7296 }
7297 }
7298
7299 RootedValue slicesValue(cx, ObjectValue(*slicesArray));
7300 if (!DefineDataProperty(cx, obj, cx->names().collections, slicesValue)) {
7301 return nullptr;
7302 }
7303
7304 return obj;
7305}
7306
7307JS_PUBLIC_API bool FireOnGarbageCollectionHookRequired(JSContext* cx) {
7308 AutoCheckCannotGC noGC;
7309
7310 for (auto& dbg : cx->runtime()->onGarbageCollectionWatchers()) {
7311 MOZ_ASSERT(dbg.getHook(Debugger::OnGarbageCollection))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg.getHook(Debugger::OnGarbageCollection))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(dbg.getHook(Debugger::OnGarbageCollection)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("dbg.getHook(Debugger::OnGarbageCollection)"
, "./../../../../js/src/debugger/Debugger.cpp", 7311); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbg.getHook(Debugger::OnGarbageCollection)"
")"); do { MOZ_CrashSequence(__null, 7311); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7312 if (dbg.observedGC(cx->runtime()->gc.majorGCCount())) {
7313 return true;
7314 }
7315 }
7316
7317 return false;
7318}
7319
7320JS_PUBLIC_API bool FireOnGarbageCollectionHook(
7321 JSContext* cx, JS::dbg::GarbageCollectionEvent::Ptr&& data) {
7322 RootedObjectVector triggered(cx);
7323
7324 {
7325 // We had better not GC (and potentially get a dangling Debugger
7326 // pointer) while finding all Debuggers observing a debuggee that
7327 // participated in this GC.
7328 AutoCheckCannotGC noGC;
7329
7330 for (auto& dbg : cx->runtime()->onGarbageCollectionWatchers()) {
7331 MOZ_ASSERT(dbg.getHook(Debugger::OnGarbageCollection))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(dbg.getHook(Debugger::OnGarbageCollection))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(dbg.getHook(Debugger::OnGarbageCollection)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("dbg.getHook(Debugger::OnGarbageCollection)"
, "./../../../../js/src/debugger/Debugger.cpp", 7331); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "dbg.getHook(Debugger::OnGarbageCollection)"
")"); do { MOZ_CrashSequence(__null, 7331); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7332 if (dbg.observedGC(data->majorGCNumber())) {
7333 if (!triggered.append(dbg.object)) {
7334 JS_ReportOutOfMemory(cx);
7335 return false;
7336 }
7337 }
7338 }
7339 }
7340
7341 // Preserve the debuggee's microtask event queue while we run the hooks, so
7342 // the debugger's microtask checkpoints don't run from the debuggee's
7343 // microtasks, and vice versa.
7344 JS::AutoDebuggerJobQueueInterruption adjqi;
7345 if (!adjqi.init(cx)) {
7346 cx->clearPendingException();
7347 return false;
7348 }
7349
7350 for (; !triggered.empty(); triggered.popBack()) {
7351 Debugger* dbg = Debugger::fromJSObject(triggered.back());
7352 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
7353
7354 if (dbg->getHook(Debugger::OnGarbageCollection)) {
7355 (void)dbg->enterDebuggerHook(cx, [&]() -> bool {
7356 return dbg->fireOnGarbageCollectionHook(cx, data);
7357 });
7358 MOZ_ASSERT(!cx->isExceptionPending())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!cx->isExceptionPending())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!cx->isExceptionPending()
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"!cx->isExceptionPending()", "./../../../../js/src/debugger/Debugger.cpp"
, 7358); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { MOZ_CrashSequence(__null, 7358); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7359 adjqi.runJobs();
7360 }
7361 }
7362
7363 return true;
7364}
7365
7366bool ShouldAvoidSideEffects(JSContext* cx) {
7367 return DebugAPI::shouldAvoidSideEffects(cx);
7368}
7369
7370} // namespace dbg
7371} // namespace JS