module: bootstrap module loaders in shadow realm

This bootstraps ESM loaders in the ShadowRealm with
`ShadowRealm.prototype.importValue` as its entry point and enables
loading ESM and CJS modules in the ShadowRealm. The module is imported
without a parent URL and resolved with the current process's working
directory.

PR-URL: https://github.com/nodejs/node/pull/48655
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
This commit is contained in:
Chengzhong Wu 2023-11-10 00:21:15 +08:00 committed by legendecas
parent 6b7197cb2b
commit fc2862b7f5
No known key found for this signature in database
GPG Key ID: CB3C9EC2BC27057C
24 changed files with 445 additions and 122 deletions

View File

@ -50,6 +50,8 @@
const { const {
ArrayFrom, ArrayFrom,
ArrayPrototypeFilter,
ArrayPrototypeIncludes,
ArrayPrototypeMap, ArrayPrototypeMap,
ArrayPrototypePush, ArrayPrototypePush,
ArrayPrototypeSlice, ArrayPrototypeSlice,
@ -215,8 +217,8 @@ const internalBuiltinIds = builtinIds
.filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId); .filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId);
// When --expose-internals is on we'll add the internal builtin ids to these. // When --expose-internals is on we'll add the internal builtin ids to these.
const canBeRequiredByUsersList = new SafeSet(publicBuiltinIds); let canBeRequiredByUsersList = new SafeSet(publicBuiltinIds);
const canBeRequiredByUsersWithoutSchemeList = let canBeRequiredByUsersWithoutSchemeList =
new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id))); new SafeSet(publicBuiltinIds.filter((id) => !schemelessBlockList.has(id)));
/** /**
@ -269,6 +271,13 @@ class BuiltinModule {
} }
} }
static setRealmAllowRequireByUsers(ids) {
canBeRequiredByUsersList =
new SafeSet(ArrayPrototypeFilter(ids, (id) => ArrayPrototypeIncludes(publicBuiltinIds, id)));
canBeRequiredByUsersWithoutSchemeList =
new SafeSet(ArrayPrototypeFilter(ids, (id) => !schemelessBlockList.has(id)));
}
// To be called during pre-execution when --expose-internals is on. // To be called during pre-execution when --expose-internals is on.
// Enables the user-land module loader to access internal modules. // Enables the user-land module loader to access internal modules.
static exposeInternals() { static exposeInternals() {

View File

@ -0,0 +1,21 @@
'use strict';
// This script sets up the context for shadow realms.
const {
prepareShadowRealmExecution,
} = require('internal/process/pre_execution');
const {
BuiltinModule,
} = require('internal/bootstrap/realm');
BuiltinModule.setRealmAllowRequireByUsers([
/**
* The built-in modules exposed in the ShadowRealm must each be providing
* platform capabilities with no authority to cause side effects such as
* I/O or mutation of values that are shared across different realms within
* the same Node.js environment.
*/
]);
prepareShadowRealmExecution();

View File

