Bug Summary

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