diff --git a/.eslintrc.js b/.eslintrc.js index ea69a2893ff188..167107a0018e08 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -337,6 +337,7 @@ module.exports = { TextEncoderStream: 'readable', TransformStream: 'readable', TransformStreamDefaultController: 'readable', + ShadowRealm: 'readable', SubtleCrypto: 'readable', WritableStream: 'readable', WritableStreamDefaultWriter: 'readable', diff --git a/doc/api/cli.md b/doc/api/cli.md index 3db570deb695bc..ad04067999ebc7 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -1618,6 +1618,7 @@ Node.js options that are allowed are: * `--experimental-modules` * `--experimental-network-imports` * `--experimental-policy` +* `--experimental-shadow-realm` * `--experimental-specifier-resolution` * `--experimental-top-level-await` * `--experimental-vm-modules` diff --git a/node.gyp b/node.gyp index 4e06d00c823290..9cee99f731464e 100644 --- a/node.gyp +++ b/node.gyp @@ -531,6 +531,7 @@ 'src/node_report_module.cc', 'src/node_report_utils.cc', 'src/node_serdes.cc', + 'src/node_shadow_realm.cc', 'src/node_snapshotable.cc', 'src/node_sockaddr.cc', 'src/node_stat_watcher.cc', diff --git a/src/api/environment.cc b/src/api/environment.cc index f3a8f49812d5ce..ccdcdefb20fe84 100644 --- a/src/api/environment.cc +++ b/src/api/environment.cc @@ -5,6 +5,7 @@ #include "node_native_module_env.h" #include "node_options-inl.h" #include "node_platform.h" +#include "node_shadow_realm.h" #include "node_v8_platform-inl.h" #include "node_wasm_web_api.h" #include "uv.h" @@ -261,6 +262,12 @@ void SetIsolateMiscHandlers(v8::Isolate* isolate, const IsolateSettings& s) { isolate->SetWasmStreamingCallback(wasm_web_api::StartStreamingCompilation); } + if (per_process::cli_options->get_per_isolate_options() + ->experimental_shadow_realm) { + isolate->SetHostCreateShadowRealmContextCallback( + shadow_realm::HostCreateShadowRealmContextCallback); + } + if ((s.flags & SHOULD_NOT_SET_PROMISE_REJECTION_CALLBACK) == 0) { auto* promise_reject_cb = s.promise_reject_callback ? s.promise_reject_callback : PromiseRejectCallback; diff --git a/src/node_options.cc b/src/node_options.cc index b2b4c2c18c7f9f..4ef017abebb9be 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -713,6 +713,15 @@ PerIsolateOptionsParser::PerIsolateOptionsParser( Implies("--harmony-top-level-await", "--experimental-top-level-await"); ImpliesNot("--no-harmony-top-level-await", "--experimental-top-level-await"); + AddOption("--experimental-shadow-realm", + "", + &PerIsolateOptions::experimental_shadow_realm, + kAllowedInEnvironment); + AddOption("--harmony-shadow-realm", "", V8Option{}); + Implies("--experimental-shadow-realm", "--harmony-shadow-realm"); + Implies("--harmony-shadow-realm", "--experimental-shadow-realm"); + ImpliesNot("--no-harmony-shadow-realm", "--experimental-shadow-realm"); + Insert(eop, &PerIsolateOptions::get_per_env_options); } diff --git a/src/node_options.h b/src/node_options.h index 0757a767a16208..a6f81d27d5a06d 100644 --- a/src/node_options.h +++ b/src/node_options.h @@ -207,6 +207,7 @@ class PerIsolateOptions : public Options { bool report_uncaught_exception = false; bool report_on_signal = false; bool experimental_top_level_await = true; + bool experimental_shadow_realm = false; std::string report_signal = "SIGUSR2"; inline EnvironmentOptions* get_per_env_options(); void CheckOptions(std::vector* errors) override; diff --git a/src/node_shadow_realm.cc b/src/node_shadow_realm.cc new file mode 100644 index 00000000000000..2ccd62edc315d7 --- /dev/null +++ b/src/node_shadow_realm.cc @@ -0,0 +1,16 @@ +#include "node_shadow_realm.h" + +namespace node { +namespace shadow_realm { +using v8::Context; +using v8::Local; +using v8::MaybeLocal; + +// static +MaybeLocal HostCreateShadowRealmContextCallback( + Local initiator_context) { + return Context::New(initiator_context->GetIsolate()); +} + +} // namespace shadow_realm +} // namespace node diff --git a/src/node_shadow_realm.h b/src/node_shadow_realm.h new file mode 100644 index 00000000000000..a10fc425661f56 --- /dev/null +++ b/src/node_shadow_realm.h @@ -0,0 +1,19 @@ +#ifndef SRC_NODE_SHADOW_REALM_H_ +#define SRC_NODE_SHADOW_REALM_H_ + +#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#include "v8.h" + +namespace node { +namespace shadow_realm { + +v8::MaybeLocal HostCreateShadowRealmContextCallback( + v8::Local initiator_context); + +} // namespace shadow_realm +} // namespace node + +#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS + +#endif // SRC_NODE_SHADOW_REALM_H_ diff --git a/test/parallel/test-shadow-realm.js b/test/parallel/test-shadow-realm.js new file mode 100644 index 00000000000000..c4fef235a146cc --- /dev/null +++ b/test/parallel/test-shadow-realm.js @@ -0,0 +1,12 @@ +// Flags: --harmony-shadow-realm +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Validates we can construct ShadowRealm successfully. +const shadowRealm = new ShadowRealm(); + +const getter = shadowRealm.evaluate('globalThis.realmValue = "inner"; () => globalThis.realmValue;'); +assert.strictEqual(getter(), 'inner'); +assert.strictEqual('realmValue' in globalThis, false);