Bug Summary

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