Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/js/src/debugger/Debugger.cpp
Warning:line 4080, 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-18/lib/clang/18 -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/js/src/js-confdefs.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 -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/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/x86_64-linux-gnu/c++/13 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../include/c++/13/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/13/../../../../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 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -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-05-16-034744-15991-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/ThreadLocal.h" // for ThreadLocal
16#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
17#include "mozilla/UniquePtr.h" // for UniquePtr
18#include "mozilla/Variant.h" // for AsVariant, AsVariantTemporary
19#include "mozilla/Vector.h" // for Vector, Vector<>::ConstRange
20
21#include <algorithm> // for std::find, std::max
22#include <functional> // for function
23#include <stddef.h> // for size_t
24#include <stdint.h> // for uint32_t, uint64_t, int32_t
25#include <string.h> // for strlen, strcmp
26#include <type_traits> // for std::underlying_type_t
27#include <utility> // for std::move
28
29#include "jsapi.h" // for CallArgs, CallArgsFromVp
30#include "jstypes.h" // for JS_PUBLIC_API
31
32#include "builtin/Array.h" // for NewDenseFullyAllocatedArray
33#include "debugger/DebugAPI.h" // for ResumeMode, DebugAPI
34#include "debugger/DebuggerMemory.h" // for DebuggerMemory
35#include "debugger/DebugScript.h" // for DebugScript
36#include "debugger/Environment.h" // for DebuggerEnvironment
37#include "debugger/Frame.h" // for DebuggerFrame
38#include "debugger/NoExecute.h" // for EnterDebuggeeNoExecute
39#include "debugger/Object.h" // for DebuggerObject
40#include "debugger/Script.h" // for DebuggerScript
41#include "debugger/Source.h" // for DebuggerSource
42#include "frontend/CompilationStencil.h" // for CompilationStencil
43#include "frontend/FrontendContext.h" // for AutoReportFrontendContext
44#include "frontend/Parser.h" // for Parser
45#include "gc/GC.h" // for IterateScripts
46#include "gc/GCContext.h" // for JS::GCContext
47#include "gc/GCMarker.h" // for GCMarker
48#include "gc/GCRuntime.h" // for GCRuntime, AutoEnterIteration
49#include "gc/HashUtil.h" // for DependentAddPtr
50#include "gc/Marking.h" // for IsAboutToBeFinalized
51#include "gc/PublicIterators.h" // for RealmsIter, CompartmentsIter
52#include "gc/Statistics.h" // for Statistics::SliceData
53#include "gc/Tracer.h" // for TraceEdge
54#include "gc/Zone.h" // for Zone
55#include "gc/ZoneAllocator.h" // for ZoneAllocPolicy
56#include "jit/BaselineDebugModeOSR.h" // for RecompileOnStackBaselineScriptsForDebugMode
57#include "jit/BaselineJIT.h" // for FinishDiscardBaselineScript
58#include "jit/Invalidation.h" // for RecompileInfoVector
59#include "jit/JitContext.h" // for JitContext
60#include "jit/JitOptions.h" // for fuzzingSafe
61#include "jit/JitScript.h" // for JitScript
62#include "jit/JSJitFrameIter.h" // for InlineFrameIterator
63#include "jit/RematerializedFrame.h" // for RematerializedFrame
64#include "js/CallAndConstruct.h" // JS::IsCallable
65#include "js/Conversions.h" // for ToBoolean, ToUint32
66#include "js/Debug.h" // for Builder::Object, Builder
67#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
68#include "js/GCAPI.h" // for GarbageCollectionEvent
69#include "js/GCVariant.h" // for GCVariant
70#include "js/HeapAPI.h" // for ExposeObjectToActiveJS
71#include "js/Promise.h" // for AutoDebuggerJobQueueInterruption
72#include "js/PropertyAndElement.h" // for JS_GetProperty
73#include "js/Proxy.h" // for PropertyDescriptor
74#include "js/SourceText.h" // for SourceOwnership, SourceText
75#include "js/StableStringChars.h" // for AutoStableStringChars
76#include "js/UbiNode.h" // for Node, RootList, Edge
77#include "js/UbiNodeBreadthFirst.h" // for BreadthFirst
78#include "js/Wrapper.h" // for CheckedUnwrapStatic
79#include "util/Identifier.h" // for IsIdentifier
80#include "util/Text.h" // for DuplicateString, js_strlen
81#include "vm/ArrayObject.h" // for ArrayObject
82#include "vm/AsyncFunction.h" // for AsyncFunctionGeneratorObject
83#include "vm/AsyncIteration.h" // for AsyncGeneratorObject
84#include "vm/BytecodeUtil.h" // for JSDVG_IGNORE_STACK
85#include "vm/Compartment.h" // for CrossCompartmentKey
86#include "vm/EnvironmentObject.h" // for IsSyntacticEnvironment
87#include "vm/ErrorReporting.h" // for ReportErrorToGlobal
88#include "vm/GeneratorObject.h" // for AbstractGeneratorObject
89#include "vm/GlobalObject.h" // for GlobalObject
90#include "vm/Interpreter.h" // for Call, ReportIsNotFunction
91#include "vm/Iteration.h" // for CreateIterResultObject
92#include "vm/JSAtomUtils.h" // for Atomize, AtomizeUTF8Chars, AtomIsMarked, AtomToId, ClassName
93#include "vm/JSContext.h" // for JSContext
94#include "vm/JSFunction.h" // for JSFunction
95#include "vm/JSObject.h" // for JSObject, RequireObject,
96#include "vm/JSScript.h" // for BaseScript, ScriptSourceObject
97#include "vm/ObjectOperations.h" // for DefineDataProperty
98#include "vm/PlainObject.h" // for js::PlainObject
99#include "vm/PromiseObject.h" // for js::PromiseObject
100#include "vm/ProxyObject.h" // for ProxyObject, JSObject::is
101#include "vm/Realm.h" // for AutoRealm, Realm
102#include "vm/Runtime.h" // for ReportOutOfMemory, JSRuntime
103#include "vm/SavedFrame.h" // for SavedFrame
104#include "vm/SavedStacks.h" // for SavedStacks
105#include "vm/Scope.h" // for Scope
106#include "vm/StringType.h" // for JSString, PropertyName
107#include "vm/WrapperObject.h" // for CrossCompartmentWrapperObject
108#include "wasm/WasmDebug.h" // for DebugState
109#include "wasm/WasmInstance.h" // for Instance
110#include "wasm/WasmJS.h" // for WasmInstanceObject
111#include "wasm/WasmRealm.h" // for Realm
112#include "wasm/WasmTypeDecls.h" // for WasmInstanceObjectVector
113
114#include "debugger/DebugAPI-inl.h"
115#include "debugger/Environment-inl.h" // for DebuggerEnvironment::owner
116#include "debugger/Frame-inl.h" // for DebuggerFrame::hasGeneratorInfo
117#include "debugger/Object-inl.h" // for DebuggerObject::owner and isInstance.
118#include "debugger/Script-inl.h" // for DebuggerScript::getReferent
119#include "gc/GC-inl.h" // for ZoneCellIter
120#include "gc/Marking-inl.h" // for MaybeForwarded
121#include "gc/StableCellHasher-inl.h"
122#include "gc/WeakMap-inl.h" // for DebuggerWeakMap::trace
123#include "vm/Compartment-inl.h" // for Compartment::wrap
124#include "vm/GeckoProfiler-inl.h" // for AutoSuppressProfilerSampling
125#include "vm/JSAtomUtils-inl.h" // for AtomToId, ValueToId
126#include "vm/JSContext-inl.h" // for JSContext::check
127#include "vm/JSObject-inl.h" // for JSObject::isCallable, NewTenuredObjectWithGivenProto
128#include "vm/JSScript-inl.h" // for JSScript::isDebuggee, JSScript
129#include "vm/NativeObject-inl.h" // for NativeObject::ensureDenseInitializedLength
130#include "vm/ObjectOperations-inl.h" // for GetProperty, HasProperty
131#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
132#include "vm/Stack-inl.h" // for AbstractFramePtr::script
133
134namespace js {
135
136namespace frontend {
137class FullParseHandler;
138}
139
140namespace gc {
141struct Cell;
142}
143
144namespace jit {
145class BaselineFrame;
146}
147
148} /* namespace js */
149
150using namespace js;
151
152using JS::AutoStableStringChars;
153using JS::CompileOptions;
154using JS::SourceOwnership;
155using JS::SourceText;
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::TimeDuration;
165using mozilla::TimeStamp;
166
167/*** Utils ******************************************************************/
168
169bool js::IsInterpretedNonSelfHostedFunction(JSFunction* fun) {
170 return fun->isInterpreted() && !fun->isSelfHostedBuiltin();
171}
172
173JSScript* js::GetOrCreateFunctionScript(JSContext* cx, HandleFunction fun) {
174 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"
, 174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsInterpretedNonSelfHostedFunction(fun)"
")"); do { *((volatile int*)__null) = 174; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
175 AutoRealm ar(cx, fun);
176 return JSFunction::getOrCreateScript(cx, fun);
177}
178
179ArrayObject* js::GetFunctionParameterNamesArray(JSContext* cx,
180 HandleFunction fun) {
181 RootedValueVector names(cx);
182
183 // The default value for each argument is |undefined|.
184 if (!names.growBy(fun->nargs())) {
185 return nullptr;
186 }
187
188 if (IsInterpretedNonSelfHostedFunction(fun) && fun->nargs() > 0) {
189 RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
190 if (!script) {
191 return nullptr;
192 }
193
194 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"
, 194); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fun->nargs() == script->numArgs()"
")"); do { *((volatile int*)__null) = 194; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
195
196 PositionalFormalParameterIter fi(script);
197 for (size_t i = 0; i < fun->nargs(); i++, fi++) {
198 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"
, 198); AnnotateMozCrashReason("MOZ_ASSERT" "(" "fi.argumentSlot() == i"
")"); do { *((volatile int*)__null) = 198; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
199 if (JSAtom* atom = fi.name()) {
200 // Skip any internal, non-identifier names, like for example ".args".
201 if (IsIdentifier(atom)) {
202 cx->markAtom(atom);
203 names[i].setString(atom);
204 }
205 }
206 }
207 }
208
209 return NewDenseCopiedArray(cx, names.length(), names.begin());
210}
211
212bool js::ValueToIdentifier(JSContext* cx, HandleValue v, MutableHandleId id) {
213 if (!ToPropertyKey(cx, v, id)) {
214 return false;
215 }
216 if (!id.isAtom() || !IsIdentifier(id.toAtom())) {
217 RootedValue val(cx, v);
218 ReportValueError(cx, JSMSG_UNEXPECTED_TYPE, JSDVG_SEARCH_STACK1, val,
219 nullptr, "not an identifier");
220 return false;
221 }
222 return true;
223}
224
225class js::AutoRestoreRealmDebugMode {
226 Realm* realm_;
227 unsigned bits_;
228
229 public:
230 explicit AutoRestoreRealmDebugMode(Realm* realm)
231 : realm_(realm), bits_(realm->debugModeBits_) {
232 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"
, 232); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realm_" ")")
; do { *((volatile int*)__null) = 232; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
233 }
234
235 ~AutoRestoreRealmDebugMode() {
236 if (realm_) {
237 realm_->debugModeBits_ = bits_;
238 }
239 }
240
241 void release() { realm_ = nullptr; }
242};
243
244/* static */
245bool DebugAPI::slowPathCheckNoExecute(JSContext* cx, HandleScript script) {
246 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"
, 246); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cx->realm()->isDebuggee()"
")"); do { *((volatile int*)__null) = 246; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
247 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"
, 247); AnnotateMozCrashReason("MOZ_ASSERT" "(" "cx->noExecuteDebuggerTop"
")"); do { *((volatile int*)__null) = 247; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
248 return EnterDebuggeeNoExecute::reportIfFoundInStack(cx, script);
249}
250
251static void PropagateForcedReturn(JSContext* cx, AbstractFramePtr frame,
252 HandleValue rval) {
253 // The Debugger's hooks may return a value that affects the completion
254 // value of the given frame. For example, a hook may return `{ return: 42 }`
255 // to terminate the frame and return `42` as the final frame result.
256 // To accomplish this, the debugger treats these return values as if
257 // execution of the JS function has been terminated without a pending
258 // exception, but with a special flag. When the error is handled by the
259 // interpreter or JIT, the special flag and the error state will be cleared
260 // and execution will continue from the end of the frame.
261 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"
, 261); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { *((volatile int*)__null) = 261; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
262 cx->setPropagatingForcedReturn();
263 frame.setReturnValue(rval);
264}
265
266[[nodiscard]] static bool AdjustGeneratorResumptionValue(JSContext* cx,
267 AbstractFramePtr frame,
268 ResumeMode& resumeMode,
269 MutableHandleValue vp);
270
271[[nodiscard]] static bool ApplyFrameResumeMode(JSContext* cx,
272 AbstractFramePtr frame,
273 ResumeMode resumeMode,
274 HandleValue rv,
275 Handle<SavedFrame*> exnStack) {
276 RootedValue rval(cx, rv);
277
278 // The value passed in here is unwrapped and has no guarantees about what
279 // compartment it may be associated with, so we explicitly wrap it into the
280 // debuggee compartment.
281 if (!cx->compartment()->wrap(cx, &rval)) {
282 return false;
283 }
284
285 if (!AdjustGeneratorResumptionValue(cx, frame, resumeMode, &rval)) {
286 return false;
287 }
288
289 switch (resumeMode) {
290 case ResumeMode::Continue:
291 break;
292
293 case ResumeMode::Throw:
294 // If we have a stack from the original throw, use it instead of
295 // associating the throw with the current execution point.
296 if (exnStack) {
297 cx->setPendingException(rval, exnStack);
298 } else {
299 cx->setPendingException(rval, ShouldCaptureStack::Always);
300 }
301 return false;
302
303 case ResumeMode::Terminate:
304 cx->clearPendingException();
305 return false;
306
307 case ResumeMode::Return:
308 PropagateForcedReturn(cx, frame, rval);
309 return false;
310
311 default:
312 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"
, 312); AnnotateMozCrashReason("MOZ_CRASH(" "bad Debugger::onEnterFrame resume mode"
")"); do { *((volatile int*)__null) = 312; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
313 }
314
315 return true;
316}
317static bool ApplyFrameResumeMode(JSContext* cx, AbstractFramePtr frame,
318 ResumeMode resumeMode, HandleValue rval) {
319 Rooted<SavedFrame*> nullStack(cx);
320 return ApplyFrameResumeMode(cx, frame, resumeMode, rval, nullStack);
321}
322
323bool js::ValueToStableChars(JSContext* cx, const char* fnname,
324 HandleValue value,
325 AutoStableStringChars& stableChars) {
326 if (!value.isString()) {
327 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
328 JSMSG_NOT_EXPECTED_TYPE, fnname, "string",
329 InformalValueTypeName(value));
330 return false;
331 }
332 Rooted<JSLinearString*> linear(cx, value.toString()->ensureLinear(cx));
333 if (!linear) {
334 return false;
335 }
336 if (!stableChars.initTwoByte(cx, linear)) {
337 return false;
338 }
339 return true;
340}
341
342bool EvalOptions::setFilename(JSContext* cx, const char* filename) {
343 JS::UniqueChars copy;
344 if (filename) {
345 copy = DuplicateString(cx, filename);
346 if (!copy) {
347 return false;
348 }
349 }
350
351 filename_ = std::move(copy);
352 return true;
353}
354
355bool js::ParseEvalOptions(JSContext* cx, HandleValue value,
356 EvalOptions& options) {
357 if (!value.isObject()) {
358 return true;
359 }
360
361 RootedObject opts(cx, &value.toObject());
362
363 RootedValue v(cx);
364 if (!JS_GetProperty(cx, opts, "url", &v)) {
365 return false;
366 }
367 if (!v.isUndefined()) {
368 RootedString url_str(cx, ToString<CanGC>(cx, v));
369 if (!url_str) {
370 return false;
371 }
372 UniqueChars url_bytes = JS_EncodeStringToUTF8(cx, url_str);
373 if (!url_bytes) {
374 return false;
375 }
376 if (!options.setFilename(cx, url_bytes.get())) {
377 return false;
378 }
379 }
380
381 if (!JS_GetProperty(cx, opts, "lineNumber", &v)) {
382 return false;
383 }
384 if (!v.isUndefined()) {
385 uint32_t lineno;
386 if (!ToUint32(cx, v, &lineno)) {
387 return false;
388 }
389 options.setLineno(lineno);
390 }
391
392 if (!JS_GetProperty(cx, opts, "hideFromDebugger", &v)) {
393 return false;
394 }
395 options.setHideFromDebugger(ToBoolean(v));
396
397 if (options.kind() == EvalOptions::EnvKind::GlobalWithExtraOuterBindings) {
398 if (!JS_GetProperty(cx, opts, "useInnerBindings", &v)) {
399 return false;
400 }
401 if (ToBoolean(v)) {
402 options.setUseInnerBindings();
403 }
404 }
405
406 return true;
407}
408
409/*** Breakpoints ************************************************************/
410
411bool BreakpointSite::isEmpty() const { return breakpoints.isEmpty(); }
412
413void BreakpointSite::trace(JSTracer* trc) {
414 for (auto p = breakpoints.begin(); p; p++) {
415 p->trace(trc);
416 }
417}
418
419void BreakpointSite::finalize(JS::GCContext* gcx) {
420 while (!breakpoints.isEmpty()) {
421 breakpoints.begin()->delete_(gcx);
422 }
423}
424
425Breakpoint* BreakpointSite::firstBreakpoint() const {
426 if (isEmpty()) {
427 return nullptr;
428 }
429 return &(*breakpoints.begin());
430}
431
432bool BreakpointSite::hasBreakpoint(Breakpoint* toFind) {
433 const BreakpointList::Iterator bp(toFind);
434 for (auto p = breakpoints.begin(); p; p++) {
435 if (p == bp) {
436 return true;
437 }
438 }
439 return false;
440}
441
442Breakpoint::Breakpoint(Debugger* debugger, HandleObject wrappedDebugger,
443 BreakpointSite* site, HandleObject handler)
444 : debugger(debugger),
445 wrappedDebugger(wrappedDebugger),
446 site(site),
447 handler(handler) {
448 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"
, 448); AnnotateMozCrashReason("MOZ_ASSERT" "(" "UncheckedUnwrap(wrappedDebugger) == debugger->object"
")"); do { *((volatile int*)__null) = 448; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
449 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"
, 449); AnnotateMozCrashReason("MOZ_ASSERT" "(" "handler->compartment() == wrappedDebugger->compartment()"
")"); do { *((volatile int*)__null) = 449; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
450
451 debugger->breakpoints.pushBack(this);
452 site->breakpoints.pushBack(this);
453}
454
455void Breakpoint::trace(JSTracer* trc) {
456 TraceEdge(trc, &wrappedDebugger, "breakpoint owner");
457 TraceEdge(trc, &handler, "breakpoint handler");
458}
459
460void Breakpoint::delete_(JS::GCContext* gcx) {
461 debugger->breakpoints.remove(this);
462 site->breakpoints.remove(this);
463 gc::Cell* cell = site->owningCell();
464 gcx->delete_(cell, this, MemoryUse::Breakpoint);
465}
466
467void Breakpoint::remove(JS::GCContext* gcx) {
468 BreakpointSite* savedSite = site;
469 delete_(gcx);
470
471 savedSite->destroyIfEmpty(gcx);
472}
473
474Breakpoint* Breakpoint::nextInDebugger() { return debuggerLink.mNext; }
475
476Breakpoint* Breakpoint::nextInSite() { return siteLink.mNext; }
477
478JSBreakpointSite::JSBreakpointSite(JSScript* script, jsbytecode* pc)
479 : script(script), pc(pc) {
480 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"
, 480); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!DebugAPI::hasBreakpointsAt(script, pc)"
")"); do { *((volatile int*)__null) = 480; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
481}
482
483void JSBreakpointSite::remove(JS::GCContext* gcx) {
484 DebugScript::destroyBreakpointSite(gcx, script, pc);
485}
486
487void JSBreakpointSite::trace(JSTracer* trc) {
488 BreakpointSite::trace(trc);
489 TraceEdge(trc, &script, "breakpoint script");
490}
491
492void JSBreakpointSite::delete_(JS::GCContext* gcx) {
493 BreakpointSite::finalize(gcx);
494
495 gcx->delete_(script, this, MemoryUse::BreakpointSite);
496}
497
498gc::Cell* JSBreakpointSite::owningCell() { return script; }
499
500Realm* JSBreakpointSite::realm() const { return script->realm(); }
501
502WasmBreakpointSite::WasmBreakpointSite(WasmInstanceObject* instanceObject_,
503 uint32_t offset_)
504 : instanceObject(instanceObject_), offset(offset_) {
505 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"
, 505); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instanceObject_"
")"); do { *((volatile int*)__null) = 505; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
506 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"
, 506); AnnotateMozCrashReason("MOZ_ASSERT" "(" "instanceObject_->instance().debugEnabled()"
")"); do { *((volatile int*)__null) = 506; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
507}
508
509void WasmBreakpointSite::trace(JSTracer* trc) {
510 BreakpointSite::trace(trc);
511 TraceEdge(trc, &instanceObject, "breakpoint Wasm instance");
512}
513
514void WasmBreakpointSite::remove(JS::GCContext* gcx) {
515 instanceObject->instance().destroyBreakpointSite(gcx, offset);
516}
517
518void WasmBreakpointSite::delete_(JS::GCContext* gcx) {
519 BreakpointSite::finalize(gcx);
520
521 gcx->delete_(instanceObject, this, MemoryUse::BreakpointSite);
522}
523
524gc::Cell* WasmBreakpointSite::owningCell() { return instanceObject; }
525
526Realm* WasmBreakpointSite::realm() const { return instanceObject->realm(); }
527
528/*** Debugger hook dispatch *************************************************/
529
530Debugger::Debugger(JSContext* cx, NativeObject* dbg)
531 : object(dbg),
532 debuggees(cx->zone()),
533 uncaughtExceptionHook(nullptr),
534 allowUnobservedAsmJS(false),
535 allowUnobservedWasm(false),
536 exclusiveDebuggerOnEval(false),
537 inspectNativeCallArguments(false),
538 collectCoverageInfo(false),
539 shouldAvoidSideEffects(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 return Debugger::dispatchResumptionHook(
907 cx, frame,
908 [frame](Debugger* dbg) -> bool {
909 return dbg->observesFrame(frame) && dbg->observesEnterFrame();
910 },
911 [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
912 -> bool { return dbg->fireEnterFrame(cx, resumeMode, vp); });
913}
914
915/* static */
916bool DebugAPI::slowPathOnResumeFrame(JSContext* cx, AbstractFramePtr frame) {
917 // Don't count on this method to be called every time a generator is
918 // resumed! This is called only if the frame's debuggee bit is set,
919 // i.e. the script has breakpoints or the frame is stepping.
920 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"
, 920); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.isGeneratorFrame()"
")"); do { *((volatile int*)__null) = 920; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
921 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"
, 921); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.isDebuggee()"
")"); do { *((volatile int*)__null) = 921; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
922
923 Rooted<AbstractGeneratorObject*> genObj(
924 cx, GetGeneratorObjectForFrame(cx, frame));
925 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"
, 925); AnnotateMozCrashReason("MOZ_ASSERT" "(" "genObj" ")")
; do { *((volatile int*)__null) = 925; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
926
927 // If there is an OOM, we mark all of the Debugger.Frame objects terminated
928 // because we want to ensure that none of the frames are in a partially
929 // initialized state where they are in "generatorFrames" but not "frames".
930 auto terminateDebuggerFramesGuard = MakeScopeExit([&] {
931 Debugger::terminateDebuggerFrames(cx, frame);
932
933 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"
, 933); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!DebugAPI::inFrameMaps(frame)"
")"); do { *((volatile int*)__null) = 933; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
934 });
935
936 // For each debugger, if there is an existing Debugger.Frame object for the
937 // resumed `frame`, update it with the new frame pointer and make sure the
938 // frame is observable.
939 FrameIter iter(cx);
940 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"
, 940); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.abstractFramePtr() == frame"
")"); do { *((volatile int*)__null) = 940; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
941 {
942 JS::AutoAssertNoGC nogc;
943 for (Realm::DebuggerVectorEntry& entry :
944 frame.global()->getDebuggers(nogc)) {
945 Debugger* dbg = entry.dbg;
946 if (Debugger::GeneratorWeakMap::Ptr generatorEntry =
947 dbg->generatorFrames.lookup(genObj)) {
948 DebuggerFrame* frameObj = generatorEntry->value();
949 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"
, 949); AnnotateMozCrashReason("MOZ_ASSERT" "(" "&frameObj->unwrappedGenerator() == genObj"
")"); do { *((volatile int*)__null) = 949; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
950 if (!dbg->frames.putNew(frame, frameObj)) {
951 ReportOutOfMemory(cx);
952 return false;
953 }
954 if (!frameObj->resume(iter)) {
955 return false;
956 }
957 }
958 }
959 }
960
961 terminateDebuggerFramesGuard.release();
962
963 return slowPathOnEnterFrame(cx, frame);
964}
965
966/* static */
967NativeResumeMode DebugAPI::slowPathOnNativeCall(JSContext* cx,
968 const CallArgs& args,
969 CallReason reason) {
970 if (!cx->realm()->debuggerObservesNativeCall()) {
971 return NativeResumeMode::Continue;
972 }
973
974 DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
975 return dbg->getHook(Debugger::OnNativeCall);
976 });
977
978 if (!debuggerList.init(cx)) {
979 return NativeResumeMode::Abort;
980 }
981
982 if (debuggerList.empty()) {
983 return NativeResumeMode::Continue;
984 }
985
986 // The onNativeCall hook is fired when self hosted functions are called,
987 // and any other self hosted function or C++ native that is directly called
988 // by the self hosted function is considered to be part of the same
989 // native call, except for the following 4 cases:
990 //
991 // * callContentFunction and constructContentFunction,
992 // which uses CallReason::CallContent
993 // * Function.prototype.call and Function.prototype.apply,
994 // which uses CallReason::FunCall
995 // * Getter call which uses CallReason::Getter
996 // * Setter call which uses CallReason::Setter
997 //
998 // We check this only after checking that debuggerList has items in order
999 // to avoid unnecessary calls to cx->currentScript(), which can be expensive
1000 // when the top frame is in jitcode.
1001 JSScript* script = cx->currentScript();
1002 if (script && script->selfHosted() && reason != CallReason::CallContent &&
1003 reason != CallReason::FunCall && reason != CallReason::Getter &&
1004 reason != CallReason::Setter) {
1005 return NativeResumeMode::Continue;
1006 }
1007
1008 RootedValue rval(cx);
1009 ResumeMode resumeMode = ResumeMode::Continue;
1010 bool result = debuggerList.dispatchHook(cx, [&](Debugger* dbg) -> bool {
1011 return dbg->fireNativeCall(cx, args, reason, resumeMode, &rval);
1012 });
1013 if (!result) {
1014 return NativeResumeMode::Abort;
1015 }
1016
1017 // Hook must follow normal native function conventions and not return
1018 // primitive values.
1019 if (resumeMode == ResumeMode::Return) {
1020 if (args.isConstructing() && !rval.isObject()) {
1021 JS_ReportErrorASCII(
1022 cx, "onNativeCall hook must return an object for constructor call");
1023 return NativeResumeMode::Abort;
1024 }
1025 }
1026
1027 // The value is not in any particular compartment, so it needs to be
1028 // explicitly wrapped into the debuggee compartment.
1029 if (!cx->compartment()->wrap(cx, &rval)) {
1030 return NativeResumeMode::Abort;
1031 }
1032
1033 switch (resumeMode) {
1034 case ResumeMode::Continue:
1035 break;
1036
1037 case ResumeMode::Throw:
1038 cx->setPendingException(rval, ShouldCaptureStack::Always);
1039 return NativeResumeMode::Abort;
1040
1041 case ResumeMode::Terminate:
1042 cx->clearPendingException();
1043 return NativeResumeMode::Abort;
1044
1045 case ResumeMode::Return:
1046 args.rval().set(rval);
1047 return NativeResumeMode::Override;
1048 }
1049
1050 return NativeResumeMode::Continue;
1051}
1052
1053/* static */
1054bool DebugAPI::slowPathShouldAvoidSideEffects(JSContext* cx) {
1055 return DebuggerExists(
1056 cx->global(), [=](Debugger* dbg) { return dbg->shouldAvoidSideEffects; });
1057}
1058
1059/*
1060 * RAII class to mark a generator as "running" temporarily while running
1061 * debugger code.
1062 *
1063 * When Debugger::slowPathOnLeaveFrame is called for a frame that is yielding
1064 * or awaiting, its generator is in the "suspended" state. Letting script
1065 * observe this state, with the generator on stack yet also reenterable, would
1066 * be bad, so we mark it running while we fire events.
1067 */
1068class MOZ_RAII AutoSetGeneratorRunning {
1069 int32_t resumeIndex_;
1070 AsyncGeneratorObject::State asyncGenState_;
1071 Rooted<AbstractGeneratorObject*> genObj_;
1072
1073 public:
1074 AutoSetGeneratorRunning(JSContext* cx,
1075 Handle<AbstractGeneratorObject*> genObj)
1076 : resumeIndex_(0),
1077 asyncGenState_(static_cast<AsyncGeneratorObject::State>(0)),
1078 genObj_(cx, genObj) {
1079 if (genObj) {
1080 if (!genObj->isClosed() && !genObj->isBeforeInitialYield() &&
1081 genObj->isSuspended()) {
1082 // Yielding or awaiting.
1083 resumeIndex_ = genObj->resumeIndex();
1084 genObj->setRunning();
1085
1086 // Async generators have additionally bookkeeping which must be
1087 // adjusted when switching over to the running state.
1088 if (genObj->is<AsyncGeneratorObject>()) {
1089 auto* generator = &genObj->as<AsyncGeneratorObject>();
1090 asyncGenState_ = generator->state();
1091 generator->setExecuting();
1092 }
1093 } else {
1094 // Returning or throwing. The generator is already closed, if
1095 // it was ever exposed at all.
1096 genObj_ = nullptr;
1097 }
1098 }
1099 }
1100
1101 ~AutoSetGeneratorRunning() {
1102 if (genObj_) {
1103 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"
, 1103); AnnotateMozCrashReason("MOZ_ASSERT" "(" "genObj_->isRunning()"
")"); do { *((volatile int*)__null) = 1103; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1104 genObj_->setResumeIndex(resumeIndex_);
1105 if (genObj_->is<AsyncGeneratorObject>()) {
1106 genObj_->as<AsyncGeneratorObject>().setState(asyncGenState_);
1107 }
1108 }
1109 }
1110};
1111
1112/*
1113 * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
1114 * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
1115 * |cx->fp()|'s return value, and return a new success value.
1116 */
1117/* static */
1118bool DebugAPI::slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame,
1119 const jsbytecode* pc, bool frameOk) {
1120 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"
, 1120); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pc" ")"); do
{ *((volatile int*)__null) = 1120; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1121
1122 mozilla::DebugOnly<Handle<GlobalObject*>> debuggeeGlobal = cx->global();
1123
1124 // These are updated below, but consulted by the cleanup code we register now,
1125 // so declare them here, initialized to quiescent values.
1126 Rooted<Completion> completion(cx);
1127 bool success = false;
1128
1129 auto frameMapsGuard = MakeScopeExit([&] {
1130 // Clean up all Debugger.Frame instances on exit. On suspending, pass the
1131 // flag that says to leave those frames `.live`. Note that if the completion
1132 // is a suspension but success is false, the generator gets closed, not
1133 // suspended.
1134 if (success && completion.get().suspending()) {
1135 Debugger::suspendGeneratorDebuggerFrames(cx, frame);
1136 } else {
1137 Debugger::terminateDebuggerFrames(cx, frame);
1138 }
1139 });
1140
1141 // The onPop handler and associated clean up logic should not run multiple
1142 // times on the same frame. If slowPathOnLeaveFrame has already been
1143 // called, the frame will not be present in the Debugger frame maps.
1144 Rooted<Debugger::DebuggerFrameVector> frames(cx);
1145 if (!Debugger::getDebuggerFrames(frame, &frames)) {
1146 // There is at least one match Debugger.Frame we failed to process, so drop
1147 // the pending exception and raise an out-of-memory instead.
1148 if (!frameOk) {
1149 cx->clearPendingException();
1150 }
1151 ReportOutOfMemory(cx);
1152 return false;
1153 }
1154 if (frames.empty()) {
1155 return frameOk;
1156 }
1157
1158 // Convert current exception state into a Completion and clear exception off
1159 // of the JSContext.
1160 completion = Completion::fromJSFramePop(cx, frame, pc, frameOk);
1161
1162 ResumeMode resumeMode = ResumeMode::Continue;
1163 RootedValue rval(cx);
1164
1165 {
1166 // Preserve the debuggee's microtask event queue while we run the hooks, so
1167 // the debugger's microtask checkpoints don't run from the debuggee's
1168 // microtasks, and vice versa.
1169 JS::AutoDebuggerJobQueueInterruption adjqi;
1170 if (!adjqi.init(cx)) {
1171 return false;
1172 }
1173
1174 // This path can be hit via unwinding the stack due to over-recursion or
1175 // OOM. In those cases, don't fire the frames' onPop handlers, because
1176 // invoking JS will only trigger the same condition. See
1177 // slowPathOnExceptionUnwind.
1178 if (!cx->isThrowingOverRecursed() && !cx->isThrowingOutOfMemory()) {
1179 Rooted<AbstractGeneratorObject*> genObj(
1180 cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
1181 : nullptr);
1182
1183 // For each Debugger.Frame, fire its onPop handler, if any.
1184 for (size_t i = 0; i < frames.length(); i++) {
1185 Handle<DebuggerFrame*> frameobj = frames[i];
1186 Debugger* dbg = frameobj->owner();
1187 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
1188
1189 // Removing a global from a Debugger's debuggee set kills all of that
1190 // Debugger's D.Fs in that global. This means that one D.F's onPop can
1191 // kill the next D.F. So we have to check whether frameobj is still "on
1192 // the stack".
1193 if (frameobj->isOnStack() && frameobj->onPopHandler()) {
1194 OnPopHandler* handler = frameobj->onPopHandler();
1195
1196 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
1197 ResumeMode nextResumeMode = ResumeMode::Continue;
1198 RootedValue nextValue(cx);
1199
1200 // Call the onPop handler.
1201 bool success;
1202 {
1203 // Mark the generator as running, to prevent reentrance.
1204 //
1205 // At certain points in a generator's lifetime,
1206 // GetGeneratorObjectForFrame can return null even when the
1207 // generator exists, but at those points the generator has not yet
1208 // been exposed to JavaScript, so reentrance isn't possible
1209 // anyway. So there's no harm done if this has no effect in that
1210 // case.
1211 AutoSetGeneratorRunning asgr(cx, genObj);
1212 success = handler->onPop(cx, frameobj, completion, nextResumeMode,
1213 &nextValue);
1214 }
1215
1216 return dbg->processParsedHandlerResult(cx, frame, pc, success,
1217 nextResumeMode, nextValue,
1218 resumeMode, &rval);
1219 });
1220 adjqi.runJobs();
1221
1222 if (!result) {
1223 return false;
1224 }
1225
1226 // At this point, we are back in the debuggee compartment, and
1227 // any error has been wrapped up as a completion value.
1228 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"
, 1228); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { *((volatile int*)__null) = 1228; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1229 }
1230 }
1231 }
1232 }
1233
1234 completion.get().updateFromHookResult(resumeMode, rval);
1235
1236 // Now that we've run all the handlers, extract the final resumption mode. */
1237 ResumeMode completionResumeMode;
1238 RootedValue completionValue(cx);
1239 Rooted<SavedFrame*> completionStack(cx);
1240 completion.get().toResumeMode(completionResumeMode, &completionValue,
1241 &completionStack);
1242
1243 // If we are returning the original value used to create the completion, then
1244 // we don't want to treat the resumption value as a Return completion, because
1245 // that would cause us to apply AdjustGeneratorResumptionValue to the
1246 // already-adjusted value that the generator actually returned.
1247 if (resumeMode == ResumeMode::Continue &&
1248 completionResumeMode == ResumeMode::Return) {
1249 completionResumeMode = ResumeMode::Continue;
1250 }
1251
1252 if (!ApplyFrameResumeMode(cx, frame, completionResumeMode, completionValue,
1253 completionStack)) {
1254 if (!cx->isPropagatingForcedReturn()) {
1255 // If this is an exception or termination, we just propagate that along.
1256 return false;
1257 }
1258
1259 // Since we are leaving the frame here, we can convert a forced return
1260 // into a normal return right away.
1261 cx->clearPropagatingForcedReturn();
1262 }
1263 success = true;
1264 return true;
1265}
1266
1267/* static */
1268bool DebugAPI::slowPathOnNewGenerator(JSContext* cx, AbstractFramePtr frame,
1269 Handle<AbstractGeneratorObject*> genObj) {
1270 // This is called from JSOp::Generator, after default parameter expressions
1271 // are evaluated and well after onEnterFrame, so Debugger.Frame objects for
1272 // `frame` may already have been exposed to debugger code. The
1273 // AbstractGeneratorObject for this generator call, though, has just been
1274 // created. It must be associated with any existing Debugger.Frames.
1275
1276 // Initializing frames with their associated generator is critical to the
1277 // functionality of the debugger, so if there is an OOM, we want to
1278 // cleanly terminate all of the frames.
1279 auto terminateDebuggerFramesGuard =
1280 MakeScopeExit([&] { Debugger::terminateDebuggerFrames(cx, frame); });
1281
1282 bool ok = true;
1283 gc::AutoSuppressGC nogc(cx);
1284 Debugger::forEachOnStackDebuggerFrame(
1285 frame, nogc, [&](Debugger* dbg, DebuggerFrame* frameObjPtr) {
1286 if (!ok) {
1287 return;
1288 }
1289
1290 Rooted<DebuggerFrame*> frameObj(cx, frameObjPtr);
1291
1292 AutoRealm ar(cx, frameObj);
1293
1294 if (!DebuggerFrame::setGeneratorInfo(cx, frameObj, genObj)) {
1295 // This leaves `genObj` and `frameObj` unassociated. It's OK
1296 // because we won't pause again with this generator on the stack:
1297 // the caller will immediately discard `genObj` and unwind `frame`.
1298 ok = false;
1299 return;
1300 }
1301
1302 DependentAddPtr<Debugger::GeneratorWeakMap> genPtr(
1303 cx, dbg->generatorFrames, genObj);
1304 if (!genPtr.add(cx, dbg->generatorFrames, genObj, frameObj)) {
1305 ok = false;
1306 }
1307 });
1308
1309 if (!ok) {
1310 return false;
1311 }
1312
1313 terminateDebuggerFramesGuard.release();
1314 return true;
1315}
1316
1317/* static */
1318bool DebugAPI::slowPathOnDebuggerStatement(JSContext* cx,
1319 AbstractFramePtr frame) {
1320 return Debugger::dispatchResumptionHook(
1321 cx, frame,
1322 [](Debugger* dbg) -> bool {
1323 return dbg->getHook(Debugger::OnDebuggerStatement);
1324 },
1325 [&](Debugger* dbg, ResumeMode& resumeMode, MutableHandleValue vp)
1326 -> bool { return dbg->fireDebuggerStatement(cx, resumeMode, vp); });
1327}
1328
1329/* static */
1330bool DebugAPI::slowPathOnExceptionUnwind(JSContext* cx,
1331 AbstractFramePtr frame) {
1332 // Invoking more JS on an over-recursed stack or after OOM is only going
1333 // to result in more of the same error.
1334 if (cx->isThrowingOverRecursed() || cx->isThrowingOutOfMemory()) {
1335 return true;
1336 }
1337
1338 // The Debugger API mustn't muck with frames from self-hosted scripts.
1339 if (frame.hasScript() && frame.script()->selfHosted()) {
1340 return true;
1341 }
1342
1343 DebuggerList debuggerList(cx, [](Debugger* dbg) -> bool {
1344 return dbg->getHook(Debugger::OnExceptionUnwind);
1345 });
1346
1347 if (!debuggerList.init(cx)) {
1348 return false;
1349 }
1350
1351 if (debuggerList.empty()) {
1352 return true;
1353 }
1354
1355 // We save and restore the exception once up front to avoid having to do it
1356 // for each 'onExceptionUnwind' hook that has been registered, and we also
1357 // only do it if the debuggerList contains items in order to avoid extra work.
1358 RootedValue exc(cx);
1359 Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
1360 if (!cx->getPendingException(&exc)) {
1361 return false;
1362 }
1363 cx->clearPendingException();
1364
1365 bool result = debuggerList.dispatchResumptionHook(
1366 cx, frame,
1367 [&](Debugger* dbg, ResumeMode& resumeMode,
1368 MutableHandleValue vp) -> bool {
1369 return dbg->fireExceptionUnwind(cx, exc, resumeMode, vp);
1370 });
1371 if (!result) {
1372 return false;
1373 }
1374
1375 cx->setPendingException(exc, stack);
1376 return true;
1377}
1378
1379// TODO: Remove Remove this function when all properties/methods returning a
1380/// DebuggerEnvironment have been given a C++ interface (bug 1271649).
1381bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1382 MutableHandleValue rval) {
1383 if (!env) {
1384 rval.setNull();
1385 return true;
1386 }
1387
1388 Rooted<DebuggerEnvironment*> envobj(cx);
1389
1390 if (!wrapEnvironment(cx, env, &envobj)) {
1391 return false;
1392 }
1393
1394 rval.setObject(*envobj);
1395 return true;
1396}
1397
1398bool Debugger::wrapEnvironment(JSContext* cx, Handle<Env*> env,
1399 MutableHandle<DebuggerEnvironment*> result) {
1400 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"
, 1400); AnnotateMozCrashReason("MOZ_ASSERT" "(" "env" ")"); do
{ *((volatile int*)__null) = 1400; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1401
1402 // DebuggerEnv should only wrap a debug scope chain obtained (transitively)
1403 // from GetDebugEnvironmentFor(Frame|Function).
1404 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"
, 1404); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsSyntacticEnvironment(env)"
")"); do { *((volatile int*)__null) = 1404; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1405
1406 DependentAddPtr<EnvironmentWeakMap> p(cx, environments, env);
1407 if (p) {
1408 result.set(&p->value()->as<DebuggerEnvironment>());
1409 } else {
1410 // Create a new Debugger.Environment for env.
1411 RootedObject proto(
1412 cx, &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject());
1413 Rooted<NativeObject*> debugger(cx, object);
1414
1415 Rooted<DebuggerEnvironment*> envobj(
1416 cx, DebuggerEnvironment::create(cx, proto, env, debugger));
1417 if (!envobj) {
1418 return false;
1419 }
1420
1421 if (!p.add(cx, environments, env, envobj)) {
1422 // We need to destroy the edge to the referent, to avoid trying to trace
1423 // it during untimely collections.
1424 envobj->clearReferent();
1425 return false;
1426 }
1427
1428 result.set(envobj);
1429 }
1430
1431 return true;
1432}
1433
1434bool Debugger::wrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1435 cx->check(object.get());
1436
1437 if (vp.isObject()) {
1438 RootedObject obj(cx, &vp.toObject());
1439 Rooted<DebuggerObject*> dobj(cx);
1440
1441 if (!wrapDebuggeeObject(cx, obj, &dobj)) {
1442 return false;
1443 }
1444
1445 vp.setObject(*dobj);
1446 } else if (vp.isMagic()) {
1447 Rooted<PlainObject*> optObj(cx, NewPlainObject(cx));
1448 if (!optObj) {
1449 return false;
1450 }
1451
1452 // We handle three sentinel values: missing arguments
1453 // (JS_MISSING_ARGUMENTS), optimized out slots (JS_OPTIMIZED_OUT),
1454 // and uninitialized bindings (JS_UNINITIALIZED_LEXICAL).
1455 //
1456 // Other magic values should not have escaped.
1457 PropertyName* name;
1458 switch (vp.whyMagic()) {
1459 case JS_MISSING_ARGUMENTS:
1460 name = cx->names().missingArguments;
1461 break;
1462 case JS_OPTIMIZED_OUT:
1463 name = cx->names().optimizedOut;
1464 break;
1465 case JS_UNINITIALIZED_LEXICAL:
1466 name = cx->names().uninitialized;
1467 break;
1468 default:
1469 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"
, 1469); AnnotateMozCrashReason("MOZ_CRASH(" "Unsupported magic value escaped to Debugger"
")"); do { *((volatile int*)__null) = 1469; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
1470 }
1471
1472 RootedValue trueVal(cx, BooleanValue(true));
1473 if (!DefineDataProperty(cx, optObj, name, trueVal)) {
1474 return false;
1475 }
1476
1477 vp.setObject(*optObj);
1478 } else if (!cx->compartment()->wrap(cx, vp)) {
1479 vp.setUndefined();
1480 return false;
1481 }
1482
1483 return true;
1484}
1485
1486bool Debugger::wrapNullableDebuggeeObject(
1487 JSContext* cx, HandleObject obj, MutableHandle<DebuggerObject*> result) {
1488 if (!obj) {
1489 result.set(nullptr);
1490 return true;
1491 }
1492
1493 return wrapDebuggeeObject(cx, obj, result);
1494}
1495
1496bool Debugger::wrapDebuggeeObject(JSContext* cx, HandleObject obj,
1497 MutableHandle<DebuggerObject*> result) {
1498 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"
, 1498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "obj" ")"); do
{ *((volatile int*)__null) = 1498; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
1499
1500 DependentAddPtr<ObjectWeakMap> p(cx, objects, obj);
1501 if (p) {
1502 result.set(&p->value()->as<DebuggerObject>());
1503 } else {
1504 // Create a new Debugger.Object for obj.
1505 Rooted<NativeObject*> debugger(cx, object);
1506 RootedObject proto(
1507 cx, &object->getReservedSlot(JSSLOT_DEBUG_OBJECT_PROTO).toObject());
1508 Rooted<DebuggerObject*> dobj(
1509 cx, DebuggerObject::create(cx, proto, obj, debugger));
1510 if (!dobj) {
1511 return false;
1512 }
1513
1514 if (!p.add(cx, objects, obj, dobj)) {
1515 // We need to destroy the edge to the referent, to avoid trying to trace
1516 // it during untimely collections.
1517 dobj->clearReferent();
1518 return false;
1519 }
1520
1521 result.set(dobj);
1522 }
1523
1524 return true;
1525}
1526
1527static DebuggerObject* ToNativeDebuggerObject(JSContext* cx,
1528 MutableHandleObject obj) {
1529 if (!obj->is<DebuggerObject>()) {
1530 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1531 JSMSG_NOT_EXPECTED_TYPE, "Debugger",
1532 "Debugger.Object", obj->getClass()->name);
1533 return nullptr;
1534 }
1535
1536 return &obj->as<DebuggerObject>();
1537}
1538
1539bool Debugger::unwrapDebuggeeObject(JSContext* cx, MutableHandleObject obj) {
1540 DebuggerObject* ndobj = ToNativeDebuggerObject(cx, obj);
1541 if (!ndobj) {
1542 return false;
1543 }
1544
1545 if (ndobj->owner() != Debugger::fromJSObject(object)) {
1546 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1547 JSMSG_DEBUG_WRONG_OWNER, "Debugger.Object");
1548 return false;
1549 }
1550
1551 obj.set(ndobj->referent());
1552 return true;
1553}
1554
1555bool Debugger::unwrapDebuggeeValue(JSContext* cx, MutableHandleValue vp) {
1556 cx->check(object.get(), vp);
1557 if (vp.isObject()) {
1558 RootedObject dobj(cx, &vp.toObject());
1559 if (!unwrapDebuggeeObject(cx, &dobj)) {
1560 return false;
1561 }
1562 vp.setObject(*dobj);
1563 }
1564 return true;
1565}
1566
1567static bool CheckArgCompartment(JSContext* cx, JSObject* obj, JSObject* arg,
1568 const char* methodname, const char* propname) {
1569 if (arg->compartment() != obj->compartment()) {
1570 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1571 JSMSG_DEBUG_COMPARTMENT_MISMATCH, methodname,
1572 propname);
1573 return false;
1574 }
1575 return true;
1576}
1577
1578static bool CheckArgCompartment(JSContext* cx, JSObject* obj, HandleValue v,
1579 const char* methodname, const char* propname) {
1580 if (v.isObject()) {
1581 return CheckArgCompartment(cx, obj, &v.toObject(), methodname, propname);
1582 }
1583 return true;
1584}
1585
1586bool Debugger::unwrapPropertyDescriptor(
1587 JSContext* cx, HandleObject obj, MutableHandle<PropertyDescriptor> desc) {
1588 if (desc.hasValue()) {
1589 RootedValue value(cx, desc.value());
1590 if (!unwrapDebuggeeValue(cx, &value) ||
1591 !CheckArgCompartment(cx, obj, value, "defineProperty", "value")) {
1592 return false;
1593 }
1594 desc.setValue(value);
1595 }
1596
1597 if (desc.hasGetter()) {
1598 RootedObject get(cx, desc.getter());
1599 if (get) {
1600 if (!unwrapDebuggeeObject(cx, &get)) {
1601 return false;
1602 }
1603 if (!CheckArgCompartment(cx, obj, get, "defineProperty", "get")) {
1604 return false;
1605 }
1606 }
1607 desc.setGetter(get);
1608 }
1609
1610 if (desc.hasSetter()) {
1611 RootedObject set(cx, desc.setter());
1612 if (set) {
1613 if (!unwrapDebuggeeObject(cx, &set)) {
1614 return false;
1615 }
1616 if (!CheckArgCompartment(cx, obj, set, "defineProperty", "set")) {
1617 return false;
1618 }
1619 }
1620 desc.setSetter(set);
1621 }
1622
1623 return true;
1624}
1625
1626/*** Debuggee resumption values and debugger error handling *****************/
1627
1628static bool GetResumptionProperty(JSContext* cx, HandleObject obj,
1629 Handle<PropertyName*> name,
1630 ResumeMode namedMode, ResumeMode& resumeMode,
1631 MutableHandleValue vp, int* hits) {
1632 bool found;
1633 if (!HasProperty(cx, obj, name, &found)) {
1634 return false;
1635 }
1636 if (found) {
1637 ++*hits;
1638 resumeMode = namedMode;
1639 if (!GetProperty(cx, obj, obj, name, vp)) {
1640 return false;
1641 }
1642 }
1643 return true;
1644}
1645
1646bool js::ParseResumptionValue(JSContext* cx, HandleValue rval,
1647 ResumeMode& resumeMode, MutableHandleValue vp) {
1648 if (rval.isUndefined()) {
1649 resumeMode = ResumeMode::Continue;
1650 vp.setUndefined();
1651 return true;
1652 }
1653 if (rval.isNull()) {
1654 resumeMode = ResumeMode::Terminate;
1655 vp.setUndefined();
1656 return true;
1657 }
1658
1659 int hits = 0;
1660 if (rval.isObject()) {
1661 RootedObject obj(cx, &rval.toObject());
1662 if (!GetResumptionProperty(cx, obj, cx->names().return_, ResumeMode::Return,
1663 resumeMode, vp, &hits)) {
1664 return false;
1665 }
1666 if (!GetResumptionProperty(cx, obj, cx->names().throw_, ResumeMode::Throw,
1667 resumeMode, vp, &hits)) {
1668 return false;
1669 }
1670 }
1671
1672 if (hits != 1) {
1673 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1674 JSMSG_DEBUG_BAD_RESUMPTION);
1675 return false;
1676 }
1677 return true;
1678}
1679
1680static bool CheckResumptionValue(JSContext* cx, AbstractFramePtr frame,
1681 const jsbytecode* pc, ResumeMode resumeMode,
1682 MutableHandleValue vp) {
1683 // Only forced returns from a frame need to be validated because forced
1684 // throw values behave just like debuggee `throw` statements. Since
1685 // forced-return is all custom logic within SpiderMonkey itself, we need
1686 // our own custom validation for it to conform with what is expected.
1687 if (resumeMode != ResumeMode::Return || !frame) {
1688 return true;
1689 }
1690
1691 // This replicates the ECMA spec's behavior for [[Construct]] in derived
1692 // class constructors (section 9.2.2 of ECMA262-2020), where returning a
1693 // non-undefined primitive causes an exception tobe thrown.
1694 if (frame.debuggerNeedsCheckPrimitiveReturn() && vp.isPrimitive()) {
1695 if (!vp.isUndefined()) {
1696 ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK0, vp,
1697 nullptr);
1698 return false;
1699 }
1700
1701 RootedValue thisv(cx);
1702 {
1703 AutoRealm ar(cx, frame.environmentChain());
1704 if (!GetThisValueForDebuggerFrameMaybeOptimizedOut(cx, frame, pc,
1705 &thisv)) {
1706 return false;
1707 }
1708 }
1709
1710 if (thisv.isMagic(JS_UNINITIALIZED_LEXICAL)) {
1711 return ThrowUninitializedThis(cx);
1712 }
1713 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"
, 1713); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!thisv.isMagic()"
")"); do { *((volatile int*)__null) = 1713; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1714
1715 if (!cx->compartment()->wrap(cx, &thisv)) {
1716 return false;
1717 }
1718 vp.set(thisv);
1719 }
1720
1721 // Check for forcing return from a generator before the initial yield. This
1722 // is not supported because some engine-internal code assumes a call to a
1723 // generator will return a GeneratorObject; see bug 1477084.
1724 if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
1725 Rooted<AbstractGeneratorObject*> genObj(cx);
1726 {
1727 AutoRealm ar(cx, frame.callee());
1728 genObj = GetGeneratorObjectForFrame(cx, frame);
1729 }
1730
1731 if (!genObj || genObj->isBeforeInitialYield()) {
1732 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1733 JSMSG_DEBUG_FORCED_RETURN_DISALLOWED);
1734 return false;
1735 }
1736 }
1737
1738 return true;
1739}
1740
1741// Last-minute sanity adjustments to resumption.
1742//
1743// This is called last, as we leave the debugger. It must happen outside the
1744// control of the uncaughtExceptionHook, because this code assumes we won't
1745// change our minds and continue execution--we must not close the generator
1746// object unless we're really going to force-return.
1747[[nodiscard]] static bool AdjustGeneratorResumptionValue(
1748 JSContext* cx, AbstractFramePtr frame, ResumeMode& resumeMode,
1749 MutableHandleValue vp) {
1750 if (resumeMode != ResumeMode::Return && resumeMode != ResumeMode::Throw) {
1751 return true;
1752 }
1753
1754 if (!frame) {
1755 return true;
1756 }
1757 // Async modules need to be handled separately, as they do not have a callee.
1758 // frame.callee will throw if it is called on a moduleFrame.
1759 bool isAsyncModule = frame.isModuleFrame() && frame.script()->isAsync();
1760 if (!frame.isFunctionFrame() && !isAsyncModule) {
1761 return true;
1762 }
1763
1764 // Treat `{return: <value>}` like a `return` statement. Simulate what the
1765 // debuggee would do for an ordinary `return` statement, using a few bytecode
1766 // instructions. It's simpler to do the work manually than to count on that
1767 // bytecode sequence existing in the debuggee, somehow jump to it, and then
1768 // avoid re-entering the debugger from it.
1769 //
1770 // Similarly treat `{throw: <value>}` like a `throw` statement.
1771 //
1772 // Note: Async modules use the same handling as async functions.
1773 if (frame.isFunctionFrame() && frame.callee()->isGenerator()) {
1774 // Throw doesn't require any special processing for (async) generators.
1775 if (resumeMode == ResumeMode::Throw) {
1776 return true;
1777 }
1778
1779 // Forcing return from a (possibly async) generator.
1780 Rooted<AbstractGeneratorObject*> genObj(
1781 cx, GetGeneratorObjectForFrame(cx, frame));
1782
1783 // We already went through CheckResumptionValue, which would have replaced
1784 // this invalid resumption value with an error if we were trying to force
1785 // return before the initial yield.
1786 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"
, 1786); AnnotateMozCrashReason("MOZ_RELEASE_ASSERT" "(" "genObj && !genObj->isBeforeInitialYield()"
")"); do { *((volatile int*)__null) = 1786; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1787
1788 // 1. `return <value>` creates and returns a new object,
1789 // `{value: <value>, done: true}`.
1790 //
1791 // For non-async generators, the iterator result object is created in
1792 // bytecode, so we have to simulate that here. For async generators, our
1793 // C++ implementation of AsyncGeneratorResolve will do this. So don't do it
1794 // twice:
1795 if (!genObj->is<AsyncGeneratorObject>()) {
1796 PlainObject* pair = CreateIterResultObject(cx, vp, true);
1797 if (!pair) {
1798 return false;
1799 }
1800 vp.setObject(*pair);
1801 }
1802
1803 // 2. The generator must be closed.
1804 genObj->setClosed(cx);
1805
1806 // Async generators have additionally bookkeeping which must be adjusted
1807 // when switching over to the closed state.
1808 if (genObj->is<AsyncGeneratorObject>()) {
1809 genObj->as<AsyncGeneratorObject>().setCompleted();
1810 }
1811 } else if (isAsyncModule || frame.callee()->isAsync()) {
1812 if (AbstractGeneratorObject* genObj =
1813 GetGeneratorObjectForFrame(cx, frame)) {
1814 // Throw doesn't require any special processing for async functions when
1815 // the internal generator object is already present.
1816 if (resumeMode == ResumeMode::Throw) {
1817 return true;
1818 }
1819
1820 Rooted<AsyncFunctionGeneratorObject*> generator(
1821 cx, &genObj->as<AsyncFunctionGeneratorObject>());
1822
1823 // 1. `return <value>` fulfills and returns the async function's promise.
1824 Rooted<PromiseObject*> promise(cx, generator->promise());
1825 if (promise->state() == JS::PromiseState::Pending) {
1826 if (!AsyncFunctionResolve(cx, generator, vp)) {
1827 return false;
1828 }
1829 }
1830 vp.setObject(*promise);
1831
1832 // 2. The generator must be closed.
1833 generator->setClosed(cx);
1834 } else {
1835 // We're before entering the actual function code.
1836
1837 // 1. `throw <value>` creates a promise rejected with the value *vp.
1838 // 1. `return <value>` creates a promise resolved with the value *vp.
1839 JSObject* promise = resumeMode == ResumeMode::Throw
1840 ? PromiseObject::unforgeableReject(cx, vp)
1841 : PromiseObject::unforgeableResolve(cx, vp);
1842 if (!promise) {
1843 return false;
1844 }
1845 vp.setObject(*promise);
1846
1847 // 2. Return normally in both cases.
1848 resumeMode = ResumeMode::Return;
1849 }
1850 }
1851
1852 return true;
1853}
1854
1855bool Debugger::processParsedHandlerResult(JSContext* cx, AbstractFramePtr frame,
1856 const jsbytecode* pc, bool success,
1857 ResumeMode resumeMode,
1858 HandleValue value,
1859 ResumeMode& resultMode,
1860 MutableHandleValue vp) {
1861 RootedValue rootValue(cx, value);
1862 if (!success || !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
1863 RootedValue exceptionRv(cx);
1864 if (!callUncaughtExceptionHandler(cx, &exceptionRv) ||
1865 !ParseResumptionValue(cx, exceptionRv, resumeMode, &rootValue) ||
1866 !prepareResumption(cx, frame, pc, resumeMode, &rootValue)) {
1867 return false;
1868 }
1869 }
1870
1871 // Since debugger hooks accumulate into the same final value handle, we
1872 // use that to throw if multiple hooks try to set a resumption value.
1873 if (resumeMode != ResumeMode::Continue) {
1874 if (resultMode != ResumeMode::Continue) {
1875 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1876 JSMSG_DEBUG_RESUMPTION_CONFLICT);
1877 return false;
1878 }
1879
1880 vp.set(rootValue);
1881 resultMode = resumeMode;
1882 }
1883
1884 return true;
1885}
1886
1887bool Debugger::processHandlerResult(JSContext* cx, bool success, HandleValue rv,
1888 AbstractFramePtr frame, jsbytecode* pc,
1889 ResumeMode& resultMode,
1890 MutableHandleValue vp) {
1891 ResumeMode resumeMode = ResumeMode::Continue;
1892 RootedValue value(cx);
1893 if (success) {
1894 success = ParseResumptionValue(cx, rv, resumeMode, &value);
1895 }
1896 return processParsedHandlerResult(cx, frame, pc, success, resumeMode, value,
1897 resultMode, vp);
1898}
1899
1900bool Debugger::prepareResumption(JSContext* cx, AbstractFramePtr frame,
1901 const jsbytecode* pc, ResumeMode& resumeMode,
1902 MutableHandleValue vp) {
1903 return unwrapDebuggeeValue(cx, vp) &&
1904 CheckResumptionValue(cx, frame, pc, resumeMode, vp);
1905}
1906
1907bool Debugger::callUncaughtExceptionHandler(JSContext* cx,
1908 MutableHandleValue vp) {
1909 // Uncaught exceptions arise from Debugger code, and so we must already be in
1910 // an NX section. This also establishes that we are already within the scope
1911 // of an AutoDebuggerJobQueueInterruption object.
1912 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"
, 1912); AnnotateMozCrashReason("MOZ_ASSERT" "(" "EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
")"); do { *((volatile int*)__null) = 1912; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1913
1914 if (cx->isExceptionPending() && uncaughtExceptionHook) {
1915 RootedValue exc(cx);
1916 if (!cx->getPendingException(&exc)) {
1917 return false;
1918 }
1919 cx->clearPendingException();
1920
1921 RootedValue fval(cx, ObjectValue(*uncaughtExceptionHook));
1922 if (js::Call(cx, fval, object, exc, vp)) {
1923 return true;
1924 }
1925 }
1926 return false;
1927}
1928
1929bool Debugger::handleUncaughtException(JSContext* cx) {
1930 RootedValue rv(cx);
1931
1932 return callUncaughtExceptionHandler(cx, &rv);
1933}
1934
1935void Debugger::reportUncaughtException(JSContext* cx) {
1936 // Uncaught exceptions arise from Debugger code, and so we must already be
1937 // in an NX section.
1938 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"
, 1938); AnnotateMozCrashReason("MOZ_ASSERT" "(" "EnterDebuggeeNoExecute::isLockedInStack(cx, *this)"
")"); do { *((volatile int*)__null) = 1938; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1939
1940 if (cx->isExceptionPending()) {
1941 // We want to report the pending exception, but we want to let the
1942 // embedding handle it however it wants to. So pretend like we're
1943 // starting a new script execution on our current compartment (which
1944 // is the debugger compartment, so reported errors won't get
1945 // reported to various onerror handlers in debuggees) and as part of
1946 // that "execution" simply throw our exception so the embedding can
1947 // deal.
1948 RootedValue exn(cx);
1949 if (cx->getPendingException(&exn)) {
1950 // Clear the exception, because ReportErrorToGlobal will assert that
1951 // we don't have one.
1952 cx->clearPendingException();
1953 ReportErrorToGlobal(cx, cx->global(), exn);
1954 }
1955
1956 // And if not, or if PrepareScriptEnvironmentAndInvoke somehow left an
1957 // exception on cx (which it totally shouldn't do), just give up.
1958 cx->clearPendingException();
1959 }
1960}
1961
1962/*** Debuggee completion values *********************************************/
1963
1964/* static */
1965Completion Completion::fromJSResult(JSContext* cx, bool ok, const Value& rv) {
1966 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"
, 1966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { *((volatile int*)__null) = 1966; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
1967
1968 if (ok) {
1969 return Completion(Return(rv));
1970 }
1971
1972 if (!cx->isExceptionPending()) {
1973 return Completion(Terminate());
1974 }
1975
1976 RootedValue exception(cx);
1977 Rooted<SavedFrame*> stack(cx, cx->getPendingExceptionStack());
1978 bool getSucceeded = cx->getPendingException(&exception);
1979 cx->clearPendingException();
1980 if (!getSucceeded) {
1981 return Completion(Terminate());
1982 }
1983
1984 return Completion(Throw(exception, stack));
1985}
1986
1987/* static */
1988Completion Completion::fromJSFramePop(JSContext* cx, AbstractFramePtr frame,
1989 const jsbytecode* pc, bool ok) {
1990 // Only Wasm frames get a null pc.
1991 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"
, 1991); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pc" ")"); do
{ *((volatile int*)__null) = 1991; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); } } while (false
)
;
1992
1993 // If this isn't a generator suspension, then that's already handled above.
1994 if (!ok || !frame.isGeneratorFrame()) {
1995 return fromJSResult(cx, ok, frame.returnValue());
1996 }
1997
1998 // A generator is being suspended or returning.
1999
2000 // Since generators are never wasm, we can assume pc is not nullptr, and
2001 // that analyzing bytecode is meaningful.
2002 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"
, 2002); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!frame.isWasmDebugFrame()"
")"); do { *((volatile int*)__null) = 2002; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2003
2004 // If we're leaving successfully at a yield opcode, we're probably
2005 // suspending; the `isClosed()` check detects a debugger forced return from
2006 // an `onStep` handler, which looks almost the same.
2007 //
2008 // GetGeneratorObjectForFrame can return nullptr even when a generator
2009 // object does exist, if the frame is paused between the Generator and
2010 // SetAliasedVar opcodes. But by checking the opcode first we eliminate that
2011 // possibility, so it's fine to call genObj->isClosed().
2012 Rooted<AbstractGeneratorObject*> generatorObj(
2013 cx, GetGeneratorObjectForFrame(cx, frame));
2014 switch (JSOp(*pc)) {
2015 case JSOp::InitialYield:
2016 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"
, 2016); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!generatorObj->isClosed()"
")"); do { *((volatile int*)__null) = 2016; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2017 return Completion(InitialYield(generatorObj));
2018
2019 case JSOp::Yield:
2020 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"
, 2020); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!generatorObj->isClosed()"
")"); do { *((volatile int*)__null) = 2020; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2021 return Completion(Yield(generatorObj, frame.returnValue()));
2022
2023 case JSOp::Await:
2024 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"
, 2024); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!generatorObj->isClosed()"
")"); do { *((volatile int*)__null) = 2024; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2025 return Completion(Await(generatorObj, frame.returnValue()));
2026
2027 default:
2028 return Completion(Return(frame.returnValue()));
2029 }
2030}
2031
2032void Completion::trace(JSTracer* trc) {
2033 variant.match([=](auto& var) { var.trace(trc); });
2034}
2035
2036struct MOZ_STACK_CLASS Completion::BuildValueMatcher {
2037 JSContext* cx;
2038 Debugger* dbg;
2039 MutableHandleValue result;
2040
2041 BuildValueMatcher(JSContext* cx, Debugger* dbg, MutableHandleValue result)
2042 : cx(cx), dbg(dbg), result(result) {
2043 cx->check(dbg->toJSObject());
2044 }
2045
2046 bool operator()(const Completion::Return& ret) {
2047 Rooted<NativeObject*> obj(cx, newObject());
2048 RootedValue retval(cx, ret.value);
2049 if (!obj || !wrap(&retval) || !add(obj, cx->names().return_, retval)) {
2050 return false;
2051 }
2052 result.setObject(*obj);
2053 return true;
2054 }
2055
2056 bool operator()(const Completion::Throw& thr) {
2057 Rooted<NativeObject*> obj(cx, newObject());
2058 RootedValue exc(cx, thr.exception);
2059 if (!obj || !wrap(&exc) || !add(obj, cx->names().throw_, exc)) {
2060 return false;
2061 }
2062 if (thr.stack) {
2063 RootedValue stack(cx, ObjectValue(*thr.stack));
2064 if (!wrapStack(&stack) || !add(obj, cx->names().stack, stack)) {
2065 return false;
2066 }
2067 }
2068 result.setObject(*obj);
2069 return true;
2070 }
2071
2072 bool operator()(const Completion::Terminate& term) {
2073 result.setNull();
2074 return true;
2075 }
2076
2077 bool operator()(const Completion::InitialYield& initialYield) {
2078 Rooted<NativeObject*> obj(cx, newObject());
2079 RootedValue gen(cx, ObjectValue(*initialYield.generatorObject));
2080 if (!obj || !wrap(&gen) || !add(obj, cx->names().return_, gen) ||
2081 !add(obj, cx->names().yield, TrueHandleValue) ||
2082 !add(obj, cx->names().initial, TrueHandleValue)) {
2083 return false;
2084 }
2085 result.setObject(*obj);
2086 return true;
2087 }
2088
2089 bool operator()(const Completion::Yield& yield) {
2090 Rooted<NativeObject*> obj(cx, newObject());
2091 RootedValue iteratorResult(cx, yield.iteratorResult);
2092 if (!obj || !wrap(&iteratorResult) ||
2093 !add(obj, cx->names().return_, iteratorResult) ||
2094 !add(obj, cx->names().yield, TrueHandleValue)) {
2095 return false;
2096 }
2097 result.setObject(*obj);
2098 return true;
2099 }
2100
2101 bool operator()(const Completion::Await& await) {
2102 Rooted<NativeObject*> obj(cx, newObject());
2103 RootedValue awaitee(cx, await.awaitee);
2104 if (!obj || !wrap(&awaitee) || !add(obj, cx->names().return_, awaitee) ||
2105 !add(obj, cx->names().await, TrueHandleValue)) {
2106 return false;
2107 }
2108 result.setObject(*obj);
2109 return true;
2110 }
2111
2112 private:
2113 NativeObject* newObject() const { return NewPlainObject(cx); }
2114
2115 bool add(Handle<NativeObject*> obj, PropertyName* name,
2116 HandleValue value) const {
2117 return NativeDefineDataProperty(cx, obj, name, value, JSPROP_ENUMERATE);
2118 }
2119
2120 bool wrap(MutableHandleValue v) const {
2121 return dbg->wrapDebuggeeValue(cx, v);
2122 }
2123
2124 // Saved stacks are wrapped for direct consumption by debugger code.
2125 bool wrapStack(MutableHandleValue stack) const {
2126 return cx->compartment()->wrap(cx, stack);
2127 }
2128};
2129
2130bool Completion::buildCompletionValue(JSContext* cx, Debugger* dbg,
2131 MutableHandleValue result) const {
2132 return variant.match(BuildValueMatcher(cx, dbg, result));
2133}
2134
2135void Completion::updateFromHookResult(ResumeMode resumeMode,
2136 HandleValue value) {
2137 switch (resumeMode) {
2138 case ResumeMode::Continue:
2139 // No change to how we'll resume.
2140 break;
2141
2142 case ResumeMode::Throw:
2143 // Since this is a new exception, the stack for the old one may not apply.
2144 // If we extend resumption values to specify stacks, we could revisit
2145 // this.
2146 variant = Variant(Throw(value, nullptr));
2147 break;
2148
2149 case ResumeMode::Terminate:
2150 variant = Variant(Terminate());
2151 break;
2152
2153 case ResumeMode::Return:
2154 variant = Variant(Return(value));
2155 break;
2156
2157 default:
2158 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"
, 2158); AnnotateMozCrashReason("MOZ_CRASH(" "invalid resumeMode value"
")"); do { *((volatile int*)__null) = 2158; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
2159 }
2160}
2161
2162struct MOZ_STACK_CLASS Completion::ToResumeModeMatcher {
2163 MutableHandleValue value;
2164 MutableHandle<SavedFrame*> exnStack;
2165 ToResumeModeMatcher(MutableHandleValue value,
2166 MutableHandle<SavedFrame*> exnStack)
2167 : value(value), exnStack(exnStack) {}
2168
2169 ResumeMode operator()(const Return& ret) {
2170 value.set(ret.value);
2171 return ResumeMode::Return;
2172 }
2173
2174 ResumeMode operator()(const Throw& thr) {
2175 value.set(thr.exception);
2176 exnStack.set(thr.stack);
2177 return ResumeMode::Throw;
2178 }
2179
2180 ResumeMode operator()(const Terminate& term) {
2181 value.setUndefined();
2182 return ResumeMode::Terminate;
2183 }
2184
2185 ResumeMode operator()(const InitialYield& initialYield) {
2186 value.setObject(*initialYield.generatorObject);
2187 return ResumeMode::Return;
2188 }
2189
2190 ResumeMode operator()(const Yield& yield) {
2191 value.set(yield.iteratorResult);
2192 return ResumeMode::Return;
2193 }
2194
2195 ResumeMode operator()(const Await& await) {
2196 value.set(await.awaitee);
2197 return ResumeMode::Return;
2198 }
2199};
2200
2201void Completion::toResumeMode(ResumeMode& resumeMode, MutableHandleValue value,
2202 MutableHandle<SavedFrame*> exnStack) const {
2203 resumeMode = variant.match(ToResumeModeMatcher(value, exnStack));
2204}
2205
2206/*** Firing debugger hooks **************************************************/
2207
2208static bool CallMethodIfPresent(JSContext* cx, HandleObject obj,
2209 const char* name, size_t argc, Value* argv,
2210 MutableHandleValue rval) {
2211 rval.setUndefined();
2212 JSAtom* atom = Atomize(cx, name, strlen(name));
2213 if (!atom) {
2214 return false;
2215 }
2216
2217 RootedId id(cx, AtomToId(atom));
2218 RootedValue fval(cx);
2219 if (!GetProperty(cx, obj, obj, id, &fval)) {
2220 return false;
2221 }
2222
2223 if (!IsCallable(fval)) {
2224 return true;
2225 }
2226
2227 InvokeArgs args(cx);
2228 if (!args.init(cx, argc)) {
2229 return false;
2230 }
2231
2232 for (size_t i = 0; i < argc; i++) {
2233 args[i].set(argv[i]);
2234 }
2235
2236 rval.setObject(*obj); // overwritten by successful Call
2237 return js::Call(cx, fval, rval, args, rval);
2238}
2239
2240bool Debugger::fireDebuggerStatement(JSContext* cx, ResumeMode& resumeMode,
2241 MutableHandleValue vp) {
2242 RootedObject hook(cx, getHook(OnDebuggerStatement));
2243 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"
, 2243); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2243; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2244 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"
, 2244); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2244; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2245
2246 ScriptFrameIter iter(cx);
2247 RootedValue scriptFrame(cx);
2248 if (!getFrame(cx, iter, &scriptFrame)) {
2249 return false;
2250 }
2251
2252 RootedValue fval(cx, ObjectValue(*hook));
2253 RootedValue rv(cx);
2254 bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
2255 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2256 resumeMode, vp);
2257}
2258
2259bool Debugger::fireExceptionUnwind(JSContext* cx, HandleValue exc,
2260 ResumeMode& resumeMode,
2261 MutableHandleValue vp) {
2262 RootedObject hook(cx, getHook(OnExceptionUnwind));
2263 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"
, 2263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2263; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2264 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"
, 2264); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2264; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2265
2266 RootedValue scriptFrame(cx);
2267 RootedValue wrappedExc(cx, exc);
2268
2269 FrameIter iter(cx);
2270 if (!getFrame(cx, iter, &scriptFrame) ||
2271 !wrapDebuggeeValue(cx, &wrappedExc)) {
2272 return false;
2273 }
2274
2275 RootedValue fval(cx, ObjectValue(*hook));
2276 RootedValue rv(cx);
2277 bool ok = js::Call(cx, fval, object, scriptFrame, wrappedExc, &rv);
2278 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2279 resumeMode, vp);
2280}
2281
2282bool Debugger::fireEnterFrame(JSContext* cx, ResumeMode& resumeMode,
2283 MutableHandleValue vp) {
2284 RootedObject hook(cx, getHook(OnEnterFrame));
2285 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"
, 2285); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2285; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2286 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"
, 2286); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2286; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2287
2288 RootedValue scriptFrame(cx);
2289
2290 FrameIter iter(cx);
2291
2292#if DEBUG1
2293 // Assert that the hook won't be able to re-enter the generator.
2294 if (iter.hasScript() && JSOp(*iter.pc()) == JSOp::AfterYield) {
2295 AutoRealm ar(cx, iter.script());
2296 auto* genObj = GetGeneratorObjectForFrame(cx, iter.abstractFramePtr());
2297 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"
, 2297); AnnotateMozCrashReason("MOZ_ASSERT" "(" "genObj->isRunning()"
")"); do { *((volatile int*)__null) = 2297; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2298 }
2299#endif
2300
2301 if (!getFrame(cx, iter, &scriptFrame)) {
2302 return false;
2303 }
2304
2305 RootedValue fval(cx, ObjectValue(*hook));
2306 RootedValue rv(cx);
2307 bool ok = js::Call(cx, fval, object, scriptFrame, &rv);
2308
2309 return processHandlerResult(cx, ok, rv, iter.abstractFramePtr(), iter.pc(),
2310 resumeMode, vp);
2311}
2312
2313bool Debugger::fireNativeCall(JSContext* cx, const CallArgs& args,
2314 CallReason reason, ResumeMode& resumeMode,
2315 MutableHandleValue vp) {
2316 RootedObject hook(cx, getHook(OnNativeCall));
2317 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"
, 2317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2317; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2318 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"
, 2318); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2318; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2319
2320 RootedValue fval(cx, ObjectValue(*hook));
2321 RootedValue calleeval(cx, args.calleev());
2322 if (!wrapDebuggeeValue(cx, &calleeval)) {
2323 return false;
2324 }
2325
2326 JSAtom* reasonAtom = nullptr;
2327 switch (reason) {
2328 case CallReason::Call:
2329 reasonAtom = cx->names().call;
2330 break;
2331 case CallReason::CallContent:
2332 reasonAtom = cx->names().call;
2333 break;
2334 case CallReason::FunCall:
2335 reasonAtom = cx->names().call;
2336 break;
2337 case CallReason::Getter:
2338 reasonAtom = cx->names().get;
2339 break;
2340 case CallReason::Setter:
2341 reasonAtom = cx->names().set;
2342 break;
2343 }
2344 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"
, 2344); AnnotateMozCrashReason("MOZ_ASSERT" "(" "AtomIsMarked(cx->zone(), reasonAtom)"
")"); do { *((volatile int*)__null) = 2344; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2345
2346 RootedValue reasonval(cx, StringValue(reasonAtom));
2347
2348 bool ok = false;
2349 RootedValue rv(cx);
2350 if (inspectNativeCallArguments) {
2351 RootedValue thisVal(cx, args.thisv());
2352 // Ignore anything that may make wrapDebuggeeValue to throw
2353 if (thisVal.isMagic() && thisVal.whyMagic() != JS_MISSING_ARGUMENTS &&
2354 thisVal.whyMagic() != JS_UNINITIALIZED_LEXICAL) {
2355 thisVal.setMagic(JS_OPTIMIZED_OUT);
2356 }
2357 if (!wrapDebuggeeValue(cx, &thisVal)) {
2358 return false;
2359 }
2360
2361 unsigned arrsize = args.length();
2362 Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, arrsize));
2363 if (!arrobj) {
2364 return false;
2365 }
2366 arrobj->ensureDenseInitializedLength(0, arrsize);
2367 for (unsigned i = 0; i < arrsize; i++) {
2368 RootedValue v(cx, args.get(i));
2369 if (!wrapDebuggeeValue(cx, &v)) {
2370 return false;
2371 }
2372 arrobj->setDenseElement(i, v);
2373 }
2374 RootedValue arrayval(cx, ObjectValue(*arrobj));
2375 if (!wrapDebuggeeValue(cx, &arrayval)) {
2376 return false;
2377 }
2378
2379 FixedInvokeArgs<4> iargs(cx);
2380 iargs[0].set(calleeval);
2381 iargs[1].set(reasonval);
2382 iargs[2].set(thisVal);
2383 iargs[3].set(arrayval);
2384
2385 RootedValue thisv(cx, ObjectOrNullValue(object));
2386 ok = js::Call(cx, fval, thisv, iargs, &rv);
2387 } else {
2388 ok = js::Call(cx, fval, object, calleeval, reasonval, &rv);
2389 }
2390
2391 return processHandlerResult(cx, ok, rv, NullFramePtr(), nullptr, resumeMode,
2392 vp);
2393}
2394
2395bool Debugger::fireNewScript(JSContext* cx,
2396 Handle<DebuggerScriptReferent> scriptReferent) {
2397 RootedObject hook(cx, getHook(OnNewScript));
2398 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"
, 2398); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2398; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2399 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"
, 2399); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2399; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2400
2401 JSObject* dsobj = wrapVariantReferent(cx, scriptReferent);
2402 if (!dsobj) {
2403 return false;
2404 }
2405
2406 RootedValue fval(cx, ObjectValue(*hook));
2407 RootedValue dsval(cx, ObjectValue(*dsobj));
2408 RootedValue rv(cx);
2409 return js::Call(cx, fval, object, dsval, &rv) || handleUncaughtException(cx);
2410}
2411
2412bool Debugger::fireOnGarbageCollectionHook(
2413 JSContext* cx, const JS::dbg::GarbageCollectionEvent::Ptr& gcData) {
2414 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"
, 2414); AnnotateMozCrashReason("MOZ_ASSERT" "(" "observedGC(gcData->majorGCNumber())"
")"); do { *((volatile int*)__null) = 2414; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2415 observedGCs.remove(gcData->majorGCNumber());
2416
2417 RootedObject hook(cx, getHook(OnGarbageCollection));
2418 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"
, 2418); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2418; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2419 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"
, 2419); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2419; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2420
2421 JSObject* dataObj = gcData->toJSObject(cx);
2422 if (!dataObj) {
2423 return false;
2424 }
2425
2426 RootedValue fval(cx, ObjectValue(*hook));
2427 RootedValue dataVal(cx, ObjectValue(*dataObj));
2428 RootedValue rv(cx);
2429 return js::Call(cx, fval, object, dataVal, &rv) ||
2430 handleUncaughtException(cx);
2431}
2432
2433template <typename HookIsEnabledFun /* bool (Debugger*) */,
2434 typename FireHookFun /* bool (Debugger*) */>
2435/* static */
2436void Debugger::dispatchQuietHook(JSContext* cx, HookIsEnabledFun hookIsEnabled,
2437 FireHookFun fireHook) {
2438 DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
2439
2440 if (!debuggerList.init(cx)) {
2441 // init may fail due to OOM. This OOM is not handlable at the
2442 // callsites of dispatchQuietHook in the engine.
2443 cx->clearPendingException();
2444 return;
2445 }
2446
2447 debuggerList.dispatchQuietHook(cx, fireHook);
2448}
2449
2450template <typename HookIsEnabledFun /* bool (Debugger*) */, typename FireHookFun /* bool (Debugger*, ResumeMode&, MutableHandleValue vp) */>
2451/* static */
2452bool Debugger::dispatchResumptionHook(JSContext* cx, AbstractFramePtr frame,
2453 HookIsEnabledFun hookIsEnabled,
2454 FireHookFun fireHook) {
2455 DebuggerList<HookIsEnabledFun> debuggerList(cx, hookIsEnabled);
2456
2457 if (!debuggerList.init(cx)) {
2458 return false;
2459 }
2460
2461 return debuggerList.dispatchResumptionHook(cx, frame, fireHook);
2462}
2463
2464// Maximum length for source URLs that can be remembered.
2465static const size_t SourceURLMaxLength = 1024;
2466
2467// Maximum number of source URLs that can be remembered in a realm.
2468static const size_t SourceURLRealmLimit = 100;
2469
2470static bool RememberSourceURL(JSContext* cx, HandleScript script) {
2471 cx->check(script);
2472
2473 // Sources introduced dynamically are not remembered.
2474 if (script->sourceObject()->unwrappedIntroductionScript()) {
2475 return true;
2476 }
2477
2478 const char* filename = script->filename();
2479 if (!filename ||
2480 strnlen(filename, SourceURLMaxLength + 1) > SourceURLMaxLength) {
2481 return true;
2482 }
2483
2484 Rooted<ArrayObject*> holder(cx, script->global().getSourceURLsHolder());
2485 if (!holder) {
2486 holder = NewDenseEmptyArray(cx);
2487 if (!holder) {
2488 return false;
2489 }
2490 script->global().setSourceURLsHolder(holder);
2491 }
2492
2493 if (holder->length() >= SourceURLRealmLimit) {
2494 return true;
2495 }
2496
2497 RootedString filenameString(cx,
2498 AtomizeUTF8Chars(cx, filename, strlen(filename)));
2499 if (!filenameString) {
2500 return false;
2501 }
2502
2503 // The source URLs holder never escapes to script, so we can treat it as a
2504 // newborn array for the purpose of adding elements.
2505 return NewbornArrayPush(cx, holder, StringValue(filenameString));
2506}
2507
2508void DebugAPI::onNewScript(JSContext* cx, HandleScript script) {
2509 if (!script->realm()->isDebuggee()) {
2510 // Remember the URLs associated with scripts in non-system realms,
2511 // in case the debugger is attached later.
2512 if (!script->realm()->isSystem()) {
2513 if (!RememberSourceURL(cx, script)) {
2514 cx->clearPendingException();
2515 }
2516 }
2517 return;
2518 }
2519
2520 Debugger::dispatchQuietHook(
2521 cx,
2522 [script](Debugger* dbg) -> bool {
2523 return dbg->observesNewScript() && dbg->observesScript(script);
2524 },
2525 [&](Debugger* dbg) -> bool {
2526 BaseScript* base = script.get();
2527 Rooted<DebuggerScriptReferent> scriptReferent(cx, base);
2528 return dbg->fireNewScript(cx, scriptReferent);
2529 });
2530}
2531
2532void DebugAPI::slowPathOnNewWasmInstance(
2533 JSContext* cx, Handle<WasmInstanceObject*> wasmInstance) {
2534 Debugger::dispatchQuietHook(
2535 cx,
2536 [wasmInstance](Debugger* dbg) -> bool {
2537 return dbg->observesNewScript() &&
2538 dbg->observesGlobal(&wasmInstance->global());
2539 },
2540 [&](Debugger* dbg) -> bool {
2541 Rooted<DebuggerScriptReferent> scriptReferent(cx, wasmInstance.get());
2542 return dbg->fireNewScript(cx, scriptReferent);
2543 });
2544}
2545
2546/* static */
2547bool DebugAPI::onTrap(JSContext* cx) {
2548 FrameIter iter(cx);
2549 JS::AutoSaveExceptionState savedExc(cx);
2550 Rooted<GlobalObject*> global(cx);
2551 BreakpointSite* site;
2552 bool isJS; // true when iter.hasScript(), false when iter.isWasm()
2553 jsbytecode* pc; // valid when isJS == true
2554 uint32_t bytecodeOffset; // valid when isJS == false
2555 if (iter.hasScript()) {
2556 RootedScript script(cx, iter.script());
2557 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"
, 2557); AnnotateMozCrashReason("MOZ_ASSERT" "(" "script->isDebuggee()"
")"); do { *((volatile int*)__null) = 2557; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2558 global.set(&script->global());
2559 isJS = true;
2560 pc = iter.pc();
2561 bytecodeOffset = 0;
2562 site = DebugScript::getBreakpointSite(script, pc);
2563 } else {
2564 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"
, 2564); AnnotateMozCrashReason("MOZ_ASSERT" "(" "iter.isWasm()"
")"); do { *((volatile int*)__null) = 2564; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2565 global.set(&iter.wasmInstance()->object()->global());
2566 isJS = false;
2567 pc = nullptr;
2568 bytecodeOffset = iter.wasmBytecodeOffset();
2569 site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
2570 }
2571
2572 // Build list of breakpoint handlers.
2573 //
2574 // This does not need to be rooted: since the JSScript/WasmInstance is on the
2575 // stack, the Breakpoints will not be GC'd. However, they may be deleted, and
2576 // we check for that case below.
2577 Vector<Breakpoint*> triggered(cx);
2578 for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = bp->nextInSite()) {
2579 if (!triggered.append(bp)) {
2580 return false;
2581 }
2582 }
2583
2584 ResumeMode resumeMode = ResumeMode::Continue;
2585 RootedValue rval(cx);
2586
2587 if (triggered.length() > 0) {
2588 // Preserve the debuggee's microtask event queue while we run the hooks, so
2589 // the debugger's microtask checkpoints don't run from the debuggee's
2590 // microtasks, and vice versa.
2591 JS::AutoDebuggerJobQueueInterruption adjqi;
2592 if (!adjqi.init(cx)) {
2593 return false;
2594 }
2595
2596 for (Breakpoint* bp : triggered) {
2597 // Handlers can clear breakpoints. Check that bp still exists.
2598 if (!site || !site->hasBreakpoint(bp)) {
2599 continue;
2600 }
2601
2602 // We have to check whether dbg is debugging this global here: a
2603 // breakpoint handler can disable other Debuggers or remove debuggees.
2604 Debugger* dbg = bp->debugger;
2605 if (dbg->debuggees.has(global)) {
2606 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
2607
2608 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
2609 RootedValue scriptFrame(cx);
2610 if (!dbg->getFrame(cx, iter, &scriptFrame)) {
2611 return false;
2612 }
2613
2614 // Re-wrap the breakpoint's handler for the Debugger's compartment.
2615 // When the handler and the Debugger are in the same compartment (the
2616 // usual case), this actually unwraps it, but there's no requirement
2617 // that they be in the same compartment, so we can't be sure.
2618 Rooted<JSObject*> handler(cx, bp->handler);
2619 if (!cx->compartment()->wrap(cx, &handler)) {
2620 return false;
2621 }
2622
2623 RootedValue rv(cx);
2624 bool ok = CallMethodIfPresent(cx, handler, "hit", 1,
2625 scriptFrame.address(), &rv);
2626
2627 return dbg->processHandlerResult(cx, ok, rv, iter.abstractFramePtr(),
2628 iter.pc(), resumeMode, &rval);
2629 });
2630 adjqi.runJobs();
2631
2632 if (!result) {
2633 return false;
2634 }
2635
2636 // Calling JS code invalidates site. Reload it.
2637 if (isJS) {
2638 site = DebugScript::getBreakpointSite(iter.script(), pc);
2639 } else {
2640 site = iter.wasmInstance()->debug().getBreakpointSite(bytecodeOffset);
2641 }
2642 }
2643 }
2644 }
2645
2646 if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
2647 savedExc.drop();
2648 return false;
2649 }
2650 return true;
2651}
2652
2653/* static */
2654bool DebugAPI::onSingleStep(JSContext* cx) {
2655 FrameIter iter(cx);
2656
2657 // We may be stepping over a JSOp::Exception, that pushes the context's
2658 // pending exception for a 'catch' clause to handle. Don't let the onStep
2659 // handlers mess with that (other than by returning a resumption value).
2660 JS::AutoSaveExceptionState savedExc(cx);
2661
2662 // Build list of Debugger.Frame instances referring to this frame with
2663 // onStep handlers.
2664 Rooted<Debugger::DebuggerFrameVector> frames(cx);
2665 if (!Debugger::getDebuggerFrames(iter.abstractFramePtr(), &frames)) {
2666 ReportOutOfMemory(cx);
2667 return false;
2668 }
2669
2670#ifdef DEBUG1
2671 // Validate the single-step count on this frame's script, to ensure that
2672 // we're not receiving traps we didn't ask for. Even when frames is
2673 // non-empty (and thus we know this trap was requested), do the check
2674 // anyway, to make sure the count has the correct non-zero value.
2675 //
2676 // The converse --- ensuring that we do receive traps when we should --- can
2677 // be done with unit tests.
2678 if (iter.hasScript()) {
2679 uint32_t liveStepperCount = 0;
2680 uint32_t suspendedStepperCount = 0;
2681 JSScript* trappingScript = iter.script();
2682 JS::AutoAssertNoGC nogc;
2683 for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
2684 Debugger* dbg = entry.dbg;
2685 for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
2686 r.popFront()) {
2687 AbstractFramePtr frame = r.front().key();
2688 NativeObject* frameobj = r.front().value();
2689 if (frame.isWasmDebugFrame()) {
2690 continue;
2691 }
2692 if (frame.script() == trappingScript &&
2693 !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
2694 .isUndefined()) {
2695 liveStepperCount++;
2696 }
2697 }
2698
2699 // Also count hooks set on suspended generator frames.
2700 for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all();
2701 !r.empty(); r.popFront()) {
2702 AbstractGeneratorObject& genObj = *r.front().key();
2703 DebuggerFrame& frameObj = *r.front().value();
2704 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"
, 2704); AnnotateMozCrashReason("MOZ_ASSERT" "(" "&frameObj.unwrappedGenerator() == &genObj"
")"); do { *((volatile int*)__null) = 2704; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2705
2706 // Live Debugger.Frames were already counted in dbg->frames loop.
2707 if (frameObj.isOnStack()) {
2708 continue;
2709 }
2710
2711 // A closed generator no longer has a callee so it will not be able to
2712 // compare with the trappingScript.
2713 if (genObj.isClosed()) {
2714 continue;
2715 }
2716
2717 // If a frame isn't live, but it has an entry in generatorFrames,
2718 // it had better be suspended.
2719 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"
, 2719); AnnotateMozCrashReason("MOZ_ASSERT" "(" "genObj.isSuspended()"
")"); do { *((volatile int*)__null) = 2719; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2720
2721 if (genObj.callee().hasBaseScript() &&
2722 genObj.callee().baseScript() == trappingScript &&
2723 !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT)
2724 .isUndefined()) {
2725 suspendedStepperCount++;
2726 }
2727 }
2728 }
2729
2730 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"
, 2731); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
")"); do { *((volatile int*)__null) = 2731; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2731 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"
, 2731); AnnotateMozCrashReason("MOZ_ASSERT" "(" "liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)"
")"); do { *((volatile int*)__null) = 2731; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2732 }
2733#endif
2734
2735 RootedValue rval(cx);
2736 ResumeMode resumeMode = ResumeMode::Continue;
2737
2738 if (frames.length() > 0) {
2739 // Preserve the debuggee's microtask event queue while we run the hooks, so
2740 // the debugger's microtask checkpoints don't run from the debuggee's
2741 // microtasks, and vice versa.
2742 JS::AutoDebuggerJobQueueInterruption adjqi;
2743 if (!adjqi.init(cx)) {
2744 return false;
2745 }
2746
2747 // Call onStep for frames that have the handler set.
2748 for (size_t i = 0; i < frames.length(); i++) {
2749 Handle<DebuggerFrame*> frame = frames[i];
2750 OnStepHandler* handler = frame->onStepHandler();
2751 if (!handler) {
2752 continue;
2753 }
2754
2755 Debugger* dbg = frame->owner();
2756 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
2757
2758 bool result = dbg->enterDebuggerHook(cx, [&]() -> bool {
2759 ResumeMode nextResumeMode = ResumeMode::Continue;
2760 RootedValue nextValue(cx);
2761
2762 bool success = handler->onStep(cx, frame, nextResumeMode, &nextValue);
2763 return dbg->processParsedHandlerResult(
2764 cx, iter.abstractFramePtr(), iter.pc(), success, nextResumeMode,
2765 nextValue, resumeMode, &rval);
2766 });
2767 adjqi.runJobs();
2768
2769 if (!result) {
2770 return false;
2771 }
2772 }
2773 }
2774
2775 if (!ApplyFrameResumeMode(cx, iter.abstractFramePtr(), resumeMode, rval)) {
2776 savedExc.drop();
2777 return false;
2778 }
2779 return true;
2780}
2781
2782bool Debugger::fireNewGlobalObject(JSContext* cx,
2783 Handle<GlobalObject*> global) {
2784 RootedObject hook(cx, getHook(OnNewGlobalObject));
2785 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"
, 2785); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook" ")");
do { *((volatile int*)__null) = 2785; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2786 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"
, 2786); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook->isCallable()"
")"); do { *((volatile int*)__null) = 2786; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2787
2788 RootedValue wrappedGlobal(cx, ObjectValue(*global));
2789 if (!wrapDebuggeeValue(cx, &wrappedGlobal)) {
2790 return false;
2791 }
2792
2793 // onNewGlobalObject is infallible, and thus is only allowed to return
2794 // undefined as a resumption value. If it returns anything else, we throw.
2795 // And if that happens, or if the hook itself throws, we invoke the
2796 // uncaughtExceptionHook so that we never leave an exception pending on the
2797 // cx. This allows JS_NewGlobalObject to avoid handling failures from
2798 // debugger hooks.
2799 RootedValue rv(cx);
2800 RootedValue fval(cx, ObjectValue(*hook));
2801 bool ok = js::Call(cx, fval, object, wrappedGlobal, &rv);
2802 if (ok && !rv.isUndefined()) {
2803 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2804 JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
2805 ok = false;
2806 }
2807
2808 return ok || handleUncaughtException(cx);
2809}
2810
2811void DebugAPI::slowPathOnNewGlobalObject(JSContext* cx,
2812 Handle<GlobalObject*> global) {
2813 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"
, 2813); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()"
")"); do { *((volatile int*)__null) = 2813; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2814 if (global->realm()->creationOptions().invisibleToDebugger()) {
2815 return;
2816 }
2817
2818 // Make a copy of the runtime's onNewGlobalObjectWatchers before running the
2819 // handlers. Since one Debugger's handler can disable another's, the list
2820 // can be mutated while we're walking it.
2821 RootedObjectVector watchers(cx);
2822 for (auto& dbg : cx->runtime()->onNewGlobalObjectWatchers()) {
2823 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"
, 2823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbg.observesNewGlobalObject()"
")"); do { *((volatile int*)__null) = 2823; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2824 JSObject* obj = dbg.object;
2825 JS::ExposeObjectToActiveJS(obj);
2826 if (!watchers.append(obj)) {
2827 if (cx->isExceptionPending()) {
2828 cx->clearPendingException();
2829 }
2830 return;
2831 }
2832 }
2833
2834 // Preserve the debuggee's microtask event queue while we run the hooks, so
2835 // the debugger's microtask checkpoints don't run from the debuggee's
2836 // microtasks, and vice versa.
2837 JS::AutoDebuggerJobQueueInterruption adjqi;
2838 if (!adjqi.init(cx)) {
2839 cx->clearPendingException();
2840 return;
2841 }
2842
2843 for (size_t i = 0; i < watchers.length(); i++) {
2844 Debugger* dbg = Debugger::fromJSObject(watchers[i]);
2845 EnterDebuggeeNoExecute nx(cx, *dbg, adjqi);
2846
2847 if (dbg->observesNewGlobalObject()) {
2848 bool result = dbg->enterDebuggerHook(
2849 cx, [&]() -> bool { return dbg->fireNewGlobalObject(cx, global); });
2850 adjqi.runJobs();
2851
2852 if (!result) {
2853 // Like other quiet hooks using dispatchQuietHook, this hook
2854 // silently ignores all errors that propagate out of it and aren't
2855 // already handled by the hook error reporting.
2856 cx->clearPendingException();
2857 break;
2858 }
2859 }
2860 }
2861 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"
, 2861); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!cx->isExceptionPending()"
")"); do { *((volatile int*)__null) = 2861; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2862}
2863
2864/* static */
2865void DebugAPI::slowPathOnGeneratorClosed(JSContext* cx,
2866 AbstractGeneratorObject* genObj) {
2867 JS::AutoAssertNoGC nogc;
2868 for (Realm::DebuggerVectorEntry& entry : cx->global()->getDebuggers(nogc)) {
2869 Debugger* dbg = entry.dbg;
2870 if (Debugger::GeneratorWeakMap::Ptr frameEntry =
2871 dbg->generatorFrames.lookup(genObj)) {
2872 DebuggerFrame* frameObj = frameEntry->value();
2873 frameObj->onGeneratorClosed(cx->gcContext());
2874 }
2875 }
2876}
2877
2878/* static */
2879void DebugAPI::slowPathNotifyParticipatesInGC(uint64_t majorGCNumber,
2880 Realm::DebuggerVector& dbgs,
2881 const JS::AutoRequireNoGC& nogc) {
2882 for (Realm::DebuggerVector::Range r = dbgs.all(); !r.empty(); r.popFront()) {
2883 if (!r.front().dbg.unbarrieredGet()->debuggeeIsBeingCollected(
2884 majorGCNumber)) {
2885#ifdef DEBUG1
2886 fprintf(stderrstderr,
2887 "OOM while notifying observing Debuggers of a GC: The "
2888 "onGarbageCollection\n"
2889 "hook will not be fired for this GC for some Debuggers!\n");
2890#endif
2891 return;
2892 }
2893 }
2894}
2895
2896/* static */
2897Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) {
2898 JS::AutoAssertNoGC nogc;
2899 Realm::DebuggerVector& dbgs = global->getDebuggers(nogc);
2900 if (dbgs.empty()) {
2901 return Nothing();
2902 }
2903
2904 DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
2905
2906 double probability = 0;
2907 bool foundAnyDebuggers = false;
2908 for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
2909 // The set of debuggers had better not change while we're iterating,
2910 // such that the vector gets reallocated.
2911 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"
, 2911); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbgs.begin() == begin"
")"); do { *((volatile int*)__null) = 2911; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2912 // Use unbarrieredGet() to prevent triggering read barrier while collecting,
2913 // this is safe as long as dbgp does not escape.
2914 Debugger* dbgp = p->dbg.unbarrieredGet();
2915
2916 if (dbgp->trackingAllocationSites) {
2917 foundAnyDebuggers = true;
2918 probability = std::max(dbgp->allocationSamplingProbability, probability);
2919 }
2920 }
2921
2922 return foundAnyDebuggers ? Some(probability) : Nothing();
2923}
2924
2925/* static */
2926bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj,
2927 Handle<SavedFrame*> frame,
2928 mozilla::TimeStamp when,
2929 Realm::DebuggerVector& dbgs,
2930 const gc::AutoSuppressGC& nogc) {
2931 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"
, 2931); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!dbgs.empty()"
")"); do { *((volatile int*)__null) = 2931; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2932 mozilla::DebugOnly<Realm::DebuggerVectorEntry*> begin = dbgs.begin();
2933
2934 // GC is suppressed so we can iterate over the debuggers; appendAllocationSite
2935 // calls Compartment::wrap, and thus could GC.
2936
2937 for (auto p = dbgs.begin(); p < dbgs.end(); p++) {
2938 // The set of debuggers had better not change while we're iterating,
2939 // such that the vector gets reallocated.
2940 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"
, 2940); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbgs.begin() == begin"
")"); do { *((volatile int*)__null) = 2940; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2941
2942 if (p->dbg->trackingAllocationSites &&
2943 !p->dbg->appendAllocationSite(cx, obj, frame, when)) {
2944 return false;
2945 }
2946 }
2947
2948 return true;
2949}
2950
2951bool Debugger::isDebuggeeUnbarriered(const Realm* realm) const {
2952 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"
, 2952); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realm" ")")
; do { *((volatile int*)__null) = 2952; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2953 return realm->isDebuggee() &&
2954 debuggees.has(realm->unsafeUnbarrieredMaybeGlobal());
2955}
2956
2957bool Debugger::appendAllocationSite(JSContext* cx, HandleObject obj,
2958 Handle<SavedFrame*> frame,
2959 mozilla::TimeStamp when) {
2960 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"
, 2960); AnnotateMozCrashReason("MOZ_ASSERT" "(" "trackingAllocationSites"
")"); do { *((volatile int*)__null) = 2960; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2961
2962 AutoRealm ar(cx, object);
2963 RootedObject wrappedFrame(cx, frame);
2964 if (!cx->compartment()->wrap(cx, &wrappedFrame)) {
2965 return false;
2966 }
2967
2968 auto className = obj->getClass()->name;
2969 auto size =
2970 JS::ubi::Node(obj.get()).size(cx->runtime()->debuggerMallocSizeOf);
2971 auto inNursery = gc::IsInsideNursery(obj);
2972
2973 if (!allocationsLog.emplaceBack(wrappedFrame, when, className, size,
2974 inNursery)) {
2975 ReportOutOfMemory(cx);
2976 return false;
2977 }
2978
2979 if (allocationsLog.length() > maxAllocationsLogLength) {
2980 allocationsLog.popFront();
2981 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"
, 2981); AnnotateMozCrashReason("MOZ_ASSERT" "(" "allocationsLog.length() == maxAllocationsLogLength"
")"); do { *((volatile int*)__null) = 2981; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2982 allocationsLogOverflowed = true;
2983 }
2984
2985 return true;
2986}
2987
2988bool Debugger::firePromiseHook(JSContext* cx, Hook hook, HandleObject promise) {
2989 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"
, 2989); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook == OnNewPromise || hook == OnPromiseSettled"
")"); do { *((volatile int*)__null) = 2989; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2990
2991 RootedObject hookObj(cx, getHook(hook));
2992 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"
, 2992); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hookObj" ")"
); do { *((volatile int*)__null) = 2992; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2993 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"
, 2993); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hookObj->isCallable()"
")"); do { *((volatile int*)__null) = 2993; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2994
2995 RootedValue dbgObj(cx, ObjectValue(*promise));
2996 if (!wrapDebuggeeValue(cx, &dbgObj)) {
2997 return false;
2998 }
2999
3000 // Like onNewGlobalObject, the Promise hooks are infallible and the comments
3001 // in |Debugger::fireNewGlobalObject| apply here as well.
3002 RootedValue fval(cx, ObjectValue(*hookObj));
3003 RootedValue rv(cx);
3004 bool ok = js::Call(cx, fval, object, dbgObj, &rv);
3005 if (ok && !rv.isUndefined()) {
3006 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3007 JSMSG_DEBUG_RESUMPTION_VALUE_DISALLOWED);
3008 ok = false;
3009 }
3010
3011 return ok || handleUncaughtException(cx);
3012}
3013
3014/* static */
3015void Debugger::slowPathPromiseHook(JSContext* cx, Hook hook,
3016 Handle<PromiseObject*> promise) {
3017 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"
, 3017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hook == OnNewPromise || hook == OnPromiseSettled"
")"); do { *((volatile int*)__null) = 3017; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3018
3019 if (hook == OnPromiseSettled) {
3020 // We should be in the right compartment, but for simplicity always enter
3021 // the promise's realm below.
3022 cx->check(promise);
3023 }
3024
3025 AutoRealm ar(cx, promise);
3026
3027 Debugger::dispatchQuietHook(
3028 cx, [hook](Debugger* dbg) -> bool { return dbg->getHook(hook); },
3029 [&](Debugger* dbg) -> bool {
3030 return dbg->firePromiseHook(cx, hook, promise);
3031 });
3032}
3033
3034/* static */
3035void DebugAPI::slowPathOnNewPromise(JSContext* cx,
3036 Handle<PromiseObject*> promise) {
3037 Debugger::slowPathPromiseHook(cx, Debugger::OnNewPromise, promise);
3038}
3039
3040/* static */
3041void DebugAPI::slowPathOnPromiseSettled(JSContext* cx,
3042 Handle<PromiseObject*> promise) {
3043 Debugger::slowPathPromiseHook(cx, Debugger::OnPromiseSettled, promise);
3044}
3045
3046/*** Debugger code invalidation for observing execution *********************/
3047
3048class MOZ_RAII ExecutionObservableRealms
3049 : public DebugAPI::ExecutionObservableSet {
3050 HashSet<Realm*> realms_;
3051 HashSet<Zone*> zones_;
3052
3053 public:
3054 explicit ExecutionObservableRealms(JSContext* cx) : realms_(cx), zones_(cx) {}
3055
3056 bool add(Realm* realm) {
3057 return realms_.put(realm) && zones_.put(realm->zone());
3058 }
3059
3060 using RealmRange = HashSet<Realm*>::Range;
3061 const HashSet<Realm*>* realms() const { return &realms_; }
3062
3063 const HashSet<Zone*>* zones() const override { return &zones_; }
3064 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3065 return script->hasBaselineScript() && realms_.has(script->realm());
3066 }
3067 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3068 // AbstractFramePtr can't refer to non-remateralized Ion frames or
3069 // non-debuggee wasm frames, so if iter refers to one such, we know we
3070 // don't match.
3071 return iter.hasUsableAbstractFramePtr() && realms_.has(iter.realm());
3072 }
3073};
3074
3075// Given a particular AbstractFramePtr F that has become observable, this
3076// represents the stack frames that need to be bailed out or marked as
3077// debuggees, and the scripts that need to be recompiled, taking inlining into
3078// account.
3079class MOZ_RAII ExecutionObservableFrame
3080 : public DebugAPI::ExecutionObservableSet {
3081 AbstractFramePtr frame_;
3082
3083 public:
3084 explicit ExecutionObservableFrame(AbstractFramePtr frame) : frame_(frame) {}
3085
3086 Zone* singleZone() const override {
3087 // We never inline across realms, let alone across zones, so
3088 // frames_'s script's zone is the only one of interest.
3089 return frame_.script()->zone();
3090 }
3091
3092 JSScript* singleScriptForZoneInvalidation() const override {
3093 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"
, 3094); AnnotateMozCrashReason("MOZ_CRASH(" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
")"); do { *((volatile int*)__null) = 3094; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
3094 "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"
, 3094); AnnotateMozCrashReason("MOZ_CRASH(" "ExecutionObservableFrame shouldn't need zone-wide invalidation."
")"); do { *((volatile int*)__null) = 3094; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
3095 return nullptr;
3096 }
3097
3098 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3099 // Normally, *this represents exactly one script: the one frame_ is
3100 // running.
3101 //
3102 // However, debug-mode OSR uses *this for both invalidating Ion frames,
3103 // and recompiling the Baseline scripts that those Ion frames will bail
3104 // out into. Suppose frame_ is an inline frame, executing a copy of its
3105 // JSScript, S_inner, that has been inlined into the IonScript of some
3106 // other JSScript, S_outer. We must match S_outer, to decide which Ion
3107 // frame to invalidate; and we must match S_inner, to decide which
3108 // Baseline script to recompile.
3109 //
3110 // Note that this does not, by design, invalidate *all* inliners of
3111 // frame_.script(), as only frame_ is made observable, not
3112 // frame_.script().
3113 if (!script->hasBaselineScript()) {
3114 return false;
3115 }
3116
3117 if (frame_.hasScript() && script == frame_.script()) {
3118 return true;
3119 }
3120
3121 return frame_.isRematerializedFrame() &&
3122 script == frame_.asRematerializedFrame()->outerScript();
3123 }
3124
3125 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3126 // AbstractFramePtr can't refer to non-remateralized Ion frames or
3127 // non-debuggee wasm frames, so if iter refers to one such, we know we
3128 // don't match.
3129 //
3130 // We never use this 'has' overload for frame invalidation, only for
3131 // frame debuggee marking; so this overload doesn't need a parallel to
3132 // the just-so inlining logic above.
3133 return iter.hasUsableAbstractFramePtr() &&
3134 iter.abstractFramePtr() == frame_;
3135 }
3136};
3137
3138class MOZ_RAII ExecutionObservableScript
3139 : public DebugAPI::ExecutionObservableSet {
3140 RootedScript script_;
3141
3142 public:
3143 ExecutionObservableScript(JSContext* cx, JSScript* script)
3144 : script_(cx, script) {}
3145
3146 Zone* singleZone() const override { return script_->zone(); }
3147 JSScript* singleScriptForZoneInvalidation() const override { return script_; }
3148 bool shouldRecompileOrInvalidate(JSScript* script) const override {
3149 return script->hasBaselineScript() && script == script_;
3150 }
3151 bool shouldMarkAsDebuggee(FrameIter& iter) const override {
3152 // AbstractFramePtr can't refer to non-remateralized Ion frames, and
3153 // while a non-rematerialized Ion frame may indeed be running script_,
3154 // we cannot mark them as debuggees until they bail out.
3155 //
3156 // Upon bailing out, any newly constructed Baseline frames that came
3157 // from Ion frames with scripts that are isDebuggee() is marked as
3158 // debuggee. This is correct in that the only other way a frame may be
3159 // marked as debuggee is via Debugger.Frame reflection, which would
3160 // have rematerialized any Ion frames.
3161 //
3162 // Also AbstractFramePtr can't refer to non-debuggee wasm frames, so if
3163 // iter refers to one such, we know we don't match.
3164 return iter.hasUsableAbstractFramePtr() && !iter.isWasm() &&
3165 iter.abstractFramePtr().script() == script_;
3166 }
3167};
3168
3169/* static */
3170bool Debugger::updateExecutionObservabilityOfFrames(
3171 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
3172 IsObserving observing) {
3173 AutoSuppressProfilerSampling suppressProfilerSampling(cx);
3174
3175 if (!jit::RecompileOnStackBaselineScriptsForDebugMode(cx, obs, observing)) {
3176 return false;
3177 }
3178
3179 AbstractFramePtr oldestEnabledFrame;
3180 for (AllFramesIter iter(cx); !iter.done(); ++iter) {
3181 if (obs.shouldMarkAsDebuggee(iter)) {
3182 if (observing) {
3183 if (!iter.abstractFramePtr().isDebuggee()) {
3184 oldestEnabledFrame = iter.abstractFramePtr();
3185 oldestEnabledFrame.setIsDebuggee();
3186 }
3187 if (iter.abstractFramePtr().isWasmDebugFrame()) {
3188 iter.abstractFramePtr().asWasmDebugFrame()->observe(cx);
3189 }
3190 } else {
3191#ifdef DEBUG1
3192 // Debugger.Frame lifetimes are managed by the debug epilogue,
3193 // so in general it's unsafe to unmark a frame if it has a
3194 // Debugger.Frame associated with it.
3195 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"
, 3195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!DebugAPI::inFrameMaps(iter.abstractFramePtr())"
")"); do { *((volatile int*)__null) = 3195; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3196#endif
3197 iter.abstractFramePtr().unsetIsDebuggee();
3198 }
3199 }
3200 }
3201
3202 // See comment in unsetPrevUpToDateUntil.
3203 if (oldestEnabledFrame) {
3204 AutoRealm ar(cx, oldestEnabledFrame.environmentChain());
3205 DebugEnvironments::unsetPrevUpToDateUntil(cx, oldestEnabledFrame);
3206 }
3207
3208 return true;
3209}
3210
3211static inline void MarkJitScriptActiveIfObservable(
3212 JSScript* script, const DebugAPI::ExecutionObservableSet& obs) {
3213 if (obs.shouldRecompileOrInvalidate(script)) {
3214 script->jitScript()->icScript()->setActive();
3215 }
3216}
3217
3218static bool AppendAndInvalidateScript(JSContext* cx, Zone* zone,
3219 JSScript* script,
3220 jit::RecompileInfoVector& invalid,
3221 Vector<JSScript*>& scripts) {
3222 // Enter the script's realm as AddPendingInvalidation attempts to
3223 // cancel off-thread compilations, whose books are kept on the
3224 // script's realm.
3225 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"
, 3225); AnnotateMozCrashReason("MOZ_ASSERT" "(" "script->zone() == zone"
")"); do { *((volatile int*)__null) = 3225; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3226 AutoRealm ar(cx, script);
3227 AddPendingInvalidation(invalid, script);
3228 return scripts.append(script);
3229}
3230
3231static bool UpdateExecutionObservabilityOfScriptsInZone(
3232 JSContext* cx, Zone* zone, const DebugAPI::ExecutionObservableSet& obs,
3233 Debugger::IsObserving observing) {
3234 using namespace js::jit;
3235
3236 AutoSuppressProfilerSampling suppressProfilerSampling(cx);
3237
3238 JS::GCContext* gcx = cx->gcContext();
3239
3240 Vector<JSScript*> scripts(cx);
3241
3242 // Iterate through observable scripts, invalidating their Ion scripts and
3243 // appending them to a vector for discarding their baseline scripts later.
3244 {
3245 RecompileInfoVector invalid;
3246 if (JSScript* script = obs.singleScriptForZoneInvalidation()) {
3247 if (obs.shouldRecompileOrInvalidate(script)) {
3248 if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
3249 return false;
3250 }
3251 }
3252 } else {
3253 for (auto base = zone->cellIter<BaseScript>(); !base.done();
3254 base.next()) {
3255 if (!base->hasJitScript()) {
3256 continue;
3257 }
3258 JSScript* script = base->asJSScript();
3259 if (obs.shouldRecompileOrInvalidate(script)) {
3260 if (!AppendAndInvalidateScript(cx, zone, script, invalid, scripts)) {
3261 return false;
3262 }
3263 }
3264 }
3265 }
3266 Invalidate(cx, invalid);
3267 }
3268
3269 for (size_t i = 0; i < scripts.length(); i++) {
3270 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"
, 3270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!scripts[i]->jitScript()->icScript()->active()"
")"); do { *((volatile int*)__null) = 3270; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3271 }
3272
3273 // Code below this point must be infallible to ensure the active bit of
3274 // BaselineScripts is in a consistent state.
3275 //
3276 // Mark active baseline scripts in the observable set so that they don't
3277 // get discarded. They will be recompiled.
3278 for (JitActivationIterator actIter(cx); !actIter.done(); ++actIter) {
3279 if (actIter->compartment()->zone() != zone) {
3280 continue;
3281 }
3282
3283 for (OnlyJSJitFrameIter iter(actIter); !iter.done(); ++iter) {
3284 const JSJitFrameIter& frame = iter.frame();
3285 switch (frame.type()) {
3286 case FrameType::BaselineJS:
3287 MarkJitScriptActiveIfObservable(frame.script(), obs);
3288 break;
3289 case FrameType::IonJS:
3290 MarkJitScriptActiveIfObservable(frame.script(), obs);
3291 for (InlineFrameIterator inlineIter(cx, &frame); inlineIter.more();
3292 ++inlineIter) {
3293 MarkJitScriptActiveIfObservable(inlineIter.script(), obs);
3294 }
3295 break;
3296 default:;
3297 }
3298 }
3299 }
3300
3301 // Iterate through the scripts again and finish discarding
3302 // BaselineScripts. This must be done as a separate phase as we can only
3303 // discard the BaselineScript on scripts that have no IonScript.
3304 for (size_t i = 0; i < scripts.length(); i++) {
3305 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"
, 3305); AnnotateMozCrashReason("MOZ_ASSERT" "(" "observing" ")"
); do { *((volatile int*)__null) = 3305; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3306 if (!scripts[i]->jitScript()->icScript()->active()) {
3307 FinishDiscardBaselineScript(gcx, scripts[i]);
3308 }
3309 scripts[i]->jitScript()->icScript()->resetActive();
3310 }
3311
3312 // Iterate through all wasm instances to find ones that need to be updated.
3313 for (RealmsInZoneIter r(zone); !r.done(); r.next()) {
3314 for (wasm::Instance* instance : r->wasm.instances()) {
3315 if (!instance->debugEnabled()) {
3316 continue;
3317 }
3318
3319 bool enableTrap = observing == Debugger::Observing;
3320 instance->debug().ensureEnterFrameTrapsState(cx, instance, enableTrap);
3321 }
3322 }
3323
3324 return true;
3325}
3326
3327/* static */
3328bool Debugger::updateExecutionObservabilityOfScripts(
3329 JSContext* cx, const DebugAPI::ExecutionObservableSet& obs,
3330 IsObserving observing) {
3331 if (Zone* zone = obs.singleZone()) {
3332 return UpdateExecutionObservabilityOfScriptsInZone(cx, zone, obs,
3333 observing);
3334 }
3335
3336 using ZoneRange = DebugAPI::ExecutionObservableSet::ZoneRange;
3337 for (ZoneRange r = obs.zones()->all(); !r.empty(); r.popFront()) {
3338 if (!UpdateExecutionObservabilityOfScriptsInZone(cx, r.front(), obs,
3339 observing)) {
3340 return false;
3341 }
3342 }
3343
3344 return true;
3345}
3346
3347template <typename FrameFn>
3348/* static */
3349void Debugger::forEachOnStackDebuggerFrame(AbstractFramePtr frame,
3350 const JS::AutoRequireNoGC& nogc,
3351 FrameFn fn) {
3352 for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
3353 Debugger* dbg = entry.dbg;
3354 if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
3355 fn(dbg, frameEntry->value());
3356 }
3357 }
3358}
3359
3360template <typename FrameFn>
3361/* static */
3362void Debugger::forEachOnStackOrSuspendedDebuggerFrame(
3363 JSContext* cx, AbstractFramePtr frame, const JS::AutoRequireNoGC& nogc,
3364 FrameFn fn) {
3365 Rooted<AbstractGeneratorObject*> genObj(
3366 cx, frame.isGeneratorFrame() ? GetGeneratorObjectForFrame(cx, frame)
3367 : nullptr);
3368
3369 for (Realm::DebuggerVectorEntry& entry : frame.global()->getDebuggers(nogc)) {
3370 Debugger* dbg = entry.dbg;
3371
3372 DebuggerFrame* frameObj = nullptr;
3373 if (FrameMap::Ptr frameEntry = dbg->frames.lookup(frame)) {
3374 frameObj = frameEntry->value();
3375 } else if (GeneratorWeakMap::Ptr frameEntry =
3376 dbg->generatorFrames.lookup(genObj)) {
3377 frameObj = frameEntry->value();
3378 }
3379
3380 if (frameObj) {
3381 fn(dbg, frameObj);
3382 }
3383 }
3384}
3385
3386/* static */
3387bool Debugger::getDebuggerFrames(AbstractFramePtr frame,
3388 MutableHandle<DebuggerFrameVector> frames) {
3389 bool hadOOM = false;
3390 JS::AutoAssertNoGC nogc;
3391 forEachOnStackDebuggerFrame(frame, nogc,
3392 [&](Debugger*, DebuggerFrame* frameobj) {
3393 if (!hadOOM && !frames.append(frameobj)) {
3394 hadOOM = true;
3395 }
3396 });
3397 return !hadOOM;
3398}
3399
3400/* static */
3401bool Debugger::updateExecutionObservability(
3402 JSContext* cx, DebugAPI::ExecutionObservableSet& obs,
3403 IsObserving observing) {
3404 if (!obs.singleZone() && obs.zones()->empty()) {
3405 return true;
3406 }
3407
3408 // Invalidate scripts first so we can set the needsArgsObj flag on scripts
3409 // before patching frames.
3410 return updateExecutionObservabilityOfScripts(cx, obs, observing) &&
3411 updateExecutionObservabilityOfFrames(cx, obs, observing);
3412}
3413
3414/* static */
3415bool Debugger::ensureExecutionObservabilityOfScript(JSContext* cx,
3416 JSScript* script) {
3417 if (script->isDebuggee()) {
3418 return true;
3419 }
3420 ExecutionObservableScript obs(cx, script);
3421 return updateExecutionObservability(cx, obs, Observing);
3422}
3423
3424/* static */
3425bool DebugAPI::ensureExecutionObservabilityOfOsrFrame(
3426 JSContext* cx, AbstractFramePtr osrSourceFrame) {
3427 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"
, 3427); AnnotateMozCrashReason("MOZ_ASSERT" "(" "osrSourceFrame.isDebuggee()"
")"); do { *((volatile int*)__null) = 3427; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3428 if (osrSourceFrame.script()->hasBaselineScript() &&
3429 osrSourceFrame.script()->baselineScript()->hasDebugInstrumentation()) {
3430 return true;
3431 }
3432 ExecutionObservableFrame obs(osrSourceFrame);
3433 return Debugger::updateExecutionObservabilityOfFrames(cx, obs, Observing);
3434}
3435
3436/* static */
3437bool Debugger::ensureExecutionObservabilityOfFrame(JSContext* cx,
3438 AbstractFramePtr frame) {
3439 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"
, 3440); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.isDebuggee()"
")"); do { *((volatile int*)__null) = 3440; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
3440 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"
, 3440); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.isDebuggee()"
")"); do { *((volatile int*)__null) = 3440; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3441 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"
, 3441); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frame.wasmInstance()->debugEnabled()"
")"); do { *((volatile int*)__null) = 3441; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3442 if (frame.isDebuggee()) {
3443 return true;
3444 }
3445 ExecutionObservableFrame obs(frame);
3446 return updateExecutionObservabilityOfFrames(cx, obs, Observing);
3447}
3448
3449/* static */
3450bool Debugger::ensureExecutionObservabilityOfRealm(JSContext* cx,
3451 Realm* realm) {
3452 if (realm->debuggerObservesAllExecution()) {
3453 return true;
3454 }
3455 ExecutionObservableRealms obs(cx);
3456 if (!obs.add(realm)) {
3457 return false;
3458 }
3459 realm->updateDebuggerObservesAllExecution();
3460 return updateExecutionObservability(cx, obs, Observing);
3461}
3462
3463/* static */
3464bool Debugger::hookObservesAllExecution(Hook which) {
3465 return which == OnEnterFrame;
3466}
3467
3468Debugger::IsObserving Debugger::observesAllExecution() const {
3469 if (!!getHook(OnEnterFrame)) {
3470 return Observing;
3471 }
3472 return NotObserving;
3473}
3474
3475Debugger::IsObserving Debugger::observesAsmJS() const {
3476 if (!allowUnobservedAsmJS) {
3477 return Observing;
3478 }
3479 return NotObserving;
3480}
3481
3482Debugger::IsObserving Debugger::observesWasm() const {
3483 if (!allowUnobservedWasm) {
3484 return Observing;
3485 }
3486 return NotObserving;
3487}
3488
3489Debugger::IsObserving Debugger::observesCoverage() const {
3490 if (collectCoverageInfo) {
3491 return Observing;
3492 }
3493 return NotObserving;
3494}
3495
3496Debugger::IsObserving Debugger::observesNativeCalls() const {
3497 if (getHook(Debugger::OnNativeCall)) {
3498 return Observing;
3499 }
3500 return NotObserving;
3501}
3502
3503bool Debugger::isExclusiveDebuggerOnEval() const {
3504 return exclusiveDebuggerOnEval;
3505}
3506
3507// Toggle whether this Debugger's debuggees observe all execution. This is
3508// called when a hook that observes all execution is set or unset. See
3509// hookObservesAllExecution.
3510bool Debugger::updateObservesAllExecutionOnDebuggees(JSContext* cx,
3511 IsObserving observing) {
3512 ExecutionObservableRealms obs(cx);
3513
3514 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3515 r.popFront()) {
3516 GlobalObject* global = r.front();
3517 JS::Realm* realm = global->realm();
3518
3519 if (realm->debuggerObservesAllExecution() == observing) {
3520 continue;
3521 }
3522
3523 // It's expensive to eagerly invalidate and recompile a realm,
3524 // so add the realm to the set only if we are observing.
3525 if (observing && !obs.add(realm)) {
3526 return false;
3527 }
3528 }
3529
3530 if (!updateExecutionObservability(cx, obs, observing)) {
3531 return false;
3532 }
3533
3534 using RealmRange = ExecutionObservableRealms::RealmRange;
3535 for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront()) {
3536 r.front()->updateDebuggerObservesAllExecution();
3537 }
3538
3539 return true;
3540}
3541
3542bool Debugger::updateObservesCoverageOnDebuggees(JSContext* cx,
3543 IsObserving observing) {
3544 ExecutionObservableRealms obs(cx);
3545
3546 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3547 r.popFront()) {
3548 GlobalObject* global = r.front();
3549 Realm* realm = global->realm();
3550
3551 if (realm->debuggerObservesCoverage() == observing) {
3552 continue;
3553 }
3554
3555 // Invalidate and recompile a realm to add or remove PCCounts
3556 // increments. We have to eagerly invalidate, as otherwise we might have
3557 // dangling pointers to freed PCCounts.
3558 if (!obs.add(realm)) {
3559 return false;
3560 }
3561 }
3562
3563 // If any frame on the stack belongs to the debuggee, then we cannot update
3564 // the ScriptCounts, because this would imply to invalidate a Debugger.Frame
3565 // to recompile it with/without ScriptCount support.
3566 for (FrameIter iter(cx); !iter.done(); ++iter) {
3567 if (obs.shouldMarkAsDebuggee(iter)) {
3568 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3569 JSMSG_DEBUG_NOT_IDLE);
3570 return false;
3571 }
3572 }
3573
3574 if (!updateExecutionObservability(cx, obs, observing)) {
3575 return false;
3576 }
3577
3578 // All realms can safely be toggled, and all scripts will be recompiled.
3579 // Thus we can update each realm accordingly.
3580 using RealmRange = ExecutionObservableRealms::RealmRange;
3581 for (RealmRange r = obs.realms()->all(); !r.empty(); r.popFront()) {
3582 r.front()->updateDebuggerObservesCoverage();
3583 }
3584
3585 return true;
3586}
3587
3588void Debugger::updateObservesAsmJSOnDebuggees(IsObserving observing) {
3589 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3590 r.popFront()) {
3591 GlobalObject* global = r.front();
3592 Realm* realm = global->realm();
3593
3594 if (realm->debuggerObservesAsmJS() == observing) {
3595 continue;
3596 }
3597
3598 realm->updateDebuggerObservesAsmJS();
3599 }
3600}
3601
3602void Debugger::updateObservesWasmOnDebuggees(IsObserving observing) {
3603 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3604 r.popFront()) {
3605 GlobalObject* global = r.front();
3606 Realm* realm = global->realm();
3607
3608 if (realm->debuggerObservesWasm() == observing) {
3609 continue;
3610 }
3611
3612 realm->updateDebuggerObservesWasm();
3613 }
3614}
3615
3616void Debugger::updateObservesNativeCallOnDebuggees(IsObserving observing) {
3617 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3618 r.popFront()) {
3619 GlobalObject* global = r.front();
3620 Realm* realm = global->realm();
3621
3622 if (realm->debuggerObservesNativeCall() == observing) {
3623 continue;
3624 }
3625
3626 realm->updateDebuggerObservesNativeCall();
3627 }
3628}
3629
3630/*** Allocations Tracking ***************************************************/
3631
3632/* static */
3633bool Debugger::cannotTrackAllocations(const GlobalObject& global) {
3634 auto existingCallback = global.realm()->getAllocationMetadataBuilder();
3635 return existingCallback && existingCallback != &SavedStacks::metadataBuilder;
3636}
3637
3638/* static */
3639bool DebugAPI::isObservedByDebuggerTrackingAllocations(
3640 const GlobalObject& debuggee) {
3641 JS::AutoAssertNoGC nogc;
3642 for (Realm::DebuggerVectorEntry& entry : debuggee.getDebuggers(nogc)) {
3643 // Use unbarrieredGet() to prevent triggering read barrier while
3644 // collecting, this is safe as long as dbg does not escape.
3645 Debugger* dbg = entry.dbg.unbarrieredGet();
3646 if (dbg->trackingAllocationSites) {
3647 return true;
3648 }
3649 }
3650
3651 return false;
3652}
3653
3654/* static */
3655bool Debugger::addAllocationsTracking(JSContext* cx,
3656 Handle<GlobalObject*> debuggee) {
3657 // Precondition: the given global object is being observed by at least one
3658 // Debugger that is tracking allocations.
3659 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"
, 3659); AnnotateMozCrashReason("MOZ_ASSERT" "(" "DebugAPI::isObservedByDebuggerTrackingAllocations(*debuggee)"
")"); do { *((volatile int*)__null) = 3659; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3660
3661 if (Debugger::cannotTrackAllocations(*debuggee)) {
3662 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3663 JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
3664 return false;
3665 }
3666
3667 debuggee->realm()->setAllocationMetadataBuilder(
3668 &SavedStacks::metadataBuilder);
3669 debuggee->realm()->chooseAllocationSamplingProbability();
3670 return true;
3671}
3672
3673/* static */
3674void Debugger::removeAllocationsTracking(GlobalObject& global) {
3675 // If there are still Debuggers that are observing allocations, we cannot
3676 // remove the metadata callback yet. Recompute the sampling probability
3677 // based on the remaining debuggers' needs.
3678 if (DebugAPI::isObservedByDebuggerTrackingAllocations(global)) {
3679 global.realm()->chooseAllocationSamplingProbability();
3680 return;
3681 }
3682
3683 if (!global.realm()->runtimeFromMainThread()->recordAllocationCallback) {
3684 // Something like the Gecko Profiler could request from the the JS runtime
3685 // to record allocations. If it is recording allocations, then do not
3686 // destroy the allocation metadata builder at this time.
3687 global.realm()->forgetAllocationMetadataBuilder();
3688 }
3689}
3690
3691bool Debugger::addAllocationsTrackingForAllDebuggees(JSContext* cx) {
3692 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"
, 3692); AnnotateMozCrashReason("MOZ_ASSERT" "(" "trackingAllocationSites"
")"); do { *((volatile int*)__null) = 3692; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3693
3694 // We don't want to end up in a state where we added allocations
3695 // tracking to some of our debuggees, but failed to do so for
3696 // others. Before attempting to start tracking allocations in *any* of
3697 // our debuggees, ensure that we will be able to track allocations for
3698 // *all* of our debuggees.
3699 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3700 r.popFront()) {
3701 if (Debugger::cannotTrackAllocations(*r.front().get())) {
3702 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
3703 JSMSG_OBJECT_METADATA_CALLBACK_ALREADY_SET);
3704 return false;
3705 }
3706 }
3707
3708 Rooted<GlobalObject*> g(cx);
3709 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3710 r.popFront()) {
3711 // This should always succeed, since we already checked for the
3712 // error case above.
3713 g = r.front().get();
3714 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"
, 3714); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "false"
") (" "Debugger::addAllocationsTracking(cx, g)" ")"); do { *
((volatile int*)__null) = 3714; __attribute__((nomerge)) ::abort
(); } while (false); } } while (false); } } while (false)
;
3715 }
3716
3717 return true;
3718}
3719
3720void Debugger::removeAllocationsTrackingForAllDebuggees() {
3721 for (WeakGlobalObjectSet::Range r = debuggees.all(); !r.empty();
3722 r.popFront()) {
3723 Debugger::removeAllocationsTracking(*r.front().get());
3724 }
3725
3726 allocationsLog.clear();
3727}
3728
3729/*** Debugger JSObjects *****************************************************/
3730
3731template <typename F>
3732inline void Debugger::forEachWeakMap(const F& f) {
3733 f(generatorFrames);
3734 f(objects);
3735 f(environments);
3736 f(scripts);
3737 f(sources);
3738 f(wasmInstanceScripts);
3739 f(wasmInstanceSources);
3740}
3741
3742void Debugger::traceCrossCompartmentEdges(JSTracer* trc) {
3743 forEachWeakMap(
3744 [trc](auto& weakMap) { weakMap.traceCrossCompartmentEdges(trc); });
3745}
3746
3747/*
3748 * Ordinarily, WeakMap keys and values are marked because at some point it was
3749 * discovered that the WeakMap was live; that is, some object containing the
3750 * WeakMap was marked during mark phase.
3751 *
3752 * However, during zone GC, we have to do something about cross-compartment
3753 * edges in non-GC'd compartments. Since the source may be live, we
3754 * conservatively assume it is and mark the edge.
3755 *
3756 * Each Debugger object keeps five cross-compartment WeakMaps: objects, scripts,
3757 * lazy scripts, script source objects, and environments. They have the property
3758 * that all their values are in the same compartment as the Debugger object,
3759 * but we have to mark the keys and the private pointer in the wrapper object.
3760 *
3761 * We must scan all Debugger objects regardless of whether they *currently* have
3762 * any debuggees in a compartment being GC'd, because the WeakMap entries
3763 * persist even when debuggees are removed.
3764 *
3765 * This happens during the initial mark phase, not iterative marking, because
3766 * all the edges being reported here are strong references.
3767 *
3768 * This method is also used during compacting GC to update cross compartment
3769 * pointers into zones that are being compacted.
3770 */
3771/* static */
3772void DebugAPI::traceCrossCompartmentEdges(JSTracer* trc) {
3773 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"
, 3773); AnnotateMozCrashReason("MOZ_ASSERT" "(" "JS::RuntimeHeapIsMajorCollecting()"
")"); do { *((volatile int*)__null) = 3773; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3774
3775 JSRuntime* rt = trc->runtime();
3776 gc::State state = rt->gc.state();
3777
3778 for (Debugger* dbg : rt->debuggerList()) {
3779 Zone* zone = MaybeForwarded(dbg->object.get())->zone();
3780 if (!zone->isCollecting() || state == gc::State::Compact) {
3781 dbg->traceCrossCompartmentEdges(trc);
3782 }
3783 }
3784}
3785
3786#ifdef DEBUG1
3787
3788static bool RuntimeHasDebugger(JSRuntime* rt, Debugger* dbg) {
3789 for (Debugger* d : rt->debuggerList()) {
3790 if (d == dbg) {
3791 return true;
3792 }
3793 }
3794 return false;
3795}
3796
3797/* static */
3798bool DebugAPI::edgeIsInDebuggerWeakmap(JSRuntime* rt, JSObject* src,
3799 JS::GCCellPtr dst) {
3800 if (!Debugger::isChildJSObject(src)) {
3801 return false;
3802 }
3803
3804 if (src->is<DebuggerFrame>()) {
3805 DebuggerFrame* frame = &src->as<DebuggerFrame>();
3806 Debugger* dbg = frame->owner();
3807 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"
, 3807); AnnotateMozCrashReason("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)"
")"); do { *((volatile int*)__null) = 3807; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3808
3809 if (dst.is<BaseScript>()) {
3810 // The generatorFrames map is not keyed on the associated JSScript. Get
3811 // the key from the source object and check everything matches.
3812 AbstractGeneratorObject* genObj = &frame->unwrappedGenerator();
3813 return frame->generatorScript() == &dst.as<BaseScript>() &&
3814 dbg->generatorFrames.hasEntry(genObj, src);
3815 }
3816 return dst.is<JSObject>() &&
3817 dst.as<JSObject>().is<AbstractGeneratorObject>() &&
3818 dbg->generatorFrames.hasEntry(
3819 &dst.as<JSObject>().as<AbstractGeneratorObject>(), src);
3820 }
3821 if (src->is<DebuggerObject>()) {
3822 Debugger* dbg = src->as<DebuggerObject>().owner();
3823 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"
, 3823); AnnotateMozCrashReason("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)"
")"); do { *((volatile int*)__null) = 3823; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3824 return dst.is<JSObject>() &&
3825 dbg->objects.hasEntry(&dst.as<JSObject>(), src);
3826 }
3827 if (src->is<DebuggerEnvironment>()) {
3828 Debugger* dbg = src->as<DebuggerEnvironment>().owner();
3829 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"
, 3829); AnnotateMozCrashReason("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)"
")"); do { *((volatile int*)__null) = 3829; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3830 return dst.is<JSObject>() &&
3831 dbg->environments.hasEntry(&dst.as<JSObject>(), src);
3832 }
3833 if (src->is<DebuggerScript>()) {
3834 Debugger* dbg = src->as<DebuggerScript>().owner();
3835 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"
, 3835); AnnotateMozCrashReason("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)"
")"); do { *((volatile int*)__null) = 3835; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3836
3837 return src->as<DebuggerScript>().getReferent().match(
3838 [=](BaseScript* script) {
3839 return dst.is<BaseScript>() && script == &dst.as<BaseScript>() &&
3840 dbg->scripts.hasEntry(script, src);
3841 },
3842 [=](WasmInstanceObject* instance) {
3843 return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
3844 dbg->wasmInstanceScripts.hasEntry(instance, src);
3845 });
3846 }
3847 if (src->is<DebuggerSource>()) {
3848 Debugger* dbg = src->as<DebuggerSource>().owner();
3849 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"
, 3849); AnnotateMozCrashReason("MOZ_ASSERT" "(" "RuntimeHasDebugger(rt, dbg)"
")"); do { *((volatile int*)__null) = 3849; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3850
3851 return src->as<DebuggerSource>().getReferent().match(
3852 [=](ScriptSourceObject* sso) {
3853 return dst.is<JSObject>() && sso == &dst.as<JSObject>() &&
3854 dbg->sources.hasEntry(sso, src);
3855 },
3856 [=](WasmInstanceObject* instance) {
3857 return dst.is<JSObject>() && instance == &dst.as<JSObject>() &&
3858 dbg->wasmInstanceSources.hasEntry(instance, src);
3859 });
3860 }
3861 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"
, 3861); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unhandled cross-compartment edge"
")"); do { *((volatile int*)__null) = 3861; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3862}
3863
3864#endif
3865
3866/* See comments in DebugAPI.h. */
3867void DebugAPI::traceFramesWithLiveHooks(JSTracer* tracer) {
3868 JSRuntime* rt = tracer->runtime();
3869
3870 // Note that we must loop over all Debuggers here, not just those known to be
3871 // reachable from JavaScript. The existence of hooks set on a Debugger.Frame
3872 // for a live stack frame makes the Debuger.Frame (and hence its Debugger)
3873 // reachable.
3874 for (Debugger* dbg : rt->debuggerList()) {
3875 // Callback tracers set their own traversal boundaries, but otherwise we're
3876 // only interested in Debugger.Frames participating in the collection.
3877 if (!dbg->zone()->isGCMarking() && !tracer->isCallbackTracer()) {
3878 continue;
3879 }
3880
3881 for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty();
3882 r.popFront()) {
3883 HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
3884 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"
, 3884); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameobj->isOnStack()"
")"); do { *((volatile int*)__null) = 3884; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3885 if (frameobj->hasAnyHooks()) {
3886 TraceEdge(tracer, &frameobj, "Debugger.Frame with live hooks");
3887 }
3888 }
3889 }
3890}
3891
3892void DebugAPI::slowPathTraceGeneratorFrame(JSTracer* tracer,
3893 AbstractGeneratorObject* generator) {
3894 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"
, 3894); AnnotateMozCrashReason("MOZ_ASSERT" "(" "generator->realm()->isDebuggee()"
")"); do { *((volatile int*)__null) = 3894; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3895
3896 // Ignore generic tracers.
3897 //
3898 // There are two kinds of generic tracers we need to bar: MovingTracers used
3899 // by compacting GC; and CompartmentCheckTracers.
3900 //
3901 // MovingTracers are used by the compacting GC to update pointers to objects
3902 // that have been moved: the MovingTracer checks each outgoing pointer to see
3903 // if it refers to a forwarding pointer, and if so, updates the pointer stored
3904 // in the object.
3905 //
3906 // Generator objects are background finalized, so the compacting GC assumes it
3907 // can update their pointers in the background as well. Since we treat
3908 // generator objects as having an owning edge to their Debugger.Frame objects,
3909 // a helper thread trying to update a generator object will end up calling
3910 // this function. However, it is verboten to do weak map lookups (e.g., in
3911 // Debugger::generatorFrames) off the main thread, since StableCellHasher
3912 // must consult the Zone to find the key's unique id.
3913 //
3914 // Fortunately, it's not necessary for compacting GC to worry about that edge
3915 // in the first place: the edge isn't a literal pointer stored on the
3916 // generator object, it's only inferred from the realm's debuggee status and
3917 // its Debuggers' generatorFrames weak maps. Those get relocated when the
3918 // Debugger itself is visited, so compacting GC can just ignore this edge.
3919 //
3920 // CompartmentCheckTracers walk the graph and verify that all
3921 // cross-compartment edges are recorded in the cross-compartment wrapper
3922 // tables. But edges between Debugger.Foo objects and their referents are not
3923 // in the CCW tables, so a CrossCompartmentCheckTracers also calls
3924 // DebugAPI::edgeIsInDebuggerWeakmap to see if a given cross-compartment edge
3925 // is accounted for there. However, edgeIsInDebuggerWeakmap only handles
3926 // debugger -> debuggee edges, so it won't recognize the edge we're
3927 // potentially traversing here, from a generator object to its Debugger.Frame.
3928 //
3929 // But since the purpose of this function is to retrieve such edges, if they
3930 // exist, from the very tables that edgeIsInDebuggerWeakmap would consult,
3931 // we're at no risk of reporting edges that they do not cover. So we can
3932 // safely hide the edges from CompartmentCheckTracers.
3933 //
3934 // We can't quite recognize MovingTracers and CompartmentCheckTracers
3935 // precisely, but they're both generic tracers, so we just show them all the
3936 // door. This means the generator -> Debugger.Frame edge is going to be
3937 // invisible to some traversals. We'll cope with that when it's a problem.
3938 if (!tracer->isMarkingTracer()) {
3939 return;
3940 }
3941
3942 mozilla::Maybe<AutoLockGC> lock;
3943 GCMarker* marker = GCMarker::fromTracer(tracer);
3944 if (marker->isParallelMarking()) {
3945 // Synchronise access to generatorFrames.
3946 lock.emplace(marker->runtime());
3947 }
3948
3949 JS::AutoAssertNoGC nogc;
3950 for (Realm::DebuggerVectorEntry& entry :
3951 generator->realm()->getDebuggers(nogc)) {
3952 Debugger* dbg = entry.dbg.unbarrieredGet();
3953
3954 if (Debugger::GeneratorWeakMap::Ptr entry =
3955 dbg->generatorFrames.lookupUnbarriered(generator)) {
3956 HeapPtr<DebuggerFrame*>& frameObj = entry->value();
3957 if (frameObj->hasAnyHooks()) {
3958 // See comment above.
3959 TraceCrossCompartmentEdge(tracer, generator, &frameObj,
3960 "Debugger.Frame with hooks for generator");
3961 }
3962 }
3963 }
3964}
3965
3966/* static */
3967void DebugAPI::traceAllForMovingGC(JSTracer* trc) {
3968 JSRuntime* rt = trc->runtime();
3969 for (Debugger* dbg : rt->debuggerList()) {
3970 dbg->traceForMovingGC(trc);
3971 }
3972}
3973
3974/*
3975 * Trace all debugger-owned GC things unconditionally. This is used during
3976 * compacting GC and in minor GC: the minor GC cannot apply the weak constraints
3977 * of the full GC because it visits only part of the heap.
3978 */
3979void Debugger::traceForMovingGC(JSTracer* trc) {
3980 trace(trc);
3981
3982 for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
3983 TraceEdge(trc, &e.mutableFront(), "Global Object");
3984 }
3985}
3986
3987/* static */
3988void Debugger::traceObject(JSTracer* trc, JSObject* obj) {
3989 if (Debugger* dbg = Debugger::fromJSObject(obj)) {
3990 dbg->trace(trc);
3991 }
3992}
3993
3994void Debugger::trace(JSTracer* trc) {
3995 TraceEdge(trc, &object, "Debugger Object");
3996
3997 TraceNullableEdge(trc, &uncaughtExceptionHook, "hooks");
3998
3999 // Mark Debugger.Frame objects. Since the Debugger is reachable, JS could call
4000 // getNewestFrame and then walk the stack, so these are all reachable from JS.
4001 //
4002 // Note that if a Debugger.Frame has hooks set, it must be retained even if
4003 // its Debugger is unreachable, since JS could observe that its hooks did not
4004 // fire. That case is handled by DebugAPI::traceFrames.
4005 //
4006 // (We have weakly-referenced Debugger.Frame objects as well, for suspended
4007 // generator frames; these are traced via generatorFrames just below.)
4008 for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
4009 HeapPtr<DebuggerFrame*>& frameobj = r.front().value();
4010 TraceEdge(trc, &frameobj, "live Debugger.Frame");
4011 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"
, 4011); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameobj->isOnStack()"
")"); do { *((volatile int*)__null) = 4011; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4012 }
4013
4014 allocationsLog.trace(trc);
4015
4016 forEachWeakMap([trc](auto& weakMap) { weakMap.trace(trc); });
4017}
4018
4019/* static */
4020void DebugAPI::traceFromRealm(JSTracer* trc, Realm* realm) {
4021 JS::AutoAssertNoGC nogc;
4022 for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
4023 TraceEdge(trc, &entry.debuggerLink, "realm debugger");
4024 }
4025}
4026
4027/* static */
4028void DebugAPI::sweepAll(JS::GCContext* gcx) {
4029 JSRuntime* rt = gcx->runtime();
4030
4031 Debugger* next;
4032 for (Debugger* dbg = rt->debuggerList().getFirst(); dbg; dbg = next) {
4033 next = dbg->getNext();
4034
4035 // Debugger.Frames for generator calls bump the JSScript's
4036 // generatorObserverCount, so the JIT will instrument the code to notify
4037 // Debugger when the generator is resumed. When a Debugger.Frame gets GC'd,
4038 // generatorObserverCount needs to be decremented. It's much easier to do
4039 // this when we know that all parties involved - the Debugger.Frame, the
4040 // generator object, and the JSScript - have not yet been finalized.
4041 //
4042 // Since DebugAPI::sweepAll is called after everything is marked, but before
4043 // anything has been finalized, this is the perfect place to drop the count.
4044 if (dbg->zone()->isGCSweeping()) {
4045 for (Debugger::GeneratorWeakMap::Enum e(dbg->generatorFrames); !e.empty();
4046 e.popFront()) {
4047 DebuggerFrame* frameObj = e.front().value();
4048 if (IsAboutToBeFinalizedUnbarriered(frameObj)) {
4049 // If the DebuggerFrame is being finalized, that means either:
4050 // 1) It is not present in "frames".
4051 // 2) The Debugger itself is also being finalized.
4052 //
4053 // In the first case, passing the frame is not necessary because there
4054 // isn't a frame entry to clear, and in the second case,
4055 // removeDebuggeeGlobal below will iterate and remove the entries
4056 // anyway, so things will be cleaned up properly.
4057 Debugger::terminateDebuggerFrame(gcx, dbg, frameObj, NullFramePtr(),
4058 nullptr, &e);
4059 }
4060 }
4061 }
4062
4063 // Detach dying debuggers and debuggees from each other. Since this
4064 // requires access to both objects it must be done before either
4065 // object is finalized.
4066 bool debuggerDying = IsAboutToBeFinalized(dbg->object);
4067 for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty();
4068 e.popFront()) {
4069 GlobalObject* global = e.front().unbarrieredGet();
4070 if (debuggerDying || IsAboutToBeFinalizedUnbarriered(global)) {
4071 dbg->removeDebuggeeGlobal(gcx, e.front().unbarrieredGet(), &e,
4072 Debugger::FromSweep::Yes);
4073 }
4074 }
4075
4076 if (debuggerDying) {
4077 gcx->delete_(dbg->object, dbg, MemoryUse::Debugger);
4078 }
4079
4080 dbg = next;
Value stored to 'dbg' is never read
4081 }
4082}
4083
4084static inline bool SweepZonesInSameGroup(Zone* a, Zone* b) {
4085 // Ensure two zones are swept in the same sweep group by adding an edge
4086 // between them in each direction.
4087 return a->addSweepGroupEdgeTo(b) && b->addSweepGroupEdgeTo(a);
4088}
4089
4090/* static */
4091bool DebugAPI::findSweepGroupEdges(JSRuntime* rt) {
4092 // Ensure that debuggers and their debuggees are finalized in the same group
4093 // by adding edges in both directions for debuggee zones. These are weak
4094 // references that are not in the cross compartment wrapper map.
4095
4096 for (Debugger* dbg : rt->debuggerList()) {
4097 Zone* debuggerZone = dbg->object->zone();
4098 if (!debuggerZone->isGCMarking()) {
4099 continue;
4100 }
4101
4102 for (auto e = dbg->debuggeeZones.all(); !e.empty(); e.popFront()) {
4103 Zone* debuggeeZone = e.front();
4104 if (!debuggeeZone->isGCMarking()) {
4105 continue;
4106 }
4107
4108 if (!SweepZonesInSameGroup(debuggerZone, debuggeeZone)) {
4109 return false;
4110 }
4111 }
4112 }
4113
4114 return true;
4115}
4116
4117template <class UnbarrieredKey, class Wrapper, bool InvisibleKeysOk>
4118bool DebuggerWeakMap<UnbarrieredKey, Wrapper,
4119 InvisibleKeysOk>::findSweepGroupEdges() {
4120 Zone* debuggerZone = zone();
4121 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"
, 4121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debuggerZone->isGCMarking()"
")"); do { *((volatile int*)__null) = 4121; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4122 for (Enum e(*this); !e.empty(); e.popFront()) {
4123 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"
, 4123); AnnotateMozCrashReason("MOZ_ASSERT" "(" "e.front().value()->zone() == debuggerZone"
")"); do { *((volatile int*)__null) = 4123; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4124
4125 Zone* keyZone = e.front().key()->zone();
4126 if (keyZone->isGCMarking() &&
4127 !SweepZonesInSameGroup(debuggerZone, keyZone)) {
4128 return false;
4129 }
4130 }
4131
4132 // Add in edges for delegates, if relevant for the key type.
4133 return Base::findSweepGroupEdges();
4134}
4135
4136const JSClassOps DebuggerInstanceObject::classOps_ = {
4137 nullptr, // addProperty
4138 nullptr, // delProperty
4139 nullptr, // enumerate
4140 nullptr, // newEnumerate
4141 nullptr, // resolve
4142 nullptr, // mayResolve
4143 nullptr, // finalize
4144 nullptr, // call
4145 nullptr, // construct
4146 Debugger::traceObject, // trace
4147};
4148
4149const JSClass DebuggerInstanceObject::class_ = {
4150 "Debugger", JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_COUNT),
4151 &classOps_};
4152
4153static_assert(Debugger::JSSLOT_DEBUG_PROTO_START == 0,
4154 "DebuggerPrototypeObject only needs slots for the proto objects");
4155
4156const JSClass DebuggerPrototypeObject::class_ = {
4157 "DebuggerPrototype",
4158 JSCLASS_HAS_RESERVED_SLOTS(Debugger::JSSLOT_DEBUG_PROTO_STOP)};
4159
4160static Debugger* Debugger_fromThisValue(JSContext* cx, const CallArgs& args,
4161 const char* fnname) {
4162 JSObject* thisobj = RequireObject(cx, args.thisv());
4163 if (!thisobj) {
4164 return nullptr;
4165 }
4166 if (!thisobj->is<DebuggerInstanceObject>()) {
4167 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4168 JSMSG_INCOMPATIBLE_PROTO, "Debugger", fnname,
4169 thisobj->getClass()->name);
4170 return nullptr;
4171 }
4172
4173 Debugger* dbg = Debugger::fromJSObject(thisobj);
4174 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"
, 4174); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbg" ")"); do
{ *((volatile int*)__null) = 4174; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
4175 return dbg;
4176}
4177
4178struct MOZ_STACK_CLASS Debugger::CallData {
4179 JSContext* cx;
4180 const CallArgs& args;
4181
4182 Debugger* dbg;
4183
4184 CallData(JSContext* cx, const CallArgs& args, Debugger* dbg)
4185 : cx(cx), args(args), dbg(dbg) {}
4186
4187 bool getOnDebuggerStatement();
4188 bool setOnDebuggerStatement();
4189 bool getOnExceptionUnwind();
4190 bool setOnExceptionUnwind();
4191 bool getOnNewScript();
4192 bool setOnNewScript();
4193 bool getOnEnterFrame();
4194 bool setOnEnterFrame();
4195 bool getOnNativeCall();
4196 bool setOnNativeCall();
4197 bool getShouldAvoidSideEffects();
4198 bool setShouldAvoidSideEffects();
4199 bool getOnNewGlobalObject();
4200 bool setOnNewGlobalObject();
4201 bool getOnNewPromise();
4202 bool setOnNewPromise();
4203 bool getOnPromiseSettled();
4204 bool setOnPromiseSettled();
4205 bool getUncaughtExceptionHook();
4206 bool setUncaughtExceptionHook();
4207 bool getAllowUnobservedAsmJS();
4208 bool setAllowUnobservedAsmJS();
4209 bool getAllowUnobservedWasm();
4210 bool setAllowUnobservedWasm();
4211 bool getExclusiveDebuggerOnEval();
4212 bool setExclusiveDebuggerOnEval();
4213 bool getInspectNativeCallArguments();
4214 bool setInspectNativeCallArguments();
4215 bool getCollectCoverageInfo();
4216 bool setCollectCoverageInfo();
4217 bool getMemory();
4218 bool addDebuggee();
4219 bool addAllGlobalsAsDebuggees();
4220 bool removeDebuggee();
4221 bool removeAllDebuggees();
4222 bool hasDebuggee();
4223 bool getDebuggees();
4224 bool getNewestFrame();
4225 bool clearAllBreakpoints();
4226 bool findScripts();
4227 bool findSources();
4228 bool findObjects();
4229 bool findAllGlobals();
4230 bool findSourceURLs();
4231 bool makeGlobalObjectReference();
4232 bool adoptDebuggeeValue();
4233 bool adoptFrame();
4234 bool adoptSource();
4235 bool enableAsyncStack();
4236 bool disableAsyncStack();
4237 bool enableUnlimitedStacksCapturing();
4238 bool disableUnlimitedStacksCapturing();
4239
4240 using Method = bool (CallData::*)();
4241
4242 template <Method MyMethod>
4243 static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
4244};
4245
4246template <Debugger::CallData::Method MyMethod>
4247/* static */
4248bool Debugger::CallData::ToNative(JSContext* cx, unsigned argc, Value* vp) {
4249 CallArgs args = CallArgsFromVp(argc, vp);
4250
4251 Debugger* dbg = Debugger_fromThisValue(cx, args, "method");
4252 if (!dbg) {
4253 return false;
4254 }
4255
4256 CallData data(cx, args, dbg);
4257 return (data.*MyMethod)();
4258}
4259
4260/* static */
4261bool Debugger::getHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
4262 Hook which) {
4263 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"
, 4263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "which >= 0 && which < HookCount"
")"); do { *((volatile int*)__null) = 4263; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4264 args.rval().set(dbg.object->getReservedSlot(
4265 JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which)));
4266 return true;
4267}
4268
4269/* static */
4270bool Debugger::setHookImpl(JSContext* cx, const CallArgs& args, Debugger& dbg,
4271 Hook which) {
4272 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"
, 4272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "which >= 0 && which < HookCount"
")"); do { *((volatile int*)__null) = 4272; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4273 if (!args.requireAtLeast(cx, "Debugger.setHook", 1)) {
4274 return false;
4275 }
4276 if (args[0].isObject()) {
4277 if (!args[0].toObject().isCallable()) {
4278 return ReportIsNotFunction(cx, args[0], args.length() - 1);
4279 }
4280 } else if (!args[0].isUndefined()) {
4281 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4282 JSMSG_NOT_CALLABLE_OR_UNDEFINED);
4283 return false;
4284 }
4285
4286 // Disallow simultaneous activation of OnEnterFrame and code coverage support;
4287 // as they both use the execution observer flag. See Bug 1608891.
4288 if (dbg.collectCoverageInfo && which == Hook::OnEnterFrame) {
4289 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4290 JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
4291 return false;
4292 }
4293
4294 uint32_t slot = JSSLOT_DEBUG_HOOK_START + std::underlying_type_t<Hook>(which);
4295 RootedValue oldHook(cx, dbg.object->getReservedSlot(slot));
4296 dbg.object->setReservedSlot(slot, args[0]);
4297 if (hookObservesAllExecution(which)) {
4298 if (!dbg.updateObservesAllExecutionOnDebuggees(
4299 cx, dbg.observesAllExecution())) {
4300 dbg.object->setReservedSlot(slot, oldHook);
4301 return false;
4302 }
4303 }
4304
4305 Rooted<DebuggerDebuggeeLink*> debuggeeLink(cx, dbg.getDebuggeeLink());
4306 if (dbg.hasAnyLiveHooks()) {
4307 debuggeeLink->setLinkSlot(dbg);
4308 } else {
4309 debuggeeLink->clearLinkSlot();
4310 }
4311
4312 args.rval().setUndefined();
4313 return true;
4314}
4315
4316/* static */
4317bool Debugger::getGarbageCollectionHook(JSContext* cx, const CallArgs& args,
4318 Debugger& dbg) {
4319 return getHookImpl(cx, args, dbg, OnGarbageCollection);
4320}
4321
4322/* static */
4323bool Debugger::setGarbageCollectionHook(JSContext* cx, const CallArgs& args,
4324 Debugger& dbg) {
4325 Rooted<JSObject*> oldHook(cx, dbg.getHook(OnGarbageCollection));
4326
4327 if (!setHookImpl(cx, args, dbg, OnGarbageCollection)) {
4328 // We want to maintain the invariant that the hook is always set when the
4329 // Debugger is in the runtime's list, and vice-versa, so if we return early
4330 // and don't adjust the watcher list below, we need to be sure that the
4331 // hook didn't change.
4332 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"
, 4332); AnnotateMozCrashReason("MOZ_ASSERT" "(" "dbg.getHook(OnGarbageCollection) == oldHook"
")"); do { *((volatile int*)__null) = 4332; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4333 return false;
4334 }
4335
4336 // Add or remove ourselves from the runtime's list of Debuggers that care
4337 // about garbage collection.
4338 JSObject* newHook = dbg.getHook(OnGarbageCollection);
4339 if (!oldHook && newHook) {
4340 cx->runtime()->onGarbageCollectionWatchers().pushBack(&dbg);
4341 } else if (oldHook && !newHook) {
4342 cx->runtime()->onGarbageCollectionWatchers().remove(&dbg);
4343 }
4344
4345 return true;
4346}
4347
4348bool Debugger::CallData::getOnDebuggerStatement() {
4349 return getHookImpl(cx, args, *dbg, OnDebuggerStatement);
4350}
4351
4352bool Debugger::CallData::setOnDebuggerStatement() {
4353 return setHookImpl(cx, args, *dbg, OnDebuggerStatement);
4354}
4355
4356bool Debugger::CallData::getOnExceptionUnwind() {
4357 return getHookImpl(cx, args, *dbg, OnExceptionUnwind);
4358}
4359
4360bool Debugger::CallData::setOnExceptionUnwind() {
4361 return setHookImpl(cx, args, *dbg, OnExceptionUnwind);
4362}
4363
4364bool Debugger::CallData::getOnNewScript() {
4365 return getHookImpl(cx, args, *dbg, OnNewScript);
4366}
4367
4368bool Debugger::CallData::setOnNewScript() {
4369 return setHookImpl(cx, args, *dbg, OnNewScript);
4370}
4371
4372bool Debugger::CallData::getOnNewPromise() {
4373 return getHookImpl(cx, args, *dbg, OnNewPromise);
4374}
4375
4376bool Debugger::CallData::setOnNewPromise() {
4377 return setHookImpl(cx, args, *dbg, OnNewPromise);
4378}
4379
4380bool Debugger::CallData::getOnPromiseSettled() {
4381 return getHookImpl(cx, args, *dbg, OnPromiseSettled);
4382}
4383
4384bool Debugger::CallData::setOnPromiseSettled() {
4385 return setHookImpl(cx, args, *dbg, OnPromiseSettled);
4386}
4387
4388bool Debugger::CallData::getOnEnterFrame() {
4389 return getHookImpl(cx, args, *dbg, OnEnterFrame);
4390}
4391
4392bool Debugger::CallData::setOnEnterFrame() {
4393 return setHookImpl(cx, args, *dbg, OnEnterFrame);
4394}
4395
4396bool Debugger::CallData::getOnNativeCall() {
4397 return getHookImpl(cx, args, *dbg, OnNativeCall);
4398}
4399
4400bool Debugger::CallData::setOnNativeCall() {
4401 RootedObject oldHook(cx, dbg->getHook(OnNativeCall));
4402
4403 if (!setHookImpl(cx, args, *dbg, OnNativeCall)) {
4404 return false;
4405 }
4406
4407 JSObject* newHook = dbg->getHook(OnNativeCall);
4408 if (!oldHook && newHook) {
4409 dbg->updateObservesNativeCallOnDebuggees(Observing);
4410 } else if (oldHook && !newHook) {
4411 dbg->updateObservesNativeCallOnDebuggees(NotObserving);
4412 }
4413
4414 return true;
4415}
4416
4417bool Debugger::CallData::getShouldAvoidSideEffects() {
4418 args.rval().setBoolean(dbg->shouldAvoidSideEffects);
4419 return true;
4420}
4421
4422bool Debugger::CallData::setShouldAvoidSideEffects() {
4423 if (!args.requireAtLeast(cx, "Debugger.set shouldAvoidSideEffects", 1)) {
4424 return false;
4425 }
4426
4427 dbg->shouldAvoidSideEffects = ToBoolean(args[0]);
4428
4429 args.rval().setUndefined();
4430 return true;
4431}
4432
4433bool Debugger::CallData::getOnNewGlobalObject() {
4434 return getHookImpl(cx, args, *dbg, OnNewGlobalObject);
4435}
4436
4437bool Debugger::CallData::setOnNewGlobalObject() {
4438 RootedObject oldHook(cx, dbg->getHook(OnNewGlobalObject));
4439
4440 if (!setHookImpl(cx, args, *dbg, OnNewGlobalObject)) {
4441 return false;
4442 }
4443
4444 // Add or remove ourselves from the runtime's list of Debuggers that care
4445 // about new globals.
4446 JSObject* newHook = dbg->getHook(OnNewGlobalObject);
4447 if (!oldHook && newHook) {
4448 cx->runtime()->onNewGlobalObjectWatchers().pushBack(dbg);
4449 } else if (oldHook && !newHook) {
4450 cx->runtime()->onNewGlobalObjectWatchers().remove(dbg);
4451 }
4452
4453 return true;
4454}
4455
4456bool Debugger::CallData::getUncaughtExceptionHook() {
4457 args.rval().setObjectOrNull(dbg->uncaughtExceptionHook);
4458 return true;
4459}
4460
4461bool Debugger::CallData::setUncaughtExceptionHook() {
4462 if (!args.requireAtLeast(cx, "Debugger.set uncaughtExceptionHook", 1)) {
4463 return false;
4464 }
4465 if (!args[0].isNull() &&
4466 (!args[0].isObject() || !args[0].toObject().isCallable())) {
4467 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4468 JSMSG_ASSIGN_FUNCTION_OR_NULL,
4469 "uncaughtExceptionHook");
4470 return false;
4471 }
4472 dbg->uncaughtExceptionHook = args[0].toObjectOrNull();
4473 args.rval().setUndefined();
4474 return true;
4475}
4476
4477bool Debugger::CallData::getAllowUnobservedAsmJS() {
4478 args.rval().setBoolean(dbg->allowUnobservedAsmJS);
4479 return true;
4480}
4481
4482bool Debugger::CallData::setAllowUnobservedAsmJS() {
4483 if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedAsmJS", 1)) {
4484 return false;
4485 }
4486 dbg->allowUnobservedAsmJS = ToBoolean(args[0]);
4487
4488 for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
4489 r.popFront()) {
4490 GlobalObject* global = r.front();
4491 Realm* realm = global->realm();
4492 realm->updateDebuggerObservesAsmJS();
4493 }
4494
4495 args.rval().setUndefined();
4496 return true;
4497}
4498
4499bool Debugger::CallData::getAllowUnobservedWasm() {
4500 args.rval().setBoolean(dbg->allowUnobservedWasm);
4501 return true;
4502}
4503
4504bool Debugger::CallData::setAllowUnobservedWasm() {
4505 if (!args.requireAtLeast(cx, "Debugger.set allowUnobservedWasm", 1)) {
4506 return false;
4507 }
4508 dbg->allowUnobservedWasm = ToBoolean(args[0]);
4509
4510 for (WeakGlobalObjectSet::Range r = dbg->debuggees.all(); !r.empty();
4511 r.popFront()) {
4512 GlobalObject* global = r.front();
4513 Realm* realm = global->realm();
4514 realm->updateDebuggerObservesWasm();
4515 }
4516
4517 args.rval().setUndefined();
4518 return true;
4519}
4520
4521bool Debugger::CallData::getExclusiveDebuggerOnEval() {
4522 args.rval().setBoolean(dbg->exclusiveDebuggerOnEval);
4523 return true;
4524}
4525
4526bool Debugger::CallData::setExclusiveDebuggerOnEval() {
4527 if (!args.requireAtLeast(cx, "Debugger.set exclusiveDebuggerOnEval", 1)) {
4528 return false;
4529 }
4530 dbg->exclusiveDebuggerOnEval = ToBoolean(args[0]);
4531
4532 args.rval().setUndefined();
4533 return true;
4534}
4535
4536bool Debugger::CallData::getInspectNativeCallArguments() {
4537 args.rval().setBoolean(dbg->inspectNativeCallArguments);
4538 return true;
4539}
4540
4541bool Debugger::CallData::setInspectNativeCallArguments() {
4542 if (!args.requireAtLeast(cx, "Debugger.set inspectNativeCallArguments", 1)) {
4543 return false;
4544 }
4545 dbg->inspectNativeCallArguments = ToBoolean(args[0]);
4546
4547 args.rval().setUndefined();
4548 return true;
4549}
4550
4551bool Debugger::CallData::getCollectCoverageInfo() {
4552 args.rval().setBoolean(dbg->collectCoverageInfo);
4553 return true;
4554}
4555
4556bool Debugger::CallData::setCollectCoverageInfo() {
4557 if (!args.requireAtLeast(cx, "Debugger.set collectCoverageInfo", 1)) {
4558 return false;
4559 }
4560
4561 // Disallow simultaneous activation of OnEnterFrame and code coverage support;
4562 // as they both use the execution observer flag. See Bug 1608891.
4563 uint32_t slot = JSSLOT_DEBUG_HOOK_START +
4564 std::underlying_type_t<Hook>(Hook::OnEnterFrame);
4565 if (!dbg->object->getReservedSlot(slot).isUndefined()) {
4566 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4567 JSMSG_DEBUG_EXCLUSIVE_FRAME_COVERAGE);
4568 return false;
4569 }
4570
4571 dbg->collectCoverageInfo = ToBoolean(args[0]);
4572
4573 IsObserving observing = dbg->collectCoverageInfo ? Observing : NotObserving;
4574 if (!dbg->updateObservesCoverageOnDebuggees(cx, observing)) {
4575 return false;
4576 }
4577
4578 args.rval().setUndefined();
4579 return true;
4580}
4581
4582bool Debugger::CallData::getMemory() {
4583 Value memoryValue =
4584 dbg->object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE);
4585
4586 if (!memoryValue.isObject()) {
4587 RootedObject memory(cx, DebuggerMemory::create(cx, dbg));
4588 if (!memory) {
4589 return false;
4590 }
4591 memoryValue = ObjectValue(*memory);
4592 }
4593
4594 args.rval().set(memoryValue);
4595 return true;
4596}
4597
4598/*
4599 * Given a value used to designate a global (there's quite a variety; see the
4600 * docs), return the actual designee.
4601 *
4602 * Note that this does not check whether the designee is marked "invisible to
4603 * Debugger" or not; different callers need to handle invisible-to-Debugger
4604 * globals in different ways.
4605 */
4606GlobalObject* Debugger::unwrapDebuggeeArgument(JSContext* cx, const Value& v) {
4607 if (!v.isObject()) {
4608 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4609 JSMSG_UNEXPECTED_TYPE, "argument",
4610 "not a global object");
4611 return nullptr;
4612 }
4613
4614 RootedObject obj(cx, &v.toObject());
4615
4616 // If it's a Debugger.Object belonging to this debugger, dereference that.
4617 if (obj->getClass() == &DebuggerObject::class_) {
4618 RootedValue rv(cx, v);
4619 if (!unwrapDebuggeeValue(cx, &rv)) {
4620 return nullptr;
4621 }
4622 obj = &rv.toObject();
4623 }
4624
4625 // If we have a cross-compartment wrapper, dereference as far as is secure.
4626 //
4627 // Since we're dealing with globals, we may have a WindowProxy here. So we
4628 // have to make sure to do a dynamic unwrap, and we want to unwrap the
4629 // WindowProxy too, if we have one.
4630 obj = CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
4631 if (!obj) {
4632 ReportAccessDenied(cx);
4633 return nullptr;
4634 }
4635
4636 if (JS_IsDeadWrapper(obj)) {
4637 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
4638 return nullptr;
4639 }
4640
4641 // If that didn't produce a global object, it's an error.
4642 if (!obj->is<GlobalObject>()) {
4643 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4644 JSMSG_UNEXPECTED_TYPE, "argument",
4645 "not a global object");
4646 return nullptr;
4647 }
4648
4649 return &obj->as<GlobalObject>();
4650}
4651
4652bool Debugger::CallData::addDebuggee() {
4653 if (!args.requireAtLeast(cx, "Debugger.addDebuggee", 1)) {
4654 return false;
4655 }
4656 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
4657 if (!global) {
4658 return false;
4659 }
4660
4661 if (!dbg->addDebuggeeGlobal(cx, global)) {
4662 return false;
4663 }
4664
4665 RootedValue v(cx, ObjectValue(*global));
4666 if (!dbg->wrapDebuggeeValue(cx, &v)) {
4667 return false;
4668 }
4669 args.rval().set(v);
4670 return true;
4671}
4672
4673bool Debugger::CallData::addAllGlobalsAsDebuggees() {
4674 for (CompartmentsIter comp(cx->runtime()); !comp.done(); comp.next()) {
4675 if (comp == dbg->object->compartment()) {
4676 continue;
4677 }
4678 for (RealmsInCompartmentIter r(comp); !r.done(); r.next()) {
4679 if (r->creationOptions().invisibleToDebugger()) {
4680 continue;
4681 }
4682 if (!r->hasInitializedGlobal()) {
4683 continue;
4684 }
4685 r->compartment()->gcState.scheduledForDestruction = false;
4686 Rooted<GlobalObject*> global(cx, r->maybeGlobal());
4687 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"
, 4687); AnnotateMozCrashReason("MOZ_ASSERT" "(" "global" ")"
); do { *((volatile int*)__null) = 4687; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4688 if (!dbg->addDebuggeeGlobal(cx, global)) {
4689 return false;
4690 }
4691 }
4692 }
4693
4694 args.rval().setUndefined();
4695 return true;
4696}
4697
4698bool Debugger::CallData::removeDebuggee() {
4699 if (!args.requireAtLeast(cx, "Debugger.removeDebuggee", 1)) {
4700 return false;
4701 }
4702 Rooted<GlobalObject*> global(cx, dbg->unwrapDebuggeeArgument(cx, args[0]));
4703 if (!global) {
4704 return false;
4705 }
4706
4707 ExecutionObservableRealms obs(cx);
4708
4709 if (dbg->debuggees.has(global)) {
4710 dbg->removeDebuggeeGlobal(cx->gcContext(), global, nullptr, FromSweep::No);
4711
4712 // Only update the realm if there are no Debuggers left, as it's
4713 // expensive to check if no other Debugger has a live script or frame
4714 // hook on any of the current on-stack debuggee frames.
4715 if (!global->hasDebuggers() && !obs.add(global->realm())) {
4716 return false;
4717 }
4718 if (!updateExecutionObservability(cx, obs, NotObserving)) {
4719 return false;
4720 }
4721 }
4722
4723 args.rval().setUndefined();
4724 return true;
4725}
4726
4727bool Debugger::CallData::removeAllDebuggees() {
4728 ExecutionObservableRealms obs(cx);
4729
4730 for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) {
4731 Rooted<GlobalObject*> global(cx, e.front());
4732 dbg->removeDebuggeeGlobal(cx->gcContext(), global, &e, FromSweep::No);
4733
4734 // See note about adding to the observable set in removeDebuggee.
4735 if (!global->hasDebuggers() && !obs.add(global->realm())) {
4736 return false;
4737 }
4738 }
4739
4740 if (!updateExecutionObservability(cx, obs, NotObserving)) {
4741 return false;
4742 }
4743
4744 args.rval().setUndefined();
4745 return true;
4746}
4747
4748bool Debugger::CallData::hasDebuggee() {
4749 if (!args.requireAtLeast(cx, "Debugger.hasDebuggee", 1)) {
4750 return false;
4751 }
4752 GlobalObject* global = dbg->unwrapDebuggeeArgument(cx, args[0]);
4753 if (!global) {
4754 return false;
4755 }
4756 args.rval().setBoolean(!!dbg->debuggees.lookup(global));
4757 return true;
4758}
4759
4760bool Debugger::CallData::getDebuggees() {
4761 // Obtain the list of debuggees before wrapping each debuggee, as a GC could
4762 // update the debuggees set while we are iterating it.
4763 unsigned count = dbg->debuggees.count();
4764 RootedValueVector debuggees(cx);
4765 if (!debuggees.resize(count)) {
4766 return false;
4767 }
4768 unsigned i = 0;
4769 {
4770 JS::AutoCheckCannotGC nogc;
4771 for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty();
4772 e.popFront()) {
4773 debuggees[i++].setObject(*e.front().get());
4774 }
4775 }
4776
4777 Rooted<ArrayObject*> arrobj(cx, NewDenseFullyAllocatedArray(cx, count));
4778 if (!arrobj) {
4779 return false;
4780 }
4781 arrobj->ensureDenseInitializedLength(0, count);
4782 for (i = 0; i < count; i++) {
4783 RootedValue v(cx, debuggees[i]);
4784 if (!dbg->wrapDebuggeeValue(cx, &v)) {
4785 return false;
4786 }
4787 arrobj->setDenseElement(i, v);
4788 }
4789
4790 args.rval().setObject(*arrobj);
4791 return true;
4792}
4793
4794bool Debugger::CallData::getNewestFrame() {
4795 // Since there may be multiple contexts, use AllFramesIter.
4796 for (AllFramesIter i(cx); !i.done(); ++i) {
4797 if (dbg->observesFrame(i)) {
4798 // Ensure that Ion frames are rematerialized. Only rematerialized
4799 // Ion frames may be used as AbstractFramePtrs.
4800 if (i.isIon() && !i.ensureHasRematerializedFrame(cx)) {
4801 return false;
4802 }
4803 AbstractFramePtr frame = i.abstractFramePtr();
4804 FrameIter iter(i.activation()->cx());
4805 while (!iter.hasUsableAbstractFramePtr() ||
4806 iter.abstractFramePtr() != frame) {
4807 ++iter;
4808 }
4809 return dbg->getFrame(cx, iter, args.rval());
4810 }
4811 }
4812 args.rval().setNull();
4813 return true;
4814}
4815
4816bool Debugger::CallData::clearAllBreakpoints() {
4817 JS::GCContext* gcx = cx->gcContext();
4818 Breakpoint* nextbp;
4819 for (Breakpoint* bp = dbg->firstBreakpoint(); bp; bp = nextbp) {
4820 nextbp = bp->nextInDebugger();
4821
4822 bp->remove(gcx);
4823 }
4824 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"
, 4824); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!dbg->firstBreakpoint()"
")"); do { *((volatile int*)__null) = 4824; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4825
4826 return true;
4827}
4828
4829/* static */
4830bool Debugger::construct(JSContext* cx, unsigned argc, Value* vp) {
4831 CallArgs args = CallArgsFromVp(argc, vp);
4832
4833 // Check that the arguments, if any, are cross-compartment wrappers.
4834 for (unsigned i = 0; i < args.length(); i++) {
4835 JSObject* argobj = RequireObject(cx, args[i]);
4836 if (!argobj) {
4837 return false;
4838 }
4839 if (!argobj->is<CrossCompartmentWrapperObject>()) {
4840 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4841 JSMSG_DEBUG_CCW_REQUIRED, "Debugger");
4842 return false;
4843 }
4844 }
4845
4846 // Get Debugger.prototype.
4847 RootedValue v(cx);
4848 RootedObject callee(cx, &args.callee());
4849 if (!GetProperty(cx, callee, callee, cx->names().prototype, &v)) {
4850 return false;
4851 }
4852 Rooted<NativeObject*> proto(cx, &v.toObject().as<NativeObject>());
4853 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"
, 4853); AnnotateMozCrashReason("MOZ_ASSERT" "(" "proto->is<DebuggerPrototypeObject>()"
")"); do { *((volatile int*)__null) = 4853; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4854
4855 // Make the new Debugger object. Each one has a reference to
4856 // Debugger.{Frame,Object,Script,Memory}.prototype in reserved slots. The
4857 // rest of the reserved slots are for hooks; they default to undefined.
4858 Rooted<DebuggerInstanceObject*> obj(
4859 cx, NewTenuredObjectWithGivenProto<DebuggerInstanceObject>(cx, proto));
4860 if (!obj) {
4861 return false;
4862 }
4863 for (unsigned slot = JSSLOT_DEBUG_PROTO_START; slot < JSSLOT_DEBUG_PROTO_STOP;
4864 slot++) {
4865 obj->setReservedSlot(slot, proto->getReservedSlot(slot));
4866 }
4867 obj->setReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE, NullValue());
4868
4869 Rooted<NativeObject*> livenessLink(
4870 cx, NewObjectWithGivenProto<DebuggerDebuggeeLink>(cx, nullptr));
4871 if (!livenessLink) {
4872 return false;
4873 }
4874 obj->setReservedSlot(JSSLOT_DEBUG_DEBUGGEE_LINK, ObjectValue(*livenessLink));
4875
4876 Debugger* debugger;
4877 {
4878 // Construct the underlying C++ object.
4879 auto dbg = cx->make_unique<Debugger>(cx, obj.get());
4880 if (!dbg) {
4881 return false;
4882 }
4883
4884 // The object owns the released pointer.
4885 debugger = dbg.release();
4886 InitReservedSlot(obj, JSSLOT_DEBUG_DEBUGGER, debugger, MemoryUse::Debugger);
4887 }
4888
4889 // Add the initial debuggees, if any.
4890 for (unsigned i = 0; i < args.length(); i++) {
4891 JSObject& wrappedObj =
4892 args[i].toObject().as<ProxyObject>().private_().toObject();
4893 Rooted<GlobalObject*> debuggee(cx, &wrappedObj.nonCCWGlobal());
4894 if (!debugger->addDebuggeeGlobal(cx, debuggee)) {
4895 return false;
4896 }
4897 }
4898
4899 args.rval().setObject(*obj);
4900 return true;
4901}
4902
4903bool Debugger::addDebuggeeGlobal(JSContext* cx, Handle<GlobalObject*> global) {
4904 if (debuggees.has(global)) {
4905 return true;
4906 }
4907
4908 // Callers should generally be unable to get a reference to a debugger-
4909 // invisible global in order to pass it to addDebuggee. But this is possible
4910 // with certain testing aides we expose in the shell, so just make addDebuggee
4911 // throw in that case.
4912 Realm* debuggeeRealm = global->realm();
4913 if (debuggeeRealm->creationOptions().invisibleToDebugger()) {
4914 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4915 JSMSG_DEBUG_CANT_DEBUG_GLOBAL);
4916 return false;
4917 }
4918
4919 // Debugger and debuggee must be in different compartments.
4920 if (debuggeeRealm->compartment() == object->compartment()) {
4921 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
4922 JSMSG_DEBUG_SAME_COMPARTMENT);
4923 return false;
4924 }
4925
4926 // Check for cycles. If global's realm is reachable from this Debugger
4927 // object's realm by following debuggee-to-debugger links, then adding
4928 // global would create a cycle. (Typically nobody is debugging the
4929 // debugger, in which case we zip through this code without looping.)
4930 Vector<Realm*> visited(cx);
4931 if (!visited.append(object->realm())) {
4932 return false;
4933 }
4934 for (size_t i = 0; i < visited.length(); i++) {
4935 Realm* realm = visited[i];
4936 if (realm == debuggeeRealm) {
4937 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP);
4938 return false;
4939 }
4940
4941 // Find all realms containing debuggers debugging realm's global object.
4942 // Add those realms to visited.
4943 if (realm->isDebuggee()) {
4944 JS::AutoAssertNoGC nogc;
4945 for (Realm::DebuggerVectorEntry& entry : realm->getDebuggers(nogc)) {
4946 Realm* next = entry.dbg->object->realm();
4947 if (std::find(visited.begin(), visited.end(), next) == visited.end()) {
4948 if (!visited.append(next)) {
4949 return false;
4950 }
4951 }
4952 }
4953 }
4954 }
4955
4956 // For global to become this js::Debugger's debuggee:
4957 //
4958 // 1. this js::Debugger must be in global->getDebuggers(),
4959 // 2. global must be in this->debuggees,
4960 // 3. the debuggee's zone must be in this->debuggeeZones,
4961 // 4. if we are tracking allocations, the SavedStacksMetadataBuilder must be
4962 // installed for this realm, and
4963 // 5. Realm::isDebuggee()'s bit must be set.
4964 //
4965 // All five indications must be kept consistent.
4966
4967 AutoRealm ar(cx, global);
4968 Zone* zone = global->zone();
4969
4970 RootedObject debuggeeLink(cx, getDebuggeeLink());
4971 if (!cx->compartment()->wrap(cx, &debuggeeLink)) {
4972 return false;
4973 }
4974
4975 // (1)
4976 JS::AutoAssertNoGC nogc;
4977 auto& globalDebuggers = global->getDebuggers(nogc);
4978 if (!globalDebuggers.append(Realm::DebuggerVectorEntry(this, debuggeeLink))) {
4979 ReportOutOfMemory(cx);
4980 return false;
4981 }
4982 auto globalDebuggersGuard = MakeScopeExit([&] { globalDebuggers.popBack(); });
4983
4984 // (2)
4985 if (!debuggees.put(global)) {
4986 ReportOutOfMemory(cx);
4987 return false;
4988 }
4989 auto debuggeesGuard = MakeScopeExit([&] { debuggees.remove(global); });
4990
4991 bool addingZoneRelation = !debuggeeZones.has(zone);
4992
4993 // (3)
4994 if (addingZoneRelation && !debuggeeZones.put(zone)) {
4995 ReportOutOfMemory(cx);
4996 return false;
4997 }
4998 auto debuggeeZonesGuard = MakeScopeExit([&] {
4999 if (addingZoneRelation) {
5000 debuggeeZones.remove(zone);
5001 }
5002 });
5003
5004 // (4)
5005 if (trackingAllocationSites &&
5006 !Debugger::addAllocationsTracking(cx, global)) {
5007 return false;
5008 }
5009
5010 auto allocationsTrackingGuard = MakeScopeExit([&] {
5011 if (trackingAllocationSites) {
5012 Debugger::removeAllocationsTracking(*global);
5013 }
5014 });
5015
5016 // (5)
5017 AutoRestoreRealmDebugMode debugModeGuard(debuggeeRealm);
5018 debuggeeRealm->setIsDebuggee();
5019 debuggeeRealm->updateDebuggerObservesAsmJS();
5020 debuggeeRealm->updateDebuggerObservesWasm();
5021 debuggeeRealm->updateDebuggerObservesCoverage();
5022 if (observesAllExecution() &&
5023 !ensureExecutionObservabilityOfRealm(cx, debuggeeRealm)) {
5024 return false;
5025 }
5026
5027 globalDebuggersGuard.release();
5028 debuggeesGuard.release();
5029 debuggeeZonesGuard.release();
5030 allocationsTrackingGuard.release();
5031 debugModeGuard.release();
5032 return true;
5033}
5034
5035void Debugger::recomputeDebuggeeZoneSet() {
5036 AutoEnterOOMUnsafeRegion oomUnsafe;
5037 debuggeeZones.clear();
5038 for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
5039 if (!debuggeeZones.put(range.front().unbarrieredGet()->zone())) {
5040 oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
5041 }
5042 }
5043}
5044
5045template <typename T, typename AP>
5046static T* findDebuggerInVector(Debugger* dbg, Vector<T, 0, AP>* vec) {
5047 T* p;
5048 for (p = vec->begin(); p != vec->end(); p++) {
5049 if (p->dbg == dbg) {
5050 break;
5051 }
5052 }
5053 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"
, 5053); AnnotateMozCrashReason("MOZ_ASSERT" "(" "p != vec->end()"
")"); do { *((volatile int*)__null) = 5053; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5054 return p;
5055}
5056
5057void Debugger::removeDebuggeeGlobal(JS::GCContext* gcx, GlobalObject* global,
5058 WeakGlobalObjectSet::Enum* debugEnum,
5059 FromSweep fromSweep) {
5060 // The caller might have found global by enumerating this->debuggees; if
5061 // so, use HashSet::Enum::removeFront rather than HashSet::remove below,
5062 // to avoid invalidating the live enumerator.
5063 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"
, 5063); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debuggees.has(global)"
")"); do { *((volatile int*)__null) = 5063; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5064 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"
, 5064); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debuggeeZones.has(global->zone())"
")"); do { *((volatile int*)__null) = 5064; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5065 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"
, 5065); AnnotateMozCrashReason("MOZ_ASSERT" "(" "debugEnum->front().unbarrieredGet() == global"
")"); do { *((volatile int*)__null) = 5065; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5066
5067 // Clear this global's generators from generatorFrames as well.
5068 //
5069 // This method can be called either from script (dbg.removeDebuggee) or during
5070 // GC sweeping, because the Debugger, debuggee global, or both are being GC'd.
5071 //
5072 // When called from script, it's okay to iterate over generatorFrames and
5073 // touch its keys and values (even when an incremental GC is in progress).
5074 // When called from GC, it's not okay; the keys and values may be dying. But
5075 // in that case, we can actually just skip the loop entirely! If the Debugger
5076 // is going away, it doesn't care about the state of its generatorFrames
5077 // table, and the Debugger.Frame finalizer will fix up the generator observer
5078 // counts.
5079 if (fromSweep == FromSweep::No) {
5080 for (GeneratorWeakMap::Enum e(generatorFrames); !e.empty(); e.popFront()) {
5081 AbstractGeneratorObject& genObj = *e.front().key();
5082 if (&genObj.global() == global) {
5083 terminateDebuggerFrame(gcx, this, e.front().value(), NullFramePtr(),
5084 nullptr, &e);
5085 }
5086 }
5087 }
5088
5089 for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
5090 AbstractFramePtr frame = e.front().key();
5091 if (frame.hasGlobal(global)) {
5092 terminateDebuggerFrame(gcx, this, e.front().value(), frame, &e);
5093 }
5094 }
5095
5096 JS::AutoAssertNoGC nogc;
5097 auto& globalDebuggersVector = global->getDebuggers(nogc);
5098
5099 // The relation must be removed from up to three places:
5100 // globalDebuggersVector and debuggees for sure, and possibly the
5101 // compartment's debuggee set.
5102 //
5103 // The debuggee zone set is recomputed on demand. This avoids refcounting
5104 // and in practice we have relatively few debuggees that tend to all be in
5105 // the same zone. If after recomputing the debuggee zone set, this global's
5106 // zone is not in the set, then we must remove ourselves from the zone's
5107 // vector of observing debuggers.
5108 globalDebuggersVector.erase(
5109 findDebuggerInVector(this, &globalDebuggersVector));
5110
5111 if (debugEnum) {
5112 debugEnum->removeFront();
5113 } else {
5114 debuggees.remove(global);
5115 }
5116
5117 recomputeDebuggeeZoneSet();
5118
5119 // Remove all breakpoints for the debuggee.
5120 Breakpoint* nextbp;
5121 for (Breakpoint* bp = firstBreakpoint(); bp; bp = nextbp) {
5122 nextbp = bp->nextInDebugger();
5123
5124 if (bp->site->realm() == global->realm()) {
5125 bp->remove(gcx);
5126 }
5127 }
5128 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"
, 5128); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!firstBreakpoint()"
")"); do { *((volatile int*)__null) = 5128; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5129
5130 // If we are tracking allocation sites, we need to remove the object
5131 // metadata callback from this global's realm.
5132 if (trackingAllocationSites) {
5133 Debugger::removeAllocationsTracking(*global);
5134 }
5135
5136 if (!global->realm()->hasDebuggers()) {
5137 global->realm()->unsetIsDebuggee();
5138 } else {
5139 global->realm()->updateDebuggerObservesAllExecution();
5140 global->realm()->updateDebuggerObservesAsmJS();
5141 global->realm()->updateDebuggerObservesWasm();
5142 global->realm()->updateDebuggerObservesCoverage();
5143 }
5144}
5145
5146class MOZ_STACK_CLASS Debugger::QueryBase {
5147 protected:
5148 QueryBase(JSContext* cx, Debugger* dbg)
5149 : cx(cx),
5150 debugger(dbg),
5151 iterMarker(&cx->runtime()->gc),
5152 realms(cx->zone()) {}
5153
5154 // The context in which we should do our work.
5155 JSContext* cx;
5156
5157 // The debugger for which we conduct queries.
5158 Debugger* debugger;
5159
5160 // Require the set of realms to stay fixed while the query is alive.
5161 gc::AutoEnterIteration iterMarker;
5162
5163 using RealmSet = HashSet<Realm*, DefaultHasher<Realm*>, ZoneAllocPolicy>;
5164
5165 // A script must be in one of these realms to match the query.
5166 RealmSet realms;
5167
5168 // Indicates whether OOM has occurred while matching.
5169 bool oom = false;
5170
5171 bool addRealm(Realm* realm) { return realms.put(realm); }
5172
5173 // Arrange for this query to match only scripts that run in |global|.
5174 bool matchSingleGlobal(GlobalObject* global) {
5175 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"
, 5175); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realms.count() == 0"
")"); do { *((volatile int*)__null) = 5175; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5176 if (!addRealm(global->realm())) {
5177 ReportOutOfMemory(cx);
5178 return false;
5179 }
5180 return true;
5181 }
5182
5183 // Arrange for this ScriptQuery to match all scripts running in debuggee
5184 // globals.
5185 bool matchAllDebuggeeGlobals() {
5186 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"
, 5186); AnnotateMozCrashReason("MOZ_ASSERT" "(" "realms.count() == 0"
")"); do { *((volatile int*)__null) = 5186; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5187 // Build our realm set from the debugger's set of debuggee globals.
5188 for (WeakGlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty();
5189 r.popFront()) {
5190 if (!addRealm(r.front()->realm())) {
5191 ReportOutOfMemory(cx);
5192 return false;
5193 }
5194 }
5195 return true;
5196 }
5197};
5198
5199/*
5200 * A class for parsing 'findScripts' query arguments and searching for
5201 * scripts that match the criteria they represent.
5202 */
5203class MOZ_STACK_CLASS Debugger::ScriptQuery : public Debugger::QueryBase {
5204 public:
5205 /* Construct a ScriptQuery to use matching scripts for |dbg|. */
5206 ScriptQuery(JSContext* cx, Debugger* dbg)
5207 : QueryBase(cx, dbg),
5208 url(cx),
5209 displayURLString(cx),
5210 source(cx, AsVariant(static_cast<ScriptSourceObject*>(nullptr))),
5211 scriptVector(cx, BaseScriptVector(cx)),
5212 partialMatchVector(cx, BaseScriptVector(cx)),
5213 wasmInstanceVector(cx, WasmInstanceObjectVector(cx)) {}
5214
5215 /*
5216 * Parse the query object |query|, and prepare to match only the scripts
5217 * it specifies.
5218 */
5219 bool parseQuery(HandleObject query) {
5220 // Check for a 'global' property, which limits the results to those
5221 // scripts scoped to a particular global object.
5222 RootedValue global(cx);
5223 if (!GetProperty(cx, query, query, cx->names().global, &global)) {
5224 return false;
5225 }
5226 if (global.isUndefined()) {
5227 if (!matchAllDebuggeeGlobals()) {
5228 return false;
5229 }
5230 } else {
5231 GlobalObject* globalObject = debugger->unwrapDebuggeeArgument(cx, global);
5232 if (!globalObject) {
5233 return false;
5234 }
5235
5236 // If the given global isn't a debuggee, just leave the set of
5237 // acceptable globals empty; we'll return no scripts.
5238 if (debugger->debuggees.has(globalObject)) {
5239 if (!matchSingleGlobal(globalObject)) {
5240 return false;
5241 }
5242 }
5243 }
5244
5245 // Check for a 'url' property.
5246 if (!GetProperty(cx, query, query, cx->names().url, &url)) {
5247 return false;
5248 }
5249 if (!url.isUndefined() && !url.isString()) {
5250 JS_ReportErrorNumberASCII(
5251 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5252 "query object's 'url' property", "neither undefined nor a string");
5253 return false;
5254 }
5255
5256 // Check for a 'source' property
5257 RootedValue debuggerSource(cx);
5258 if (!GetProperty(cx, query, query, cx->names().source, &debuggerSource)) {
5259 return false;
5260 }
5261 if (!debuggerSource.isUndefined()) {
5262 if (!debuggerSource.isObject() ||
5263 !debuggerSource.toObject().is<DebuggerSource>()) {
5264 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5265 JSMSG_UNEXPECTED_TYPE,
5266 "query object's 'source' property",
5267 "not undefined nor a Debugger.Source object");
5268 return false;
5269 }
5270
5271 DebuggerSource& debuggerSourceObj =
5272 debuggerSource.toObject().as<DebuggerSource>();
5273
5274 // If it does have an owner, it should match the Debugger we're
5275 // calling findScripts on. It would work fine even if it didn't,
5276 // but mixing Debugger.Sources is probably a sign of confusion.
5277 if (debuggerSourceObj.owner() != debugger) {
5278 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5279 JSMSG_DEBUG_WRONG_OWNER, "Debugger.Source");
5280 return false;
5281 }
5282
5283 hasSource = true;
5284 source = debuggerSourceObj.getReferent();
5285 }
5286
5287 // Check for a 'displayURL' property.
5288 RootedValue displayURL(cx);
5289 if (!GetProperty(cx, query, query, cx->names().displayURL, &displayURL)) {
5290 return false;
5291 }
5292 if (!displayURL.isUndefined() && !displayURL.isString()) {
5293 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5294 JSMSG_UNEXPECTED_TYPE,
5295 "query object's 'displayURL' property",
5296 "neither undefined nor a string");
5297 return false;
5298 }
5299
5300 if (displayURL.isString()) {
5301 displayURLString = displayURL.toString()->ensureLinear(cx);
5302 if (!displayURLString) {
5303 return false;
5304 }
5305 }
5306
5307 // Check for a 'line' property.
5308 RootedValue lineProperty(cx);
5309 if (!GetProperty(cx, query, query, cx->names().line, &lineProperty)) {
5310 return false;
5311 }
5312 if (lineProperty.isUndefined()) {
5313 hasLine = false;
5314 } else if (lineProperty.isNumber()) {
5315 if (displayURL.isUndefined() && url.isUndefined() && !hasSource) {
5316 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5317 JSMSG_QUERY_LINE_WITHOUT_URL);
5318 return false;
5319 }
5320 double doubleLine = lineProperty.toNumber();
5321 uint32_t uintLine = (uint32_t)doubleLine;
5322 if (doubleLine <= 0 || uintLine != doubleLine) {
5323 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5324 JSMSG_DEBUG_BAD_LINE);
5325 return false;
5326 }
5327 hasLine = true;
5328 line = uintLine;
5329 } else {
5330 JS_ReportErrorNumberASCII(
5331 cx, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
5332 "query object's 'line' property", "neither undefined nor an integer");
5333 return false;
5334 }
5335
5336 // Check for an 'innermost' property.
5337 PropertyName* innermostName = cx->names().innermost;
5338 RootedValue innermostProperty(cx);
5339 if (!GetProperty(cx, query, query, innermostName, &innermostProperty)) {
5340 return false;
5341 }
5342 innermost = ToBoolean(innermostProperty);
5343 if (innermost) {
5344 // Technically, we need only check hasLine, but this is clearer.
5345 if ((displayURL.isUndefined() && url.isUndefined() && !hasSource) ||
5346 !hasLine) {
5347 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
5348 JSMSG_QUERY_INNERMOST_WITHOUT_LINE_URL);
5349 return false;
5350 }
5351 }
5352
5353 return true;
5354 }
5355
5356 /* Set up this ScriptQuery appropriately for a missing query argument. */
5357 bool omittedQuery() {
5358 url.setUndefined();
5359 hasLine = false;
5360 innermost = false;
5361 displayURLString = nullptr;
5362 return matchAllDebuggeeGlobals();
5363 }
5364
5365 /*
5366 * Search all relevant realms and the stack for scripts matching
5367 * this query, and append the matching scripts to |scriptVector|.
5368 */
5369 bool findScripts() {
5370 if (!prepareQuery()) {
5371 return false;
5372 }
5373
5374 Realm* singletonRealm = nullptr;
5375 if (realms.count() == 1) {
5376 singletonRealm = realms.all().front();
5377 }
5378
5379 // Search each realm for debuggee scripts.
5380 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"
, 5380); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scriptVector.empty()"
")"); do { *((volatile int*)__null) = 5380; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5381 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"
, 5381); AnnotateMozCrashReason("MOZ_ASSERT" "(" "partialMatchVector.empty()"
")"); do { *((volatile int*)__null) = 5381; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5382 oom = false;
5383 IterateScripts(cx, singletonRealm, this, considerScript);
5384 if (oom) {
5385 ReportOutOfMemory(cx);
5386 return false;
5387 }
5388
5389 // If we are filtering by line number, the lazy BaseScripts were not checked
5390 // yet since they do not implement `GetScriptLineExtent`. Instead we revisit
5391 // each result script and delazify its children and add any matching ones to
5392 // the results list.
5393 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"
, 5393); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hasLine || partialMatchVector.empty()"
")"); do { *((volatile int*)__null) = 5393; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5394 Rooted<BaseScript*> script(cx);
5395 RootedFunction fun(cx);
5396 while (!partialMatchVector.empty()) {
5397 script = partialMatchVector.popCopy();
5398
5399 // As a performance optimization, we can skip scripts that are definitely
5400 // out-of-bounds for the target line. This was checked before adding to
5401 // the partialMatchVector, but the bound may have improved since then.
5402 if (script->extent().sourceEnd <= sourceOffsetLowerBound) {
5403 continue;
5404 }
5405
5406 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"
, 5406); AnnotateMozCrashReason("MOZ_ASSERT" "(" "script->isFunction()"
")"); do { *((volatile int*)__null) = 5406; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5407 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"
, 5407); AnnotateMozCrashReason("MOZ_ASSERT" "(" "script->isReadyForDelazification()"
")"); do { *((volatile int*)__null) = 5407; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5408
5409 fun = script->function();
5410
5411 // Ignore any delazification placeholder functions. These should not be
5412 // exposed to debugger in any way.
5413 if (fun->isGhost()) {
5414 continue;
5415 }
5416
5417 // Delazify script.
5418 JSScript* compiledScript = GetOrCreateFunctionScript(cx, fun);
5419 if (!compiledScript) {
5420 return false;
5421 }
5422
5423 // If target line isn't in script, we are done with it.
5424 if (!scriptIsLineMatch(compiledScript)) {
5425 continue;
5426 }
5427
5428 // Add script to results now that we've completed checks.
5429 if (!scriptVector.append(compiledScript)) {
5430 return false;
5431 }
5432
5433 // If script was a leaf we are done with it. This is an optional
5434 // optimization to avoid inspecting the `gcthings` list below.
5435 if (!script->hasInnerFunctions()) {
5436 continue;
5437 }
5438
5439 // Now add inner scripts to `partialMatchVector` work list to determine if
5440 // they are matches. Note that out IterateScripts callback ignored them
5441 // already since they did not have a compiled parent at the time.
5442 for (JS::GCCellPtr thing : script->gcthings()) {
5443 if (!thing.is<JSObject>() || !thing.as<JSObject>().is<JSFunction>()) {
5444 continue;
5445 }
5446 JSFunction* fun = &thing.as<JSObject>().as<JSFunction>();
5447 if (!fun->hasBaseScript()) {
5448 continue;
5449 }
5450 BaseScript* inner = fun->baseScript();
5451 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"
, 5451); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inner" ")")
; do { *((volatile int*)__null) = 5451; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5452 if (!inner) {
5453 // If the function doesn't have script, ignore it.
5454 continue;
5455 }
5456
5457 if (!scriptIsPartialLineMatch(inner)) {
5458 continue;
5459 }
5460
5461 // Add the matching inner script to the back of the results queue
5462 // where it will be processed recursively.
5463 if (!partialMatchVector.append(inner)) {
5464 return false;
5465 }
5466 }
5467 }
5468
5469 // If this is an 'innermost' query, we want to filter the results again to
5470 // only return the innermost script for each realm. To do this we build a
5471 // hashmap to track innermost and then recreate the `scriptVector` with the
5472 // results that remain in the hashmap.
5473 if (innermost) {
5474 using RealmToScriptMap =
5475 GCHashMap<Realm*, BaseScript*, DefaultHasher<Realm*>>;
5476
5477 Rooted<RealmToScriptMap> innermostForRealm(cx, cx);
5478
5479 // Visit each candidate script and find innermost in each realm.
5480 for (BaseScript* script : scriptVector) {
5481 Realm* realm = script->realm();
5482 RealmToScriptMap::AddPtr p = innermostForRealm.lookupForAdd(realm);
5483 if (p) {
5484 // Is our newly found script deeper than the last one we found?
5485 BaseScript* incumbent = p->value();
5486 if (script->asJSScript()->innermostScope()->chainLength() >
5487 incumbent->asJSScript()->innermostScope()->chainLength()) {
5488 p->value() = script;
5489 }
5490 } else {
5491 // This is the first matching script we've encountered for this
5492 // realm, so it is thus the innermost such script.
5493 if (!innermostForRealm.add(p, realm, script)) {
5494 return false;
5495 }
5496 }
5497 }
5498
5499 // Reset the results vector.
5500 scriptVector.clear();
5501
5502 // Re-add only the innermost scripts to the results.
5503 for (RealmToScriptMap::Range r = innermostForRealm.all(); !r.empty();
5504 r.popFront()) {
5505 if (!scriptVector.append(r.front().value())) {
5506 return false;
5507 }
5508 }
5509 }
5510
5511 // TODO: Until such time that wasm modules are real ES6 modules,
5512 // unconditionally consider all wasm toplevel instance scripts.
5513 for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty();
5514 r.popFront()) {
5515 for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
5516 consider(instance->object());
5517 if (oom) {
5518 ReportOutOfMemory(cx);
5519 return false;
5520 }
5521 }
5522 }
5523
5524 return true;
5525 }
5526
5527 Handle<BaseScriptVector> foundScripts() const { return scriptVector; }
5528
5529 Handle<WasmInstanceObjectVector> foundWasmInstances() const {
5530 return wasmInstanceVector;
5531 }
5532
5533 private:
5534 /* If this is a string, matching scripts have urls equal to it. */
5535 RootedValue url;
5536
5537 /* url as a C string. */
5538 UniqueChars urlCString;
5539
5540 /* If this is a string, matching scripts' sources have displayURLs equal to
5541 * it. */
5542 Rooted<JSLinearString*> displayURLString;
5543
5544 /*
5545 * If this is a source referent, matching scripts will have sources equal
5546 * to this instance. Ideally we'd use a Maybe here, but Maybe interacts
5547 * very badly with Rooted's LIFO invariant.
5548 */
5549 bool hasSource = false;
5550 Rooted<DebuggerSourceReferent> source;
5551
5552 /* True if the query contained a 'line' property. */
5553 bool hasLine = false;
5554
5555 /* The line matching scripts must cover. */
5556 uint32_t line = 0;
5557
5558 // As a performance optimization (and to avoid delazifying as many scripts),
5559 // we would like to know the source offset of the target line.
5560 //
5561 // Since we do not have a simple way to compute this precisely, we instead
5562 // track a lower-bound of the offset value. As we collect SourceExtent
5563 // examples with (line,column) <-> sourceStart mappings, we can improve the
5564 // bound. The target line is within the range [sourceOffsetLowerBound, Inf).
5565 //
5566 // NOTE: Using a SourceExtent for updating the bound happens independently of
5567 // if the script matches the target line or not in the in the end.
5568 mutable uint32_t sourceOffsetLowerBound = 0;
5569
5570 /* True if the query has an 'innermost' property whose value is true. */
5571 bool innermost = false;
5572
5573 /*
5574 * Accumulate the scripts in an Rooted<BaseScriptVector> instead of creating
5575 * the JS array as we go, because we mustn't allocate JS objects or GC while
5576 * we use the CellIter.
5577 */
5578 Rooted<BaseScriptVector> scriptVector;
5579
5580 /*
5581 * While in the CellIter we may find BaseScripts that need to be compiled
5582 * before the query can be fully checked. Since we cannot compile while under
5583 * CellIter we accumulate them here instead.
5584 *
5585 * This occurs when matching line numbers since `GetScriptLineExtent` cannot
5586 * be computed without bytecode existing.
5587 */
5588 Rooted<BaseScriptVector> partialMatchVector;
5589
5590 /*
5591 * Like above, but for wasm modules.
5592 */
5593 Rooted<WasmInstanceObjectVector> wasmInstanceVector;
5594
5595 /*
5596 * Given that parseQuery or omittedQuery has been called, prepare to match
5597 * scripts. Set urlCString and displayURLChars as appropriate.
5598 */
5599 bool prepareQuery() {
5600 // Compute urlCString and displayURLChars, if a url or displayURL was
5601 // given respectively.
5602 if (url.isString()) {
5603 Rooted<JSString*> str(cx, url.toString());
5604 urlCString = JS_EncodeStringToUTF8(cx, str);
5605 if (!urlCString) {
5606 return false;
5607 }
5608 }
5609
5610 return true;
5611 }
5612
5613 void updateSourceOffsetLowerBound(const SourceExtent& extent) {
5614 // We trying to find the offset of (target-line, 0) so just ignore any
5615 // extents on target line to keep things simple.
5616 MOZ_ASSERT(extent.lineno <= line)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(extent.lineno <= line)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(extent.lineno <= line))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("extent.lineno <= line"
, "/var/lib/jenkins/workspace/firefox-scan-build/js/src/debugger/Debugger.cpp"
, 5616); AnnotateMozCrashReason("MOZ_ASSERT" "(" "extent.lineno <= line"
")"); do { *((volatile int*)__null) = 5616; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5617 if (extent.lineno == line) {
5618 return;
5619 }
5620
5621 // The extent.sourceStart position is now definitely *before* the target
5622 // line, so update sourceOffsetLowerBound if extent.sourceStart is a tighter
5623 // bound.
5624 if (extent.sourceStart > sourceOffsetLowerBound) {
5625 sourceOffsetLowerBound = extent.sourceStart;
5626 }
5627 }
5628
5629 // A partial match is a script that starts before the target line, but may or
5630 // may not end before it. If we can prove the script definitely ends before
5631 // the target line, we may return false here.
5632 bool scriptIsPartialLineMatch(BaseScript* script) {
5633 const SourceExtent& extent = script->extent();
5634
5635 // Check that start of script is before or on target line.
5636 if (extent.lineno > line) {
5637 return false;
5638 }
5639
5640 // Use the implicit (line, column) <-> sourceStart mapping from the
5641 // SourceExtent to update our bounds on possible matches. We call this
5642 // without knowing if the script is a match or not.
5643 updateSourceOffsetLowerBound(script->extent());
5644
5645 // As an optional performance optimization, we rule out any script that ends
5646 // before the lower-bound on where target line exists.
5647 return extent.sourceEnd > sourceOffsetLowerBound;
5648 }
5649
5650 // True if any part of script source is on the target line.
5651 bool scriptIsLineMatch(JSScript* script) {
5652 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"
, 5652); AnnotateMozCrashReason("MOZ_ASSERT" "(" "scriptIsPartialLineMatch(script)"
")"); do { *((volatile int*)__null) = 5652; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5653
5654 uint32_t lineCount = GetScriptLineExtent(script);
5655 return (script->lineno() + lineCount > line);
5656 }
5657
5658 static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
5659 const JS::AutoRequireNoGC& nogc) {
5660 ScriptQuery* self = static_cast<ScriptQuery*>(data);
5661 self->consider(script, nogc);
5662 }
5663
5664 template <typename T>
5665 [[nodiscard]] bool commonFilter(T script, const JS::AutoRequireNoGC& nogc) {
5666 if (urlCString) {
5667 bool gotFilename = false;
5668 if (script->filename() &&
5669 strcmp(script->filename(), urlCString.get()) == 0) {
5670 gotFilename = true;
5671 }
5672
5673 bool gotSourceURL = false;
5674 if (!gotFilename && script->scriptSource()->introducerFilename() &&
5675 strcmp(script->scriptSource()->introducerFilename(),
5676 urlCString.get()) == 0) {
5677 gotSourceURL = true;
5678 }
5679 if (!gotFilename && !gotSourceURL) {
5680 return false;
5681 }
5682 }
5683 if (displayURLString) {
5684 if (!script->scriptSource() || !script->scriptSource()->hasDisplayURL()) {
5685 return false;
5686 }
5687
5688 const char16_t* s = script->scriptSource()->displayURL();
5689 if (CompareChars(s, js_strlen(s), displayURLString) != 0) {
5690 return false;
5691 }
5692 }
5693 if (hasSource && !(source.is<ScriptSourceObject*>() &&
5694 source.as<ScriptSourceObject*>()->source() ==
5695 script->scriptSource())) {
5696 return false;
5697 }
5698 return true;
5699 }
5700
5701 /*
5702 * If |script| matches this query, append it to |scriptVector|. Set |oom| if
5703 * an out of memory condition occurred.
5704 */
5705 void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
5706 if (oom || script->selfHosted()) {
5707 return;
5708 }
5709
5710 Realm* realm = script->realm();
5711 if (!realms.has(realm)) {
5712 return;
5713 }
5714
5715 if (!commonFilter(script, nogc)) {
5716 return;
5717 }
5718
5719 bool partial = false;
5720
5721 if (hasLine) {
5722 if (!scriptIsPartialLineMatch(script)) {
5723 return;
5724 }
5725
5726 if (script->hasBytecode()) {
5727 // Check if line is within script (or any of its inner scripts).
5728 if (!scriptIsLineMatch(script->asJSScript())) {
5729 return;
5730 }
5731 } else {
5732 // GetScriptLineExtent is not available on lazy scripts so instead to
5733 // the partial match list for be compiled and reprocessed later. We only
5734 // add scripts that are ready for delazification and they may in turn
5735 // process their inner functions.
5736 if (!script->isReadyForDelazification()) {
5737 return;
5738 }
5739 partial = true;
5740 }
5741 }
5742
5743 // If innermost filter is required, we collect everything that matches the
5744 // line number and filter at the end of `findScripts`.
5745 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"
, 5745); AnnotateMozCrashReason("MOZ_ASSERT" "(" "hasLine" ")"
); do { *((volatile int*)__null) = 5745; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
5746
5747 Rooted<BaseScriptVector>& vec = partial ? partialMatchVector : scriptVector;
5748 if (!vec.append(script)) {
5749 oom = true;
5750 }
5751 }
5752
5753 /*
5754 * If |instanceObject| matches this query, append it to |wasmInstanceVector|.
5755 * Set |oom| if an out of memory condition occurred.
5756 */
5757 void consider(WasmInstanceObject* instanceObject) {
5758 if (oom) {
5759 return;
5760 }
5761
5762 if (hasSource && source != AsVariant(instanceObject)) {
5763 return;
5764 }
5765
5766 if (!wasmInstanceVector.append(instanceObject)) {
5767 oom = true;
5768 }
5769 }
5770};
5771
5772bool Debugger::CallData::findScripts() {
5773 ScriptQuery query(cx, dbg);
5774
5775 if (args.length() >= 1) {
5776 RootedObject queryObject(cx, RequireObject(cx, args[0]));
5777 if (!queryObject || !query.parseQuery(queryObject)) {
5778 return false;
5779 }
5780 } else {
5781 if (!query.omittedQuery()) {
5782 return false;
5783 }
5784 }
5785
5786 if (!query.findScripts()) {
5787 return false;
5788 }
5789
5790 Handle<BaseScriptVector> scripts(query.foundScripts());
5791 Handle<WasmInstanceObjectVector> wasmInstances(query.foundWasmInstances());
5792
5793 size_t resultLength = scripts.length() + wasmInstances.length();
5794 Rooted<ArrayObject*> result(cx,
5795 NewDenseFullyAllocatedArray(cx, resultLength));
5796 if (!result) {
5797 return false;
5798 }
5799
5800 result->ensureDenseInitializedLength(0, resultLength);
5801
5802 for (size_t i = 0; i < scripts.length(); i++) {
5803 JSObject* scriptObject = dbg->wrapScript(cx, scripts[i]);
5804 if (!scriptObject) {
5805 return false;
5806 }
5807 result->setDenseElement(i, ObjectValue(*scriptObject));
5808 }
5809
5810 size_t wasmStart = scripts.length();
5811 for (size_t i = 0; i < wasmInstances.length(); i++) {
5812 JSObject* scriptObject = dbg->wrapWasmScript(cx, wasmInstances[i]);
5813 if (!scriptObject) {
5814 return false;
5815 }
5816 result->setDenseElement(wasmStart + i, ObjectValue(*scriptObject));
5817 }
5818
5819 args.rval().setObject(*result);
5820 return true;
5821}
5822
5823/*
5824 * A class for searching sources for 'findSources'.
5825 */
5826class MOZ_STACK_CLASS Debugger::SourceQuery : public Debugger::QueryBase {
5827 public:
5828 using SourceSet = JS::GCHashSet<JSObject*, js::StableCellHasher<JSObject*>,
5829 ZoneAllocPolicy>;
5830
5831 SourceQuery(JSContext* cx, Debugger* dbg)
5832 : QueryBase(cx, dbg), sources(cx, SourceSet(cx->zone())) {}
5833
5834 bool findSources() {
5835 if (!matchAllDebuggeeGlobals()) {
5836 return false;
5837 }
5838
5839 Realm* singletonRealm = nullptr;
5840 if (realms.count() == 1) {
5841 singletonRealm = realms.all().front();
5842 }
5843
5844 // Search each realm for debuggee scripts.
5845 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"
, 5845); AnnotateMozCrashReason("MOZ_ASSERT" "(" "sources.empty()"
")"); do { *((volatile int*)__null) = 5845; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5846 oom = false;
5847 IterateScripts(cx, singletonRealm, this, considerScript);
5848 if (oom) {
5849 ReportOutOfMemory(cx);
5850 return false;
5851 }
5852
5853 // TODO: Until such time that wasm modules are real ES6 modules,
5854 // unconditionally consider all wasm toplevel instance scripts.
5855 for (WeakGlobalObjectSet::Range r = debugger->allDebuggees(); !r.empty();
5856 r.popFront()) {
5857 for (wasm::Instance* instance : r.front()->realm()->wasm.instances()) {
5858 consider(instance->object());
5859 if (oom) {
5860 ReportOutOfMemory(cx);
5861 return false;
5862 }
5863 }
5864 }
5865
5866 return true;
5867 }
5868
5869 Handle<SourceSet> foundSources() const { return sources; }
5870
5871 private:
5872 Rooted<SourceSet> sources;
5873
5874 static void considerScript(JSRuntime* rt, void* data, BaseScript* script,
5875 const JS::AutoRequireNoGC& nogc) {
5876 SourceQuery* self = static_cast<SourceQuery*>(data);
5877 self->consider(script, nogc);
5878 }
5879
5880 void consider(BaseScript* script, const JS::AutoRequireNoGC& nogc) {
5881 if (oom || script->selfHosted()) {
5882 return;
5883 }
5884
5885 Realm* realm