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/Source.h"
8 
9 #include "mozilla/Assertions.h"  // for AssertionConditionType, MOZ_ASSERT
10 #include "mozilla/Maybe.h"       // for Some, Maybe, Nothing
11 #include "mozilla/Variant.h"     // for AsVariant, Variant
12 
13 #include <stdint.h>  // for uint32_t
14 #include <string.h>  // for memcpy
15 #include <utility>   // for move
16 
17 #include "jsapi.h"        // for JS_ReportErrorNumberASCII, JS_CopyStringCharsZ
18 #include "jsfriendapi.h"  // for GetErrorMessage, JS_NewUint8Array
19 
20 #include "debugger/Debugger.h"  // for DebuggerSourceReferent, Debugger
21 #include "debugger/Script.h"    // for DebuggerScript
22 #include "gc/Tracer.h"  // for TraceManuallyBarrieredCrossCompartmentEdge
23 #include "js/CompilationAndEvaluation.h"  // for Compile
24 #include "vm/BytecodeUtil.h"              // for JSDVG_SEARCH_STACK
25 #include "vm/JSContext.h"                 // for JSContext (ptr only)
26 #include "vm/JSObject.h"                  // for JSObject, RequireObject
27 #include "vm/JSScript.h"          // for ScriptSource, ScriptSourceObject
28 #include "vm/ObjectGroup.h"       // for TenuredObject
29 #include "vm/StringType.h"        // for NewStringCopyZ, JSString (ptr only)
30 #include "vm/TypedArrayObject.h"  // for TypedArrayObject, JSObject::is
31 #include "wasm/WasmCode.h"        // for Metadata
32 #include "wasm/WasmDebug.h"       // for DebugState
33 #include "wasm/WasmInstance.h"    // for Instance
34 #include "wasm/WasmJS.h"          // for WasmInstanceObject
35 #include "wasm/WasmTypes.h"       // for Bytes, RootedWasmInstanceObject
36 
37 #include "vm/JSObject-inl.h"      // for InitClass
38 #include "vm/NativeObject-inl.h"  // for NewTenuredObjectWithGivenProto
39 
40 namespace js {
41 class GlobalObject;
42 }
43 
44 using namespace js;
45 
46 using mozilla::AsVariant;
47 using mozilla::Maybe;
48 using mozilla::Nothing;
49 using mozilla::Some;
50 
51 const JSClassOps DebuggerSource::classOps_ = {
52     nullptr,                          // addProperty
53     nullptr,                          // delProperty
54     nullptr,                          // enumerate
55     nullptr,                          // newEnumerate
56     nullptr,                          // resolve
57     nullptr,                          // mayResolve
58     nullptr,                          // finalize
59     nullptr,                          // call
60     nullptr,                          // hasInstance
61     nullptr,                          // construct
62     CallTraceMethod<DebuggerSource>,  // trace
63 };
64 
65 const JSClass DebuggerSource::class_ = {
66     "Source", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
67     &classOps_};
68 
69 /* static */
initClass(JSContext * cx,Handle<GlobalObject * > global,HandleObject debugCtor)70 NativeObject* DebuggerSource::initClass(JSContext* cx,
71                                         Handle<GlobalObject*> global,
72                                         HandleObject debugCtor) {
73   return InitClass(cx, debugCtor, nullptr, &class_, construct, 0, properties_,
74                    methods_, nullptr, nullptr);
75 }
76 
77 /* static */
create(JSContext * cx,HandleObject proto,Handle<DebuggerSourceReferent> referent,HandleNativeObject debugger)78 DebuggerSource* DebuggerSource::create(JSContext* cx, HandleObject proto,
79                                        Handle<DebuggerSourceReferent> referent,
80                                        HandleNativeObject debugger) {
81   Rooted<DebuggerSource*> sourceObj(
82       cx, NewTenuredObjectWithGivenProto<DebuggerSource>(cx, proto));
83   if (!sourceObj) {
84     return nullptr;
85   }
86   sourceObj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
87   referent.get().match(
88       [&](auto sourceHandle) { sourceObj->setPrivateGCThing(sourceHandle); });
89 
90   return sourceObj;
91 }
92 
93 // For internal use only.
getReferentRawObject() const94 NativeObject* DebuggerSource::getReferentRawObject() const {
95   return static_cast<NativeObject*>(getPrivate());
96 }
97 
getReferent() const98 DebuggerSourceReferent DebuggerSource::getReferent() const {
99   if (NativeObject* referent = getReferentRawObject()) {
100     if (referent->is<ScriptSourceObject>()) {
101       return AsVariant(&referent->as<ScriptSourceObject>());
102     }
103     return AsVariant(&referent->as<WasmInstanceObject>());
104   }
105   return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
106 }
107 
trace(JSTracer * trc)108 void DebuggerSource::trace(JSTracer* trc) {
109   // There is a barrier on private pointers, so the Unbarriered marking
110   // is okay.
111   if (JSObject* referent = getReferentRawObject()) {
112     TraceManuallyBarrieredCrossCompartmentEdge(
113         trc, static_cast<JSObject*>(this), &referent,
114         "Debugger.Source referent");
115     setPrivateUnbarriered(referent);
116   }
117 }
118 
119 /* static */
construct(JSContext * cx,unsigned argc,Value * vp)120 bool DebuggerSource::construct(JSContext* cx, unsigned argc, Value* vp) {
121   JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
122                             "Debugger.Source");
123   return false;
124 }
125 
126 /* static */
check(JSContext * cx,HandleValue thisv)127 DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv) {
128   JSObject* thisobj = RequireObject(cx, thisv);
129   if (!thisobj) {
130     return nullptr;
131   }
132   if (!thisobj->is<DebuggerSource>()) {
133     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
134                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
135                               "method", thisobj->getClass()->name);
136     return nullptr;
137   }
138 
139   DebuggerSource* thisSourceObj = &thisobj->as<DebuggerSource>();
140 
141   if (!thisSourceObj->getReferentRawObject()) {
142     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
143                               JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
144                               "method", "prototype object");
145     return nullptr;
146   }
147 
148   return thisSourceObj;
149 }
150 
151 struct MOZ_STACK_CLASS DebuggerSource::CallData {
152   JSContext* cx;
153   const CallArgs& args;
154 
155   HandleDebuggerSource obj;
156   Rooted<DebuggerSourceReferent> referent;
157 
CallDataDebuggerSource::CallData158   CallData(JSContext* cx, const CallArgs& args, HandleDebuggerSource obj)
159       : cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()) {}
160 
161   bool getText();
162   bool getBinary();
163   bool getURL();
164   bool getStartLine();
165   bool getId();
166   bool getDisplayURL();
167   bool getElement();
168   bool getElementProperty();
169   bool getIntroductionScript();
170   bool getIntroductionOffset();
171   bool getIntroductionType();
172   bool setSourceMapURL();
173   bool getSourceMapURL();
174   bool reparse();
175 
176   using Method = bool (CallData::*)();
177 
178   template <Method MyMethod>
179   static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
180 };
181 
182 template <DebuggerSource::CallData::Method MyMethod>
183 /* static */
ToNative(JSContext * cx,unsigned argc,Value * vp)184 bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc,
185                                         Value* vp) {
186   CallArgs args = CallArgsFromVp(argc, vp);
187 
188   RootedDebuggerSource obj(cx, DebuggerSource::check(cx, args.thisv()));
189   if (!obj) {
190     return false;
191   }
192 
193   CallData data(cx, args, obj);
194   return (data.*MyMethod)();
195 }
196 
197 class DebuggerSourceGetTextMatcher {
198   JSContext* cx_;
199 
200  public:
DebuggerSourceGetTextMatcher(JSContext * cx)201   explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) {}
202 
203   using ReturnType = JSString*;
204 
match(HandleScriptSourceObject sourceObject)205   ReturnType match(HandleScriptSourceObject sourceObject) {
206     ScriptSource* ss = sourceObject->source();
207     bool hasSourceText;
208     if (!ScriptSource::loadSource(cx_, ss, &hasSourceText)) {
209       return nullptr;
210     }
211     if (!hasSourceText) {
212       return NewStringCopyZ<CanGC>(cx_, "[no source]");
213     }
214 
215     if (ss->isFunctionBody()) {
216       return ss->functionBodyString(cx_);
217     }
218 
219     return ss->substring(cx_, 0, ss->length());
220   }
221 
match(Handle<WasmInstanceObject * > instanceObj)222   ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
223     wasm::Instance& instance = instanceObj->instance();
224     const char* msg;
225     if (!instance.debugEnabled()) {
226       msg = "Restart with developer tools open to view WebAssembly source.";
227     } else {
228       msg = "[debugger missing wasm binary-to-text conversion]";
229     }
230     return NewStringCopyZ<CanGC>(cx_, msg);
231   }
232 };
233 
getText()234 bool DebuggerSource::CallData::getText() {
235   Value textv = obj->getReservedSlot(TEXT_SLOT);
236   if (!textv.isUndefined()) {
237     MOZ_ASSERT(textv.isString());
238     args.rval().set(textv);
239     return true;
240   }
241 
242   DebuggerSourceGetTextMatcher matcher(cx);
243   JSString* str = referent.match(matcher);
244   if (!str) {
245     return false;
246   }
247 
248   args.rval().setString(str);
249   obj->setReservedSlot(TEXT_SLOT, args.rval());
250   return true;
251 }
252 
getBinary()253 bool DebuggerSource::CallData::getBinary() {
254   if (!referent.is<WasmInstanceObject*>()) {
255     ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
256                      args.thisv(), nullptr, "a wasm source");
257     return false;
258   }
259 
260   RootedWasmInstanceObject instanceObj(cx, referent.as<WasmInstanceObject*>());
261   wasm::Instance& instance = instanceObj->instance();
262 
263   if (!instance.debugEnabled()) {
264     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
265                               JSMSG_DEBUG_NO_BINARY_SOURCE);
266     return false;
267   }
268 
269   const wasm::Bytes& bytecode = instance.debug().bytecode();
270   RootedObject arr(cx, JS_NewUint8Array(cx, bytecode.length()));
271   if (!arr) {
272     return false;
273   }
274 
275   memcpy(arr->as<TypedArrayObject>().dataPointerUnshared(), bytecode.begin(),
276          bytecode.length());
277 
278   args.rval().setObject(*arr);
279   return true;
280 }
281 
282 class DebuggerSourceGetURLMatcher {
283   JSContext* cx_;
284 
285  public:
DebuggerSourceGetURLMatcher(JSContext * cx)286   explicit DebuggerSourceGetURLMatcher(JSContext* cx) : cx_(cx) {}
287 
288   using ReturnType = Maybe<JSString*>;
289 
match(HandleScriptSourceObject sourceObject)290   ReturnType match(HandleScriptSourceObject sourceObject) {
291     ScriptSource* ss = sourceObject->source();
292     MOZ_ASSERT(ss);
293     if (ss->filename()) {
294       JSString* str = NewStringCopyZ<CanGC>(cx_, ss->filename());
295       return Some(str);
296     }
297     return Nothing();
298   }
match(Handle<WasmInstanceObject * > instanceObj)299   ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
300     return Some(instanceObj->instance().createDisplayURL(cx_));
301   }
302 };
303 
getURL()304 bool DebuggerSource::CallData::getURL() {
305   DebuggerSourceGetURLMatcher matcher(cx);
306   Maybe<JSString*> str = referent.match(matcher);
307   if (str.isSome()) {
308     if (!*str) {
309       return false;
310     }
311     args.rval().setString(*str);
312   } else {
313     args.rval().setNull();
314   }
315   return true;
316 }
317 
318 class DebuggerSourceGetStartLineMatcher {
319  public:
320   using ReturnType = uint32_t;
321 
match(HandleScriptSourceObject sourceObject)322   ReturnType match(HandleScriptSourceObject sourceObject) {
323     ScriptSource* ss = sourceObject->source();
324     return ss->startLine();
325   }
match(Handle<WasmInstanceObject * > instanceObj)326   ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
327 };
328 
getStartLine()329 bool DebuggerSource::CallData::getStartLine() {
330   DebuggerSourceGetStartLineMatcher matcher;
331   uint32_t line = referent.match(matcher);
332   args.rval().setNumber(line);
333   return true;
334 }
335 
336 class DebuggerSourceGetIdMatcher {
337  public:
338   using ReturnType = uint32_t;
339 
match(HandleScriptSourceObject sourceObject)340   ReturnType match(HandleScriptSourceObject sourceObject) {
341     ScriptSource* ss = sourceObject->source();
342     return ss->id();
343   }
match(Handle<WasmInstanceObject * > instanceObj)344   ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
345 };
346 
getId()347 bool DebuggerSource::CallData::getId() {
348   DebuggerSourceGetIdMatcher matcher;
349   uint32_t id = referent.match(matcher);
350   args.rval().setNumber(id);
351   return true;
352 }
353 
354 struct DebuggerSourceGetDisplayURLMatcher {
355   using ReturnType = const char16_t*;
matchDebuggerSourceGetDisplayURLMatcher356   ReturnType match(HandleScriptSourceObject sourceObject) {
357     ScriptSource* ss = sourceObject->source();
358     MOZ_ASSERT(ss);
359     return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
360   }
matchDebuggerSourceGetDisplayURLMatcher361   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
362     return wasmInstance->instance().metadata().displayURL();
363   }
364 };
365 
getDisplayURL()366 bool DebuggerSource::CallData::getDisplayURL() {
367   DebuggerSourceGetDisplayURLMatcher matcher;
368   if (const char16_t* displayURL = referent.match(matcher)) {
369     JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
370     if (!str) {
371       return false;
372     }
373     args.rval().setString(str);
374   } else {
375     args.rval().setNull();
376   }
377   return true;
378 }
379 
380 struct DebuggerSourceGetElementMatcher {
381   JSContext* mCx = nullptr;
DebuggerSourceGetElementMatcherDebuggerSourceGetElementMatcher382   explicit DebuggerSourceGetElementMatcher(JSContext* cx_) : mCx(cx_) {}
383   using ReturnType = JSObject*;
matchDebuggerSourceGetElementMatcher384   ReturnType match(HandleScriptSourceObject sourceObject) {
385     return sourceObject->unwrappedElement(mCx);
386   }
matchDebuggerSourceGetElementMatcher387   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return nullptr; }
388 };
389 
getElement()390 bool DebuggerSource::CallData::getElement() {
391   DebuggerSourceGetElementMatcher matcher(cx);
392   if (JSObject* element = referent.match(matcher)) {
393     args.rval().setObjectOrNull(element);
394     if (!Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval())) {
395       return false;
396     }
397   } else {
398     args.rval().setUndefined();
399   }
400   return true;
401 }
402 
403 struct DebuggerSourceGetElementPropertyMatcher {
404   using ReturnType = Value;
matchDebuggerSourceGetElementPropertyMatcher405   ReturnType match(HandleScriptSourceObject sourceObject) {
406     return sourceObject->unwrappedElementAttributeName();
407   }
matchDebuggerSourceGetElementPropertyMatcher408   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
409     return UndefinedValue();
410   }
411 };
412 
getElementProperty()413 bool DebuggerSource::CallData::getElementProperty() {
414   DebuggerSourceGetElementPropertyMatcher matcher;
415   args.rval().set(referent.match(matcher));
416   return Debugger::fromChildJSObject(obj)->wrapDebuggeeValue(cx, args.rval());
417 }
418 
419 class DebuggerSourceGetIntroductionScriptMatcher {
420   JSContext* cx_;
421   Debugger* dbg_;
422   MutableHandleValue rval_;
423 
424  public:
DebuggerSourceGetIntroductionScriptMatcher(JSContext * cx,Debugger * dbg,MutableHandleValue rval)425   DebuggerSourceGetIntroductionScriptMatcher(JSContext* cx, Debugger* dbg,
426                                              MutableHandleValue rval)
427       : cx_(cx), dbg_(dbg), rval_(rval) {}
428 
429   using ReturnType = bool;
430 
match(HandleScriptSourceObject sourceObject)431   ReturnType match(HandleScriptSourceObject sourceObject) {
432     Rooted<BaseScript*> script(cx_,
433                                sourceObject->unwrappedIntroductionScript());
434     if (script) {
435       RootedObject scriptDO(cx_, dbg_->wrapScript(cx_, script));
436       if (!scriptDO) {
437         return false;
438       }
439       rval_.setObject(*scriptDO);
440     } else {
441       rval_.setUndefined();
442     }
443     return true;
444   }
445 
match(Handle<WasmInstanceObject * > wasmInstance)446   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
447     RootedObject ds(cx_, dbg_->wrapWasmScript(cx_, wasmInstance));
448     if (!ds) {
449       return false;
450     }
451     rval_.setObject(*ds);
452     return true;
453   }
454 };
455 
getIntroductionScript()456 bool DebuggerSource::CallData::getIntroductionScript() {
457   Debugger* dbg = Debugger::fromChildJSObject(obj);
458   DebuggerSourceGetIntroductionScriptMatcher matcher(cx, dbg, args.rval());
459   return referent.match(matcher);
460 }
461 
462 struct DebuggerGetIntroductionOffsetMatcher {
463   using ReturnType = Value;
matchDebuggerGetIntroductionOffsetMatcher464   ReturnType match(HandleScriptSourceObject sourceObject) {
465     // Regardless of what's recorded in the ScriptSourceObject and
466     // ScriptSource, only hand out the introduction offset if we also have
467     // the script within which it applies.
468     ScriptSource* ss = sourceObject->source();
469     if (ss->hasIntroductionOffset() &&
470         sourceObject->unwrappedIntroductionScript()) {
471       return Int32Value(ss->introductionOffset());
472     }
473     return UndefinedValue();
474   }
matchDebuggerGetIntroductionOffsetMatcher475   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
476     return UndefinedValue();
477   }
478 };
479 
getIntroductionOffset()480 bool DebuggerSource::CallData::getIntroductionOffset() {
481   DebuggerGetIntroductionOffsetMatcher matcher;
482   args.rval().set(referent.match(matcher));
483   return true;
484 }
485 
486 struct DebuggerSourceGetIntroductionTypeMatcher {
487   using ReturnType = const char*;
matchDebuggerSourceGetIntroductionTypeMatcher488   ReturnType match(HandleScriptSourceObject sourceObject) {
489     ScriptSource* ss = sourceObject->source();
490     MOZ_ASSERT(ss);
491     return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
492   }
matchDebuggerSourceGetIntroductionTypeMatcher493   ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return "wasm"; }
494 };
495 
getIntroductionType()496 bool DebuggerSource::CallData::getIntroductionType() {
497   DebuggerSourceGetIntroductionTypeMatcher matcher;
498   if (const char* introductionType = referent.match(matcher)) {
499     JSString* str = NewStringCopyZ<CanGC>(cx, introductionType);
500     if (!str) {
501       return false;
502     }
503     args.rval().setString(str);
504   } else {
505     args.rval().setUndefined();
506   }
507 
508   return true;
509 }
510 
EnsureSourceObject(JSContext * cx,HandleDebuggerSource obj)511 ScriptSourceObject* EnsureSourceObject(JSContext* cx,
512                                        HandleDebuggerSource obj) {
513   if (!obj->getReferent().is<ScriptSourceObject*>()) {
514     RootedValue v(cx, ObjectValue(*obj));
515     ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK, v,
516                      nullptr, "a JS source");
517     return nullptr;
518   }
519   return obj->getReferent().as<ScriptSourceObject*>();
520 }
521 
setSourceMapURL()522 bool DebuggerSource::CallData::setSourceMapURL() {
523   RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
524   if (!sourceObject) {
525     return false;
526   }
527   ScriptSource* ss = sourceObject->source();
528   MOZ_ASSERT(ss);
529 
530   if (!args.requireAtLeast(cx, "set sourceMapURL", 1)) {
531     return false;
532   }
533 
534   JSString* str = ToString<CanGC>(cx, args[0]);
535   if (!str) {
536     return false;
537   }
538 
539   UniqueTwoByteChars chars = JS_CopyStringCharsZ(cx, str);
540   if (!chars) {
541     return false;
542   }
543 
544   if (!ss->setSourceMapURL(cx, std::move(chars))) {
545     return false;
546   }
547 
548   args.rval().setUndefined();
549   return true;
550 }
551 
552 class DebuggerSourceGetSourceMapURLMatcher {
553   JSContext* cx_;
554   MutableHandleString result_;
555 
556  public:
DebuggerSourceGetSourceMapURLMatcher(JSContext * cx,MutableHandleString result)557   explicit DebuggerSourceGetSourceMapURLMatcher(JSContext* cx,
558                                                 MutableHandleString result)
559       : cx_(cx), result_(result) {}
560 
561   using ReturnType = bool;
match(HandleScriptSourceObject sourceObject)562   ReturnType match(HandleScriptSourceObject sourceObject) {
563     ScriptSource* ss = sourceObject->source();
564     MOZ_ASSERT(ss);
565     if (!ss->hasSourceMapURL()) {
566       result_.set(nullptr);
567       return true;
568     }
569     JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
570     if (!str) {
571       return false;
572     }
573     result_.set(str);
574     return true;
575   }
match(Handle<WasmInstanceObject * > instanceObj)576   ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
577     wasm::Instance& instance = instanceObj->instance();
578     if (!instance.debugEnabled()) {
579       result_.set(nullptr);
580       return true;
581     }
582 
583     RootedString str(cx_);
584     if (!instance.debug().getSourceMappingURL(cx_, &str)) {
585       return false;
586     }
587 
588     result_.set(str);
589     return true;
590   }
591 };
592 
getSourceMapURL()593 bool DebuggerSource::CallData::getSourceMapURL() {
594   RootedString result(cx);
595   DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
596   if (!referent.match(matcher)) {
597     return false;
598   }
599   if (result) {
600     args.rval().setString(result);
601   } else {
602     args.rval().setNull();
603   }
604   return true;
605 }
606 
607 template <typename Unit>
ReparseSource(JSContext * cx,HandleScriptSourceObject sso)608 static JSScript* ReparseSource(JSContext* cx, HandleScriptSourceObject sso) {
609   AutoRealm ar(cx, sso);
610   ScriptSource* ss = sso->source();
611 
612   JS::CompileOptions options(cx);
613   options.hideScriptFromDebugger = true;
614   options.setFileAndLine(ss->filename(), ss->startLine());
615 
616   UncompressedSourceCache::AutoHoldEntry holder;
617 
618   ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, 0, ss->length());
619   if (!units.get()) {
620     return nullptr;
621   }
622 
623   JS::SourceText<Unit> srcBuf;
624   if (!srcBuf.init(cx, units.get(), ss->length(),
625                    JS::SourceOwnership::Borrowed)) {
626     return nullptr;
627   }
628 
629   return JS::Compile(cx, options, srcBuf);
630 }
631 
reparse()632 bool DebuggerSource::CallData::reparse() {
633   RootedScriptSourceObject sourceObject(cx, EnsureSourceObject(cx, obj));
634   if (!sourceObject) {
635     return false;
636   }
637 
638   if (!sourceObject->source()->hasSourceText()) {
639     JS_ReportErrorASCII(cx, "Source object missing text");
640     return false;
641   }
642 
643   RootedScript script(cx);
644   if (sourceObject->source()->hasSourceType<mozilla::Utf8Unit>()) {
645     script = ReparseSource<mozilla::Utf8Unit>(cx, sourceObject);
646   } else {
647     script = ReparseSource<char16_t>(cx, sourceObject);
648   }
649 
650   if (!script) {
651     return false;
652   }
653 
654   Debugger* dbg = Debugger::fromChildJSObject(obj);
655   RootedObject scriptDO(cx, dbg->wrapScript(cx, script));
656   if (!scriptDO) {
657     return false;
658   }
659 
660   args.rval().setObject(*scriptDO);
661   return true;
662 }
663 
664 const JSPropertySpec DebuggerSource::properties_[] = {
665     JS_DEBUG_PSG("text", getText),
666     JS_DEBUG_PSG("binary", getBinary),
667     JS_DEBUG_PSG("url", getURL),
668     JS_DEBUG_PSG("startLine", getStartLine),
669     JS_DEBUG_PSG("id", getId),
670     JS_DEBUG_PSG("element", getElement),
671     JS_DEBUG_PSG("displayURL", getDisplayURL),
672     JS_DEBUG_PSG("introductionScript", getIntroductionScript),
673     JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),
674     JS_DEBUG_PSG("introductionType", getIntroductionType),
675     JS_DEBUG_PSG("elementAttributeName", getElementProperty),
676     JS_DEBUG_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL),
677     JS_PS_END};
678 
679 const JSFunctionSpec DebuggerSource::methods_[] = {
680     JS_DEBUG_FN("reparse", reparse, 0), JS_FS_END};
681