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