Bug Summary

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