Bug Summary

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