Bug Summary

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

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_js_src_debugger1.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/debugger -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src/debugger -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D WASM_SUPPORTS_HUGE_MEMORY -D JS_CACHEIR_SPEW -D JS_STRUCTURED_SPEW -D JS_HAS_CTYPES -D FFI_BUILDING -D EXPORT_JS_API -D MOZ_HAS_MOZGLUE -D MOZ_SUPPORT_LEAKCHECKING -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/js/src -I /root/firefox-clang/js/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_js_src_debugger1.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#include "mozilla/Maybe.h" // for Some, Maybe
6#include "mozilla/Span.h" // for Span
7#include "mozilla/Vector.h" // for Vector
8
9#include <stddef.h> // for ptrdiff_t
10#include <stdint.h> // for uint32_t, UINT32_MAX, SIZE_MAX, int32_t
11
12#include "NamespaceImports.h" // for CallArgs, RootedValue
13
14#include "builtin/Array.h" // for NewDenseEmptyArray
15#include "builtin/Number.h" // for ToNumber
16#include "debugger/Debugger.h" // for DebuggerScriptReferent, Debugger
17#include "debugger/DebugScript.h" // for DebugScript
18#include "debugger/Source.h" // for DebuggerSource
19#include "gc/GC.h" // for MemoryUse, MemoryUse::Breakpoint
20#include "gc/Tracer.h" // for TraceManuallyBarrieredCrossCompartmentEdge
21#include "gc/Zone.h" // for Zone
22#include "gc/ZoneAllocator.h" // for AddCellMemory
23#include "js/CallArgs.h" // for CallArgs, CallArgsFromVp
24#include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin, JS::WasmFunctionIndex
25#include "js/friend/ErrorMessages.h" // for GetErrorMessage, JSMSG_*
26#include "js/GCVariant.h" // for GCVariant
27#include "js/HeapAPI.h" // for GCCellPtr
28#include "js/RootingAPI.h" // for Rooted
29#include "js/Wrapper.h" // for UncheckedUnwrap
30#include "vm/ArrayObject.h" // for ArrayObject
31#include "vm/BytecodeUtil.h" // for GET_JUMP_OFFSET
32#include "vm/Compartment.h" // for JS::Compartment
33#include "vm/EnvironmentObject.h" // for EnvironmentCoordinateNameSlow
34#include "vm/GlobalObject.h" // for GlobalObject
35#include "vm/JSContext.h" // for JSContext, ReportValueError
36#include "vm/JSFunction.h" // for JSFunction
37#include "vm/JSObject.h" // for RequireObject, JSObject
38#include "vm/JSScript.h" // for BaseScript
39#include "vm/ObjectOperations.h" // for DefineDataProperty, HasOwnProperty
40#include "vm/PlainObject.h" // for js::PlainObject
41#include "vm/Realm.h" // for AutoRealm
42#include "vm/Runtime.h" // for JSAtomState, JSRuntime
43#include "vm/StringType.h" // for NameToId, PropertyName, JSAtom
44#include "wasm/WasmDebug.h" // for ExprLoc, DebugState
45#include "wasm/WasmInstance.h" // for Instance
46#include "wasm/WasmJS.h" // for WasmInstanceObject
47#include "wasm/WasmTypeDecls.h" // for Bytes
48
49#include "debugger/Script-inl.h"
50#include "gc/Marking-inl.h" // for MaybeForwardedObjectIs
51#include "vm/BytecodeUtil-inl.h" // for BytecodeRangeWithPosition
52#include "vm/JSAtomUtils-inl.h" // for PrimitiveValueToId
53#include "vm/JSObject-inl.h" // for NewBuiltinClassInstance, NewObjectWithGivenProto
54#include "vm/JSScript-inl.h" // for JSScript::global
55#include "vm/ObjectOperations-inl.h" // for GetProperty
56#include "vm/Realm-inl.h" // for AutoRealm::AutoRealm
57
58using namespace js;
59
60using mozilla::Maybe;
61using mozilla::Some;
62
63const JSClassOps DebuggerScript::classOps_ = {
64 .trace = CallTraceMethod<DebuggerScript>,
65};
66
67const JSClass DebuggerScript::class_ = {
68 "Script",
69 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
70 &classOps_,
71};
72
73void DebuggerScript::trace(JSTracer* trc) {
74 // This comes from a private pointer, so no barrier needed.
75 gc::Cell* cell = getReferentCell();
76 if (cell) {
77 if (cell->is<BaseScript>()) {
78 BaseScript* script = cell->as<BaseScript>();
79 TraceManuallyBarrieredCrossCompartmentEdge(
80 trc, this, &script, "Debugger.Script script referent");
81 if (script != cell->as<BaseScript>()) {
82 setReservedSlotGCThingAsPrivateUnbarriered(SCRIPT_SLOT, script);
83 }
84 } else {
85 JSObject* wasm = cell->as<JSObject>();
86 TraceManuallyBarrieredCrossCompartmentEdge(
87 trc, this, &wasm, "Debugger.Script wasm referent");
88 if (wasm != cell->as<JSObject>()) {
89 MOZ_ASSERT(gc::MaybeForwardedObjectIs<WasmInstanceObject>(wasm))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(gc::MaybeForwardedObjectIs<WasmInstanceObject>
(wasm))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(gc::MaybeForwardedObjectIs<WasmInstanceObject>
(wasm)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("gc::MaybeForwardedObjectIs<WasmInstanceObject>(wasm)"
, "./../../../../js/src/debugger/Script.cpp", 89); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "gc::MaybeForwardedObjectIs<WasmInstanceObject>(wasm)"
")"); do { MOZ_CrashSequence(__null, 89); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
90 setReservedSlotGCThingAsPrivateUnbarriered(SCRIPT_SLOT, wasm);
91 }
92 }
93 }
94}
95
96/* static */
97NativeObject* DebuggerScript::initClass(JSContext* cx,
98 Handle<GlobalObject*> global,
99 HandleObject debugCtor) {
100 return InitClass(cx, debugCtor, nullptr, nullptr, "Script", construct, 0,
101 properties_, methods_, nullptr, nullptr);
102}
103
104/* static */
105DebuggerScript* DebuggerScript::create(JSContext* cx, HandleObject proto,
106 Handle<DebuggerScriptReferent> referent,
107 Handle<NativeObject*> debugger) {
108 DebuggerScript* scriptobj = NewObjectWithGivenProto<DebuggerScript>(
109 cx, proto, {.newKind = TenuredObject});
110 if (!scriptobj) {
111 return nullptr;
112 }
113
114 scriptobj->setReservedSlot(DebuggerScript::OWNER_SLOT,
115 ObjectValue(*debugger));
116 referent.get().match([&](auto& scriptHandle) {
117 scriptobj->setReservedSlotGCThingAsPrivate(SCRIPT_SLOT, scriptHandle);
118 });
119
120 return scriptobj;
121}
122
123static JSScript* DelazifyScript(JSContext* cx, Handle<BaseScript*> script) {
124 if (script->hasBytecode()) {
125 return script->asJSScript();
126 }
127 MOZ_ASSERT(script->isFunction())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->isFunction())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(script->isFunction()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("script->isFunction()"
, "./../../../../js/src/debugger/Script.cpp", 127); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->isFunction()" ")"); do { MOZ_CrashSequence
(__null, 127); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
128
129 // JSFunction::getOrCreateScript requires an enclosing scope. This requires
130 // the enclosing script to be non-lazy.
131 if (script->hasEnclosingScript()) {
132 Rooted<BaseScript*> enclosingScript(cx, script->enclosingScript());
133 if (!DelazifyScript(cx, enclosingScript)) {
134 return nullptr;
135 }
136
137 if (!script->isReadyForDelazification()) {
138 // It didn't work! Delazifying the enclosing script still didn't
139 // delazify this script. This happens when the function
140 // corresponding to this script was removed by constant folding.
141 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
142 JSMSG_DEBUG_OPTIMIZED_OUT_FUN);
143 return nullptr;
144 }
145 }
146
147 MOZ_ASSERT(script->enclosingScope())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script->enclosingScope())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(script->enclosingScope())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("script->enclosingScope()"
, "./../../../../js/src/debugger/Script.cpp", 147); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "script->enclosingScope()" ")"); do { MOZ_CrashSequence
(__null, 147); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
148
149 RootedFunction fun(cx, script->function());
150 AutoRealm ar(cx, fun);
151 return JSFunction::getOrCreateScript(cx, fun);
152}
153
154/* static */
155DebuggerScript* DebuggerScript::check(JSContext* cx, HandleValue v) {
156 JSObject* thisobj = RequireObject(cx, v);
157 if (!thisobj) {
158 return nullptr;
159 }
160 if (!thisobj->is<DebuggerScript>()) {
161 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
162 JSMSG_INCOMPATIBLE_PROTO, "Debugger.Script",
163 "method", thisobj->getClass()->name);
164 return nullptr;
165 }
166
167 return &thisobj->as<DebuggerScript>();
168}
169
170struct MOZ_STACK_CLASS DebuggerScript::CallData {
171 JSContext* cx;
172 const CallArgs& args;
173
174 Handle<DebuggerScript*> obj;
175 Rooted<DebuggerScriptReferent> referent;
176 RootedScript script;
177
178 CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerScript*> obj)
179 : cx(cx),
180 args(args),
181 obj(obj),
182 referent(cx, obj->getReferent()),
183 script(cx) {}
184
185 [[nodiscard]] bool ensureScriptMaybeLazy() {
186 if (!referent.is<BaseScript*>()) {
187 ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK1,
188 args.thisv(), nullptr, "a JS script");
189 return false;
190 }
191 return true;
192 }
193
194 [[nodiscard]] bool ensureScript() {
195 if (!ensureScriptMaybeLazy()) {
196 return false;
197 }
198 script = DelazifyScript(cx, referent.as<BaseScript*>());
199 if (!script) {
200 return false;
201 }
202 return true;
203 }
204
205 bool getIsGeneratorFunction();
206 bool getIsAsyncFunction();
207 bool getIsFunction();
208 bool getIsModule();
209 bool getDisplayName();
210 bool getParameterNames();
211 bool getUrl();
212 bool getStartLine();
213 bool getStartColumn();
214 bool getLineCount();
215 bool getSource();
216 bool getSourceStart();
217 bool getSourceLength();
218 bool getMainOffset();
219 bool getGlobal();
220 bool getFormat();
221 bool getChildScripts();
222 bool getPossibleBreakpoints();
223 bool getPossibleBreakpointOffsets();
224 bool getOffsetMetadata();
225 bool getOffsetLocation();
226 bool getEffectfulOffsets();
227 bool getAllOffsets();
228 bool getAllColumnOffsets();
229 bool getLineOffsets();
230 bool setBreakpoint();
231 bool getBreakpoints();
232 bool clearBreakpoint();
233 bool clearAllBreakpoints();
234 bool isInCatchScope();
235 bool getOffsetsCoverage();
236
237 using Method = bool (CallData::*)();
238
239 template <Method MyMethod>
240 static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
241};
242
243template <DebuggerScript::CallData::Method MyMethod>
244/* static */
245bool DebuggerScript::CallData::ToNative(JSContext* cx, unsigned argc,
246 Value* vp) {
247 CallArgs args = CallArgsFromVp(argc, vp);
248
249 Rooted<DebuggerScript*> obj(cx, DebuggerScript::check(cx, args.thisv()));
250 if (!obj) {
251 return false;
252 }
253
254 CallData data(cx, args, obj);
255 return (data.*MyMethod)();
256}
257
258bool DebuggerScript::CallData::getIsGeneratorFunction() {
259 if (!ensureScriptMaybeLazy()) {
260 return false;
261 }
262 args.rval().setBoolean(obj->getReferentScript()->isGenerator());
263 return true;
264}
265
266bool DebuggerScript::CallData::getIsAsyncFunction() {
267 if (!ensureScriptMaybeLazy()) {
268 return false;
269 }
270 args.rval().setBoolean(obj->getReferentScript()->isAsync());
271 return true;
272}
273
274bool DebuggerScript::CallData::getIsFunction() {
275 if (!ensureScriptMaybeLazy()) {
276 return false;
277 }
278
279 args.rval().setBoolean(obj->getReferentScript()->function());
280 return true;
281}
282
283bool DebuggerScript::CallData::getIsModule() {
284 if (!ensureScriptMaybeLazy()) {
285 return false;
286 }
287 BaseScript* script = referent.as<BaseScript*>();
288
289 args.rval().setBoolean(script->isModule());
290 return true;
291}
292
293bool DebuggerScript::CallData::getDisplayName() {
294 if (!ensureScriptMaybeLazy()) {
295 return false;
296 }
297
298 JSFunction* func = obj->getReferentScript()->function();
299 if (!func) {
300 args.rval().setUndefined();
301 return true;
302 }
303
304 JSAtom* name = func->fullDisplayAtom();
305 if (!name) {
306 args.rval().setUndefined();
307 return true;
308 }
309
310 RootedValue namev(cx, StringValue(name));
311 Debugger* dbg = obj->owner();
312 if (!dbg->wrapDebuggeeValue(cx, &namev)) {
313 return false;
314 }
315 args.rval().set(namev);
316 return true;
317}
318
319bool DebuggerScript::CallData::getParameterNames() {
320 if (!ensureScript()) {
321 return false;
322 }
323
324 RootedFunction fun(cx, referent.as<BaseScript*>()->function());
325 if (!fun) {
326 args.rval().setUndefined();
327 return true;
328 }
329
330 ArrayObject* arr = GetFunctionParameterNamesArray(cx, fun);
331 if (!arr) {
332 return false;
333 }
334
335 args.rval().setObject(*arr);
336 return true;
337}
338
339bool DebuggerScript::CallData::getUrl() {
340 if (!ensureScriptMaybeLazy()) {
341 return false;
342 }
343
344 Rooted<BaseScript*> script(cx, referent.as<BaseScript*>());
345
346 if (script->filename()) {
347 JSString* str;
348 if (const char* introducer = script->scriptSource()->introducerFilename()) {
349 str =
350 NewStringCopyUTF8N(cx, JS::UTF8Chars(introducer, strlen(introducer)));
351 } else {
352 const char* filename = script->filename();
353 str = NewStringCopyUTF8N(cx, JS::UTF8Chars(filename, strlen(filename)));
354 }
355 if (!str) {
356 return false;
357 }
358 args.rval().setString(str);
359 } else {
360 args.rval().setNull();
361 }
362 return true;
363}
364
365bool DebuggerScript::CallData::getStartLine() {
366 args.rval().setNumber(
367 referent.get().match([](BaseScript*& s) { return s->lineno(); },
368 [](WasmInstanceObject*&) { return (uint32_t)1; }));
369 return true;
370}
371
372bool DebuggerScript::CallData::getStartColumn() {
373 JS::LimitedColumnNumberOneOrigin column = referent.get().match(
374 [](BaseScript*& s) { return s->column(); },
375 [](WasmInstanceObject*&) {
376 return JS::LimitedColumnNumberOneOrigin(
377 JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin);
378 });
379 args.rval().setNumber(column.oneOriginValue());
380 return true;
381}
382
383struct DebuggerScript::GetLineCountMatcher {
384 JSContext* cx_;
385 double totalLines;
386
387 explicit GetLineCountMatcher(JSContext* cx) : cx_(cx), totalLines(0.0) {}
388 using ReturnType = bool;
389
390 ReturnType match(Handle<BaseScript*> base) {
391 RootedScript script(cx_, DelazifyScript(cx_, base));
392 if (!script) {
393 return false;
394 }
395 totalLines = double(GetScriptLineExtent(script));
396 return true;
397 }
398 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
399 wasm::Instance& instance = instanceObj->instance();
400 if (instance.debugEnabled()) {
401 totalLines = double(instance.debug().bytecode().length());
402 } else {
403 totalLines = 0;
404 }
405 return true;
406 }
407};
408
409bool DebuggerScript::CallData::getLineCount() {
410 GetLineCountMatcher matcher(cx);
411 if (!referent.match(matcher)) {
412 return false;
413 }
414 args.rval().setNumber(matcher.totalLines);
415 return true;
416}
417
418class DebuggerScript::GetSourceMatcher {
419 JSContext* cx_;
420 Debugger* dbg_;
421
422 public:
423 GetSourceMatcher(JSContext* cx, Debugger* dbg) : cx_(cx), dbg_(dbg) {}
424
425 using ReturnType = DebuggerSource*;
426
427 ReturnType match(Handle<BaseScript*> script) {
428 Rooted<ScriptSourceObject*> source(cx_, script->sourceObject());
429 return dbg_->wrapSource(cx_, source);
430 }
431 ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
432 return dbg_->wrapWasmSource(cx_, wasmInstance);
433 }
434};
435
436bool DebuggerScript::CallData::getSource() {
437 Debugger* dbg = obj->owner();
438
439 GetSourceMatcher matcher(cx, dbg);
440 Rooted<DebuggerSource*> sourceObject(cx, referent.match(matcher));
441 if (!sourceObject) {
442 return false;
443 }
444
445 args.rval().setObject(*sourceObject);
446 return true;
447}
448
449bool DebuggerScript::CallData::getSourceStart() {
450 if (!ensureScriptMaybeLazy()) {
451 return false;
452 }
453 args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceStart()));
454 return true;
455}
456
457bool DebuggerScript::CallData::getSourceLength() {
458 if (!ensureScriptMaybeLazy()) {
459 return false;
460 }
461 args.rval().setNumber(uint32_t(obj->getReferentScript()->sourceLength()));
462 return true;
463}
464
465bool DebuggerScript::CallData::getMainOffset() {
466 if (!ensureScript()) {
467 return false;
468 }
469 args.rval().setNumber(uint32_t(script->mainOffset()));
470 return true;
471}
472
473bool DebuggerScript::CallData::getGlobal() {
474 if (!ensureScript()) {
475 return false;
476 }
477 Debugger* dbg = obj->owner();
478
479 RootedValue v(cx, ObjectValue(script->global()));
480 if (!dbg->wrapDebuggeeValue(cx, &v)) {
481 return false;
482 }
483 args.rval().set(v);
484 return true;
485}
486
487bool DebuggerScript::CallData::getFormat() {
488 args.rval().setString(referent.get().match(
489 [this](BaseScript*&) { return cx->names().js.get(); },
490 [this](WasmInstanceObject*&) { return cx->names().wasm.get(); }));
491 return true;
492}
493
494static bool PushFunctionScript(JSContext* cx, Debugger* dbg, HandleFunction fun,
495 HandleObject array) {
496 if (!IsInterpretedNonSelfHostedFunction(fun)) {
497 return true;
498 }
499
500 Rooted<BaseScript*> script(cx, fun->baseScript());
501 MOZ_ASSERT(script)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(script)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(script))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("script", "./../../../../js/src/debugger/Script.cpp"
, 501); AnnotateMozCrashReason("MOZ_ASSERT" "(" "script" ")")
; do { MOZ_CrashSequence(__null, 501); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
502 if (!script) {
503 // If the function doesn't have script, ignore it.
504 return true;
505 }
506 RootedObject wrapped(cx, dbg->wrapScript(cx, script));
507 if (!wrapped) {
508 return false;
509 }
510
511 return NewbornArrayPush(cx, array, ObjectValue(*wrapped));
512}
513
514static bool PushInnerFunctions(JSContext* cx, Debugger* dbg, HandleObject array,
515 mozilla::Span<const JS::GCCellPtr> gcThings) {
516 RootedFunction fun(cx);
517
518 for (JS::GCCellPtr gcThing : gcThings) {
519 if (!gcThing.is<JSObject>()) {
520 continue;
521 }
522
523 JSObject* obj = &gcThing.as<JSObject>();
524 if (obj->is<JSFunction>()) {
525 fun = &obj->as<JSFunction>();
526
527 // Ignore any delazification placeholder functions. These should not be
528 // exposed to debugger in any way.
529 if (fun->isGhost()) {
530 continue;
531 }
532
533 if (!PushFunctionScript(cx, dbg, fun, array)) {
534 return false;
535 }
536 }
537 }
538
539 return true;
540}
541
542bool DebuggerScript::CallData::getChildScripts() {
543 if (!ensureScriptMaybeLazy()) {
544 return false;
545 }
546 Debugger* dbg = obj->owner();
547
548 RootedObject result(cx, NewDenseEmptyArray(cx));
549 if (!result) {
550 return false;
551 }
552
553 Rooted<BaseScript*> script(cx, obj->getReferent().as<BaseScript*>());
554 if (!PushInnerFunctions(cx, dbg, result, script->gcthings())) {
555 return false;
556 }
557
558 args.rval().setObject(*result);
559 return true;
560}
561
562static bool ScriptOffset(JSContext* cx, const Value& v, size_t* offsetp) {
563 double d;
564 size_t off;
565
566 bool ok = v.isNumber();
567 if (ok) {
568 d = v.toNumber();
569 off = size_t(d);
570 }
571 if (!ok || off != d) {
572 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
573 JSMSG_DEBUG_BAD_OFFSET);
574 return false;
575 }
576 *offsetp = off;
577 return true;
578}
579
580static bool EnsureScriptOffsetIsValid(JSContext* cx, JSScript* script,
581 size_t offset) {
582 if (IsValidBytecodeOffset(cx, script, offset)) {
583 return true;
584 }
585 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
586 JSMSG_DEBUG_BAD_OFFSET);
587 return false;
588}
589
590static bool IsGeneratorSlotInitialization(JSScript* script, size_t offset,
591 JSContext* cx) {
592 jsbytecode* pc = script->offsetToPC(offset);
593 if (JSOp(*pc) != JSOp::SetAliasedVar) {
594 return false;
595 }
596
597 PropertyName* name = EnvironmentCoordinateNameSlow(script, pc);
598 return name == cx->names().dot_generator_;
599}
600
601static bool EnsureBreakpointIsAllowed(JSContext* cx, JSScript* script,
602 size_t offset) {
603 // Disallow breakpoint for `JSOp::SetAliasedVar` after `JSOp::Generator`.
604 // Those 2 instructions are supposed to be atomic, and nothing should happen
605 // in between them.
606 //
607 // Hitting a breakpoint there breaks the assumption around the existence of
608 // the frame's `GeneratorInfo`.
609 // (see `DebugAPI::slowPathOnNewGenerator` and `DebuggerFrame::create`)
610 if (IsGeneratorSlotInitialization(script, offset, cx)) {
611 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
612 JSMSG_DEBUG_BREAKPOINT_NOT_ALLOWED);
613 return false;
614 }
615
616 return true;
617}
618
619template <bool OnlyOffsets>
620class DebuggerScript::GetPossibleBreakpointsMatcher {
621 JSContext* cx_;
622 MutableHandleObject result_;
623
624 Maybe<size_t> minOffset;
625 Maybe<size_t> maxOffset;
626
627 Maybe<uint32_t> minLine;
628 JS::LimitedColumnNumberOneOrigin minColumn;
629 Maybe<uint32_t> maxLine;
630 JS::LimitedColumnNumberOneOrigin maxColumn;
631
632 bool passesQuery(size_t offset, uint32_t lineno,
633 JS::LimitedColumnNumberOneOrigin colno) {
634 // [minOffset, maxOffset) - Inclusive minimum and exclusive maximum.
635 if ((minOffset && offset < *minOffset) ||
636 (maxOffset && offset >= *maxOffset)) {
637 return false;
638 }
639
640 if (minLine) {
641 if (lineno < *minLine || (lineno == *minLine && colno < minColumn)) {
642 return false;
643 }
644 }
645
646 if (maxLine) {
647 if (lineno > *maxLine || (lineno == *maxLine && colno >= maxColumn)) {
648 return false;
649 }
650 }
651
652 return true;
653 }
654
655 bool maybeAppendEntry(size_t offset, uint32_t lineno,
656 JS::LimitedColumnNumberOneOrigin colno,
657 bool isStepStart) {
658 if (!passesQuery(offset, lineno, colno)) {
659 return true;
660 }
661
662 if (OnlyOffsets) {
663 if (!NewbornArrayPush(cx_, result_, NumberValue(offset))) {
664 return false;
665 }
666
667 return true;
668 }
669
670 Rooted<PlainObject*> entry(cx_, NewPlainObject(cx_));
671 if (!entry) {
672 return false;
673 }
674
675 RootedValue value(cx_, NumberValue(offset));
676 if (!DefineDataProperty(cx_, entry, cx_->names().offset, value)) {
677 return false;
678 }
679
680 value = NumberValue(lineno);
681 if (!DefineDataProperty(cx_, entry, cx_->names().lineNumber, value)) {
682 return false;
683 }
684
685 value = NumberValue(colno.oneOriginValue());
686 if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
687 return false;
688 }
689
690 value = BooleanValue(isStepStart);
691 if (!DefineDataProperty(cx_, entry, cx_->names().isStepStart, value)) {
692 return false;
693 }
694
695 if (!NewbornArrayPush(cx_, result_, ObjectValue(*entry))) {
696 return false;
697 }
698 return true;
699 }
700
701 template <typename T>
702 bool parseIntValueImpl(HandleValue value, T* result) {
703 if (!value.isNumber()) {
704 return false;
705 }
706
707 double doubleOffset = value.toNumber();
708 if (doubleOffset < 0 || (unsigned int)doubleOffset != doubleOffset) {
709 return false;
710 }
711
712 *result = doubleOffset;
713 return true;
714 }
715
716 bool parseUint32Value(HandleValue value, uint32_t* result) {
717 return parseIntValueImpl(value, result);
718 }
719 bool parseColumnValue(HandleValue value,
720 JS::LimitedColumnNumberOneOrigin* result) {
721 uint32_t tmp;
722 if (!parseIntValueImpl(value, &tmp)) {
723 return false;
724 }
725 if (tmp == 0) {
726 return false;
727 }
728 *result->addressOfValueForTranscode() = tmp;
729 if (!result->valid()) {
730 return false;
731 }
732 return true;
733 }
734 bool parseSizeTValue(HandleValue value, size_t* result) {
735 return parseIntValueImpl(value, result);
736 }
737
738 template <typename T>
739 bool parseIntValueMaybeImpl(HandleValue value, Maybe<T>* result) {
740 T result_;
741 if (!parseIntValueImpl(value, &result_)) {
742 return false;
743 }
744
745 *result = Some(result_);
746 return true;
747 }
748
749 bool parseUint32Value(HandleValue value, Maybe<uint32_t>* result) {
750 return parseIntValueMaybeImpl(value, result);
751 }
752 bool parseSizeTValue(HandleValue value, Maybe<size_t>* result) {
753 return parseIntValueMaybeImpl(value, result);
754 }
755
756 public:
757 explicit GetPossibleBreakpointsMatcher(JSContext* cx,
758 MutableHandleObject result)
759 : cx_(cx), result_(result) {}
760
761 bool parseQuery(HandleObject query) {
762 RootedValue lineValue(cx_);
763 if (!GetProperty(cx_, query, query, cx_->names().line, &lineValue)) {
764 return false;
765 }
766
767 RootedValue minLineValue(cx_);
768 if (!GetProperty(cx_, query, query, cx_->names().minLine, &minLineValue)) {
769 return false;
770 }
771
772 RootedValue minColumnValue(cx_);
773 if (!GetProperty(cx_, query, query, cx_->names().minColumn,
774 &minColumnValue)) {
775 return false;
776 }
777
778 RootedValue minOffsetValue(cx_);
779 if (!GetProperty(cx_, query, query, cx_->names().minOffset,
780 &minOffsetValue)) {
781 return false;
782 }
783
784 RootedValue maxLineValue(cx_);
785 if (!GetProperty(cx_, query, query, cx_->names().maxLine, &maxLineValue)) {
786 return false;
787 }
788
789 RootedValue maxColumnValue(cx_);
790 if (!GetProperty(cx_, query, query, cx_->names().maxColumn,
791 &maxColumnValue)) {
792 return false;
793 }
794
795 RootedValue maxOffsetValue(cx_);
796 if (!GetProperty(cx_, query, query, cx_->names().maxOffset,
797 &maxOffsetValue)) {
798 return false;
799 }
800
801 if (!minOffsetValue.isUndefined()) {
802 if (!parseSizeTValue(minOffsetValue, &minOffset)) {
803 JS_ReportErrorNumberASCII(
804 cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
805 "getPossibleBreakpoints' 'minOffset'", "not an integer");
806 return false;
807 }
808 }
809 if (!maxOffsetValue.isUndefined()) {
810 if (!parseSizeTValue(maxOffsetValue, &maxOffset)) {
811 JS_ReportErrorNumberASCII(
812 cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
813 "getPossibleBreakpoints' 'maxOffset'", "not an integer");
814 return false;
815 }
816 }
817
818 if (!lineValue.isUndefined()) {
819 if (!minLineValue.isUndefined() || !maxLineValue.isUndefined()) {
820 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
821 JSMSG_UNEXPECTED_TYPE,
822 "getPossibleBreakpoints' 'line'",
823 "not allowed alongside 'minLine'/'maxLine'");
824 return false;
825 }
826
827 uint32_t line;
828 if (!parseUint32Value(lineValue, &line)) {
829 JS_ReportErrorNumberASCII(
830 cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
831 "getPossibleBreakpoints' 'line'", "not an integer");
832 return false;
833 }
834
835 // If no end column is given, we use the default of 0 and wrap to
836 // the next line.
837 minLine = Some(line);
838 maxLine = Some(line + (maxColumnValue.isUndefined() ? 1 : 0));
839 }
840
841 if (!minLineValue.isUndefined()) {
842 if (!parseUint32Value(minLineValue, &minLine)) {
843 JS_ReportErrorNumberASCII(
844 cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
845 "getPossibleBreakpoints' 'minLine'", "not an integer");
846 return false;
847 }
848 }
849
850 if (!minColumnValue.isUndefined()) {
851 if (!minLine) {
852 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
853 JSMSG_UNEXPECTED_TYPE,
854 "getPossibleBreakpoints' 'minColumn'",
855 "not allowed without 'line' or 'minLine'");
856 return false;
857 }
858
859 if (!parseColumnValue(minColumnValue, &minColumn)) {
860 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
861 JSMSG_UNEXPECTED_TYPE,
862 "getPossibleBreakpoints' 'minColumn'",
863 "not a positive integer in valid range");
864 return false;
865 }
866 }
867
868 if (!maxLineValue.isUndefined()) {
869 if (!parseUint32Value(maxLineValue, &maxLine)) {
870 JS_ReportErrorNumberASCII(
871 cx_, GetErrorMessage, nullptr, JSMSG_UNEXPECTED_TYPE,
872 "getPossibleBreakpoints' 'maxLine'", "not an integer");
873 return false;
874 }
875 }
876
877 if (!maxColumnValue.isUndefined()) {
878 if (!maxLine) {
879 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
880 JSMSG_UNEXPECTED_TYPE,
881 "getPossibleBreakpoints' 'maxColumn'",
882 "not allowed without 'line' or 'maxLine'");
883 return false;
884 }
885
886 if (!parseColumnValue(maxColumnValue, &maxColumn)) {
887 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
888 JSMSG_UNEXPECTED_TYPE,
889 "getPossibleBreakpoints' 'maxColumn'",
890 "not a positive integer in valid range");
891 return false;
892 }
893 }
894
895 return true;
896 }
897
898 using ReturnType = bool;
899 ReturnType match(Handle<BaseScript*> base) {
900 RootedScript script(cx_, DelazifyScript(cx_, base));
901 if (!script) {
902 return false;
903 }
904
905 // Second pass: build the result array.
906 result_.set(NewDenseEmptyArray(cx_));
907 if (!result_) {
908 return false;
909 }
910
911 for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
912 !r.empty(); r.popFront()) {
913 if (!r.frontIsBreakablePoint()) {
914 continue;
915 }
916
917 size_t offset = r.frontOffset();
918 uint32_t lineno = r.frontLineNumber();
919 JS::LimitedColumnNumberOneOrigin colno = r.frontColumnNumber();
920
921 if (!maybeAppendEntry(offset, lineno, colno,
922 r.frontIsBreakableStepPoint())) {
923 return false;
924 }
925 }
926
927 return true;
928 }
929 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
930 wasm::Instance& instance = instanceObj->instance();
931
932 Vector<wasm::ExprLoc> offsets(cx_);
933 if (instance.debugEnabled() &&
934 !instance.debug().getAllColumnOffsets(&offsets)) {
935 return false;
936 }
937
938 result_.set(NewDenseEmptyArray(cx_));
939 if (!result_) {
940 return false;
941 }
942
943 for (uint32_t i = 0; i < offsets.length(); i++) {
944 uint32_t lineno = offsets[i].lineno;
945 JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
946 size_t offset = offsets[i].offset;
947 if (!maybeAppendEntry(offset, lineno, column, true)) {
948 return false;
949 }
950 }
951 return true;
952 }
953};
954
955bool DebuggerScript::CallData::getPossibleBreakpoints() {
956 RootedObject result(cx);
957 GetPossibleBreakpointsMatcher<false> matcher(cx, &result);
958 if (args.length() >= 1 && !args[0].isUndefined()) {
959 RootedObject queryObject(cx, RequireObject(cx, args[0]));
960 if (!queryObject || !matcher.parseQuery(queryObject)) {
961 return false;
962 }
963 }
964 if (!referent.match(matcher)) {
965 return false;
966 }
967
968 args.rval().setObject(*result);
969 return true;
970}
971
972bool DebuggerScript::CallData::getPossibleBreakpointOffsets() {
973 RootedObject result(cx);
974 GetPossibleBreakpointsMatcher<true> matcher(cx, &result);
975 if (args.length() >= 1 && !args[0].isUndefined()) {
976 RootedObject queryObject(cx, RequireObject(cx, args[0]));
977 if (!queryObject || !matcher.parseQuery(queryObject)) {
978 return false;
979 }
980 }
981 if (!referent.match(matcher)) {
982 return false;
983 }
984
985 args.rval().setObject(*result);
986 return true;
987}
988
989class DebuggerScript::GetOffsetMetadataMatcher {
990 JSContext* cx_;
991 size_t offset_;
992 MutableHandle<PlainObject*> result_;
993
994 public:
995 explicit GetOffsetMetadataMatcher(JSContext* cx, size_t offset,
996 MutableHandle<PlainObject*> result)
997 : cx_(cx), offset_(offset), result_(result) {}
998 using ReturnType = bool;
999 ReturnType match(Handle<BaseScript*> base) {
1000 RootedScript script(cx_, DelazifyScript(cx_, base));
1001 if (!script) {
1002 return false;
1003 }
1004
1005 if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
1006 return false;
1007 }
1008
1009 result_.set(NewPlainObject(cx_));
1010 if (!result_) {
1011 return false;
1012 }
1013
1014 // Use SkipPrologueOps::No to ensure we return isBreakpoint = false and
1015 // isStepStart = false for prologue ops, instead of the metadata for the
1016 // first 'main' op.
1017 BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::No);
1018 while (!r.empty() && r.frontOffset() < offset_) {
1019 r.popFront();
1020 }
1021 MOZ_ASSERT(r.frontOffset() == offset_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(r.frontOffset() == offset_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(r.frontOffset() == offset_))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("r.frontOffset() == offset_"
, "./../../../../js/src/debugger/Script.cpp", 1021); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "r.frontOffset() == offset_" ")"); do { MOZ_CrashSequence
(__null, 1021); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1022
1023 RootedValue value(cx_, NumberValue(r.frontLineNumber()));
1024 if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
1025 return false;
1026 }
1027
1028 value = NumberValue(r.frontColumnNumber().oneOriginValue());
1029 if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
1030 return false;
1031 }
1032
1033 value = BooleanValue(r.frontIsBreakablePoint());
1034 if (!DefineDataProperty(cx_, result_, cx_->names().isBreakpoint, value)) {
1035 return false;
1036 }
1037
1038 value = BooleanValue(r.frontIsBreakableStepPoint());
1039 if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
1040 return false;
1041 }
1042
1043 return true;
1044 }
1045 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
1046 wasm::Instance& instance = instanceObj->instance();
1047 if (!instance.debugEnabled()) {
1048 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
1049 JSMSG_DEBUG_BAD_OFFSET);
1050 return false;
1051 }
1052
1053 uint32_t lineno;
1054 JS::LimitedColumnNumberOneOrigin column;
1055 if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
1056 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
1057 JSMSG_DEBUG_BAD_OFFSET);
1058 return false;
1059 }
1060
1061 result_.set(NewPlainObject(cx_));
1062 if (!result_) {
1063 return false;
1064 }
1065
1066 RootedValue value(cx_, NumberValue(lineno));
1067 if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
1068 return false;
1069 }
1070
1071 value = NumberValue(column.oneOriginValue());
1072 if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
1073 return false;
1074 }
1075
1076 value.setBoolean(true);
1077 if (!DefineDataProperty(cx_, result_, cx_->names().isBreakpoint, value)) {
1078 return false;
1079 }
1080
1081 value.setBoolean(true);
1082 if (!DefineDataProperty(cx_, result_, cx_->names().isStepStart, value)) {
1083 return false;
1084 }
1085
1086 return true;
1087 }
1088};
1089
1090bool DebuggerScript::CallData::getOffsetMetadata() {
1091 if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetMetadata", 1)) {
1092 return false;
1093 }
1094 size_t offset;
1095 if (!ScriptOffset(cx, args[0], &offset)) {
1096 return false;
1097 }
1098
1099 Rooted<PlainObject*> result(cx);
1100 GetOffsetMetadataMatcher matcher(cx, offset, &result);
1101 if (!referent.match(matcher)) {
1102 return false;
1103 }
1104
1105 args.rval().setObject(*result);
1106 return true;
1107}
1108
1109namespace {
1110
1111/*
1112 * FlowGraphSummary::populate(cx, script) computes a summary of script's
1113 * control flow graph used by DebuggerScript_{getAllOffsets,getLineOffsets}.
1114 *
1115 * An instruction on a given line is an entry point for that line if it can be
1116 * reached from (an instruction on) a different line. We distinguish between the
1117 * following cases:
1118 * - hasNoEdges:
1119 * The instruction cannot be reached, so the instruction is not an entry
1120 * point for the line it is on.
1121 * - hasSingleEdge:
1122 * The instruction can be reached from a single line. If this line is
1123 * different from the line the instruction is on, the instruction is an
1124 * entry point for that line.
1125 *
1126 * Similarly, an instruction on a given position (line/column pair) is an
1127 * entry point for that position if it can be reached from (an instruction on) a
1128 * different position. Again, we distinguish between the following cases:
1129 * - hasNoEdges:
1130 * The instruction cannot be reached, so the instruction is not an entry
1131 * point for the position it is on.
1132 * - hasSingleEdge:
1133 * The instruction can be reached from a single position. If this line is
1134 * different from the position the instruction is on, the instruction is
1135 * an entry point for that position.
1136 */
1137class FlowGraphSummary {
1138 public:
1139 class Entry {
1140 public:
1141 static constexpr uint32_t Line_HasNoEdge = UINT32_MAX(4294967295U);
1142 static constexpr uint32_t Column_HasMultipleEdge = UINT32_MAX(4294967295U);
1143
1144 // NOTE: column can be Column_HasMultipleEdge.
1145 static Entry createWithSingleEdgeOrMultipleEdge(uint32_t lineno,
1146 uint32_t column) {
1147 return Entry(lineno, column);
1148 }
1149
1150 static Entry createWithMultipleEdgesFromSingleLine(uint32_t lineno) {
1151 return Entry(lineno, Column_HasMultipleEdge);
1152 }
1153
1154 static Entry createWithMultipleEdgesFromMultipleLines() {
1155 return Entry(Line_HasNoEdge, Column_HasMultipleEdge);
1156 }
1157
1158 Entry() : lineno_(Line_HasNoEdge), column_(1) {}
1159
1160 bool hasNoEdges() const {
1161 return lineno_ == Line_HasNoEdge && column_ != Column_HasMultipleEdge;
1162 }
1163
1164 bool hasSingleEdge() const {
1165 return lineno_ != Line_HasNoEdge && column_ != Column_HasMultipleEdge;
1166 }
1167
1168 uint32_t lineno() const { return lineno_; }
1169
1170 // Returns 1-origin column number or the sentinel value
1171 // Column_HasMultipleEdge.
1172 uint32_t columnOrSentinel() const { return column_; }
1173
1174 JS::LimitedColumnNumberOneOrigin column() const {
1175 MOZ_ASSERT(column_ != Column_HasMultipleEdge)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(column_ != Column_HasMultipleEdge)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(column_ != Column_HasMultipleEdge
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"column_ != Column_HasMultipleEdge", "./../../../../js/src/debugger/Script.cpp"
, 1175); AnnotateMozCrashReason("MOZ_ASSERT" "(" "column_ != Column_HasMultipleEdge"
")"); do { MOZ_CrashSequence(__null, 1175); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1176 return JS::LimitedColumnNumberOneOrigin(column_);
1177 }
1178
1179 private:
1180 Entry(uint32_t lineno, uint32_t column)
1181 : lineno_(lineno), column_(column) {}
1182
1183 // Line number (1-origin).
1184 // Line_HasNoEdge for no edge.
1185 uint32_t lineno_;
1186
1187 // Column number in UTF-16 code units (1-origin).
1188 // Column_HasMultipleEdge for multiple edge.
1189 uint32_t column_;
1190 };
1191
1192 explicit FlowGraphSummary(JSContext* cx) : entries_(cx) {}
1193
1194 Entry& operator[](size_t index) { return entries_[index]; }
1195
1196 bool populate(JSContext* cx, JSScript* script) {
1197 if (!entries_.growBy(script->length())) {
1198 return false;
1199 }
1200 unsigned mainOffset = script->pcToOffset(script->main());
1201 entries_[mainOffset] = Entry::createWithMultipleEdgesFromMultipleLines();
1202
1203 // The following code uses uint32_t for column numbers.
1204 // The value is either 1-origin column number,
1205 // or Entry::Column_HasMultipleEdge.
1206
1207 uint32_t prevLineno = script->lineno();
1208 if (prevLineno == Entry::Line_HasNoEdge) {
1209 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1210 JSMSG_BAD_LINE_NUMBER);
1211 return false;
1212 }
1213 uint32_t prevColumn = 1;
1214 JSOp prevOp = JSOp::Nop;
1215 for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
1216 !r.empty(); r.popFront()) {
1217 uint32_t lineno = prevLineno;
1218 uint32_t column = prevColumn;
1219 JSOp op = r.frontOpcode();
1220
1221 if (BytecodeFallsThrough(prevOp)) {
1222 addEdge(prevLineno, prevColumn, r.frontOffset());
1223 }
1224
1225 // If we visit the branch target before we visit the
1226 // branch op itself, just reuse the previous location.
1227 // This is reasonable for the time being because this
1228 // situation can currently only arise from loop heads,
1229 // where this assumption holds.
1230 if (BytecodeIsJumpTarget(op) && !entries_[r.frontOffset()].hasNoEdges()) {
1231 lineno = entries_[r.frontOffset()].lineno();
1232 column = entries_[r.frontOffset()].columnOrSentinel();
1233 }
1234
1235 if (r.frontIsEntryPoint()) {
1236 lineno = r.frontLineNumber();
1237 column = r.frontColumnNumber().oneOriginValue();
1238 if (lineno == Entry::Line_HasNoEdge) {
1239 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
1240 JSMSG_BAD_LINE_NUMBER);
1241 return false;
1242 }
1243 // NOTE: The column data types cannot represent Column_HasMultipleEdge,
1244 // and also the column number is limited in the frontend.
1245 // See GeneralTokenStreamChars::computeColumn.
1246 MOZ_ASSERT(column != Entry::Column_HasMultipleEdge)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(column != Entry::Column_HasMultipleEdge)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(column != Entry::Column_HasMultipleEdge))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("column != Entry::Column_HasMultipleEdge"
, "./../../../../js/src/debugger/Script.cpp", 1246); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "column != Entry::Column_HasMultipleEdge" ")"
); do { MOZ_CrashSequence(__null, 1246); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1247 }
1248
1249 if (IsJumpOpcode(op)) {
1250 addEdge(lineno, column, r.frontOffset() + GET_JUMP_OFFSET(r.frontPC()));
1251 } else if (op == JSOp::TableSwitch) {
1252 jsbytecode* const switchPC = r.frontPC();
1253 jsbytecode* pc = switchPC;
1254 size_t offset = r.frontOffset();
1255 ptrdiff_t step = JUMP_OFFSET_LEN;
1256 size_t defaultOffset = offset + GET_JUMP_OFFSET(pc);
1257 pc += step;
1258 addEdge(lineno, column, defaultOffset);
1259
1260 int32_t low = GET_JUMP_OFFSET(pc);
1261 pc += JUMP_OFFSET_LEN;
1262 int ncases = GET_JUMP_OFFSET(pc) - low + 1;
1263 pc += JUMP_OFFSET_LEN;
Value stored to 'pc' is never read
1264
1265 for (int i = 0; i < ncases; i++) {
1266 size_t target = script->tableSwitchCaseOffset(switchPC, i);
1267 addEdge(lineno, column, target);
1268 }
1269 } else if (op == JSOp::Try) {
1270 // As there is no literal incoming edge into the catch block, we
1271 // make a fake one by copying the JSOp::Try location, as-if this
1272 // was an incoming edge of the catch block. This is needed
1273 // because we only report offsets of entry points which have
1274 // valid incoming edges.
1275 for (const TryNote& tn : script->trynotes()) {
1276 if (tn.start == r.frontOffset() + JSOpLength_Try) {
1277 uint32_t catchOffset = tn.start + tn.length;
1278 if (tn.kind() == TryNoteKind::Catch ||
1279 tn.kind() == TryNoteKind::Finally) {
1280 addEdge(lineno, column, catchOffset);
1281 }
1282 }
1283 }
1284 }
1285
1286 prevLineno = lineno;
1287 prevColumn = column;
1288 prevOp = op;
1289 }
1290
1291 return true;
1292 }
1293
1294 private:
1295 // sourceColumn is either 1-origin column number,
1296 // or Entry::Column_HasMultipleEdge.
1297 void addEdge(uint32_t sourceLineno, uint32_t sourceColumn,
1298 size_t targetOffset) {
1299 if (entries_[targetOffset].hasNoEdges()) {
1300 entries_[targetOffset] =
1301 Entry::createWithSingleEdgeOrMultipleEdge(sourceLineno, sourceColumn);
1302 } else if (entries_[targetOffset].lineno() != sourceLineno) {
1303 entries_[targetOffset] =
1304 Entry::createWithMultipleEdgesFromMultipleLines();
1305 } else if (entries_[targetOffset].columnOrSentinel() != sourceColumn) {
1306 entries_[targetOffset] =
1307 Entry::createWithMultipleEdgesFromSingleLine(sourceLineno);
1308 }
1309 }
1310
1311 Vector<Entry> entries_;
1312};
1313
1314} /* anonymous namespace */
1315
1316class DebuggerScript::GetOffsetLocationMatcher {
1317 JSContext* cx_;
1318 size_t offset_;
1319 MutableHandle<PlainObject*> result_;
1320
1321 public:
1322 explicit GetOffsetLocationMatcher(JSContext* cx, size_t offset,
1323 MutableHandle<PlainObject*> result)
1324 : cx_(cx), offset_(offset), result_(result) {}
1325 using ReturnType = bool;
1326 ReturnType match(Handle<BaseScript*> base) {
1327 RootedScript script(cx_, DelazifyScript(cx_, base));
1328 if (!script) {
1329 return false;
1330 }
1331
1332 if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
1333 return false;
1334 }
1335
1336 FlowGraphSummary flowData(cx_);
1337 if (!flowData.populate(cx_, script)) {
1338 return false;
1339 }
1340
1341 result_.set(NewPlainObject(cx_));
1342 if (!result_) {
1343 return false;
1344 }
1345
1346 // Use SkipPrologueOps::No to ensure we return isEntryPoint = false for
1347 // prologue ops, instead of the value for the first 'main' op.
1348 BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::No);
1349 while (!r.empty() && r.frontOffset() < offset_) {
1350 r.popFront();
1351 }
1352 MOZ_ASSERT(r.frontOffset() == offset_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(r.frontOffset() == offset_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(r.frontOffset() == offset_))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("r.frontOffset() == offset_"
, "./../../../../js/src/debugger/Script.cpp", 1352); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "r.frontOffset() == offset_" ")"); do { MOZ_CrashSequence
(__null, 1352); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1353
1354 bool isEntryPoint = r.frontIsEntryPoint();
1355
1356 // Line numbers are only correctly defined on entry points. Thus looks
1357 // either for the next valid offset in the flowData, being the last entry
1358 // point flowing into the current offset, or for the next valid entry point.
1359 while (!r.frontIsEntryPoint() &&
1360 !flowData[r.frontOffset()].hasSingleEdge()) {
1361 r.popFront();
1362 MOZ_ASSERT(!r.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!r.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!r.empty()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!r.empty()", "./../../../../js/src/debugger/Script.cpp"
, 1362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!r.empty()"
")"); do { MOZ_CrashSequence(__null, 1362); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1363 }
1364
1365 // If this is an entry point, take the line number associated with the entry
1366 // point, otherwise settle on the next instruction and take the incoming
1367 // edge position.
1368 uint32_t lineno;
1369 JS::LimitedColumnNumberOneOrigin column;
1370 if (r.frontIsEntryPoint()) {
1371 lineno = r.frontLineNumber();
1372 column = r.frontColumnNumber();
1373 } else {
1374 MOZ_ASSERT(flowData[r.frontOffset()].hasSingleEdge())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(flowData[r.frontOffset()].hasSingleEdge())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(flowData[r.frontOffset()].hasSingleEdge()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("flowData[r.frontOffset()].hasSingleEdge()"
, "./../../../../js/src/debugger/Script.cpp", 1374); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "flowData[r.frontOffset()].hasSingleEdge()"
")"); do { MOZ_CrashSequence(__null, 1374); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1375 lineno = flowData[r.frontOffset()].lineno();
1376 column = flowData[r.frontOffset()].column();
1377 }
1378
1379 RootedValue value(cx_, NumberValue(lineno));
1380 if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
1381 return false;
1382 }
1383
1384 value = NumberValue(column.oneOriginValue());
1385 if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
1386 return false;
1387 }
1388
1389 // The same entry point test that is used by getAllColumnOffsets.
1390 isEntryPoint = (isEntryPoint && !flowData[offset_].hasNoEdges() &&
1391 (flowData[offset_].lineno() != r.frontLineNumber() ||
1392 flowData[offset_].columnOrSentinel() !=
1393 r.frontColumnNumber().oneOriginValue()));
1394 value.setBoolean(isEntryPoint);
1395 if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
1396 return false;
1397 }
1398
1399 return true;
1400 }
1401 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
1402 wasm::Instance& instance = instanceObj->instance();
1403 if (!instance.debugEnabled()) {
1404 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
1405 JSMSG_DEBUG_BAD_OFFSET);
1406 return false;
1407 }
1408
1409 uint32_t lineno;
1410 JS::LimitedColumnNumberOneOrigin column;
1411 if (!instance.debug().getOffsetLocation(offset_, &lineno, &column)) {
1412 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
1413 JSMSG_DEBUG_BAD_OFFSET);
1414 return false;
1415 }
1416
1417 result_.set(NewPlainObject(cx_));
1418 if (!result_) {
1419 return false;
1420 }
1421
1422 RootedValue value(cx_, NumberValue(lineno));
1423 if (!DefineDataProperty(cx_, result_, cx_->names().lineNumber, value)) {
1424 return false;
1425 }
1426
1427 value = NumberValue(column.oneOriginValue());
1428 if (!DefineDataProperty(cx_, result_, cx_->names().columnNumber, value)) {
1429 return false;
1430 }
1431
1432 value.setBoolean(true);
1433 if (!DefineDataProperty(cx_, result_, cx_->names().isEntryPoint, value)) {
1434 return false;
1435 }
1436
1437 return true;
1438 }
1439};
1440
1441bool DebuggerScript::CallData::getOffsetLocation() {
1442 if (!args.requireAtLeast(cx, "Debugger.Script.getOffsetLocation", 1)) {
1443 return false;
1444 }
1445 size_t offset;
1446 if (!ScriptOffset(cx, args[0], &offset)) {
1447 return false;
1448 }
1449
1450 Rooted<PlainObject*> result(cx);
1451 GetOffsetLocationMatcher matcher(cx, offset, &result);
1452 if (!referent.match(matcher)) {
1453 return false;
1454 }
1455
1456 args.rval().setObject(*result);
1457 return true;
1458}
1459
1460// Return whether an opcode is considered effectful: it can have direct side
1461// effects that can be observed outside of the current frame. Opcodes are not
1462// effectful if they only modify the current frame's state, modify objects
1463// created by the current frame, or can potentially call other scripts or
1464// natives which could have side effects.
1465static bool BytecodeIsEffectful(JSScript* script, size_t offset) {
1466 jsbytecode* pc = script->offsetToPC(offset);
1467 JSOp op = JSOp(*pc);
1468 switch (op) {
1469 case JSOp::SetProp:
1470 case JSOp::StrictSetProp:
1471 case JSOp::SetPropSuper:
1472 case JSOp::StrictSetPropSuper:
1473 case JSOp::SetElem:
1474 case JSOp::StrictSetElem:
1475 case JSOp::SetElemSuper:
1476 case JSOp::StrictSetElemSuper:
1477 case JSOp::SetName:
1478 case JSOp::StrictSetName:
1479 case JSOp::SetGName:
1480 case JSOp::StrictSetGName:
1481 case JSOp::DelProp:
1482 case JSOp::StrictDelProp:
1483 case JSOp::DelElem:
1484 case JSOp::StrictDelElem:
1485 case JSOp::DelName:
1486 case JSOp::SetAliasedVar:
1487 case JSOp::InitHomeObject:
1488 case JSOp::SetIntrinsic:
1489 case JSOp::InitGLexical:
1490 case JSOp::GlobalOrEvalDeclInstantiation:
1491 case JSOp::SetFunName:
1492 case JSOp::MutateProto:
1493 case JSOp::DynamicImport:
1494 case JSOp::InitialYield:
1495 case JSOp::Yield:
1496 case JSOp::Await:
1497 case JSOp::CanSkipAwait:
1498 case JSOp::AddDisposable:
1499 return true;
1500
1501 case JSOp::Nop:
1502 case JSOp::NopDestructuring:
1503 case JSOp::NopIsAssignOp:
1504 case JSOp::TryDestructuring:
1505 case JSOp::Lineno:
1506 case JSOp::JumpTarget:
1507 case JSOp::Undefined:
1508 case JSOp::JumpIfTrue:
1509 case JSOp::JumpIfFalse:
1510 case JSOp::Return:
1511 case JSOp::RetRval:
1512 case JSOp::And:
1513 case JSOp::Or:
1514 case JSOp::Coalesce:
1515 case JSOp::Try:
1516 case JSOp::Throw:
1517 case JSOp::ThrowWithStack:
1518 case JSOp::TakeDisposeCapability:
1519 case JSOp::CreateSuppressedError:
1520 case JSOp::Goto:
1521 case JSOp::TableSwitch:
1522 case JSOp::Case:
1523 case JSOp::Default:
1524 case JSOp::BitNot:
1525 case JSOp::BitAnd:
1526 case JSOp::BitOr:
1527 case JSOp::BitXor:
1528 case JSOp::Lsh:
1529 case JSOp::Rsh:
1530 case JSOp::Ursh:
1531 case JSOp::Add:
1532 case JSOp::Sub:
1533 case JSOp::Mul:
1534 case JSOp::Div:
1535 case JSOp::Mod:
1536 case JSOp::Pow:
1537 case JSOp::Pos:
1538 case JSOp::ToNumeric:
1539 case JSOp::Neg:
1540 case JSOp::Inc:
1541 case JSOp::Dec:
1542 case JSOp::ToString:
1543 case JSOp::Eq:
1544 case JSOp::Ne:
1545 case JSOp::StrictEq:
1546 case JSOp::StrictNe:
1547 case JSOp::StrictConstantEq:
1548 case JSOp::StrictConstantNe:
1549 case JSOp::Lt:
1550 case JSOp::Le:
1551 case JSOp::Gt:
1552 case JSOp::Ge:
1553 case JSOp::Double:
1554 case JSOp::BigInt:
1555 case JSOp::String:
1556 case JSOp::Symbol:
1557 case JSOp::Zero:
1558 case JSOp::One:
1559 case JSOp::Null:
1560 case JSOp::Void:
1561 case JSOp::Hole:
1562 case JSOp::False:
1563 case JSOp::True:
1564 case JSOp::Arguments:
1565 case JSOp::Rest:
1566 case JSOp::GetArg:
1567 case JSOp::GetFrameArg:
1568 case JSOp::SetArg:
1569 case JSOp::GetLocal:
1570 case JSOp::SetLocal:
1571 case JSOp::GetActualArg:
1572 case JSOp::ArgumentsLength:
1573 case JSOp::ThrowSetConst:
1574 case JSOp::CheckLexical:
1575 case JSOp::CheckAliasedLexical:
1576 case JSOp::InitLexical:
1577 case JSOp::Uninitialized:
1578 case JSOp::Pop:
1579 case JSOp::PopN:
1580 case JSOp::DupAt:
1581 case JSOp::NewArray:
1582 case JSOp::NewInit:
1583 case JSOp::NewObject:
1584 case JSOp::InitElem:
1585 case JSOp::InitHiddenElem:
1586 case JSOp::InitLockedElem:
1587 case JSOp::InitElemInc:
1588 case JSOp::InitElemArray:
1589 case JSOp::InitProp:
1590 case JSOp::InitLockedProp:
1591 case JSOp::InitHiddenProp:
1592 case JSOp::InitPropGetter:
1593 case JSOp::InitHiddenPropGetter:
1594 case JSOp::InitPropSetter:
1595 case JSOp::InitHiddenPropSetter:
1596 case JSOp::InitElemGetter:
1597 case JSOp::InitHiddenElemGetter:
1598 case JSOp::InitElemSetter:
1599 case JSOp::InitHiddenElemSetter:
1600 case JSOp::SpreadCall:
1601 case JSOp::Call:
1602 case JSOp::CallContent:
1603 case JSOp::CallIgnoresRv:
1604 case JSOp::CallIter:
1605 case JSOp::CallContentIter:
1606 case JSOp::New:
1607 case JSOp::NewContent:
1608 case JSOp::Eval:
1609 case JSOp::StrictEval:
1610 case JSOp::Int8:
1611 case JSOp::Uint16:
1612 case JSOp::ResumeKind:
1613 case JSOp::GetGName:
1614 case JSOp::GetName:
1615 case JSOp::GetIntrinsic:
1616 case JSOp::GetImport:
1617 case JSOp::BindName:
1618 case JSOp::BindUnqualifiedName:
1619 case JSOp::BindUnqualifiedGName:
1620 case JSOp::BindVar:
1621 case JSOp::Dup:
1622 case JSOp::Dup2:
1623 case JSOp::Swap:
1624 case JSOp::Pick:
1625 case JSOp::Unpick:
1626 case JSOp::GetAliasedDebugVar:
1627 case JSOp::GetAliasedVar:
1628 case JSOp::Uint24:
1629 case JSOp::Int32:
1630 case JSOp::LoopHead:
1631 case JSOp::GetElem:
1632 case JSOp::Not:
1633 case JSOp::FunctionThis:
1634 case JSOp::GlobalThis:
1635 case JSOp::NonSyntacticGlobalThis:
1636 case JSOp::Callee:
1637 case JSOp::EnvCallee:
1638 case JSOp::SuperBase:
1639 case JSOp::GetPropSuper:
1640 case JSOp::GetElemSuper:
1641 case JSOp::GetProp:
1642 case JSOp::RegExp:
1643 case JSOp::CallSiteObj:
1644 case JSOp::Object:
1645 case JSOp::Typeof:
1646 case JSOp::TypeofExpr:
1647 case JSOp::TypeofEq:
1648 case JSOp::ToAsyncIter:
1649 case JSOp::ToPropertyKey:
1650 case JSOp::Lambda:
1651 case JSOp::PushLexicalEnv:
1652 case JSOp::PopLexicalEnv:
1653 case JSOp::FreshenLexicalEnv:
1654 case JSOp::RecreateLexicalEnv:
1655 case JSOp::PushClassBodyEnv:
1656 case JSOp::Iter:
1657 case JSOp::MoreIter:
1658 case JSOp::IsNoIter:
1659 case JSOp::EndIter:
1660 case JSOp::CloseIter:
1661 case JSOp::OptimizeGetIterator:
1662 case JSOp::IsNullOrUndefined:
1663 case JSOp::In:
1664 case JSOp::HasOwn:
1665 case JSOp::CheckPrivateField:
1666 case JSOp::NewPrivateName:
1667 case JSOp::SetRval:
1668 case JSOp::Instanceof:
1669 case JSOp::DebugLeaveLexicalEnv:
1670 case JSOp::Debugger:
1671 case JSOp::ImplicitThis:
1672 case JSOp::NewTarget:
1673 case JSOp::CheckIsObj:
1674 case JSOp::CheckObjCoercible:
1675 case JSOp::DebugCheckSelfHosted:
1676 case JSOp::IsConstructing:
1677 case JSOp::OptimizeSpreadCall:
1678 case JSOp::ImportMeta:
1679 case JSOp::EnterWith:
1680 case JSOp::LeaveWith:
1681 case JSOp::SpreadNew:
1682 case JSOp::SpreadEval:
1683 case JSOp::StrictSpreadEval:
1684 case JSOp::CheckClassHeritage:
1685 case JSOp::FunWithProto:
1686 case JSOp::ObjWithProto:
1687 case JSOp::BuiltinObject:
1688 case JSOp::CheckThis:
1689 case JSOp::CheckReturn:
1690 case JSOp::CheckThisReinit:
1691 case JSOp::SuperFun:
1692 case JSOp::SpreadSuperCall:
1693 case JSOp::SuperCall:
1694 case JSOp::PushVarEnv:
1695 case JSOp::GetBoundName:
1696 case JSOp::Exception:
1697 case JSOp::ExceptionAndStack:
1698 case JSOp::FinalYieldRval:
1699 case JSOp::Resume:
1700 case JSOp::AfterYield:
1701 case JSOp::MaybeExtractAwaitValue:
1702 case JSOp::Generator:
1703 case JSOp::AsyncAwait:
1704 case JSOp::AsyncResolve:
1705 case JSOp::AsyncReject:
1706 case JSOp::Finally:
1707 case JSOp::GetRval:
1708 case JSOp::ThrowMsg:
1709 return false;
1710
1711 case JSOp::InitAliasedLexical: {
1712 uint32_t hops = EnvironmentCoordinate(pc).hops();
1713 if (hops == 0) {
1714 // Initializing aliased lexical in the current scope is almost same
1715 // as JSOp::InitLexical.
1716 return false;
1717 }
1718
1719 // Otherwise this can touch an environment outside of the current scope.
1720 return true;
1721 }
1722 }
1723
1724 MOZ_ASSERT_UNREACHABLE("Invalid opcode")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: "
"Invalid opcode" ")", "./../../../../js/src/debugger/Script.cpp"
, 1724); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Invalid opcode" ")"); do { MOZ_CrashSequence
(__null, 1724); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1725 return false;
1726}
1727
1728bool DebuggerScript::CallData::getEffectfulOffsets() {
1729 if (!ensureScript()) {
1730 return false;
1731 }
1732
1733 RootedObject result(cx, NewDenseEmptyArray(cx));
1734 if (!result) {
1735 return false;
1736 }
1737 for (BytecodeRange r(cx, script); !r.empty(); r.popFront()) {
1738 size_t offset = r.frontOffset();
1739 if (!BytecodeIsEffectful(script, offset)) {
1740 continue;
1741 }
1742
1743 if (IsGeneratorSlotInitialization(script, offset, cx)) {
1744 // This is engine-internal operation and not visible outside the
1745 // currently executing frame.
1746 //
1747 // Also this offset is not allowed for setting breakpoint.
1748 continue;
1749 }
1750
1751 if (!NewbornArrayPush(cx, result, NumberValue(offset))) {
1752 return false;
1753 }
1754 }
1755
1756 args.rval().setObject(*result);
1757 return true;
1758}
1759
1760bool DebuggerScript::CallData::getAllOffsets() {
1761 if (!ensureScript()) {
1762 return false;
1763 }
1764
1765 // First pass: determine which offsets in this script are jump targets and
1766 // which line numbers jump to them.
1767 FlowGraphSummary flowData(cx);
1768 if (!flowData.populate(cx, script)) {
1769 return false;
1770 }
1771
1772 // Second pass: build the result array.
1773 RootedObject result(cx, NewDenseEmptyArray(cx));
1774 if (!result) {
1775 return false;
1776 }
1777 for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
1778 !r.empty(); r.popFront()) {
1779 if (!r.frontIsEntryPoint()) {
1780 continue;
1781 }
1782
1783 size_t offset = r.frontOffset();
1784 uint32_t lineno = r.frontLineNumber();
1785
1786 // Make a note, if the current instruction is an entry point for the current
1787 // line.
1788 if (!flowData[offset].hasNoEdges() && flowData[offset].lineno() != lineno) {
1789 // Get the offsets array for this line.
1790 RootedObject offsets(cx);
1791 RootedValue offsetsv(cx);
1792
1793 RootedId id(cx, PropertyKey::Int(lineno));
1794
1795 bool found;
1796 if (!HasOwnProperty(cx, result, id, &found)) {
1797 return false;
1798 }
1799 if (found && !GetProperty(cx, result, result, id, &offsetsv)) {
1800 return false;
1801 }
1802
1803 if (offsetsv.isObject()) {
1804 offsets = &offsetsv.toObject();
1805 } else {
1806 MOZ_ASSERT(offsetsv.isUndefined())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(offsetsv.isUndefined())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(offsetsv.isUndefined()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("offsetsv.isUndefined()"
, "./../../../../js/src/debugger/Script.cpp", 1806); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "offsetsv.isUndefined()" ")"); do { MOZ_CrashSequence
(__null, 1806); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1807
1808 // Create an empty offsets array for this line.
1809 // Store it in the result array.
1810 RootedId id(cx);
1811 RootedValue v(cx, NumberValue(lineno));
1812 offsets = NewDenseEmptyArray(cx);
1813 if (!offsets || !PrimitiveValueToId<CanGC>(cx, v, &id)) {
1814 return false;
1815 }
1816
1817 RootedValue value(cx, ObjectValue(*offsets));
1818 if (!DefineDataProperty(cx, result, id, value)) {
1819 return false;
1820 }
1821 }
1822
1823 // Append the current offset to the offsets array.
1824 if (!NewbornArrayPush(cx, offsets, NumberValue(offset))) {
1825 return false;
1826 }
1827 }
1828 }
1829
1830 args.rval().setObject(*result);
1831 return true;
1832}
1833
1834class DebuggerScript::GetAllColumnOffsetsMatcher {
1835 JSContext* cx_;
1836 MutableHandleObject result_;
1837
1838 bool appendColumnOffsetEntry(uint32_t lineno,
1839 JS::LimitedColumnNumberOneOrigin column,
1840 size_t offset) {
1841 Rooted<PlainObject*> entry(cx_, NewPlainObject(cx_));
1842 if (!entry) {
1843 return false;
1844 }
1845
1846 RootedValue value(cx_, NumberValue(lineno));
1847 if (!DefineDataProperty(cx_, entry, cx_->names().lineNumber, value)) {
1848 return false;
1849 }
1850
1851 value = NumberValue(column.oneOriginValue());
1852 if (!DefineDataProperty(cx_, entry, cx_->names().columnNumber, value)) {
1853 return false;
1854 }
1855
1856 value = NumberValue(offset);
1857 if (!DefineDataProperty(cx_, entry, cx_->names().offset, value)) {
1858 return false;
1859 }
1860
1861 return NewbornArrayPush(cx_, result_, ObjectValue(*entry));
1862 }
1863
1864 public:
1865 explicit GetAllColumnOffsetsMatcher(JSContext* cx, MutableHandleObject result)
1866 : cx_(cx), result_(result) {}
1867 using ReturnType = bool;
1868 ReturnType match(Handle<BaseScript*> base) {
1869 RootedScript script(cx_, DelazifyScript(cx_, base));
1870 if (!script) {
1871 return false;
1872 }
1873
1874 // First pass: determine which offsets in this script are jump targets
1875 // and which positions jump to them.
1876 FlowGraphSummary flowData(cx_);
1877 if (!flowData.populate(cx_, script)) {
1878 return false;
1879 }
1880
1881 // Second pass: build the result array.
1882 result_.set(NewDenseEmptyArray(cx_));
1883 if (!result_) {
1884 return false;
1885 }
1886
1887 for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
1888 !r.empty(); r.popFront()) {
1889 uint32_t lineno = r.frontLineNumber();
1890 JS::LimitedColumnNumberOneOrigin column = r.frontColumnNumber();
1891 size_t offset = r.frontOffset();
1892
1893 // Make a note, if the current instruction is an entry point for
1894 // the current position.
1895 if (r.frontIsEntryPoint() && !flowData[offset].hasNoEdges() &&
1896 (flowData[offset].lineno() != lineno ||
1897 flowData[offset].columnOrSentinel() != column.oneOriginValue())) {
1898 if (!appendColumnOffsetEntry(lineno, column, offset)) {
1899 return false;
1900 }
1901 }
1902 }
1903 return true;
1904 }
1905 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
1906 wasm::Instance& instance = instanceObj->instance();
1907
1908 Vector<wasm::ExprLoc> offsets(cx_);
1909 if (instance.debugEnabled() &&
1910 !instance.debug().getAllColumnOffsets(&offsets)) {
1911 return false;
1912 }
1913
1914 result_.set(NewDenseEmptyArray(cx_));
1915 if (!result_) {
1916 return false;
1917 }
1918
1919 for (uint32_t i = 0; i < offsets.length(); i++) {
1920 uint32_t lineno = offsets[i].lineno;
1921 JS::LimitedColumnNumberOneOrigin column(offsets[i].column);
1922 size_t offset = offsets[i].offset;
1923 if (!appendColumnOffsetEntry(lineno, column, offset)) {
1924 return false;
1925 }
1926 }
1927 return true;
1928 }
1929};
1930
1931bool DebuggerScript::CallData::getAllColumnOffsets() {
1932 RootedObject result(cx);
1933 GetAllColumnOffsetsMatcher matcher(cx, &result);
1934 if (!referent.match(matcher)) {
1935 return false;
1936 }
1937
1938 args.rval().setObject(*result);
1939 return true;
1940}
1941
1942class DebuggerScript::GetLineOffsetsMatcher {
1943 JSContext* cx_;
1944 uint32_t lineno_;
1945 MutableHandleObject result_;
1946
1947 public:
1948 explicit GetLineOffsetsMatcher(JSContext* cx, uint32_t lineno,
1949 MutableHandleObject result)
1950 : cx_(cx), lineno_(lineno), result_(result) {}
1951 using ReturnType = bool;
1952 ReturnType match(Handle<BaseScript*> base) {
1953 RootedScript script(cx_, DelazifyScript(cx_, base));
1954 if (!script) {
1955 return false;
1956 }
1957
1958 // First pass: determine which offsets in this script are jump targets and
1959 // which line numbers jump to them.
1960 FlowGraphSummary flowData(cx_);
1961 if (!flowData.populate(cx_, script)) {
1962 return false;
1963 }
1964
1965 result_.set(NewDenseEmptyArray(cx_));
1966 if (!result_) {
1967 return false;
1968 }
1969
1970 // Second pass: build the result array.
1971 for (BytecodeRangeWithPosition r(cx_, script, SkipPrologueOps::Yes);
1972 !r.empty(); r.popFront()) {
1973 if (!r.frontIsEntryPoint()) {
1974 continue;
1975 }
1976
1977 size_t offset = r.frontOffset();
1978
1979 // If the op at offset is an entry point, append offset to result.
1980 if (r.frontLineNumber() == lineno_ && !flowData[offset].hasNoEdges() &&
1981 flowData[offset].lineno() != lineno_) {
1982 if (!NewbornArrayPush(cx_, result_, NumberValue(offset))) {
1983 return false;
1984 }
1985 }
1986 }
1987
1988 return true;
1989 }
1990 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
1991 wasm::Instance& instance = instanceObj->instance();
1992
1993 Vector<uint32_t> offsets(cx_);
1994 if (instance.debugEnabled() &&
1995 !instance.debug().getLineOffsets(lineno_, &offsets)) {
1996 return false;
1997 }
1998
1999 result_.set(NewDenseEmptyArray(cx_));
2000 if (!result_) {
2001 return false;
2002 }
2003
2004 for (uint32_t i = 0; i < offsets.length(); i++) {
2005 if (!NewbornArrayPush(cx_, result_, NumberValue(offsets[i]))) {
2006 return false;
2007 }
2008 }
2009 return true;
2010 }
2011};
2012
2013bool DebuggerScript::CallData::getLineOffsets() {
2014 if (!args.requireAtLeast(cx, "Debugger.Script.getLineOffsets", 1)) {
2015 return false;
2016 }
2017
2018 // Parse lineno argument.
2019 RootedValue linenoValue(cx, args[0]);
2020 uint32_t lineno;
2021 if (!ToNumber(cx, &linenoValue)) {
2022 return false;
2023 }
2024 {
2025 double d = linenoValue.toNumber();
2026 lineno = uint32_t(d);
2027 if (lineno != d) {
2028 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
2029 JSMSG_DEBUG_BAD_LINE);
2030 return false;
2031 }
2032 }
2033
2034 RootedObject result(cx);
2035 GetLineOffsetsMatcher matcher(cx, lineno, &result);
2036 if (!referent.match(matcher)) {
2037 return false;
2038 }
2039
2040 args.rval().setObject(*result);
2041 return true;
2042}
2043
2044struct DebuggerScript::SetBreakpointMatcher {
2045 JSContext* cx_;
2046 Debugger* dbg_;
2047 size_t offset_;
2048 RootedObject handler_;
2049 RootedObject debuggerObject_;
2050
2051 bool wrapCrossCompartmentEdges() {
2052 if (!cx_->compartment()->wrap(cx_, &handler_) ||
2053 !cx_->compartment()->wrap(cx_, &debuggerObject_)) {
2054 return false;
2055 }
2056
2057 // If the Debugger's compartment has killed incoming wrappers, we may not
2058 // have gotten usable results from the 'wrap' calls. Treat it as a
2059 // failure.
2060 if (IsDeadProxyObject(handler_) || IsDeadProxyObject(debuggerObject_)) {
2061 ReportAccessDenied(cx_);
2062 return false;
2063 }
2064
2065 return true;
2066 }
2067
2068 public:
2069 explicit SetBreakpointMatcher(JSContext* cx, Debugger* dbg, size_t offset,
2070 HandleObject handler)
2071 : cx_(cx),
2072 dbg_(dbg),
2073 offset_(offset),
2074 handler_(cx, handler),
2075 debuggerObject_(cx_, dbg_->toJSObject()) {}
2076
2077 using ReturnType = bool;
2078
2079 ReturnType match(Handle<BaseScript*> base) {
2080 RootedScript script(cx_, DelazifyScript(cx_, base));
2081 if (!script) {
2082 return false;
2083 }
2084
2085 if (!dbg_->observesScript(script)) {
2086 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
2087 JSMSG_DEBUG_NOT_DEBUGGING);
2088 return false;
2089 }
2090
2091 if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
2092 return false;
2093 }
2094
2095 if (!EnsureBreakpointIsAllowed(cx_, script, offset_)) {
2096 return false;
2097 }
2098
2099 // Ensure observability *before* setting the breakpoint. If the script is
2100 // not already a debuggee, trying to ensure observability after setting
2101 // the breakpoint (and thus marking the script as a debuggee) will skip
2102 // actually ensuring observability.
2103 if (!dbg_->ensureExecutionObservabilityOfScript(cx_, script)) {
2104 return false;
2105 }
2106
2107 // A Breakpoint belongs logically to its script's compartment, so its
2108 // references to its Debugger and handler must be properly wrapped.
2109 AutoRealm ar(cx_, script);
2110 if (!wrapCrossCompartmentEdges()) {
2111 return false;
2112 }
2113
2114 jsbytecode* pc = script->offsetToPC(offset_);
2115 JSBreakpointSite* site =
2116 DebugScript::getOrCreateBreakpointSite(cx_, script, pc);
2117 if (!site) {
2118 return false;
2119 }
2120
2121 if (!cx_->zone()->new_<Breakpoint>(dbg_, debuggerObject_, site, handler_)) {
2122 site->destroyIfEmpty(cx_->runtime()->gcContext());
2123 ReportOutOfMemory(cx_);
2124 return false;
2125 }
2126 AddCellMemory(script, sizeof(Breakpoint), MemoryUse::Breakpoint);
2127
2128 return true;
2129 }
2130 ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
2131 wasm::Instance& instance = wasmInstance->instance();
2132 if (!instance.debugEnabled() ||
2133 !instance.debug().hasBreakpointTrapAtOffset(offset_)) {
2134 JS_ReportErrorNumberASCII(cx_, GetErrorMessage, nullptr,
2135 JSMSG_DEBUG_BAD_OFFSET);
2136 return false;
2137 }
2138
2139 // A Breakpoint belongs logically to its Instance's compartment, so its
2140 // references to its Debugger and handler must be properly wrapped.
2141 AutoRealm ar(cx_, wasmInstance);
2142 if (!wrapCrossCompartmentEdges()) {
2143 return false;
2144 }
2145
2146 WasmBreakpointSite* site = instance.getOrCreateBreakpointSite(cx_, offset_);
2147 if (!site) {
2148 return false;
2149 }
2150
2151 if (!cx_->zone()->new_<Breakpoint>(dbg_, debuggerObject_, site, handler_)) {
2152 site->destroyIfEmpty(cx_->runtime()->gcContext());
2153 ReportOutOfMemory(cx_);
2154 return false;
2155 }
2156 AddCellMemory(wasmInstance, sizeof(Breakpoint), MemoryUse::Breakpoint);
2157
2158 return true;
2159 }
2160};
2161
2162bool DebuggerScript::CallData::setBreakpoint() {
2163 if (!args.requireAtLeast(cx, "Debugger.Script.setBreakpoint", 2)) {
2164 return false;
2165 }
2166 Debugger* dbg = obj->owner();
2167
2168 size_t offset;
2169 if (!ScriptOffset(cx, args[0], &offset)) {
2170 return false;
2171 }
2172
2173 RootedObject handler(cx, RequireObject(cx, args[1]));
2174 if (!handler) {
2175 return false;
2176 }
2177
2178 SetBreakpointMatcher matcher(cx, dbg, offset, handler);
2179 if (!referent.match(matcher)) {
2180 return false;
2181 }
2182 args.rval().setUndefined();
2183 return true;
2184}
2185
2186bool DebuggerScript::CallData::getBreakpoints() {
2187 if (!ensureScript()) {
2188 return false;
2189 }
2190 Debugger* dbg = obj->owner();
2191
2192 jsbytecode* pc;
2193 if (args.length() > 0) {
2194 size_t offset;
2195 if (!ScriptOffset(cx, args[0], &offset) ||
2196 !EnsureScriptOffsetIsValid(cx, script, offset)) {
2197 return false;
2198 }
2199 pc = script->offsetToPC(offset);
2200 } else {
2201 pc = nullptr;
2202 }
2203
2204 RootedObject arr(cx, NewDenseEmptyArray(cx));
2205 if (!arr) {
2206 return false;
2207 }
2208
2209 for (unsigned i = 0; i < script->length(); i++) {
2210 JSBreakpointSite* site =
2211 DebugScript::getBreakpointSite(script, script->offsetToPC(i));
2212 if (!site) {
2213 continue;
2214 }
2215 if (!pc || site->pc == pc) {
2216 for (Breakpoint* bp = site->firstBreakpoint(); bp;
2217 bp = bp->nextInSite()) {
2218 if (bp->debugger == dbg) {
2219 RootedObject handler(cx, bp->getHandler());
2220 if (!cx->compartment()->wrap(cx, &handler) ||
2221 !NewbornArrayPush(cx, arr, ObjectValue(*handler))) {
2222 return false;
2223 }
2224 }
2225 }
2226 }
2227 }
2228 args.rval().setObject(*arr);
2229 return true;
2230}
2231
2232class DebuggerScript::ClearBreakpointMatcher {
2233 JSContext* cx_;
2234 Debugger* dbg_;
2235 RootedObject handler_;
2236
2237 public:
2238 ClearBreakpointMatcher(JSContext* cx, Debugger* dbg, JSObject* handler)
2239 : cx_(cx), dbg_(dbg), handler_(cx, handler) {}
2240 using ReturnType = bool;
2241
2242 ReturnType match(Handle<BaseScript*> base) {
2243 RootedScript script(cx_, DelazifyScript(cx_, base));
2244 if (!script) {
2245 return false;
2246 }
2247
2248 // A Breakpoint belongs logically to its script's compartment, so it holds
2249 // its handler via a cross-compartment wrapper. But the handler passed to
2250 // `clearBreakpoint` is same-compartment with the Debugger. Wrap it here,
2251 // so that `DebugScript::clearBreakpointsIn` gets the right value to
2252 // search for.
2253 AutoRealm ar(cx_, script);
2254 if (!cx_->compartment()->wrap(cx_, &handler_)) {
2255 return false;
2256 }
2257
2258 DebugScript::clearBreakpointsIn(cx_->runtime()->gcContext(), script, dbg_,
2259 handler_);
2260 return true;
2261 }
2262 ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
2263 wasm::Instance& instance = instanceObj->instance();
2264 if (!instance.debugEnabled()) {
2265 return true;
2266 }
2267
2268 // A Breakpoint belongs logically to its instance's compartment, so it
2269 // holds its handler via a cross-compartment wrapper. But the handler
2270 // passed to `clearBreakpoint` is same-compartment with the Debugger. Wrap
2271 // it here, so that `DebugState::clearBreakpointsIn` gets the right value
2272 // to search for.
2273 AutoRealm ar(cx_, instanceObj);
2274 if (!cx_->compartment()->wrap(cx_, &handler_)) {
2275 return false;
2276 }
2277
2278 instance.debug().clearBreakpointsIn(cx_->runtime()->gcContext(),
2279 instanceObj, dbg_, handler_);
2280 return true;
2281 }
2282};
2283
2284bool DebuggerScript::CallData::clearBreakpoint() {
2285 if (!args.requireAtLeast(cx, "Debugger.Script.clearBreakpoint", 1)) {
2286 return false;
2287 }
2288 Debugger* dbg = obj->owner();
2289
2290 JSObject* handler = RequireObject(cx, args[0]);
2291 if (!handler) {
2292 return false;
2293 }
2294
2295 ClearBreakpointMatcher matcher(cx, dbg, handler);
2296 if (!referent.match(matcher)) {
2297 return false;
2298 }
2299
2300 args.rval().setUndefined();
2301 return true;
2302}
2303
2304bool DebuggerScript::CallData::clearAllBreakpoints() {
2305 Debugger* dbg = obj->owner();
2306 ClearBreakpointMatcher matcher(cx, dbg, nullptr);
2307 if (!referent.match(matcher)) {
2308 return false;
2309 }
2310 args.rval().setUndefined();
2311 return true;
2312}
2313
2314class DebuggerScript::IsInCatchScopeMatcher {
2315 JSContext* cx_;
2316 size_t offset_;
2317 bool isInCatch_;
2318
2319 public:
2320 explicit IsInCatchScopeMatcher(JSContext* cx, size_t offset)
2321 : cx_(cx), offset_(offset), isInCatch_(false) {}
2322 using ReturnType = bool;
2323
2324 inline bool isInCatch() const { return isInCatch_; }
2325
2326 ReturnType match(Handle<BaseScript*> base) {
2327 RootedScript script(cx_, DelazifyScript(cx_, base));
2328 if (!script) {
2329 return false;
2330 }
2331
2332 if (!EnsureScriptOffsetIsValid(cx_, script, offset_)) {
2333 return false;
2334 }
2335
2336 MOZ_ASSERT(!isInCatch_)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!isInCatch_)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!isInCatch_))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!isInCatch_", "./../../../../js/src/debugger/Script.cpp"
, 2336); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!isInCatch_"
")"); do { MOZ_CrashSequence(__null, 2336); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2337
2338 auto inRange = [this](const TryNote& tn) {
2339 return tn.start <= offset_ && offset_ < tn.start + tn.length;
2340 };
2341 auto notes = script->trynotes();
2342
2343 for (size_t i = 0; i < notes.size(); i++) {
2344 const TryNote& tn = notes[i];
2345 if (tn.kind() != TryNoteKind::Catch || !inRange(tn)) {
2346 continue;
2347 }
2348
2349 // For-of loops generate a synthetic catch block to handle
2350 // closing the iterator when throwing an exception. The
2351 // debugger should ignore these synthetic catch blocks, so
2352 // we skip any Catch trynote that is immediately followed
2353 // by a ForOf trynote.
2354 if (i + 1 < notes.size() && notes[i + 1].kind() == TryNoteKind::ForOf &&
2355 inRange(notes[i + 1])) {
2356 continue;
2357 }
2358
2359 isInCatch_ = true;
2360 return true;
2361 }
2362
2363 return true;
2364 }
2365 ReturnType match(Handle<WasmInstanceObject*> instance) {
2366 isInCatch_ = false;
2367 return true;
2368 }
2369};
2370
2371bool DebuggerScript::CallData::isInCatchScope() {
2372 if (!args.requireAtLeast(cx, "Debugger.Script.isInCatchScope", 1)) {
2373 return false;
2374 }
2375
2376 size_t offset;
2377 if (!ScriptOffset(cx, args[0], &offset)) {
2378 return false;
2379 }
2380
2381 IsInCatchScopeMatcher matcher(cx, offset);
2382 if (!referent.match(matcher)) {
2383 return false;
2384 }
2385 args.rval().setBoolean(matcher.isInCatch());
2386 return true;
2387}
2388
2389bool DebuggerScript::CallData::getOffsetsCoverage() {
2390 if (!ensureScript()) {
2391 return false;
2392 }
2393
2394 Debugger* dbg = obj->owner();
2395 if (dbg->observesCoverage() != Debugger::Observing) {
2396 args.rval().setNull();
2397 return true;
2398 }
2399
2400 // If the script has no coverage information, then skip this and return null
2401 // instead.
2402 if (!script->hasScriptCounts()) {
2403 args.rval().setNull();
2404 return true;
2405 }
2406
2407 ScriptCounts* sc = &script->getScriptCounts();
2408
2409 // If the main ever got visited, then assume that any code before main got
2410 // visited once.
2411 uint64_t hits = 0;
2412 const PCCounts* counts =
2413 sc->maybeGetPCCounts(script->pcToOffset(script->main()));
2414 if (counts->numExec()) {
2415 hits = 1;
2416 }
2417
2418 // Build an array of objects which are composed of 4 properties:
2419 // - offset PC offset of the current opcode.
2420 // - lineNumber Line of the current opcode.
2421 // - columnNumber Column of the current opcode.
2422 // - count Number of times the instruction got executed.
2423 RootedObject result(cx, NewDenseEmptyArray(cx));
2424 if (!result) {
2425 return false;
2426 }
2427
2428 RootedId offsetId(cx, NameToId(cx->names().offset));
2429 RootedId lineNumberId(cx, NameToId(cx->names().lineNumber));
2430 RootedId columnNumberId(cx, NameToId(cx->names().columnNumber));
2431 RootedId countId(cx, NameToId(cx->names().count));
2432
2433 RootedObject item(cx);
2434 RootedValue offsetValue(cx);
2435 RootedValue lineNumberValue(cx);
2436 RootedValue columnNumberValue(cx);
2437 RootedValue countValue(cx);
2438
2439 // Iterate linearly over the bytecode.
2440 for (BytecodeRangeWithPosition r(cx, script, SkipPrologueOps::Yes);
2441 !r.empty(); r.popFront()) {
2442 size_t offset = r.frontOffset();
2443
2444 // The beginning of each non-branching sequences of instruction set the
2445 // number of execution of the current instruction and any following
2446 // instruction.
2447 counts = sc->maybeGetPCCounts(offset);
2448 if (counts) {
2449 hits = counts->numExec();
2450 }
2451
2452 offsetValue.setNumber(double(offset));
2453 lineNumberValue.setNumber(double(r.frontLineNumber()));
2454 columnNumberValue.setNumber(double(r.frontColumnNumber().oneOriginValue()));
2455 countValue.setNumber(double(hits));
2456
2457 // Create a new object with the offset, line number, column number, the
2458 // number of hit counts, and append it to the array.
2459 item = NewPlainObjectWithProto(cx, nullptr);
2460 if (!item || !DefineDataProperty(cx, item, offsetId, offsetValue) ||
2461 !DefineDataProperty(cx, item, lineNumberId, lineNumberValue) ||
2462 !DefineDataProperty(cx, item, columnNumberId, columnNumberValue) ||
2463 !DefineDataProperty(cx, item, countId, countValue) ||
2464 !NewbornArrayPush(cx, result, ObjectValue(*item))) {
2465 return false;
2466 }
2467
2468 // If the current instruction has thrown, then decrement the hit counts
2469 // with the number of throws.
2470 counts = sc->maybeGetThrowCounts(offset);
2471 if (counts) {
2472 hits -= counts->numExec();
2473 }
2474 }
2475
2476 args.rval().setObject(*result);
2477 return true;
2478}
2479
2480/* static */
2481bool DebuggerScript::construct(JSContext* cx, unsigned argc, Value* vp) {
2482 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
2483 "Debugger.Script");
2484 return false;
2485}
2486
2487const JSPropertySpec DebuggerScript::properties_[] = {
2488 JS_DEBUG_PSG("isGeneratorFunction", getIsGeneratorFunction)JSPropertySpec::nativeAccessors("isGeneratorFunction", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getIsGeneratorFunction
>, nullptr)
,
2489 JS_DEBUG_PSG("isAsyncFunction", getIsAsyncFunction)JSPropertySpec::nativeAccessors("isAsyncFunction", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getIsAsyncFunction
>, nullptr)
,
2490 JS_DEBUG_PSG("isFunction", getIsFunction)JSPropertySpec::nativeAccessors("isFunction", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getIsFunction
>, nullptr)
,
2491 JS_DEBUG_PSG("isModule", getIsModule)JSPropertySpec::nativeAccessors("isModule", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getIsModule
>, nullptr)
,
2492 JS_DEBUG_PSG("displayName", getDisplayName)JSPropertySpec::nativeAccessors("displayName", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getDisplayName
>, nullptr)
,
2493 JS_DEBUG_PSG("parameterNames", getParameterNames)JSPropertySpec::nativeAccessors("parameterNames", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getParameterNames
>, nullptr)
,
2494 JS_DEBUG_PSG("url", getUrl)JSPropertySpec::nativeAccessors("url", CheckAccessorAttrs<
0>(), CallData::ToNative<&CallData::getUrl>, nullptr
)
,
2495 JS_DEBUG_PSG("startLine", getStartLine)JSPropertySpec::nativeAccessors("startLine", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getStartLine
>, nullptr)
,
2496 JS_DEBUG_PSG("startColumn", getStartColumn)JSPropertySpec::nativeAccessors("startColumn", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getStartColumn
>, nullptr)
,
2497 JS_DEBUG_PSG("lineCount", getLineCount)JSPropertySpec::nativeAccessors("lineCount", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getLineCount
>, nullptr)
,
2498 JS_DEBUG_PSG("source", getSource)JSPropertySpec::nativeAccessors("source", CheckAccessorAttrs<
0>(), CallData::ToNative<&CallData::getSource>, nullptr
)
,
2499 JS_DEBUG_PSG("sourceStart", getSourceStart)JSPropertySpec::nativeAccessors("sourceStart", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getSourceStart
>, nullptr)
,
2500 JS_DEBUG_PSG("sourceLength", getSourceLength)JSPropertySpec::nativeAccessors("sourceLength", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getSourceLength
>, nullptr)
,
2501 JS_DEBUG_PSG("mainOffset", getMainOffset)JSPropertySpec::nativeAccessors("mainOffset", CheckAccessorAttrs
<0>(), CallData::ToNative<&CallData::getMainOffset
>, nullptr)
,
2502 JS_DEBUG_PSG("global", getGlobal)JSPropertySpec::nativeAccessors("global", CheckAccessorAttrs<
0>(), CallData::ToNative<&CallData::getGlobal>, nullptr
)
,
2503 JS_DEBUG_PSG("format", getFormat)JSPropertySpec::nativeAccessors("format", CheckAccessorAttrs<
0>(), CallData::ToNative<&CallData::getFormat>, nullptr
)
,
2504 JS_PS_ENDJSPropertySpec::sentinel(),
2505};
2506
2507const JSFunctionSpec DebuggerScript::methods_[] = {
2508 JS_DEBUG_FN("getChildScripts", getChildScripts, 0){JSFunctionSpec::Name("getChildScripts"), {CallData::ToNative
<&CallData::getChildScripts>, nullptr}, 0, 0, nullptr
}
,
2509 JS_DEBUG_FN("getPossibleBreakpoints", getPossibleBreakpoints, 0){JSFunctionSpec::Name("getPossibleBreakpoints"), {CallData::ToNative
<&CallData::getPossibleBreakpoints>, nullptr}, 0, 0
, nullptr}
,
2510 JS_DEBUG_FN("getPossibleBreakpointOffsets", getPossibleBreakpointOffsets,{JSFunctionSpec::Name("getPossibleBreakpointOffsets"), {CallData
::ToNative<&CallData::getPossibleBreakpointOffsets>
, nullptr}, 0, 0, nullptr}
2511 0){JSFunctionSpec::Name("getPossibleBreakpointOffsets"), {CallData
::ToNative<&CallData::getPossibleBreakpointOffsets>
, nullptr}, 0, 0, nullptr}
,
2512 JS_DEBUG_FN("setBreakpoint", setBreakpoint, 2){JSFunctionSpec::Name("setBreakpoint"), {CallData::ToNative<
&CallData::setBreakpoint>, nullptr}, 2, 0, nullptr}
,
2513 JS_DEBUG_FN("getBreakpoints", getBreakpoints, 1){JSFunctionSpec::Name("getBreakpoints"), {CallData::ToNative<
&CallData::getBreakpoints>, nullptr}, 1, 0, nullptr}
,
2514 JS_DEBUG_FN("clearBreakpoint", clearBreakpoint, 1){JSFunctionSpec::Name("clearBreakpoint"), {CallData::ToNative
<&CallData::clearBreakpoint>, nullptr}, 1, 0, nullptr
}
,
2515 JS_DEBUG_FN("clearAllBreakpoints", clearAllBreakpoints, 0){JSFunctionSpec::Name("clearAllBreakpoints"), {CallData::ToNative
<&CallData::clearAllBreakpoints>, nullptr}, 0, 0, nullptr
}
,
2516 JS_DEBUG_FN("isInCatchScope", isInCatchScope, 1){JSFunctionSpec::Name("isInCatchScope"), {CallData::ToNative<
&CallData::isInCatchScope>, nullptr}, 1, 0, nullptr}
,
2517 JS_DEBUG_FN("getOffsetMetadata", getOffsetMetadata, 1){JSFunctionSpec::Name("getOffsetMetadata"), {CallData::ToNative
<&CallData::getOffsetMetadata>, nullptr}, 1, 0, nullptr
}
,
2518 JS_DEBUG_FN("getOffsetsCoverage", getOffsetsCoverage, 0){JSFunctionSpec::Name("getOffsetsCoverage"), {CallData::ToNative
<&CallData::getOffsetsCoverage>, nullptr}, 0, 0, nullptr
}
,
2519 JS_DEBUG_FN("getEffectfulOffsets", getEffectfulOffsets, 1){JSFunctionSpec::Name("getEffectfulOffsets"), {CallData::ToNative
<&CallData::getEffectfulOffsets>, nullptr}, 1, 0, nullptr
}
,
2520
2521 // The following APIs are deprecated due to their reliance on the
2522 // under-defined 'entrypoint' concept. Make use of getPossibleBreakpoints,
2523 // getPossibleBreakpointOffsets, or getOffsetMetadata instead.
2524 JS_DEBUG_FN("getAllOffsets", getAllOffsets, 0){JSFunctionSpec::Name("getAllOffsets"), {CallData::ToNative<
&CallData::getAllOffsets>, nullptr}, 0, 0, nullptr}
,
2525 JS_DEBUG_FN("getAllColumnOffsets", getAllColumnOffsets, 0){JSFunctionSpec::Name("getAllColumnOffsets"), {CallData::ToNative
<&CallData::getAllColumnOffsets>, nullptr}, 0, 0, nullptr
}
,
2526 JS_DEBUG_FN("getLineOffsets", getLineOffsets, 1){JSFunctionSpec::Name("getLineOffsets"), {CallData::ToNative<
&CallData::getLineOffsets>, nullptr}, 1, 0, nullptr}
,
2527 JS_DEBUG_FN("getOffsetLocation", getOffsetLocation, 0){JSFunctionSpec::Name("getOffsetLocation"), {CallData::ToNative
<&CallData::getOffsetLocation>, nullptr}, 0, 0, nullptr
}
,
2528 JS_FS_END{JSFunctionSpec::Name(nullptr), {nullptr, nullptr}, 0, 0, nullptr
}
,
2529};