tor-browser

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

ShadowRealm.cpp (24860B)


      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 "builtin/ShadowRealm.h"
      8 
      9 #include "mozilla/Assertions.h"
     10 
     11 #include "jsapi.h"
     12 #include "jsfriendapi.h"
     13 #include "builtin/ModuleObject.h"
     14 #include "builtin/Promise.h"
     15 #include "builtin/WrappedFunctionObject.h"
     16 #include "frontend/BytecodeCompiler.h"  // CompileEvalScript
     17 #include "js/ErrorReport.h"
     18 #include "js/Exception.h"
     19 #include "js/GlobalObject.h"
     20 #include "js/Principals.h"
     21 #include "js/Promise.h"
     22 #include "js/PropertyAndElement.h"
     23 #include "js/PropertyDescriptor.h"
     24 #include "js/ShadowRealmCallbacks.h"
     25 #include "js/SourceText.h"
     26 #include "js/StableStringChars.h"
     27 #include "js/StructuredClone.h"
     28 #include "js/TypeDecls.h"
     29 #include "js/Wrapper.h"
     30 #include "vm/GlobalObject.h"
     31 #include "vm/Interpreter.h"
     32 #include "vm/JSObject.h"
     33 #include "vm/Modules.h"
     34 #include "vm/ObjectOperations.h"
     35 
     36 #include "builtin/HandlerFunction-inl.h"
     37 #include "vm/Compartment-inl.h"
     38 #include "vm/JSObject-inl.h"
     39 #include "vm/Realm-inl.h"
     40 
     41 using namespace js;
     42 
     43 using JS::AutoStableStringChars;
     44 using JS::CompileOptions;
     45 using JS::SourceText;
     46 
     47 static JSObject* DefaultNewShadowRealmGlobal(JSContext* cx,
     48                                             JS::RealmOptions& options,
     49                                             JSPrincipals* principals,
     50                                             Handle<JSObject*> unused) {
     51  static const JSClass shadowRealmGlobal = {
     52      "ShadowRealmGlobal",
     53      JSCLASS_GLOBAL_FLAGS,
     54      &JS::DefaultGlobalClassOps,
     55  };
     56 
     57  return JS_NewGlobalObject(cx, &shadowRealmGlobal, principals,
     58                            JS::FireOnNewGlobalHook, options);
     59 }
     60 
     61 // https://tc39.es/proposal-shadowrealm/#sec-shadowrealm-constructor
     62 /*static*/
     63 bool ShadowRealmObject::construct(JSContext* cx, unsigned argc, Value* vp) {
     64  CallArgs args = CallArgsFromVp(argc, vp);
     65 
     66  // Step 1. If NewTarget is undefined, throw a TypeError exception.
     67  if (!args.isConstructing()) {
     68    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
     69                              JSMSG_NOT_CONSTRUCTOR, "ShadowRealm");
     70    return false;
     71  }
     72 
     73  // Step 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget,
     74  // "%ShadowRealm.prototype%", « [[ShadowRealm]], [[ExecutionContext]] »).
     75  Rooted<JSObject*> proto(cx);
     76  if (!GetPrototypeFromBuiltinConstructor(cx, args, JSProto_ShadowRealm,
     77                                          &proto)) {
     78    return false;
     79  }
     80 
     81  Rooted<ShadowRealmObject*> shadowRealmObj(
     82      cx, NewObjectWithClassProto<ShadowRealmObject>(cx, proto));
     83  if (!shadowRealmObj) {
     84    return false;
     85  }
     86 
     87  // Instead of managing Realms, spidermonkey associates a realm with a global
     88  // object, and so we will manage and store a global.
     89 
     90  // Step 3. Let realmRec be CreateRealm().
     91 
     92  // Initially steal creation options from current realm:
     93  JS::RealmOptions options(cx->realm()->creationOptions(),
     94                           cx->realm()->behaviors());
     95 
     96  // We don't want to have to deal with CCWs in addition to
     97  // WrappedFunctionObjects.
     98  options.creationOptions().setExistingCompartment(cx->compartment());
     99 
    100  JS::GlobalCreationCallback newGlobal =
    101      cx->runtime()->getShadowRealmGlobalCreationCallback();
    102  // If an embedding didn't provide a callback to initialize the global,
    103  // use the basic default one.
    104  if (!newGlobal) {
    105    newGlobal = DefaultNewShadowRealmGlobal;
    106  }
    107 
    108  // Our shadow realm inherits the principals of the current realm,
    109  // but is otherwise constrained.
    110  JSPrincipals* principals = JS::GetRealmPrincipals(cx->realm());
    111 
    112  // Steps 5-11: In SpiderMonkey these fall under the aegis of the global
    113  //             creation. It's worth noting that the newGlobal callback
    114  //             needs to respect the SetRealmGlobalObject call below, which
    115  //             sets the global to
    116  //             OrdinaryObjectCreate(intrinsics.[[%Object.prototype%]]).
    117  //
    118  // Step 5. Let context be a new execution context.
    119  // Step 6. Set the Function of context to null.
    120  // Step 7. Set the Realm of context to realmRec.
    121  // Step 8. Set the ScriptOrModule of context to null.
    122  // Step 9. Set O.[[ExecutionContext]] to context.
    123  // Step 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined).
    124  // Step 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]).
    125  Rooted<JSObject*> global(cx,
    126                           newGlobal(cx, options, principals, cx->global()));
    127  if (!global) {
    128    return false;
    129  }
    130 
    131  // Make sure the new global hook obeyed our request in the
    132  // creation options to have a same compartment global.
    133  MOZ_RELEASE_ASSERT(global->compartment() == cx->compartment());
    134 
    135  // Step 4. Set O.[[ShadowRealm]] to realmRec.
    136  shadowRealmObj->initFixedSlot(GlobalSlot, ObjectValue(*global));
    137 
    138  // Step 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]).
    139  JS::GlobalInitializeCallback hostInitializeShadowRealm =
    140      cx->runtime()->getShadowRealmInitializeGlobalCallback();
    141  if (hostInitializeShadowRealm) {
    142    if (!hostInitializeShadowRealm(cx, global)) {
    143      return false;
    144    }
    145  }
    146 
    147  // Step 13. Return O.
    148  args.rval().setObject(*shadowRealmObj);
    149  return true;
    150 }
    151 
    152 // https://tc39.es/proposal-shadowrealm/#sec-validateshadowrealmobject
    153 // (slightly modified into a cast operator too)
    154 static ShadowRealmObject* ValidateShadowRealmObject(JSContext* cx,
    155                                                    Handle<Value> value) {
    156  // Step 1. Perform ? RequireInternalSlot(O, [[ShadowRealm]]).
    157  // Step 2. Perform ? RequireInternalSlot(O, [[ExecutionContext]]).
    158  return UnwrapAndTypeCheckValue<ShadowRealmObject>(cx, value, [cx]() {
    159    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    160                              JSMSG_NOT_SHADOW_REALM);
    161  });
    162 }
    163 
    164 void js::ReportPotentiallyDetailedMessage(JSContext* cx,
    165                                          const unsigned detailedError,
    166                                          const unsigned genericError) {
    167  // Return for non-catchable exceptions like interrupt requests.
    168  if (!cx->isExceptionPending()) {
    169    return;
    170  }
    171 
    172  Rooted<Value> exception(cx);
    173  if (!cx->getPendingException(&exception)) {
    174    return;
    175  }
    176  cx->clearPendingException();
    177 
    178  JS::ErrorReportBuilder jsReport(cx);
    179  JS::ExceptionStack exnStack(cx, exception, nullptr);
    180  if (!jsReport.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) {
    181    cx->clearPendingException();
    182    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, genericError);
    183    return;
    184  }
    185 
    186  JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, detailedError,
    187                           jsReport.toStringResult().c_str());
    188 }
    189 
    190 //  PerformShadowRealmEval ( sourceText: a String, callerRealm: a Realm Record,
    191 //                          evalRealm: a Realm Record, )
    192 //
    193 // https://tc39.es/proposal-shadowrealm/#sec-performshadowrealmeval
    194 static bool PerformShadowRealmEval(JSContext* cx, Handle<JSString*> sourceText,
    195                                   Realm* callerRealm, Realm* evalRealm,
    196                                   MutableHandle<Value> rval) {
    197  MOZ_ASSERT(callerRealm != evalRealm);
    198 
    199  // Step 1. Perform ? HostEnsureCanCompileStrings(callerRealm, evalRealm).
    200  JS::RootedVector<JSString*> parameterStrings(cx);
    201  JS::RootedVector<Value> parameterArgs(cx);
    202  bool canCompileStrings = false;
    203  if (!cx->isRuntimeCodeGenEnabled(JS::RuntimeCode::JS, sourceText,
    204                                   JS::CompilationType::Undefined,
    205                                   parameterStrings, nullptr, parameterArgs,
    206                                   NullHandleValue, &canCompileStrings)) {
    207    return false;
    208  }
    209  if (!canCompileStrings) {
    210    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    211                              JSMSG_CSP_BLOCKED_SHADOWREALM);
    212    return false;
    213  }
    214 
    215  // Need to compile the script into the realm we will execute into.
    216  //
    217  // We hoist the error handling out however to ensure that errors
    218  // are thrown from the correct realm.
    219  bool compileSuccess = false;
    220  bool evalSuccess = false;
    221 
    222  do {
    223    Rooted<GlobalObject*> evalRealmGlobal(cx, evalRealm->maybeGlobal());
    224    AutoRealm ar(cx, evalRealmGlobal);
    225 
    226    // Step 2. Perform the following substeps in an implementation-defined
    227    // order, possibly interleaving parsing and error detection:
    228    //     a. Let script be ParseText(! StringToCodePoints(sourceText), Script).
    229    //     b. If script is a List of errors, throw a SyntaxError exception.
    230    //     c. If script Contains ScriptBody is false, return undefined.
    231    //     d. Let body be the ScriptBody of script.
    232    //     e. If body Contains NewTarget is true, throw a SyntaxError exception.
    233    //     f. If body Contains SuperProperty is true, throw a SyntaxError
    234    //     exception. g. If body Contains SuperCall is true, throw a SyntaxError
    235    //     exception.
    236 
    237    AutoStableStringChars linearChars(cx);
    238    if (!linearChars.initTwoByte(cx, sourceText)) {
    239      return false;
    240    }
    241    SourceText<char16_t> srcBuf;
    242    if (!srcBuf.initMaybeBorrowed(cx, linearChars)) {
    243      return false;
    244    }
    245 
    246    // Lets propagate some information into the compilation here.
    247    //
    248    // We may need to censor the stacks eventually, see
    249    // https://bugzilla.mozilla.org/show_bug.cgi?id=1770017
    250    RootedScript callerScript(cx);
    251    const char* filename;
    252    uint32_t lineno;
    253    uint32_t pcOffset;
    254    bool mutedErrors;
    255    DescribeScriptedCallerForCompilation(cx, &callerScript, &filename, &lineno,
    256                                         &pcOffset, &mutedErrors);
    257 
    258    CompileOptions options(cx);
    259    options.setIsRunOnce(true)
    260        .setNoScriptRval(false)
    261        .setMutedErrors(mutedErrors)
    262        .setFileAndLine(filename, lineno);
    263 
    264    Rooted<Scope*> enclosing(cx, &evalRealmGlobal->emptyGlobalScope());
    265    RootedScript script(
    266        cx, frontend::CompileEvalScript(cx, options, srcBuf, enclosing,
    267                                        evalRealmGlobal));
    268 
    269    compileSuccess = !!script;
    270    if (!compileSuccess) {
    271      break;
    272    }
    273 
    274    // Step 3. Let strictEval be IsStrict of script.
    275    // Step 4. Let runningContext be the running execution context.
    276    // Step 5. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
    277    // Step 6. Let varEnv be evalRealm.[[GlobalEnv]].
    278    // Step 7. If strictEval is true, set varEnv to lexEnv.
    279    // Step 8. If runningContext is not already suspended, suspend
    280    // runningContext. Step 9. Let evalContext be a new ECMAScript code
    281    // execution context. Step 10. Set evalContext's Function to null. Step 11.
    282    // Set evalContext's Realm to evalRealm. Step 12. Set evalContext's
    283    // ScriptOrModule to null. Step 13. Set evalContext's VariableEnvironment to
    284    // varEnv. Step 14. Set evalContext's LexicalEnvironment to lexEnv. Step 15.
    285    // Push evalContext onto the execution context stack; evalContext is
    286    //          now the running execution context.
    287    // Step 16. Let result be  EvalDeclarationInstantiation(body, varEnv,
    288    //            lexEnv,  null, strictEval).
    289    // Step 17. If result.[[Type]] is normal, then
    290    //     a. Set result to the result of evaluating body.
    291    // Step 18. If result.[[Type]] is normal and result.[[Value]] is empty, then
    292    //     a. Set result to NormalCompletion(undefined).
    293 
    294    // Step 19. Suspend evalContext and remove it from the execution context
    295    // stack.
    296    // Step 20. Resume the context that is now on the top of the execution
    297    // context stack as the running execution context.
    298    Rooted<JSObject*> environment(cx, &evalRealmGlobal->lexicalEnvironment());
    299    evalSuccess = ExecuteKernel(cx, script, environment,
    300                                /* evalInFrame = */ NullFramePtr(), rval);
    301  } while (false);  // AutoRealm
    302 
    303  if (!compileSuccess) {
    304    if (!cx->isExceptionPending()) {
    305      return false;
    306    }
    307 
    308    // Clone the exception into the current global and re-throw, as the
    309    // exception has to come from the current global.
    310    Rooted<Value> exception(cx);
    311    if (!cx->getPendingException(&exception)) {
    312      return false;
    313    }
    314 
    315    // Clear our exception now that we've got it, so that we don't
    316    // do the following call with an exception already pending.
    317    cx->clearPendingException();
    318 
    319    Rooted<Value> clonedException(cx);
    320    if (!JS_StructuredClone(cx, exception, &clonedException, nullptr,
    321                            nullptr)) {
    322      return false;
    323    }
    324 
    325    cx->setPendingException(clonedException, ShouldCaptureStack::Always);
    326    return false;
    327  }
    328 
    329  if (!evalSuccess) {
    330    // Step 21. If result.[[Type]]  is not normal, throw a TypeError
    331    // exception.
    332    //
    333    // The type error here needs to come from the calling global, so has to
    334    // happen outside the AutoRealm above.
    335    ReportPotentiallyDetailedMessage(cx,
    336                                     JSMSG_SHADOW_REALM_EVALUATE_FAILURE_DETAIL,
    337                                     JSMSG_SHADOW_REALM_EVALUATE_FAILURE);
    338 
    339    return false;
    340  }
    341 
    342  // Wrap |rval| into the current compartment.
    343  if (!cx->compartment()->wrap(cx, rval)) {
    344    return false;
    345  }
    346 
    347  // Step 22. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
    348  return GetWrappedValue(cx, callerRealm, rval, rval);
    349 }
    350 
    351 // ShadowRealm.prototype.evaluate ( sourceText )
    352 // https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.evaluate
    353 static bool ShadowRealm_evaluate(JSContext* cx, unsigned argc, Value* vp) {
    354  CallArgs args = CallArgsFromVp(argc, vp);
    355 
    356  // Step 1. Let O be this value.
    357  HandleValue obj = args.thisv();
    358 
    359  // Step 2. Perform ? ValidateShadowRealmObject(O)
    360  Rooted<ShadowRealmObject*> shadowRealm(cx,
    361                                         ValidateShadowRealmObject(cx, obj));
    362  if (!shadowRealm) {
    363    return false;
    364  }
    365 
    366  // Step 3. If Type(sourceText) is not String, throw a TypeError exception.
    367  if (!args.get(0).isString()) {
    368    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    369                              JSMSG_SHADOW_REALM_EVALUATE_NOT_STRING);
    370    return false;
    371  }
    372  Rooted<JSString*> sourceText(cx, args.get(0).toString());
    373 
    374  // Step 4. Let callerRealm be the current Realm Record.
    375  Realm* callerRealm = cx->realm();
    376 
    377  // Step 5. Let evalRealm be O.[[ShadowRealm]].
    378  Realm* evalRealm = shadowRealm->getShadowRealm();
    379  // Step 6. Return ? PerformShadowRealmEval(sourceText, callerRealm,
    380  // evalRealm).
    381  return PerformShadowRealmEval(cx, sourceText, callerRealm, evalRealm,
    382                                args.rval());
    383 }
    384 
    385 enum class ImportValueIndices : uint32_t {
    386  CalleRealm = 0,
    387 
    388  ExportNameString,
    389 
    390  Length,
    391 };
    392 
    393 // MG:XXX: Cribbed/Overlapping with StartDynamicModuleImport; may need to
    394 // refactor to share.
    395 // https://tc39.es/proposal-shadowrealm/#sec-shadowrealmimportvalue
    396 static JSObject* ShadowRealmImportValue(JSContext* cx,
    397                                        Handle<JSString*> specifierString,
    398                                        Handle<JSString*> exportName,
    399                                        Realm* callerRealm, Realm* evalRealm) {
    400  // Step 1. Assert: evalContext is an execution context associated to a
    401  // ShadowRealm instance's [[ExecutionContext]].
    402 
    403  // Step 2. Let innerCapability be ! NewPromiseCapability(%Promise%).
    404  Rooted<JSObject*> promiseConstructor(cx, JS::GetPromiseConstructor(cx));
    405  if (!promiseConstructor) {
    406    return nullptr;
    407  }
    408 
    409  Rooted<JSObject*> promiseObject(cx, JS::NewPromiseObject(cx, nullptr));
    410  if (!promiseObject) {
    411    return nullptr;
    412  }
    413 
    414  Handle<PromiseObject*> promise = promiseObject.as<PromiseObject>();
    415 
    416  {
    417    // Step 3. Let runningContext be the running execution context. (Implicit)
    418    // Step 4. If runningContext is not already suspended, suspend
    419    //         runningContext. (Implicit)
    420    // Step 5. Push evalContext onto the execution context stack; evalContext is
    421    //         now the running execution context. (Implicit)
    422    Rooted<GlobalObject*> evalRealmGlobal(cx, evalRealm->maybeGlobal());
    423    AutoRealm ar(cx, evalRealmGlobal);
    424    Rooted<JSAtom*> specifierAtom(cx, AtomizeString(cx, specifierString));
    425    if (!specifierAtom) {
    426      if (!RejectPromiseWithPendingError(cx, promise)) {
    427        return nullptr;
    428      }
    429      return promise;
    430    }
    431 
    432    Rooted<ImportAttributeVector> attributes(cx);
    433    Rooted<JSObject*> moduleRequest(
    434        cx, ModuleRequestObject::create(cx, specifierAtom, attributes));
    435    if (!moduleRequest) {
    436      if (!RejectPromiseWithPendingError(cx, promise)) {
    437        return nullptr;
    438      }
    439      return promise;
    440    }
    441 
    442    // Step 7. Perform ! HostLoadImportedModule(referrer, specifierString,
    443    // EMPTY, innerCapability).
    444    Rooted<Value> payload(cx, ObjectValue(*promise));
    445    if (!js::HostLoadImportedModule(cx, nullptr, moduleRequest,
    446                                    JS::UndefinedHandleValue, payload)) {
    447      if (!RejectPromiseWithPendingError(cx, promise)) {
    448        return nullptr;
    449      }
    450      return promise;
    451    }
    452 
    453    // Step 8. Suspend evalContext and remove it from the execution context
    454    //         stack. (Implicit)
    455    // Step 9. Resume the context that is now on the top of the execution
    456    //         context stack as the running execution context (Implicit)
    457  }
    458 
    459  // Step 10.  Let steps be the steps of an ExportGetter function as described
    460  //          below.
    461  // Step 11. Let onFulfilled be ! CreateBuiltinFunction(steps, 1, "", «
    462  //          [[ExportNameString]] », callerRealm).
    463 
    464  // The handler can only hold onto a single object, so we pack that into a new
    465  // array, and store there.
    466  Rooted<ArrayObject*> handlerObject(
    467      cx,
    468      NewDenseFullyAllocatedArray(cx, uint32_t(ImportValueIndices::Length)));
    469  if (!handlerObject) {
    470    return nullptr;
    471  }
    472 
    473  handlerObject->setDenseInitializedLength(
    474      uint32_t(ImportValueIndices::Length));
    475  handlerObject->initDenseElement(uint32_t(ImportValueIndices::CalleRealm),
    476                                  PrivateValue(callerRealm));
    477  handlerObject->initDenseElement(
    478      uint32_t(ImportValueIndices::ExportNameString), StringValue(exportName));
    479 
    480  Rooted<JSFunction*> onFulfilled(
    481      cx,
    482      NewHandlerWithExtra(
    483          cx,
    484          [](JSContext* cx, unsigned argc, Value* vp) {
    485            // This is the export getter function from
    486            // https://tc39.es/proposal-shadowrealm/#sec-shadowrealmimportvalue
    487            CallArgs args = CallArgsFromVp(argc, vp);
    488            MOZ_ASSERT(args.length() == 1);
    489 
    490            auto* handlerObject = ExtraFromHandler<ArrayObject>(args);
    491 
    492            Rooted<Value> realmValue(
    493                cx, handlerObject->getDenseElement(
    494                        uint32_t(ImportValueIndices::CalleRealm)));
    495            Rooted<Value> exportNameValue(
    496                cx, handlerObject->getDenseElement(
    497                        uint32_t(ImportValueIndices::ExportNameString)));
    498 
    499            // Step 1. Assert: exports is a module namespace exotic object.
    500            Handle<Value> exportsValue = args[0];
    501            MOZ_ASSERT(exportsValue.isObject() &&
    502                       exportsValue.toObject().is<ModuleNamespaceObject>());
    503 
    504            Rooted<ModuleNamespaceObject*> exports(
    505                cx, &exportsValue.toObject().as<ModuleNamespaceObject>());
    506 
    507            // Step 2. Let f be the active function object. (not implemented
    508            // this way)
    509            //
    510            // Step 3. Let string be f.[[ExportNameString]]. Step 4.
    511            // Assert: Type(string) is String.
    512            MOZ_ASSERT(exportNameValue.isString());
    513 
    514            Rooted<JSAtom*> stringAtom(
    515                cx, AtomizeString(cx, exportNameValue.toString()));
    516            if (!stringAtom) {
    517              return false;
    518            }
    519            Rooted<jsid> stringId(cx, AtomToId(stringAtom));
    520 
    521            // Step 5. Let hasOwn be ? HasOwnProperty(exports, string).
    522            bool hasOwn = false;
    523            if (!HasOwnProperty(cx, exports, stringId, &hasOwn)) {
    524              return false;
    525            }
    526 
    527            // Step 6. If hasOwn is false, throw a TypeError exception.
    528            if (!hasOwn) {
    529              JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    530                                        JSMSG_SHADOW_REALM_VALUE_NOT_EXPORTED);
    531              return false;
    532            }
    533 
    534            // Step 7. Let value be ? Get(exports, string).
    535            Rooted<Value> value(cx);
    536            if (!GetProperty(cx, exports, exports, stringId, &value)) {
    537              return false;
    538            }
    539 
    540            // Step 8. Let realm be f.[[Realm]].
    541            Realm* callerRealm = static_cast<Realm*>(realmValue.toPrivate());
    542 
    543            // Step 9. Return ? GetWrappedValue(realm, value).
    544            return GetWrappedValue(cx, callerRealm, value, args.rval());
    545          },
    546          promise, handlerObject));
    547  if (!onFulfilled) {
    548    return nullptr;
    549  }
    550 
    551  Rooted<JSFunction*> onRejected(
    552      cx, NewHandler(
    553              cx,
    554              [](JSContext* cx, unsigned argc, Value* vp) {
    555                JS_ReportErrorNumberASCII(
    556                    cx, GetErrorMessage, nullptr,
    557                    JSMSG_SHADOW_REALM_IMPORTVALUE_FAILED);
    558                return false;
    559              },
    560              promise));
    561  if (!onRejected) {
    562    return nullptr;
    563  }
    564 
    565  // Step 12. Set onFulfilled.[[ExportNameString]] to exportNameString.
    566  // Step 15. Let promiseCapability be ! NewPromiseCapability(%Promise%).
    567  // Step 16. Return ! PerformPromiseThen(innerCapability.[[Promise]],
    568  //           onFulfilled, callerRealm.[[Intrinsics]].[[%ThrowTypeError%]],
    569  //           promiseCapability).
    570  return OriginalPromiseThen(cx, promise, onFulfilled, onRejected);
    571 }
    572 
    573 //  ShadowRealm.prototype.importValue ( specifier, exportName )
    574 // https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype.importvalue
    575 static bool ShadowRealm_importValue(JSContext* cx, unsigned argc, Value* vp) {
    576  CallArgs args = CallArgsFromVp(argc, vp);
    577 
    578  // Step 1. Let O be this value.
    579  HandleValue obj = args.thisv();
    580 
    581  // Step 2. Perform ? ValidateShadowRealmObject(O).
    582  Rooted<ShadowRealmObject*> shadowRealm(cx,
    583                                         ValidateShadowRealmObject(cx, obj));
    584  if (!shadowRealm) {
    585    return false;
    586  }
    587 
    588  // Step 3. Let specifierString be ? ToString(specifier).
    589  Rooted<JSString*> specifierString(cx, ToString<CanGC>(cx, args.get(0)));
    590  if (!specifierString) {
    591    return false;
    592  }
    593 
    594  // Step 4. If Type(exportName) is not String, throw a TypeError exception.
    595  if (!args.get(1).isString()) {
    596    JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
    597                              JSMSG_SHADOW_REALM_EXPORT_NOT_STRING);
    598    return false;
    599  }
    600 
    601  Rooted<JSString*> exportName(cx, args.get(1).toString());
    602  if (!exportName) {
    603    return false;
    604  }
    605 
    606  // Step 5. Let callerRealm be the current Realm Record.
    607  Realm* callerRealm = cx->realm();
    608 
    609  // Step 6. Let evalRealm be O.[[ShadowRealm]].
    610  Realm* evalRealm = shadowRealm->getShadowRealm();
    611 
    612  // Step 7. Let evalContext be O.[[ExecutionContext]]
    613  // (we dont' pass this explicitly, instead using the realm+global to
    614  // represent)
    615 
    616  // Step 8. Return ?
    617  // ShadowRealmImportValue(specifierString, exportName,
    618  //                                         callerRealm, evalRealm,
    619  //                                         evalContext).
    620 
    621  JSObject* res = ShadowRealmImportValue(cx, specifierString, exportName,
    622                                         callerRealm, evalRealm);
    623  if (!res) {
    624    return false;
    625  }
    626 
    627  args.rval().set(ObjectValue(*res));
    628  return true;
    629 }
    630 
    631 static const JSFunctionSpec shadowrealm_methods[] = {
    632    JS_FN("evaluate", ShadowRealm_evaluate, 1, 0),
    633    JS_FN("importValue", ShadowRealm_importValue, 2, 0),
    634    JS_FS_END,
    635 };
    636 
    637 static const JSPropertySpec shadowrealm_properties[] = {
    638    JS_STRING_SYM_PS(toStringTag, "ShadowRealm", JSPROP_READONLY),
    639    JS_PS_END,
    640 };
    641 
    642 static const ClassSpec ShadowRealmObjectClassSpec = {
    643    GenericCreateConstructor<ShadowRealmObject::construct, 0,
    644                             gc::AllocKind::FUNCTION>,
    645    GenericCreatePrototype<ShadowRealmObject>,
    646    nullptr,                 // Static methods
    647    nullptr,                 // Static properties
    648    shadowrealm_methods,     // Methods
    649    shadowrealm_properties,  // Properties
    650 };
    651 
    652 const JSClass ShadowRealmObject::class_ = {
    653    "ShadowRealm",
    654    JSCLASS_HAS_CACHED_PROTO(JSProto_ShadowRealm) |
    655        JSCLASS_HAS_RESERVED_SLOTS(ShadowRealmObject::SlotCount),
    656    JS_NULL_CLASS_OPS,
    657    &ShadowRealmObjectClassSpec,
    658 };
    659 
    660 const JSClass ShadowRealmObject::protoClass_ = {
    661    "ShadowRealm.prototype",
    662    JSCLASS_HAS_CACHED_PROTO(JSProto_ShadowRealm),
    663    JS_NULL_CLASS_OPS,
    664    &ShadowRealmObjectClassSpec,
    665 };