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 };