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