@ -136,6 +136,7 @@ port.on('message', (message) => {
const isLoaderWorker = const isLoaderWorker =
doEval === 'internal' && doEval === 'internal' &&
filename === require('internal/modules/esm/utils').loaderWorkerId; filename === require('internal/modules/esm/utils').loaderWorkerId;
// Disable custom loaders in loader worker.
setupUserModules(isLoaderWorker); setupUserModules(isLoaderWorker);
if (!hasStdin) if (!hasStdin)

View File

@ -528,9 +528,10 @@ let emittedLoaderFlagWarning = false;
*/ */
function createModuleLoader() { function createModuleLoader() {
let customizations = null; let customizations = null;
// Don't spawn a new worker if we're already in a worker thread created by instantiating CustomizedModuleLoader; // Don't spawn a new worker if custom loaders are disabled. For instance, if
// doing so would cause an infinite loop. // we're already in a worker thread created by instantiating
if (!require('internal/modules/esm/utils').isLoaderWorker()) { // CustomizedModuleLoader; doing so would cause an infinite loop.
if (!require('internal/modules/esm/utils').forceDefaultLoader()) {
const userLoaderPaths = getOptionValue('--experimental-loader'); const userLoaderPaths = getOptionValue('--experimental-loader');
if (userLoaderPaths.length > 0) { if (userLoaderPaths.length > 0) {
if (!emittedLoaderFlagWarning) { if (!emittedLoaderFlagWarning) {

View File

@ -4,6 +4,7 @@ const {
ArrayIsArray, ArrayIsArray,
SafeSet, SafeSet,
SafeWeakMap, SafeWeakMap,
Symbol,
ObjectFreeze, ObjectFreeze,
} = primordials; } = primordials;
@ -157,6 +158,26 @@ function registerModule(referrer, registry) {
moduleRegistries.set(idSymbol, registry); moduleRegistries.set(idSymbol, registry);
} }
/**
* Registers the ModuleRegistry for dynamic import() calls with a realm
* as the referrer. Similar to {@link registerModule}, but this function
* generates a new id symbol instead of using the one from the referrer
* object.
* @param {globalThis} globalThis The globalThis object of the realm.
* @param {ModuleRegistry} registry
*/
function registerRealm(globalThis, registry) {
let idSymbol = globalThis[host_defined_option_symbol];
// If the per-realm host-defined options is already registered, do nothing.
if (idSymbol) {
return;
}
// Otherwise, register the per-realm host-defined options.
idSymbol = Symbol('Realm globalThis');
globalThis[host_defined_option_symbol] = idSymbol;
moduleRegistries.set(idSymbol, registry);
}
/** /**
* Defines the `import.meta` object for a given module. * Defines the `import.meta` object for a given module.
* @param {symbol} symbol - Reference to the module. * @param {symbol} symbol - Reference to the module.
@ -192,28 +213,29 @@ async function importModuleDynamicallyCallback(referrerSymbol, specifier, attrib
throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING(); throw new ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING();
} }
let _isLoaderWorker = false; let _forceDefaultLoader = false;
/** /**
* Initializes handling of ES modules. * Initializes handling of ES modules.
* This is configured during pre-execution. Specifically it's set to true for * This is configured during pre-execution. Specifically it's set to true for
* the loader worker in internal/main/worker_thread.js. * the loader worker in internal/main/worker_thread.js.
* @param {boolean} [isLoaderWorker=false] - A boolean indicating whether the loader is a worker or not. * @param {boolean} [forceDefaultLoader=false] - A boolean indicating disabling custom loaders.
*/ */
function initializeESM(isLoaderWorker = false) { function initializeESM(forceDefaultLoader = false) {
_isLoaderWorker = isLoaderWorker; _forceDefaultLoader = forceDefaultLoader;
initializeDefaultConditions(); initializeDefaultConditions();
// Setup per-isolate callbacks that locate data or callbacks that we keep // Setup per-realm callbacks that locate data or callbacks that we keep
// track of for different ESM modules. // track of for different ESM modules.
setInitializeImportMetaObjectCallback(initializeImportMetaObject); setInitializeImportMetaObjectCallback(initializeImportMetaObject);
setImportModuleDynamicallyCallback(importModuleDynamicallyCallback); setImportModuleDynamicallyCallback(importModuleDynamicallyCallback);
} }
/** /**
* Determine whether the current process is a loader worker. * Determine whether custom loaders are disabled and it is forced to use the
* @returns {boolean} Whether the current process is a loader worker. * default loader.
* @returns {boolean}
*/ */
function isLoaderWorker() { function forceDefaultLoader() {
return _isLoaderWorker; return _forceDefaultLoader;
} }
/** /**
@ -251,10 +273,11 @@ async function initializeHooks() {
module.exports = { module.exports = {
registerModule, registerModule,
registerRealm,
initializeESM, initializeESM,
initializeHooks, initializeHooks,
getDefaultConditions, getDefaultConditions,
getConditionsSet, getConditionsSet,
loaderWorkerId: 'internal/modules/esm/worker', loaderWorkerId: 'internal/modules/esm/worker',
isLoaderWorker, forceDefaultLoader,
}; };

View File

@ -67,6 +67,26 @@ function prepareWorkerThreadExecution() {
}); });
} }
function prepareShadowRealmExecution() {
const { registerRealm } = require('internal/modules/esm/utils');
// Patch the process object with legacy properties and normalizations.
// Do not expand argv1 as it is not available in ShadowRealm.
patchProcessObject(false);
setupDebugEnv();
// Disable custom loaders in ShadowRealm.
setupUserModules(true);
registerRealm(globalThis, {
__proto__: null,
importModuleDynamically: (specifier, _referrer, attributes) => {
// The handler for `ShadowRealm.prototype.importValue`.
const { esmLoader } = require('internal/process/esm_loader');
// `parentURL` is not set in the case of a ShadowRealm top-level import.
return esmLoader.import(specifier, undefined, attributes);
},
});
}
function prepareExecution(options) { function prepareExecution(options) {
const { expandArgv1, initializeModules, isMainThread } = options; const { expandArgv1, initializeModules, isMainThread } = options;
@ -161,16 +181,17 @@ function setupSymbolDisposePolyfill() {
} }
} }
function setupUserModules(isLoaderWorker = false) { function setupUserModules(forceDefaultLoader = false) {
initializeCJSLoader(); initializeCJSLoader();
initializeESMLoader(isLoaderWorker); initializeESMLoader(forceDefaultLoader);
const CJSLoader = require('internal/modules/cjs/loader'); const CJSLoader = require('internal/modules/cjs/loader');
assert(!CJSLoader.hasLoadedAnyUserCJSModule); assert(!CJSLoader.hasLoadedAnyUserCJSModule);
// Loader workers are responsible for doing this themselves. // Do not enable preload modules if custom loaders are disabled.
if (isLoaderWorker) { // For example, loader workers are responsible for doing this themselves.
return; // And preload modules are not supported in ShadowRealm as well.
if (!forceDefaultLoader) {
loadPreloadModules();
} }
loadPreloadModules();
// Need to be done after --require setup. // Need to be done after --require setup.
initializeFrozenIntrinsics(); initializeFrozenIntrinsics();
} }
@ -701,9 +722,9 @@ function initializeCJSLoader() {
initializeCJS(); initializeCJS();
} }
function initializeESMLoader(isLoaderWorker) { function initializeESMLoader(forceDefaultLoader) {
const { initializeESM } = require('internal/modules/esm/utils'); const { initializeESM } = require('internal/modules/esm/utils');
initializeESM(isLoaderWorker); initializeESM(forceDefaultLoader);
// Patch the vm module when --experimental-vm-modules is on. // Patch the vm module when --experimental-vm-modules is on.
// Please update the comments in vm.js when this block changes. // Please update the comments in vm.js when this block changes.
@ -779,6 +800,7 @@ module.exports = {
setupUserModules, setupUserModules,
prepareMainThreadExecution, prepareMainThreadExecution,
prepareWorkerThreadExecution, prepareWorkerThreadExecution,
prepareShadowRealmExecution,
markBootstrapComplete, markBootstrapComplete,
loadPreloadModules, loadPreloadModules,
initializeFrozenIntrinsics, initializeFrozenIntrinsics,

View File

@ -372,8 +372,9 @@ void AsyncWrap::CreatePerContextProperties(Local<Object> target,
Local<Value> unused, Local<Value> unused,
Local<Context> context, Local<Context> context,
void* priv) { void* priv) {
Environment* env = Environment::GetCurrent(context); Realm* realm = Realm::GetCurrent(context);
Isolate* isolate = env->isolate(); Environment* env = realm->env();
Isolate* isolate = realm->isolate();
HandleScope scope(isolate); HandleScope scope(isolate);
PropertyAttribute ReadOnlyDontDelete = PropertyAttribute ReadOnlyDontDelete =
@ -446,13 +447,16 @@ void AsyncWrap::CreatePerContextProperties(Local<Object> target,
#undef FORCE_SET_TARGET_FIELD #undef FORCE_SET_TARGET_FIELD
env->set_async_hooks_init_function(Local<Function>()); // TODO(legendecas): async hook functions are not realm-aware yet.
env->set_async_hooks_before_function(Local<Function>()); // This simply avoid overriding principal realm's functions when a
env->set_async_hooks_after_function(Local<Function>()); // ShadowRealm initializes the binding.
env->set_async_hooks_destroy_function(Local<Function>()); realm->set_async_hooks_init_function(Local<Function>());
env->set_async_hooks_promise_resolve_function(Local<Function>()); realm->set_async_hooks_before_function(Local<Function>());
env->set_async_hooks_callback_trampoline(Local<Function>()); realm->set_async_hooks_after_function(Local<Function>());
env->set_async_hooks_binding(target); realm->set_async_hooks_destroy_function(Local<Function>());
realm->set_async_hooks_promise_resolve_function(Local<Function>());
realm->set_async_hooks_callback_trampoline(Local<Function>());
realm->set_async_hooks_binding(target);
} }
void AsyncWrap::RegisterExternalReferences( void AsyncWrap::RegisterExternalReferences(

View File

@ -1651,10 +1651,13 @@ void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const {
void AsyncHooks::grow_async_ids_stack() { void AsyncHooks::grow_async_ids_stack() {
async_ids_stack_.reserve(async_ids_stack_.Length() * 3); async_ids_stack_.reserve(async_ids_stack_.Length() * 3);
env()->async_hooks_binding()->Set( env()
env()->context(), ->principal_realm()
env()->async_ids_stack_string(), ->async_hooks_binding()
async_ids_stack_.GetJSArray()).Check(); ->Set(env()->context(),
env()->async_ids_stack_string(),
async_ids_stack_.GetJSArray())
.Check();
} }
void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) { void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {

View File

@ -39,6 +39,7 @@ using v8::MicrotaskQueue;
using v8::Module; using v8::Module;
using v8::ModuleRequest; using v8::ModuleRequest;
using v8::Object; using v8::Object;
using v8::ObjectTemplate;
using v8::PrimitiveArray; using v8::PrimitiveArray;
using v8::Promise; using v8::Promise;
using v8::ScriptCompiler; using v8::ScriptCompiler;
@ -49,15 +50,17 @@ using v8::UnboundModuleScript;
using v8::Undefined; using v8::Undefined;
using v8::Value; using v8::Value;
ModuleWrap::ModuleWrap(Environment* env, ModuleWrap::ModuleWrap(Realm* realm,
Local<Object> object, Local<Object> object,
Local<Module> module, Local<Module> module,
Local<String> url, Local<String> url,
Local<Object> context_object, Local<Object> context_object,
Local<Value> synthetic_evaluation_step) Local<Value> synthetic_evaluation_step)
: BaseObject(env, object), : BaseObject(realm, object),
module_(env->isolate(), module), module_(realm->isolate(), module),
module_hash_(module->GetIdentityHash()) { module_hash_(module->GetIdentityHash()) {
realm->env()->hash_to_module_map.emplace(module_hash_, this);
object->SetInternalField(kModuleSlot, module); object->SetInternalField(kModuleSlot, module);
object->SetInternalField(kURLSlot, url); object->SetInternalField(kURLSlot, url);
object->SetInternalField(kSyntheticEvaluationStepsSlot, object->SetInternalField(kSyntheticEvaluationStepsSlot,
@ -72,7 +75,6 @@ ModuleWrap::ModuleWrap(Environment* env,
} }
ModuleWrap::~ModuleWrap() { ModuleWrap::~ModuleWrap() {
HandleScope scope(env()->isolate());
auto range = env()->hash_to_module_map.equal_range(module_hash_); auto range = env()->hash_to_module_map.equal_range(module_hash_);
for (auto it = range.first; it != range.second; ++it) { for (auto it = range.first; it != range.second; ++it) {
if (it->second == this) { if (it->second == this) {
@ -107,8 +109,8 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
CHECK(args.IsConstructCall()); CHECK(args.IsConstructCall());
CHECK_GE(args.Length(), 3); CHECK_GE(args.Length(), 3);
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = env->isolate(); Isolate* isolate = realm->isolate();
Local<Object> that = args.This(); Local<Object> that = args.This();
@ -122,7 +124,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
} else { } else {
CHECK(args[1]->IsObject()); CHECK(args[1]->IsObject());
contextify_context = ContextifyContext::ContextFromContextifiedSandbox( contextify_context = ContextifyContext::ContextFromContextifiedSandbox(
env, args[1].As<Object>()); realm->env(), args[1].As<Object>());
CHECK_NOT_NULL(contextify_context); CHECK_NOT_NULL(contextify_context);
context = contextify_context->context(); context = contextify_context->context();
} }
@ -148,8 +150,8 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
Local<Symbol> id_symbol = Symbol::New(isolate, url); Local<Symbol> id_symbol = Symbol::New(isolate, url);
host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol); host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol);
ShouldNotAbortOnUncaughtScope no_abort_scope(env); ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
TryCatchScope try_catch(env); TryCatchScope try_catch(realm->env());
Local<Module> module; Local<Module> module;
@ -206,7 +208,9 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty()); CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty()); CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), AppendExceptionLine(realm->env(),
try_catch.Exception(),
try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR); ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow(); try_catch.ReThrow();
} }
@ -215,18 +219,21 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
if (options == ScriptCompiler::kConsumeCodeCache && if (options == ScriptCompiler::kConsumeCodeCache &&
source.GetCachedData()->rejected) { source.GetCachedData()->rejected) {
THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED( THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
env, "cachedData buffer was rejected"); realm, "cachedData buffer was rejected");
try_catch.ReThrow(); try_catch.ReThrow();
return; return;
} }
} }
} }
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) { if (!that->Set(context, realm->isolate_data()->url_string(), url)
.FromMaybe(false)) {
return; return;
} }
if (that->SetPrivate(context, env->host_defined_option_symbol(), id_symbol) if (that->SetPrivate(context,
realm->isolate_data()->host_defined_option_symbol(),
id_symbol)
.IsNothing()) { .IsNothing()) {
return; return;
} }
@ -236,28 +243,26 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
// be stored in an internal field. // be stored in an internal field.
Local<Object> context_object = context->GetExtrasBindingObject(); Local<Object> context_object = context->GetExtrasBindingObject();
Local<Value> synthetic_evaluation_step = Local<Value> synthetic_evaluation_step =
synthetic ? args[3] : Undefined(env->isolate()).As<v8::Value>(); synthetic ? args[3] : Undefined(realm->isolate()).As<v8::Value>();
ModuleWrap* obj = new ModuleWrap( ModuleWrap* obj = new ModuleWrap(
env, that, module, url, context_object, synthetic_evaluation_step); realm, that, module, url, context_object, synthetic_evaluation_step);
obj->contextify_context_ = contextify_context; obj->contextify_context_ = contextify_context;
env->hash_to_module_map.emplace(module->GetIdentityHash(), obj);
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen); that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
args.GetReturnValue().Set(that); args.GetReturnValue().Set(that);
} }
static Local<Object> createImportAttributesContainer( static Local<Object> createImportAttributesContainer(
Environment* env, Isolate* isolate, Local<FixedArray> raw_attributes) { Realm* realm, Isolate* isolate, Local<FixedArray> raw_attributes) {
Local<Object> attributes = Local<Object> attributes =
Object::New(isolate, v8::Null(env->isolate()), nullptr, nullptr, 0); Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0);
for (int i = 0; i < raw_attributes->Length(); i += 3) { for (int i = 0; i < raw_attributes->Length(); i += 3) {
attributes attributes
->Set(env->context(), ->Set(realm->context(),
raw_attributes->Get(env->context(), i).As<String>(), raw_attributes->Get(realm->context(), i).As<String>(),
raw_attributes->Get(env->context(), i + 1).As<Value>()) raw_attributes->Get(realm->context(), i + 1).As<Value>())
.ToChecked(); .ToChecked();
} }
@ -265,7 +270,7 @@ static Local<Object> createImportAttributesContainer(
} }
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) { void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
CHECK_EQ(args.Length(), 1); CHECK_EQ(args.Length(), 1);
@ -292,14 +297,14 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
// call the dependency resolve callbacks // call the dependency resolve callbacks
for (int i = 0; i < module_requests_length; i++) { for (int i = 0; i < module_requests_length; i++) {
Local<ModuleRequest> module_request = Local<ModuleRequest> module_request =
module_requests->Get(env->context(), i).As<ModuleRequest>(); module_requests->Get(realm->context(), i).As<ModuleRequest>();
Local<String> specifier = module_request->GetSpecifier(); Local<String> specifier = module_request->GetSpecifier();
Utf8Value specifier_utf8(env->isolate(), specifier); Utf8Value specifier_utf8(realm->isolate(), specifier);
std::string specifier_std(*specifier_utf8, specifier_utf8.length()); std::string specifier_std(*specifier_utf8, specifier_utf8.length());
Local<FixedArray> raw_attributes = module_request->GetImportAssertions(); Local<FixedArray> raw_attributes = module_request->GetImportAssertions();
Local<Object> attributes = Local<Object> attributes =
createImportAttributesContainer(env, isolate, raw_attributes); createImportAttributesContainer(realm, isolate, raw_attributes);
Local<Value> argv[] = { Local<Value> argv[] = {
specifier, specifier,
@ -315,11 +320,11 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
maybe_resolve_return_value.ToLocalChecked(); maybe_resolve_return_value.ToLocalChecked();
if (!resolve_return_value->IsPromise()) { if (!resolve_return_value->IsPromise()) {
THROW_ERR_VM_MODULE_LINK_FAILURE( THROW_ERR_VM_MODULE_LINK_FAILURE(
env, "request for '%s' did not return promise", specifier_std); realm, "request for '%s' did not return promise", specifier_std);
return; return;
} }
Local<Promise> resolve_promise = resolve_return_value.As<Promise>(); Local<Promise> resolve_promise = resolve_return_value.As<Promise>();
obj->resolve_cache_[specifier_std].Reset(env->isolate(), resolve_promise); obj->resolve_cache_[specifier_std].Reset(isolate, resolve_promise);
promises[i] = resolve_promise; promises[i] = resolve_promise;
} }
@ -329,13 +334,13 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
} }
void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) { void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
ModuleWrap* obj; ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Context> context = obj->context(); Local<Context> context = obj->context();
Local<Module> module = obj->module_.Get(isolate); Local<Module> module = obj->module_.Get(isolate);
TryCatchScope try_catch(env); TryCatchScope try_catch(realm->env());
USE(module->InstantiateModule(context, ResolveModuleCallback)); USE(module->InstantiateModule(context, ResolveModuleCallback));
// clear resolve cache on instantiate // clear resolve cache on instantiate
@ -344,7 +349,9 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
CHECK(!try_catch.Message().IsEmpty()); CHECK(!try_catch.Message().IsEmpty());
CHECK(!try_catch.Exception().IsEmpty()); CHECK(!try_catch.Exception().IsEmpty());
AppendExceptionLine(env, try_catch.Exception(), try_catch.Message(), AppendExceptionLine(realm->env(),
try_catch.Exception(),
try_catch.Message(),
ErrorHandlingMode::MODULE_ERROR); ErrorHandlingMode::MODULE_ERROR);
try_catch.ReThrow(); try_catch.ReThrow();
return; return;
@ -352,8 +359,8 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
} }
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) { void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = env->isolate(); Isolate* isolate = realm->isolate();
ModuleWrap* obj; ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Context> context = obj->context(); Local<Context> context = obj->context();
@ -368,14 +375,14 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
CHECK_EQ(args.Length(), 2); CHECK_EQ(args.Length(), 2);
CHECK(args[0]->IsNumber()); CHECK(args[0]->IsNumber());
int64_t timeout = args[0]->IntegerValue(env->context()).FromJust(); int64_t timeout = args[0]->IntegerValue(realm->context()).FromJust();
CHECK(args[1]->IsBoolean()); CHECK(args[1]->IsBoolean());
bool break_on_sigint = args[1]->IsTrue(); bool break_on_sigint = args[1]->IsTrue();
ShouldNotAbortOnUncaughtScope no_abort_scope(env); ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
TryCatchScope try_catch(env); TryCatchScope try_catch(realm->env());
Isolate::SafeForTerminationScope safe_for_termination(env->isolate()); Isolate::SafeForTerminationScope safe_for_termination(isolate);
bool timed_out = false; bool timed_out = false;
bool received_signal = false; bool received_signal = false;
@ -406,16 +413,15 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
// Convert the termination exception into a regular exception. // Convert the termination exception into a regular exception.
if (timed_out || received_signal) { if (timed_out || received_signal) {
if (!env->is_main_thread() && env->is_stopping()) if (!realm->env()->is_main_thread() && realm->env()->is_stopping()) return;
return; isolate->CancelTerminateExecution();
env->isolate()->CancelTerminateExecution();
// It is possible that execution was terminated by another timeout in // It is possible that execution was terminated by another timeout in
// which this timeout is nested, so check whether one of the watchdogs // which this timeout is nested, so check whether one of the watchdogs
// from this invocation is responsible for termination. // from this invocation is responsible for termination.
if (timed_out) { if (timed_out) {
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout); THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(realm->env(), timeout);
} else if (received_signal) { } else if (received_signal) {
THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(env); THROW_ERR_SCRIPT_EXECUTION_INTERRUPTED(realm->env());
} }
} }
@ -429,7 +435,7 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
} }
void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) { void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
ModuleWrap* obj; ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
@ -439,7 +445,7 @@ void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
switch (module->GetStatus()) { switch (module->GetStatus()) {
case v8::Module::Status::kUninstantiated: case v8::Module::Status::kUninstantiated:
case v8::Module::Status::kInstantiating: case v8::Module::Status::kInstantiating:
return env->ThrowError( return realm->env()->ThrowError(
"cannot get namespace, module has not been instantiated"); "cannot get namespace, module has not been instantiated");
case v8::Module::Status::kInstantiated: case v8::Module::Status::kInstantiated:
case v8::Module::Status::kEvaluating: case v8::Module::Status::kEvaluating:
@ -466,11 +472,11 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
void ModuleWrap::GetStaticDependencySpecifiers( void ModuleWrap::GetStaticDependencySpecifiers(
const FunctionCallbackInfo<Value>& args) { const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
ModuleWrap* obj; ModuleWrap* obj;
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This()); ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
Local<Module> module = obj->module_.Get(env->isolate()); Local<Module> module = obj->module_.Get(realm->isolate());
Local<FixedArray> module_requests = module->GetModuleRequests(); Local<FixedArray> module_requests = module->GetModuleRequests();
int count = module_requests->Length(); int count = module_requests->Length();
@ -479,12 +485,12 @@ void ModuleWrap::GetStaticDependencySpecifiers(
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
Local<ModuleRequest> module_request = Local<ModuleRequest> module_request =
module_requests->Get(env->context(), i).As<ModuleRequest>(); module_requests->Get(realm->context(), i).As<ModuleRequest>();
specifiers[i] = module_request->GetSpecifier(); specifiers[i] = module_request->GetSpecifier();
} }
args.GetReturnValue().Set( args.GetReturnValue().Set(
Array::New(env->isolate(), specifiers.out(), count)); Array::New(realm->isolate(), specifiers.out(), count));
} }
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) { void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
@ -501,15 +507,13 @@ MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
Local<String> specifier, Local<String> specifier,
Local<FixedArray> import_attributes, Local<FixedArray> import_attributes,
Local<Module> referrer) { Local<Module> referrer) {
Isolate* isolate = context->GetIsolate();
Environment* env = Environment::GetCurrent(context); Environment* env = Environment::GetCurrent(context);
if (env == nullptr) { if (env == nullptr) {
Isolate* isolate = context->GetIsolate();
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate); THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
return MaybeLocal<Module>(); return MaybeLocal<Module>();
} }
Isolate* isolate = env->isolate();
Utf8Value specifier_utf8(isolate, specifier); Utf8Value specifier_utf8(isolate, specifier);
std::string specifier_std(*specifier_utf8, specifier_utf8.length()); std::string specifier_std(*specifier_utf8, specifier_utf8.length());
@ -559,11 +563,16 @@ static MaybeLocal<Promise> ImportModuleDynamically(
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate); THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
return MaybeLocal<Promise>(); return MaybeLocal<Promise>();
} }
Realm* realm = Realm::GetCurrent(context);
if (realm == nullptr) {
// Fallback to the principal realm if it's in a vm context.
realm = env->principal_realm();
}
EscapableHandleScope handle_scope(isolate); EscapableHandleScope handle_scope(isolate);
Local<Function> import_callback = Local<Function> import_callback =
env->host_import_module_dynamically_callback(); realm->host_import_module_dynamically_callback();
Local<Value> id; Local<Value> id;
Local<FixedArray> options = host_defined_options.As<FixedArray>(); Local<FixedArray> options = host_defined_options.As<FixedArray>();
@ -579,7 +588,7 @@ static MaybeLocal<Promise> ImportModuleDynamically(
} }
Local<Object> attributes = Local<Object> attributes =
createImportAttributesContainer(env, isolate, import_attributes); createImportAttributesContainer(realm, isolate, import_attributes);
Local<Value> import_args[] = { Local<Value> import_args[] = {
id, id,
@ -603,13 +612,13 @@ static MaybeLocal<Promise> ImportModuleDynamically(
void ModuleWrap::SetImportModuleDynamicallyCallback( void ModuleWrap::SetImportModuleDynamicallyCallback(
const FunctionCallbackInfo<Value>& args) { const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate(); Isolate* isolate = args.GetIsolate();
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
CHECK_EQ(args.Length(), 1); CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsFunction()); CHECK(args[0]->IsFunction());
Local<Function> import_callback = args[0].As<Function>(); Local<Function> import_callback = args[0].As<Function>();
env->set_host_import_module_dynamically_callback(import_callback); realm->set_host_import_module_dynamically_callback(import_callback);
isolate->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically); isolate->SetHostImportModuleDynamicallyCallback(ImportModuleDynamically);
} }
@ -624,10 +633,15 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
if (module_wrap == nullptr) { if (module_wrap == nullptr) {
return; return;
} }
Realm* realm = Realm::GetCurrent(context);
if (realm == nullptr) {
// Fallback to the principal realm if it's in a vm context.
realm = env->principal_realm();
}
Local<Object> wrap = module_wrap->object(); Local<Object> wrap = module_wrap->object();
Local<Function> callback = Local<Function> callback =
env->host_initialize_import_meta_object_callback(); realm->host_initialize_import_meta_object_callback();
Local<Value> id; Local<Value> id;
if (!wrap->GetPrivate(context, env->host_defined_option_symbol()) if (!wrap->GetPrivate(context, env->host_defined_option_symbol())
.ToLocal(&id)) { .ToLocal(&id)) {
@ -637,7 +651,7 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
Local<Value> args[] = {id, meta}; Local<Value> args[] = {id, meta};
TryCatchScope try_catch(env); TryCatchScope try_catch(env);
USE(callback->Call( USE(callback->Call(
context, Undefined(env->isolate()), arraysize(args), args)); context, Undefined(realm->isolate()), arraysize(args), args));
if (try_catch.HasCaught() && !try_catch.HasTerminated()) { if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
try_catch.ReThrow(); try_catch.ReThrow();
} }
@ -645,13 +659,13 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
void ModuleWrap::SetInitializeImportMetaObjectCallback( void ModuleWrap::SetInitializeImportMetaObjectCallback(
const FunctionCallbackInfo<Value>& args) { const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args); Realm* realm = Realm::GetCurrent(args);
Isolate* isolate = env->isolate(); Isolate* isolate = realm->isolate();
CHECK_EQ(args.Length(), 1); CHECK_EQ(args.Length(), 1);
CHECK(args[0]->IsFunction()); CHECK(args[0]->IsFunction());
Local<Function> import_meta_callback = args[0].As<Function>(); Local<Function> import_meta_callback = args[0].As<Function>();
env->set_host_initialize_import_meta_object_callback(import_meta_callback); realm->set_host_initialize_import_meta_object_callback(import_meta_callback);
isolate->SetHostInitializeImportMetaObjectCallback( isolate->SetHostInitializeImportMetaObjectCallback(
HostInitializeImportMetaObjectCallback); HostInitializeImportMetaObjectCallback);
@ -742,12 +756,9 @@ void ModuleWrap::CreateCachedData(const FunctionCallbackInfo<Value>& args) {
} }
} }
void ModuleWrap::Initialize(Local<Object> target, void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
Local<Value> unused, Local<ObjectTemplate> target) {
Local<Context> context, Isolate* isolate = isolate_data->isolate();
void* priv) {
Environment* env = Environment::GetCurrent(context);
Isolate* isolate = env->isolate();
Local<FunctionTemplate> tpl = NewFunctionTemplate(isolate, New); Local<FunctionTemplate> tpl = NewFunctionTemplate(isolate, New);
tpl->InstanceTemplate()->SetInternalFieldCount( tpl->InstanceTemplate()->SetInternalFieldCount(
@ -767,28 +778,36 @@ void ModuleWrap::Initialize(Local<Object> target,
"getStaticDependencySpecifiers", "getStaticDependencySpecifiers",
GetStaticDependencySpecifiers); GetStaticDependencySpecifiers);
SetConstructorFunction(context, target, "ModuleWrap", tpl); SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
SetMethod(context, SetMethod(isolate,
target, target,
"setImportModuleDynamicallyCallback", "setImportModuleDynamicallyCallback",
SetImportModuleDynamicallyCallback); SetImportModuleDynamicallyCallback);
SetMethod(context, SetMethod(isolate,
target, target,
"setInitializeImportMetaObjectCallback", "setInitializeImportMetaObjectCallback",
SetInitializeImportMetaObjectCallback); SetInitializeImportMetaObjectCallback);
}
void ModuleWrap::CreatePerContextProperties(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
Realm* realm = Realm::GetCurrent(context);
Isolate* isolate = realm->isolate();
#define V(name) \ #define V(name) \
target->Set(context, \ target \
FIXED_ONE_BYTE_STRING(env->isolate(), #name), \ ->Set(context, \
Integer::New(env->isolate(), Module::Status::name)) \ FIXED_ONE_BYTE_STRING(isolate, #name), \
.FromJust() Integer::New(isolate, Module::Status::name)) \
V(kUninstantiated); .FromJust()
V(kInstantiating); V(kUninstantiated);
V(kInstantiated); V(kInstantiating);
V(kEvaluating); V(kInstantiated);
V(kEvaluated); V(kEvaluating);
V(kErrored); V(kEvaluated);
V(kErrored);
#undef V #undef V
} }
@ -812,7 +831,9 @@ void ModuleWrap::RegisterExternalReferences(
} // namespace loader } // namespace loader
} // namespace node } // namespace node
NODE_BINDING_CONTEXT_AWARE_INTERNAL(module_wrap, NODE_BINDING_CONTEXT_AWARE_INTERNAL(
node::loader::ModuleWrap::Initialize) module_wrap, node::loader::ModuleWrap::CreatePerContextProperties)
NODE_BINDING_PER_ISOLATE_INIT(
module_wrap, node::loader::ModuleWrap::CreatePerIsolateProperties)
NODE_BINDING_EXTERNAL_REFERENCE( NODE_BINDING_EXTERNAL_REFERENCE(
module_wrap, node::loader::ModuleWrap::RegisterExternalReferences) module_wrap, node::loader::ModuleWrap::RegisterExternalReferences)

View File

@ -10,6 +10,7 @@
namespace node { namespace node {
class IsolateData;
class Environment; class Environment;
class ExternalReferenceRegistry; class ExternalReferenceRegistry;
@ -40,10 +41,12 @@ class ModuleWrap : public BaseObject {
kInternalFieldCount kInternalFieldCount
}; };
static void Initialize(v8::Local<v8::Object> target, static void CreatePerIsolateProperties(IsolateData* isolate_data,
v8::Local<v8::Value> unused, v8::Local<v8::ObjectTemplate> target);
v8::Local<v8::Context> context, static void CreatePerContextProperties(v8::Local<v8::Object> target,
void* priv); v8::Local<v8::Value> unused,
v8::Local<v8::Context> context,
void* priv);
static void RegisterExternalReferences(ExternalReferenceRegistry* registry); static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
static void HostInitializeImportMetaObjectCallback( static void HostInitializeImportMetaObjectCallback(
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
@ -66,7 +69,7 @@ class ModuleWrap : public BaseObject {
} }
private: private:
ModuleWrap(Environment* env, ModuleWrap(Realm* realm,
v8::Local<v8::Object> object, v8::Local<v8::Object> object,
v8::Local<v8::Module> module, v8::Local<v8::Module> module,
v8::Local<v8::String> url, v8::Local<v8::String> url,

View File

@ -40,6 +40,7 @@ static_assert(static_cast<int>(NM_F_LINKED) ==
V(fs_dir) \ V(fs_dir) \
V(messaging) \ V(messaging) \
V(mksnapshot) \ V(mksnapshot) \
V(module_wrap) \
V(performance) \ V(performance) \
V(process_methods) \ V(process_methods) \
V(timers) \ V(timers) \

View File

@ -123,6 +123,10 @@ void AppendExceptionLine(Environment* env,
inline void THROW_##code( \ inline void THROW_##code( \
Environment* env, const char* format, Args&&... args) { \ Environment* env, const char* format, Args&&... args) { \
THROW_##code(env->isolate(), format, std::forward<Args>(args)...); \ THROW_##code(env->isolate(), format, std::forward<Args>(args)...); \
} \
template <typename... Args> \
inline void THROW_##code(Realm* realm, const char* format, Args&&... args) { \
THROW_##code(realm->isolate(), format, std::forward<Args>(args)...); \
} }
ERRORS_WITH_CODE(V) ERRORS_WITH_CODE(V)
#undef V #undef V

View File

@ -1,6 +1,7 @@
#include "node_shadow_realm.h" #include "node_shadow_realm.h"
#include "env-inl.h" #include "env-inl.h"
#include "node_errors.h" #include "node_errors.h"
#include "node_process.h"
namespace node { namespace node {
namespace shadow_realm { namespace shadow_realm {
@ -9,6 +10,8 @@ using v8::EscapableHandleScope;
using v8::HandleScope; using v8::HandleScope;
using v8::Local; using v8::Local;
using v8::MaybeLocal; using v8::MaybeLocal;
using v8::Object;
using v8::String;
using v8::Value; using v8::Value;
using TryCatchScope = node::errors::TryCatchScope; using TryCatchScope = node::errors::TryCatchScope;
@ -16,6 +19,11 @@ using TryCatchScope = node::errors::TryCatchScope;
// static // static
ShadowRealm* ShadowRealm::New(Environment* env) { ShadowRealm* ShadowRealm::New(Environment* env) {
ShadowRealm* realm = new ShadowRealm(env); ShadowRealm* realm = new ShadowRealm(env);
// TODO(legendecas): required by node::PromiseRejectCallback.
// Remove this once promise rejection doesn't need to be handled across
// realms.
realm->context()->SetSecurityToken(
env->principal_realm()->context()->GetSecurityToken());
// We do not expect the realm bootstrapping to throw any // We do not expect the realm bootstrapping to throw any
// exceptions. If it does, exit the current Node.js instance. // exceptions. If it does, exit the current Node.js instance.
@ -32,6 +40,10 @@ MaybeLocal<Context> HostCreateShadowRealmContextCallback(
Local<Context> initiator_context) { Local<Context> initiator_context) {
Environment* env = Environment::GetCurrent(initiator_context); Environment* env = Environment::GetCurrent(initiator_context);
EscapableHandleScope scope(env->isolate()); EscapableHandleScope scope(env->isolate());
// We do not expect the realm bootstrapping to throw any
// exceptions. If it does, exit the current Node.js instance.
TryCatchScope try_catch(env, TryCatchScope::CatchMode::kFatal);
ShadowRealm* realm = ShadowRealm::New(env); ShadowRealm* realm = ShadowRealm::New(env);
if (realm != nullptr) { if (realm != nullptr) {
return scope.Escape(realm->context()); return scope.Escape(realm->context());
@ -137,6 +149,28 @@ v8::MaybeLocal<v8::Value> ShadowRealm::BootstrapRealm() {
} }
} }
// The process object is not exposed globally in ShadowRealm yet.
// However, the process properties need to be setup for built-in modules.
// Specifically, process.cwd() is needed by the ESM loader.
if (ExecuteBootstrapper(
"internal/bootstrap/switches/does_not_own_process_state")
.IsEmpty()) {
return MaybeLocal<Value>();
}
// Setup process.env proxy.
Local<String> env_string = FIXED_ONE_BYTE_STRING(isolate_, "env");
Local<Object> env_proxy;
if (!isolate_data()->env_proxy_template()->NewInstance(context()).ToLocal(
&env_proxy) ||
process_object()->Set(context(), env_string, env_proxy).IsNothing()) {
return MaybeLocal<Value>();
}
if (ExecuteBootstrapper("internal/bootstrap/shadow_realm").IsEmpty()) {
return MaybeLocal<Value>();
}
return v8::True(isolate_); return v8::True(isolate_);
} }

View File

@ -0,0 +1,15 @@
// This fixture is used to test that custom loaders are not enabled in the ShadowRealm.
'use strict';
const assert = require('assert');
async function workInChildProcess() {
// Assert that the process is running with a custom loader.
const moduleNamespace = await import('file:///42.mjs');
assert.strictEqual(moduleNamespace.default, 42);
const realm = new ShadowRealm();
await assert.rejects(realm.importValue('file:///42.mjs', 'default'), TypeError);
}
workInChildProcess();

View File

@ -0,0 +1,9 @@
// This fixture is used to test that --require preload modules are not enabled in the ShadowRealm.
'use strict';
const assert = require('assert');
assert.strictEqual(globalThis.preload, 42);
const realm = new ShadowRealm();
const value = realm.evaluate(`globalThis.preload`);
assert.strictEqual(value, undefined);

View File

@ -0,0 +1 @@
globalThis.preload = 42;

View File

@ -0,0 +1,3 @@
// This module verifies that the module specifier is resolved relative to the
// current module and not the current working directory in the ShadowRealm.
export { getCounter } from "./state-counter.mjs";

View File

@ -0,0 +1,4 @@
let counter = 0;
export const getCounter = () => {
return counter++;
};

View File

@ -0,0 +1,21 @@
// Flags: --experimental-shadow-realm
'use strict';
const common = require('../common');
const assert = require('assert');
async function main() {
// Verifies that builtin modules can not be imported in the ShadowRealm.
const realm = new ShadowRealm();
// The error object created inside the ShadowRealm with the error code
// property is not copied on the realm boundary. Only the error message
// is copied. Simply check the error message here.
await assert.rejects(realm.importValue('fs', 'readFileSync'), {
message: /Cannot find package 'fs'/,
});
// As above, we can only validate the error message, not the error code.
await assert.rejects(realm.importValue('node:fs', 'readFileSync'), {
message: /No such built-in module: node:fs/,
});
}
main().then(common.mustCall());

View File

@ -0,0 +1,26 @@
'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
const commonArgs = [
'--experimental-shadow-realm',
'--no-warnings',
];
async function main() {
// Verifies that custom loaders are not enabled in the ShadowRealm.
const child = await common.spawnPromisified(process.execPath, [
...commonArgs,
'--experimental-loader',
fixtures.fileURL('es-module-loaders', 'loader-resolve-shortcircuit.mjs'),
'--experimental-loader',
fixtures.fileURL('es-module-loaders', 'loader-load-foo-or-42.mjs'),
fixtures.path('es-module-shadow-realm', 'custom-loaders.js'),
]);
assert.strictEqual(child.stderr, '');
assert.strictEqual(child.code, 0);
}
main().then(common.mustCall());

View File

@ -0,0 +1,20 @@
// Flags: --experimental-shadow-realm --max-old-space-size=20
'use strict';
/**
* Verifying modules imported by ShadowRealm instances can be correctly
* garbage collected.
*/
const common = require('../common');
const fixtures = require('../common/fixtures');
async function main() {
const mod = fixtures.fileURL('es-module-shadow-realm', 'state-counter.mjs');
for (let i = 0; i < 100; i++) {
const realm = new ShadowRealm();
await realm.importValue(mod, 'getCounter');
}
}
main().then(common.mustCall());

View File

@ -0,0 +1,28 @@
// Flags: --experimental-shadow-realm
'use strict';
const common = require('../common');
const assert = require('assert');
const path = require('path');
common.skipIfWorker('process.chdir is not supported in workers.');
async function main() {
const realm = new ShadowRealm();
const dirname = __dirname;
// Set process cwd to the parent directory of __dirname.
const cwd = path.dirname(dirname);
process.chdir(cwd);
// Hardcode the relative path to ensure the string is still a valid relative
// URL string.
const relativePath = './fixtures/es-module-shadow-realm/re-export-state-counter.mjs';
// Make sure that the module can not be resolved relative to __filename.
assert.throws(() => require.resolve(relativePath), { code: 'MODULE_NOT_FOUND' });
// Resolve relative to the current working directory.
const getCounter = await realm.importValue(relativePath, 'getCounter');
assert.strictEqual(typeof getCounter, 'function');
}
main().then(common.mustCall());

View File

@ -0,0 +1,29 @@
// Flags: --experimental-shadow-realm
'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
const assert = require('assert');
async function main() {
const realm = new ShadowRealm();
const mod = fixtures.fileURL('es-module-shadow-realm', 'state-counter.mjs');
const getCounter = await realm.importValue(mod, 'getCounter');
assert.strictEqual(getCounter(), 0);
const getCounter1 = await realm.importValue(mod, 'getCounter');
// Returned value is a newly wrapped function.
assert.notStrictEqual(getCounter, getCounter1);
// Verify that the module state is shared between two `importValue` calls.
assert.strictEqual(getCounter1(), 1);
assert.strictEqual(getCounter(), 2);
const { getCounter: getCounterThisRealm } = await import(mod);
assert.notStrictEqual(getCounterThisRealm, getCounter);
// Verify that the module state is not shared between two realms.
assert.strictEqual(getCounterThisRealm(), 0);
assert.strictEqual(getCounter(), 3);
// Verify that shadow realm rejects to import a non-existing module.
await assert.rejects(realm.importValue('non-exists', 'exports'), TypeError);
}
main().then(common.mustCall());

View File

@ -0,0 +1,20 @@
'use strict';
const common = require('../common');
const fixtures = require('../common/fixtures');
const { spawnSyncAndExitWithoutError } = require('../common/child_process');
const commonArgs = [
'--experimental-shadow-realm',
];
async function main() {
// Verifies that --require preload modules are not enabled in the ShadowRealm.
spawnSyncAndExitWithoutError(process.execPath, [
...commonArgs,
'--require',
fixtures.path('es-module-shadow-realm', 'preload.js'),
fixtures.path('es-module-shadow-realm', 'preload-main.js'),
]);
}
main().then(common.mustCall());