tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Source.cpp (21108B)


      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 "debugger/Debugger.h"         // for DebuggerSourceReferent, Debugger
     18 #include "debugger/Script.h"           // for DebuggerScript
     19 #include "frontend/FrontendContext.h"  // for AutoReportFrontendContext
     20 #include "gc/Tracer.h"        // for TraceManuallyBarrieredCrossCompartmentEdge
     21 #include "js/ColumnNumber.h"  // JS::WasmFunctionIndex, JS::ColumnNumberOneOrigin
     22 #include "js/CompilationAndEvaluation.h"  // for Compile
     23 #include "js/ErrorReport.h"  // for JS_ReportErrorASCII,  JS_ReportErrorNumberASCII
     24 #include "js/experimental/TypedData.h"  // for JS_NewUint8Array
     25 #include "js/friend/ErrorMessages.h"    // for GetErrorMessage, JSMSG_*
     26 #include "js/GCVariant.h"               // for GCVariant
     27 #include "js/SourceText.h"              // for JS::SourceOwnership
     28 #include "js/String.h"                  // for JS_CopyStringCharsZ
     29 #include "vm/BytecodeUtil.h"            // for JSDVG_SEARCH_STACK
     30 #include "vm/JSContext.h"               // for JSContext (ptr only)
     31 #include "vm/JSObject.h"                // for JSObject, RequireObject
     32 #include "vm/JSScript.h"                // for ScriptSource, ScriptSourceObject
     33 #include "vm/StringType.h"        // for NewStringCopyZ, JSString (ptr only)
     34 #include "vm/TypedArrayObject.h"  // for TypedArrayObject, JSObject::is
     35 #include "wasm/WasmCode.h"        // for Metadata
     36 #include "wasm/WasmDebug.h"       // for DebugState
     37 #include "wasm/WasmInstance.h"    // for Instance
     38 #include "wasm/WasmJS.h"          // for WasmInstanceObject
     39 #include "wasm/WasmTypeDecls.h"   // for Bytes, Rooted<WasmInstanceObject*>
     40 
     41 #include "debugger/Debugger-inl.h"  // for Debugger::fromJSObject
     42 #include "vm/JSObject-inl.h"        // for InitClass
     43 #include "vm/NativeObject-inl.h"    // for NewTenuredObjectWithGivenProto
     44 #include "wasm/WasmInstance-inl.h"
     45 
     46 namespace js {
     47 class GlobalObject;
     48 }
     49 
     50 using namespace js;
     51 
     52 using mozilla::AsVariant;
     53 using mozilla::Maybe;
     54 using mozilla::Nothing;
     55 using mozilla::Some;
     56 
     57 const JSClassOps DebuggerSource::classOps_ = {
     58    nullptr,                          // addProperty
     59    nullptr,                          // delProperty
     60    nullptr,                          // enumerate
     61    nullptr,                          // newEnumerate
     62    nullptr,                          // resolve
     63    nullptr,                          // mayResolve
     64    nullptr,                          // finalize
     65    nullptr,                          // call
     66    nullptr,                          // construct
     67    CallTraceMethod<DebuggerSource>,  // trace
     68 };
     69 
     70 const JSClass DebuggerSource::class_ = {
     71    "Source",
     72    JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS),
     73    &classOps_,
     74 };
     75 
     76 /* static */
     77 NativeObject* DebuggerSource::initClass(JSContext* cx,
     78                                        Handle<GlobalObject*> global,
     79                                        HandleObject debugCtor) {
     80  return InitClass(cx, debugCtor, nullptr, nullptr, "Source", construct, 0,
     81                   properties_, methods_, nullptr, nullptr);
     82 }
     83 
     84 /* static */
     85 DebuggerSource* DebuggerSource::create(JSContext* cx, HandleObject proto,
     86                                       Handle<DebuggerSourceReferent> referent,
     87                                       Handle<NativeObject*> debugger) {
     88  Rooted<DebuggerSource*> sourceObj(
     89      cx, NewTenuredObjectWithGivenProto<DebuggerSource>(cx, proto));
     90  if (!sourceObj) {
     91    return nullptr;
     92  }
     93  sourceObj->setReservedSlot(OWNER_SLOT, ObjectValue(*debugger));
     94  referent.get().match([&](auto sourceHandle) {
     95    sourceObj->setReservedSlotGCThingAsPrivate(SOURCE_SLOT, sourceHandle);
     96  });
     97 
     98  return sourceObj;
     99 }
    100 
    101 Debugger* DebuggerSource::owner() const {
    102  JSObject* dbgobj = &getReservedSlot(OWNER_SLOT).toObject();
    103  return Debugger::fromJSObject(dbgobj);
    104 }
    105 
    106 // For internal use only.
    107 NativeObject* DebuggerSource::getReferentRawObject() const {
    108  return maybePtrFromReservedSlot<NativeObject>(SOURCE_SLOT);
    109 }
    110 
    111 DebuggerSourceReferent DebuggerSource::getReferent() const {
    112  if (NativeObject* referent = getReferentRawObject()) {
    113    if (referent->is<ScriptSourceObject>()) {
    114      return AsVariant(&referent->as<ScriptSourceObject>());
    115    }
    116    return AsVariant(&referent->as<WasmInstanceObject>());
    117  }
    118  return AsVariant(static_cast<ScriptSourceObject*>(nullptr));
    119 }
    120 
    121 void DebuggerSource::trace(JSTracer* trc) {
    122  // There is a barrier on private pointers, so the Unbarriered marking
    123  // is okay.
    124  if (JSObject* referent = getReferentRawObject()) {
    125    TraceManuallyBarrieredCrossCompartmentEdge(trc, this, &referent,
    126                                               "Debugger.Source referent");
    127    if (referent != getReferentRawObject()) {
    128      setReservedSlotGCThingAsPrivateUnbarriered(SOURCE_SLOT, referent);
    129    }
    130  }
    131 }
    132 
    133 /* static */
    134 bool DebuggerSource::construct(JSContext* cx, unsigned argc, Value* vp) {
    135  JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
    136                            "Debugger.Source");
    137  return false;
    138 }
    139 
    140 /* static */
    141 DebuggerSource* DebuggerSource::check(JSContext* cx, HandleValue thisv) {
    142  JSObject* thisobj = RequireObject(cx, thisv);
    143  if (!thisobj) {
    144    return nullptr;
    145  }
    146  if (!thisobj->is<DebuggerSource>()) {
    147    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    148                              JSMSG_INCOMPATIBLE_PROTO, "Debugger.Source",
    149                              "method", thisobj->getClass()->name);
    150    return nullptr;
    151  }
    152 
    153  return &thisobj->as<DebuggerSource>();
    154 }
    155 
    156 struct MOZ_STACK_CLASS DebuggerSource::CallData {
    157  JSContext* cx;
    158  const CallArgs& args;
    159 
    160  Handle<DebuggerSource*> obj;
    161  Rooted<DebuggerSourceReferent> referent;
    162 
    163  CallData(JSContext* cx, const CallArgs& args, Handle<DebuggerSource*> obj)
    164      : cx(cx), args(args), obj(obj), referent(cx, obj->getReferent()) {}
    165 
    166  bool getText();
    167  bool getBinary();
    168  bool getURL();
    169  bool getStartLine();
    170  bool getStartColumn();
    171  bool getId();
    172  bool getDisplayURL();
    173  bool getElementProperty();
    174  bool getIntroductionScript();
    175  bool getIntroductionOffset();
    176  bool getIntroductionType();
    177  bool setSourceMapURL();
    178  bool getSourceMapURL();
    179  bool reparse();
    180 
    181  using Method = bool (CallData::*)();
    182 
    183  template <Method MyMethod>
    184  static bool ToNative(JSContext* cx, unsigned argc, Value* vp);
    185 };
    186 
    187 template <DebuggerSource::CallData::Method MyMethod>
    188 /* static */
    189 bool DebuggerSource::CallData::ToNative(JSContext* cx, unsigned argc,
    190                                        Value* vp) {
    191  CallArgs args = CallArgsFromVp(argc, vp);
    192 
    193  Rooted<DebuggerSource*> obj(cx, DebuggerSource::check(cx, args.thisv()));
    194  if (!obj) {
    195    return false;
    196  }
    197 
    198  CallData data(cx, args, obj);
    199  return (data.*MyMethod)();
    200 }
    201 
    202 class DebuggerSourceGetTextMatcher {
    203  JSContext* cx_;
    204 
    205 public:
    206  explicit DebuggerSourceGetTextMatcher(JSContext* cx) : cx_(cx) {}
    207 
    208  using ReturnType = JSString*;
    209 
    210  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    211    ScriptSource* ss = sourceObject->source();
    212    bool hasSourceText;
    213    if (!ScriptSource::loadSource(cx_, ss, &hasSourceText)) {
    214      return nullptr;
    215    }
    216    if (!hasSourceText) {
    217      return NewStringCopyZ<CanGC>(cx_, "[no source]");
    218    }
    219 
    220    if (ss->shouldUnwrapEventHandlerBody()) {
    221      return ss->functionBodyString(cx_);
    222    }
    223 
    224    return ss->substring(cx_, 0, ss->length());
    225  }
    226 
    227  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    228    wasm::Instance& instance = instanceObj->instance();
    229    const char* msg;
    230    if (!instance.debugEnabled()) {
    231      msg = "Restart with developer tools open to view WebAssembly source.";
    232    } else {
    233      msg = "[debugger missing wasm binary-to-text conversion]";
    234    }
    235    return NewStringCopyZ<CanGC>(cx_, msg);
    236  }
    237 };
    238 
    239 bool DebuggerSource::CallData::getText() {
    240  Value textv = obj->getReservedSlot(TEXT_SLOT);
    241  if (!textv.isUndefined()) {
    242    MOZ_ASSERT(textv.isString());
    243    args.rval().set(textv);
    244    return true;
    245  }
    246 
    247  DebuggerSourceGetTextMatcher matcher(cx);
    248  JSString* str = referent.match(matcher);
    249  if (!str) {
    250    return false;
    251  }
    252 
    253  args.rval().setString(str);
    254  obj->setReservedSlot(TEXT_SLOT, args.rval());
    255  return true;
    256 }
    257 
    258 bool DebuggerSource::CallData::getBinary() {
    259  if (!referent.is<WasmInstanceObject*>()) {
    260    ReportValueError(cx, JSMSG_DEBUG_BAD_REFERENT, JSDVG_SEARCH_STACK,
    261                     args.thisv(), nullptr, "a wasm source");
    262    return false;
    263  }
    264 
    265  Rooted<WasmInstanceObject*> instanceObj(cx,
    266                                          referent.as<WasmInstanceObject*>());
    267  wasm::Instance& instance = instanceObj->instance();
    268 
    269  if (!instance.debugEnabled()) {
    270    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    271                              JSMSG_DEBUG_NO_BINARY_SOURCE);
    272    return false;
    273  }
    274 
    275  const wasm::BytecodeSource& bytecode = instance.debug().bytecode();
    276  RootedObject arr(cx, JS_NewUint8Array(cx, bytecode.length()));
    277  if (!arr) {
    278    return false;
    279  }
    280 
    281  bytecode.copyTo((uint8_t*)arr->as<TypedArrayObject>().dataPointerUnshared());
    282 
    283  args.rval().setObject(*arr);
    284  return true;
    285 }
    286 
    287 class DebuggerSourceGetURLMatcher {
    288  JSContext* cx_;
    289 
    290 public:
    291  explicit DebuggerSourceGetURLMatcher(JSContext* cx) : cx_(cx) {}
    292 
    293  using ReturnType = Maybe<JSString*>;
    294 
    295  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    296    ScriptSource* ss = sourceObject->source();
    297    MOZ_ASSERT(ss);
    298    if (const char* filename = ss->filename()) {
    299      JS::UTF8Chars utf8chars(filename, strlen(filename));
    300      JSString* str = NewStringCopyUTF8N(cx_, utf8chars);
    301      return Some(str);
    302    }
    303    return Nothing();
    304  }
    305  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    306    return Some(instanceObj->instance().createDisplayURL(cx_));
    307  }
    308 };
    309 
    310 bool DebuggerSource::CallData::getURL() {
    311  DebuggerSourceGetURLMatcher matcher(cx);
    312  Maybe<JSString*> str = referent.match(matcher);
    313  if (str.isSome()) {
    314    if (!*str) {
    315      return false;
    316    }
    317    args.rval().setString(*str);
    318  } else {
    319    args.rval().setNull();
    320  }
    321  return true;
    322 }
    323 
    324 class DebuggerSourceGetStartLineMatcher {
    325 public:
    326  using ReturnType = uint32_t;
    327 
    328  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    329    ScriptSource* ss = sourceObject->source();
    330    return ss->startLine();
    331  }
    332  ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
    333 };
    334 
    335 bool DebuggerSource::CallData::getStartLine() {
    336  DebuggerSourceGetStartLineMatcher matcher;
    337  uint32_t line = referent.match(matcher);
    338  args.rval().setNumber(line);
    339  return true;
    340 }
    341 
    342 class DebuggerSourceGetStartColumnMatcher {
    343 public:
    344  using ReturnType = JS::LimitedColumnNumberOneOrigin;
    345 
    346  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    347    ScriptSource* ss = sourceObject->source();
    348    return ss->startColumn();
    349  }
    350  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    351    return JS::LimitedColumnNumberOneOrigin(
    352        JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin);
    353  }
    354 };
    355 
    356 bool DebuggerSource::CallData::getStartColumn() {
    357  DebuggerSourceGetStartColumnMatcher matcher;
    358  JS::LimitedColumnNumberOneOrigin column = referent.match(matcher);
    359  args.rval().setNumber(column.oneOriginValue());
    360  return true;
    361 }
    362 
    363 class DebuggerSourceGetIdMatcher {
    364 public:
    365  using ReturnType = uint32_t;
    366 
    367  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    368    ScriptSource* ss = sourceObject->source();
    369    return ss->id();
    370  }
    371  ReturnType match(Handle<WasmInstanceObject*> instanceObj) { return 0; }
    372 };
    373 
    374 bool DebuggerSource::CallData::getId() {
    375  DebuggerSourceGetIdMatcher matcher;
    376  uint32_t id = referent.match(matcher);
    377  args.rval().setNumber(id);
    378  return true;
    379 }
    380 
    381 struct DebuggerSourceGetDisplayURLMatcher {
    382  using ReturnType = const char16_t*;
    383  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    384    ScriptSource* ss = sourceObject->source();
    385    MOZ_ASSERT(ss);
    386    return ss->hasDisplayURL() ? ss->displayURL() : nullptr;
    387  }
    388  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
    389    return wasmInstance->instance().codeMetaForAsmJS()
    390               ? wasmInstance->instance()
    391                     .codeMetaForAsmJS()
    392                     ->displayURL()  // asm.js
    393               : nullptr;            // wasm
    394  }
    395 };
    396 
    397 bool DebuggerSource::CallData::getDisplayURL() {
    398  DebuggerSourceGetDisplayURLMatcher matcher;
    399  if (const char16_t* displayURL = referent.match(matcher)) {
    400    JSString* str = JS_NewUCStringCopyZ(cx, displayURL);
    401    if (!str) {
    402      return false;
    403    }
    404    args.rval().setString(str);
    405  } else {
    406    args.rval().setNull();
    407  }
    408  return true;
    409 }
    410 
    411 struct DebuggerSourceGetElementPropertyMatcher {
    412  using ReturnType = Value;
    413  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    414    return sourceObject->unwrappedElementAttributeName();
    415  }
    416  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
    417    return UndefinedValue();
    418  }
    419 };
    420 
    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:
    433  DebuggerSourceGetIntroductionScriptMatcher(JSContext* cx, Debugger* dbg,
    434                                             MutableHandleValue rval)
    435      : cx_(cx), dbg_(dbg), rval_(rval) {}
    436 
    437  using ReturnType = bool;
    438 
    439  ReturnType match(Handle<ScriptSourceObject*> 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 
    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 
    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;
    472  ReturnType match(Handle<ScriptSourceObject*> 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  }
    483  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) {
    484    return UndefinedValue();
    485  }
    486 };
    487 
    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*;
    496  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    497    ScriptSource* ss = sourceObject->source();
    498    MOZ_ASSERT(ss);
    499    return ss->hasIntroductionType() ? ss->introductionType() : nullptr;
    500  }
    501  ReturnType match(Handle<WasmInstanceObject*> wasmInstance) { return "wasm"; }
    502 };
    503 
    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 
    519 ScriptSourceObject* EnsureSourceObject(JSContext* cx,
    520                                       Handle<DebuggerSource*> 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 
    530 bool DebuggerSource::CallData::setSourceMapURL() {
    531  Rooted<ScriptSourceObject*> 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  AutoReportFrontendContext fc(cx);
    553  if (!ss->setSourceMapURL(&fc, std::move(chars))) {
    554    return false;
    555  }
    556 
    557  args.rval().setUndefined();
    558  return true;
    559 }
    560 
    561 class DebuggerSourceGetSourceMapURLMatcher {
    562  JSContext* cx_;
    563  MutableHandleString result_;
    564 
    565 public:
    566  explicit DebuggerSourceGetSourceMapURLMatcher(JSContext* cx,
    567                                                MutableHandleString result)
    568      : cx_(cx), result_(result) {}
    569 
    570  using ReturnType = bool;
    571  ReturnType match(Handle<ScriptSourceObject*> sourceObject) {
    572    ScriptSource* ss = sourceObject->source();
    573    MOZ_ASSERT(ss);
    574    if (!ss->hasSourceMapURL()) {
    575      result_.set(nullptr);
    576      return true;
    577    }
    578    JSString* str = JS_NewUCStringCopyZ(cx_, ss->sourceMapURL());
    579    if (!str) {
    580      return false;
    581    }
    582    result_.set(str);
    583    return true;
    584  }
    585  ReturnType match(Handle<WasmInstanceObject*> instanceObj) {
    586    wasm::Instance& instance = instanceObj->instance();
    587    if (!instance.debugEnabled()) {
    588      result_.set(nullptr);
    589      return true;
    590    }
    591 
    592    RootedString str(cx_);
    593    if (!instance.debug().getSourceMappingURL(cx_, &str)) {
    594      return false;
    595    }
    596 
    597    result_.set(str);
    598    return true;
    599  }
    600 };
    601 
    602 bool DebuggerSource::CallData::getSourceMapURL() {
    603  RootedString result(cx);
    604  DebuggerSourceGetSourceMapURLMatcher matcher(cx, &result);
    605  if (!referent.match(matcher)) {
    606    return false;
    607  }
    608  if (result) {
    609    args.rval().setString(result);
    610  } else {
    611    args.rval().setNull();
    612  }
    613  return true;
    614 }
    615 
    616 template <typename Unit>
    617 static JSScript* ReparseSource(JSContext* cx, Handle<ScriptSourceObject*> sso) {
    618  AutoRealm ar(cx, sso);
    619  ScriptSource* ss = sso->source();
    620 
    621  JS::CompileOptions options(cx);
    622  options.setHideScriptFromDebugger(true);
    623  options.setFileAndLine(ss->filename(), ss->startLine());
    624  options.setColumn(JS::ColumnNumberOneOrigin(ss->startColumn()));
    625 
    626  UncompressedSourceCache::AutoHoldEntry holder;
    627 
    628  ScriptSource::PinnedUnits<Unit> units(cx, ss, holder, 0, ss->length());
    629  if (!units.get()) {
    630    return nullptr;
    631  }
    632 
    633  JS::SourceText<Unit> srcBuf;
    634  if (!srcBuf.init(cx, units.get(), ss->length(),
    635                   JS::SourceOwnership::Borrowed)) {
    636    return nullptr;
    637  }
    638 
    639  return JS::Compile(cx, options, srcBuf);
    640 }
    641 
    642 bool DebuggerSource::CallData::reparse() {
    643  Rooted<ScriptSourceObject*> sourceObject(cx, EnsureSourceObject(cx, obj));
    644  if (!sourceObject) {
    645    return false;
    646  }
    647 
    648  if (!sourceObject->source()->hasSourceText()) {
    649    JS_ReportErrorASCII(cx, "Source object missing text");
    650    return false;
    651  }
    652 
    653  RootedScript script(cx);
    654  if (sourceObject->source()->hasSourceType<mozilla::Utf8Unit>()) {
    655    script = ReparseSource<mozilla::Utf8Unit>(cx, sourceObject);
    656  } else {
    657    script = ReparseSource<char16_t>(cx, sourceObject);
    658  }
    659 
    660  if (!script) {
    661    return false;
    662  }
    663 
    664  Debugger* dbg = obj->owner();
    665  RootedObject scriptDO(cx, dbg->wrapScript(cx, script));
    666  if (!scriptDO) {
    667    return false;
    668  }
    669 
    670  args.rval().setObject(*scriptDO);
    671  return true;
    672 }
    673 
    674 const JSPropertySpec DebuggerSource::properties_[] = {
    675    JS_DEBUG_PSG("text", getText),
    676    JS_DEBUG_PSG("binary", getBinary),
    677    JS_DEBUG_PSG("url", getURL),
    678    JS_DEBUG_PSG("startLine", getStartLine),
    679    JS_DEBUG_PSG("startColumn", getStartColumn),
    680    JS_DEBUG_PSG("id", getId),
    681    JS_DEBUG_PSG("displayURL", getDisplayURL),
    682    JS_DEBUG_PSG("introductionScript", getIntroductionScript),
    683    JS_DEBUG_PSG("introductionOffset", getIntroductionOffset),
    684    JS_DEBUG_PSG("introductionType", getIntroductionType),
    685    JS_DEBUG_PSG("elementAttributeName", getElementProperty),
    686    JS_DEBUG_PSGS("sourceMapURL", getSourceMapURL, setSourceMapURL),
    687    JS_PS_END,
    688 };
    689 
    690 const JSFunctionSpec DebuggerSource::methods_[] = {
    691    JS_DEBUG_FN("reparse", reparse, 0),
    692    JS_FS_END,
    693 };