mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
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:
parent
6b7197cb2b
commit
fc2862b7f5
@ -50,6 +50,8 @@
|
||||
|
||||
const {
|
||||
ArrayFrom,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSlice,
|
||||
@ -215,8 +217,8 @@ const internalBuiltinIds = builtinIds
|
||||
.filter((id) => StringPrototypeStartsWith(id, 'internal/') && id !== selfId);
|
||||
|
||||
// When --expose-internals is on we'll add the internal builtin ids to these.
|
||||
const canBeRequiredByUsersList = new SafeSet(publicBuiltinIds);
|
||||
const canBeRequiredByUsersWithoutSchemeList =
|
||||
let canBeRequiredByUsersList = new SafeSet(publicBuiltinIds);
|
||||
let canBeRequiredByUsersWithoutSchemeList =
|
||||
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.
|
||||
// Enables the user-land module loader to access internal modules.
|
||||
static exposeInternals() {
|
||||
|
21
lib/internal/bootstrap/shadow_realm.js
Normal file
21
lib/internal/bootstrap/shadow_realm.js
Normal 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();
|
@ -136,6 +136,7 @@ port.on('message', (message) => {
|
||||
const isLoaderWorker =
|
||||
doEval === 'internal' &&
|
||||
filename === require('internal/modules/esm/utils').loaderWorkerId;
|
||||
// Disable custom loaders in loader worker.
|
||||
setupUserModules(isLoaderWorker);
|
||||
|
||||
if (!hasStdin)
|
||||
|
@ -528,9 +528,10 @@ let emittedLoaderFlagWarning = false;
|
||||
*/
|
||||
function createModuleLoader() {
|
||||
let customizations = null;
|
||||
// Don't spawn a new worker if we're already in a worker thread created by instantiating CustomizedModuleLoader;
|
||||
// doing so would cause an infinite loop.
|
||||
if (!require('internal/modules/esm/utils').isLoaderWorker()) {
|
||||
// Don't spawn a new worker if custom loaders are disabled. For instance, if
|
||||
// we're already in a worker thread created by instantiating
|
||||
// CustomizedModuleLoader; doing so would cause an infinite loop.
|
||||
if (!require('internal/modules/esm/utils').forceDefaultLoader()) {
|
||||
const userLoaderPaths = getOptionValue('--experimental-loader');
|
||||
if (userLoaderPaths.length > 0) {
|
||||
if (!emittedLoaderFlagWarning) {
|
||||
|
@ -4,6 +4,7 @@ const {
|
||||
ArrayIsArray,
|
||||
SafeSet,
|
||||
SafeWeakMap,
|
||||
Symbol,
|
||||
ObjectFreeze,
|
||||
} = primordials;
|
||||
|
||||
@ -157,6 +158,26 @@ function registerModule(referrer, 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.
|
||||
* @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();
|
||||
}
|
||||
|
||||
let _isLoaderWorker = false;
|
||||
let _forceDefaultLoader = false;
|
||||
/**
|
||||
* Initializes handling of ES modules.
|
||||
* This is configured during pre-execution. Specifically it's set to true for
|
||||
* 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) {
|
||||
_isLoaderWorker = isLoaderWorker;
|
||||
function initializeESM(forceDefaultLoader = false) {
|
||||
_forceDefaultLoader = forceDefaultLoader;
|
||||
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.
|
||||
setInitializeImportMetaObjectCallback(initializeImportMetaObject);
|
||||
setImportModuleDynamicallyCallback(importModuleDynamicallyCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the current process is a loader worker.
|
||||
* @returns {boolean} Whether the current process is a loader worker.
|
||||
* Determine whether custom loaders are disabled and it is forced to use the
|
||||
* default loader.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isLoaderWorker() {
|
||||
return _isLoaderWorker;
|
||||
function forceDefaultLoader() {
|
||||
return _forceDefaultLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -251,10 +273,11 @@ async function initializeHooks() {
|
||||
|
||||
module.exports = {
|
||||
registerModule,
|
||||
registerRealm,
|
||||
initializeESM,
|
||||
initializeHooks,
|
||||
getDefaultConditions,
|
||||
getConditionsSet,
|
||||
loaderWorkerId: 'internal/modules/esm/worker',
|
||||
isLoaderWorker,
|
||||
forceDefaultLoader,
|
||||
};
|
||||
|
@ -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) {
|
||||
const { expandArgv1, initializeModules, isMainThread } = options;
|
||||
|
||||
@ -161,16 +181,17 @@ function setupSymbolDisposePolyfill() {
|
||||
}
|
||||
}
|
||||
|
||||
function setupUserModules(isLoaderWorker = false) {
|
||||
function setupUserModules(forceDefaultLoader = false) {
|
||||
initializeCJSLoader();
|
||||
initializeESMLoader(isLoaderWorker);
|
||||
initializeESMLoader(forceDefaultLoader);
|
||||
const CJSLoader = require('internal/modules/cjs/loader');
|
||||
assert(!CJSLoader.hasLoadedAnyUserCJSModule);
|
||||
// Loader workers are responsible for doing this themselves.
|
||||
if (isLoaderWorker) {
|
||||
return;
|
||||
// Do not enable preload modules if custom loaders are disabled.
|
||||
// For example, loader workers are responsible for doing this themselves.
|
||||
// And preload modules are not supported in ShadowRealm as well.
|
||||
if (!forceDefaultLoader) {
|
||||
loadPreloadModules();
|
||||
}
|
||||
loadPreloadModules();
|
||||
// Need to be done after --require setup.
|
||||
initializeFrozenIntrinsics();
|
||||
}
|
||||
@ -701,9 +722,9 @@ function initializeCJSLoader() {
|
||||
initializeCJS();
|
||||
}
|
||||
|
||||
function initializeESMLoader(isLoaderWorker) {
|
||||
function initializeESMLoader(forceDefaultLoader) {
|
||||
const { initializeESM } = require('internal/modules/esm/utils');
|
||||
initializeESM(isLoaderWorker);
|
||||
initializeESM(forceDefaultLoader);
|
||||
|
||||
// Patch the vm module when --experimental-vm-modules is on.
|
||||
// Please update the comments in vm.js when this block changes.
|
||||
@ -779,6 +800,7 @@ module.exports = {
|
||||
setupUserModules,
|
||||
prepareMainThreadExecution,
|
||||
prepareWorkerThreadExecution,
|
||||
prepareShadowRealmExecution,
|
||||
markBootstrapComplete,
|
||||
loadPreloadModules,
|
||||
initializeFrozenIntrinsics,
|
||||
|
@ -372,8 +372,9 @@ void AsyncWrap::CreatePerContextProperties(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context,
|
||||
void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Isolate* isolate = env->isolate();
|
||||
Realm* realm = Realm::GetCurrent(context);
|
||||
Environment* env = realm->env();
|
||||
Isolate* isolate = realm->isolate();
|
||||
HandleScope scope(isolate);
|
||||
|
||||
PropertyAttribute ReadOnlyDontDelete =
|
||||
@ -446,13 +447,16 @@ void AsyncWrap::CreatePerContextProperties(Local<Object> target,
|
||||
|
||||
#undef FORCE_SET_TARGET_FIELD
|
||||
|
||||
env->set_async_hooks_init_function(Local<Function>());
|
||||
env->set_async_hooks_before_function(Local<Function>());
|
||||
env->set_async_hooks_after_function(Local<Function>());
|
||||
env->set_async_hooks_destroy_function(Local<Function>());
|
||||
env->set_async_hooks_promise_resolve_function(Local<Function>());
|
||||
env->set_async_hooks_callback_trampoline(Local<Function>());
|
||||
env->set_async_hooks_binding(target);
|
||||
// TODO(legendecas): async hook functions are not realm-aware yet.
|
||||
// This simply avoid overriding principal realm's functions when a
|
||||
// ShadowRealm initializes the binding.
|
||||
realm->set_async_hooks_init_function(Local<Function>());
|
||||
realm->set_async_hooks_before_function(Local<Function>());
|
||||
realm->set_async_hooks_after_function(Local<Function>());
|
||||
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(
|
||||
|
11
src/env.cc
11
src/env.cc
@ -1651,10 +1651,13 @@ void AsyncHooks::MemoryInfo(MemoryTracker* tracker) const {
|
||||
void AsyncHooks::grow_async_ids_stack() {
|
||||
async_ids_stack_.reserve(async_ids_stack_.Length() * 3);
|
||||
|
||||
env()->async_hooks_binding()->Set(
|
||||
env()->context(),
|
||||
env()->async_ids_stack_string(),
|
||||
async_ids_stack_.GetJSArray()).Check();
|
||||
env()
|
||||
->principal_realm()
|
||||
->async_hooks_binding()
|
||||
->Set(env()->context(),
|
||||
env()->async_ids_stack_string(),
|
||||
async_ids_stack_.GetJSArray())
|
||||
.Check();
|
||||
}
|
||||
|
||||
void AsyncHooks::FailWithCorruptedAsyncStack(double expected_async_id) {
|
||||
|
@ -39,6 +39,7 @@ using v8::MicrotaskQueue;
|
||||
using v8::Module;
|
||||
using v8::ModuleRequest;
|
||||
using v8::Object;
|
||||
using v8::ObjectTemplate;
|
||||
using v8::PrimitiveArray;
|
||||
using v8::Promise;
|
||||
using v8::ScriptCompiler;
|
||||
@ -49,15 +50,17 @@ using v8::UnboundModuleScript;
|
||||
using v8::Undefined;
|
||||
using v8::Value;
|
||||
|
||||
ModuleWrap::ModuleWrap(Environment* env,
|
||||
ModuleWrap::ModuleWrap(Realm* realm,
|
||||
Local<Object> object,
|
||||
Local<Module> module,
|
||||
Local<String> url,
|
||||
Local<Object> context_object,
|
||||
Local<Value> synthetic_evaluation_step)
|
||||
: BaseObject(env, object),
|
||||
module_(env->isolate(), module),
|
||||
: BaseObject(realm, object),
|
||||
module_(realm->isolate(), module),
|
||||
module_hash_(module->GetIdentityHash()) {
|
||||
realm->env()->hash_to_module_map.emplace(module_hash_, this);
|
||||
|
||||
object->SetInternalField(kModuleSlot, module);
|
||||
object->SetInternalField(kURLSlot, url);
|
||||
object->SetInternalField(kSyntheticEvaluationStepsSlot,
|
||||
@ -72,7 +75,6 @@ ModuleWrap::ModuleWrap(Environment* env,
|
||||
}
|
||||
|
||||
ModuleWrap::~ModuleWrap() {
|
||||
HandleScope scope(env()->isolate());
|
||||
auto range = env()->hash_to_module_map.equal_range(module_hash_);
|
||||
for (auto it = range.first; it != range.second; ++it) {
|
||||
if (it->second == this) {
|
||||
@ -107,8 +109,8 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK(args.IsConstructCall());
|
||||
CHECK_GE(args.Length(), 3);
|
||||
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = realm->isolate();
|
||||
|
||||
Local<Object> that = args.This();
|
||||
|
||||
@ -122,7 +124,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
} else {
|
||||
CHECK(args[1]->IsObject());
|
||||
contextify_context = ContextifyContext::ContextFromContextifiedSandbox(
|
||||
env, args[1].As<Object>());
|
||||
realm->env(), args[1].As<Object>());
|
||||
CHECK_NOT_NULL(contextify_context);
|
||||
context = contextify_context->context();
|
||||
}
|
||||
@ -148,8 +150,8 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
Local<Symbol> id_symbol = Symbol::New(isolate, url);
|
||||
host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol);
|
||||
|
||||
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
|
||||
TryCatchScope try_catch(env);
|
||||
ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
|
||||
TryCatchScope try_catch(realm->env());
|
||||
|
||||
Local<Module> module;
|
||||
|
||||
@ -206,7 +208,9 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
||||
CHECK(!try_catch.Message().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);
|
||||
try_catch.ReThrow();
|
||||
}
|
||||
@ -215,18 +219,21 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
if (options == ScriptCompiler::kConsumeCodeCache &&
|
||||
source.GetCachedData()->rejected) {
|
||||
THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
|
||||
env, "cachedData buffer was rejected");
|
||||
realm, "cachedData buffer was rejected");
|
||||
try_catch.ReThrow();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!that->Set(context, env->url_string(), url).FromMaybe(false)) {
|
||||
if (!that->Set(context, realm->isolate_data()->url_string(), url)
|
||||
.FromMaybe(false)) {
|
||||
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()) {
|
||||
return;
|
||||
}
|
||||
@ -236,28 +243,26 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
|
||||
// be stored in an internal field.
|
||||
Local<Object> context_object = context->GetExtrasBindingObject();
|
||||
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(
|
||||
env, that, module, url, context_object, synthetic_evaluation_step);
|
||||
realm, that, module, url, context_object, synthetic_evaluation_step);
|
||||
|
||||
obj->contextify_context_ = contextify_context;
|
||||
|
||||
env->hash_to_module_map.emplace(module->GetIdentityHash(), obj);
|
||||
|
||||
that->SetIntegrityLevel(context, IntegrityLevel::kFrozen);
|
||||
args.GetReturnValue().Set(that);
|
||||
}
|
||||
|
||||
static Local<Object> createImportAttributesContainer(
|
||||
Environment* env, Isolate* isolate, Local<FixedArray> raw_attributes) {
|
||||
Realm* realm, Isolate* isolate, Local<FixedArray> raw_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) {
|
||||
attributes
|
||||
->Set(env->context(),
|
||||
raw_attributes->Get(env->context(), i).As<String>(),
|
||||
raw_attributes->Get(env->context(), i + 1).As<Value>())
|
||||
->Set(realm->context(),
|
||||
raw_attributes->Get(realm->context(), i).As<String>(),
|
||||
raw_attributes->Get(realm->context(), i + 1).As<Value>())
|
||||
.ToChecked();
|
||||
}
|
||||
|
||||
@ -265,7 +270,7 @@ static Local<Object> createImportAttributesContainer(
|
||||
}
|
||||
|
||||
void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
@ -292,14 +297,14 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
// call the dependency resolve callbacks
|
||||
for (int i = 0; i < module_requests_length; i++) {
|
||||
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();
|
||||
Utf8Value specifier_utf8(env->isolate(), specifier);
|
||||
Utf8Value specifier_utf8(realm->isolate(), specifier);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
Local<FixedArray> raw_attributes = module_request->GetImportAssertions();
|
||||
Local<Object> attributes =
|
||||
createImportAttributesContainer(env, isolate, raw_attributes);
|
||||
createImportAttributesContainer(realm, isolate, raw_attributes);
|
||||
|
||||
Local<Value> argv[] = {
|
||||
specifier,
|
||||
@ -315,11 +320,11 @@ void ModuleWrap::Link(const FunctionCallbackInfo<Value>& args) {
|
||||
maybe_resolve_return_value.ToLocalChecked();
|
||||
if (!resolve_return_value->IsPromise()) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -329,13 +334,13 @@ void ModuleWrap::Link(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();
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
||||
Local<Context> context = obj->context();
|
||||
Local<Module> module = obj->module_.Get(isolate);
|
||||
TryCatchScope try_catch(env);
|
||||
TryCatchScope try_catch(realm->env());
|
||||
USE(module->InstantiateModule(context, ResolveModuleCallback));
|
||||
|
||||
// clear resolve cache on instantiate
|
||||
@ -344,7 +349,9 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
||||
CHECK(!try_catch.Message().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);
|
||||
try_catch.ReThrow();
|
||||
return;
|
||||
@ -352,8 +359,8 @@ void ModuleWrap::Instantiate(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
|
||||
void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = realm->isolate();
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
||||
Local<Context> context = obj->context();
|
||||
@ -368,14 +375,14 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||
CHECK_EQ(args.Length(), 2);
|
||||
|
||||
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());
|
||||
bool break_on_sigint = args[1]->IsTrue();
|
||||
|
||||
ShouldNotAbortOnUncaughtScope no_abort_scope(env);
|
||||
TryCatchScope try_catch(env);
|
||||
Isolate::SafeForTerminationScope safe_for_termination(env->isolate());
|
||||
ShouldNotAbortOnUncaughtScope no_abort_scope(realm->env());
|
||||
TryCatchScope try_catch(realm->env());
|
||||
Isolate::SafeForTerminationScope safe_for_termination(isolate);
|
||||
|
||||
bool timed_out = false;
|
||||
bool received_signal = false;
|
||||
@ -406,16 +413,15 @@ void ModuleWrap::Evaluate(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
// Convert the termination exception into a regular exception.
|
||||
if (timed_out || received_signal) {
|
||||
if (!env->is_main_thread() && env->is_stopping())
|
||||
return;
|
||||
env->isolate()->CancelTerminateExecution();
|
||||
if (!realm->env()->is_main_thread() && realm->env()->is_stopping()) return;
|
||||
isolate->CancelTerminateExecution();
|
||||
// It is possible that execution was terminated by another timeout in
|
||||
// which this timeout is nested, so check whether one of the watchdogs
|
||||
// from this invocation is responsible for termination.
|
||||
if (timed_out) {
|
||||
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(env, timeout);
|
||||
THROW_ERR_SCRIPT_EXECUTION_TIMEOUT(realm->env(), timeout);
|
||||
} 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) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
ModuleWrap* obj;
|
||||
ASSIGN_OR_RETURN_UNWRAP(&obj, args.This());
|
||||
@ -439,7 +445,7 @@ void ModuleWrap::GetNamespace(const FunctionCallbackInfo<Value>& args) {
|
||||
switch (module->GetStatus()) {
|
||||
case v8::Module::Status::kUninstantiated:
|
||||
case v8::Module::Status::kInstantiating:
|
||||
return env->ThrowError(
|
||||
return realm->env()->ThrowError(
|
||||
"cannot get namespace, module has not been instantiated");
|
||||
case v8::Module::Status::kInstantiated:
|
||||
case v8::Module::Status::kEvaluating:
|
||||
@ -466,11 +472,11 @@ void ModuleWrap::GetStatus(const FunctionCallbackInfo<Value>& args) {
|
||||
|
||||
void ModuleWrap::GetStaticDependencySpecifiers(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
ModuleWrap* obj;
|
||||
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();
|
||||
int count = module_requests->Length();
|
||||
@ -479,12 +485,12 @@ void ModuleWrap::GetStaticDependencySpecifiers(
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
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();
|
||||
}
|
||||
|
||||
args.GetReturnValue().Set(
|
||||
Array::New(env->isolate(), specifiers.out(), count));
|
||||
Array::New(realm->isolate(), specifiers.out(), count));
|
||||
}
|
||||
|
||||
void ModuleWrap::GetError(const FunctionCallbackInfo<Value>& args) {
|
||||
@ -501,15 +507,13 @@ MaybeLocal<Module> ModuleWrap::ResolveModuleCallback(
|
||||
Local<String> specifier,
|
||||
Local<FixedArray> import_attributes,
|
||||
Local<Module> referrer) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
if (env == nullptr) {
|
||||
Isolate* isolate = context->GetIsolate();
|
||||
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
|
||||
return MaybeLocal<Module>();
|
||||
}
|
||||
|
||||
Isolate* isolate = env->isolate();
|
||||
|
||||
Utf8Value specifier_utf8(isolate, specifier);
|
||||
std::string specifier_std(*specifier_utf8, specifier_utf8.length());
|
||||
|
||||
@ -559,11 +563,16 @@ static MaybeLocal<Promise> ImportModuleDynamically(
|
||||
THROW_ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE(isolate);
|
||||
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);
|
||||
|
||||
Local<Function> import_callback =
|
||||
env->host_import_module_dynamically_callback();
|
||||
realm->host_import_module_dynamically_callback();
|
||||
Local<Value> id;
|
||||
|
||||
Local<FixedArray> options = host_defined_options.As<FixedArray>();
|
||||
@ -579,7 +588,7 @@ static MaybeLocal<Promise> ImportModuleDynamically(
|
||||
}
|
||||
|
||||
Local<Object> attributes =
|
||||
createImportAttributesContainer(env, isolate, import_attributes);
|
||||
createImportAttributesContainer(realm, isolate, import_attributes);
|
||||
|
||||
Local<Value> import_args[] = {
|
||||
id,
|
||||
@ -603,13 +612,13 @@ static MaybeLocal<Promise> ImportModuleDynamically(
|
||||
void ModuleWrap::SetImportModuleDynamicallyCallback(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Isolate* isolate = args.GetIsolate();
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
HandleScope handle_scope(isolate);
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
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);
|
||||
}
|
||||
@ -624,10 +633,15 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
||||
if (module_wrap == nullptr) {
|
||||
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<Function> callback =
|
||||
env->host_initialize_import_meta_object_callback();
|
||||
realm->host_initialize_import_meta_object_callback();
|
||||
Local<Value> id;
|
||||
if (!wrap->GetPrivate(context, env->host_defined_option_symbol())
|
||||
.ToLocal(&id)) {
|
||||
@ -637,7 +651,7 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
||||
Local<Value> args[] = {id, meta};
|
||||
TryCatchScope try_catch(env);
|
||||
USE(callback->Call(
|
||||
context, Undefined(env->isolate()), arraysize(args), args));
|
||||
context, Undefined(realm->isolate()), arraysize(args), args));
|
||||
if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
|
||||
try_catch.ReThrow();
|
||||
}
|
||||
@ -645,13 +659,13 @@ void ModuleWrap::HostInitializeImportMetaObjectCallback(
|
||||
|
||||
void ModuleWrap::SetInitializeImportMetaObjectCallback(
|
||||
const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
Isolate* isolate = env->isolate();
|
||||
Realm* realm = Realm::GetCurrent(args);
|
||||
Isolate* isolate = realm->isolate();
|
||||
|
||||
CHECK_EQ(args.Length(), 1);
|
||||
CHECK(args[0]->IsFunction());
|
||||
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(
|
||||
HostInitializeImportMetaObjectCallback);
|
||||
@ -742,12 +756,9 @@ void ModuleWrap::CreateCachedData(const FunctionCallbackInfo<Value>& args) {
|
||||
}
|
||||
}
|
||||
|
||||
void ModuleWrap::Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context,
|
||||
void* priv) {
|
||||
Environment* env = Environment::GetCurrent(context);
|
||||
Isolate* isolate = env->isolate();
|
||||
void ModuleWrap::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
Local<ObjectTemplate> target) {
|
||||
Isolate* isolate = isolate_data->isolate();
|
||||
|
||||
Local<FunctionTemplate> tpl = NewFunctionTemplate(isolate, New);
|
||||
tpl->InstanceTemplate()->SetInternalFieldCount(
|
||||
@ -767,28 +778,36 @@ void ModuleWrap::Initialize(Local<Object> target,
|
||||
"getStaticDependencySpecifiers",
|
||||
GetStaticDependencySpecifiers);
|
||||
|
||||
SetConstructorFunction(context, target, "ModuleWrap", tpl);
|
||||
SetConstructorFunction(isolate, target, "ModuleWrap", tpl);
|
||||
|
||||
SetMethod(context,
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
"setImportModuleDynamicallyCallback",
|
||||
SetImportModuleDynamicallyCallback);
|
||||
SetMethod(context,
|
||||
SetMethod(isolate,
|
||||
target,
|
||||
"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) \
|
||||
target->Set(context, \
|
||||
FIXED_ONE_BYTE_STRING(env->isolate(), #name), \
|
||||
Integer::New(env->isolate(), Module::Status::name)) \
|
||||
.FromJust()
|
||||
V(kUninstantiated);
|
||||
V(kInstantiating);
|
||||
V(kInstantiated);
|
||||
V(kEvaluating);
|
||||
V(kEvaluated);
|
||||
V(kErrored);
|
||||
target \
|
||||
->Set(context, \
|
||||
FIXED_ONE_BYTE_STRING(isolate, #name), \
|
||||
Integer::New(isolate, Module::Status::name)) \
|
||||
.FromJust()
|
||||
V(kUninstantiated);
|
||||
V(kInstantiating);
|
||||
V(kInstantiated);
|
||||
V(kEvaluating);
|
||||
V(kEvaluated);
|
||||
V(kErrored);
|
||||
#undef V
|
||||
}
|
||||
|
||||
@ -812,7 +831,9 @@ void ModuleWrap::RegisterExternalReferences(
|
||||
} // namespace loader
|
||||
} // namespace node
|
||||
|
||||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(module_wrap,
|
||||
node::loader::ModuleWrap::Initialize)
|
||||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(
|
||||
module_wrap, node::loader::ModuleWrap::CreatePerContextProperties)
|
||||
NODE_BINDING_PER_ISOLATE_INIT(
|
||||
module_wrap, node::loader::ModuleWrap::CreatePerIsolateProperties)
|
||||
NODE_BINDING_EXTERNAL_REFERENCE(
|
||||
module_wrap, node::loader::ModuleWrap::RegisterExternalReferences)
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
namespace node {
|
||||
|
||||
class IsolateData;
|
||||
class Environment;
|
||||
class ExternalReferenceRegistry;
|
||||
|
||||
@ -40,10 +41,12 @@ class ModuleWrap : public BaseObject {
|
||||
kInternalFieldCount
|
||||
};
|
||||
|
||||
static void Initialize(v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv);
|
||||
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||
v8::Local<v8::ObjectTemplate> target);
|
||||
static void CreatePerContextProperties(v8::Local<v8::Object> target,
|
||||
v8::Local<v8::Value> unused,
|
||||
v8::Local<v8::Context> context,
|
||||
void* priv);
|
||||
static void RegisterExternalReferences(ExternalReferenceRegistry* registry);
|
||||
static void HostInitializeImportMetaObjectCallback(
|
||||
v8::Local<v8::Context> context,
|
||||
@ -66,7 +69,7 @@ class ModuleWrap : public BaseObject {
|
||||
}
|
||||
|
||||
private:
|
||||
ModuleWrap(Environment* env,
|
||||
ModuleWrap(Realm* realm,
|
||||
v8::Local<v8::Object> object,
|
||||
v8::Local<v8::Module> module,
|
||||
v8::Local<v8::String> url,
|
||||
|
@ -40,6 +40,7 @@ static_assert(static_cast<int>(NM_F_LINKED) ==
|
||||
V(fs_dir) \
|
||||
V(messaging) \
|
||||
V(mksnapshot) \
|
||||
V(module_wrap) \
|
||||
V(performance) \
|
||||
V(process_methods) \
|
||||
V(timers) \
|
||||
|
@ -123,6 +123,10 @@ void AppendExceptionLine(Environment* env,
|
||||
inline void THROW_##code( \
|
||||
Environment* env, const char* format, 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)
|
||||
#undef V
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "node_shadow_realm.h"
|
||||
#include "env-inl.h"
|
||||
#include "node_errors.h"
|
||||
#include "node_process.h"
|
||||
|
||||
namespace node {
|
||||
namespace shadow_realm {
|
||||
@ -9,6 +10,8 @@ using v8::EscapableHandleScope;
|
||||
using v8::HandleScope;
|
||||
using v8::Local;
|
||||
using v8::MaybeLocal;
|
||||
using v8::Object;
|
||||
using v8::String;
|
||||
using v8::Value;
|
||||
|
||||
using TryCatchScope = node::errors::TryCatchScope;
|
||||
@ -16,6 +19,11 @@ using TryCatchScope = node::errors::TryCatchScope;
|
||||
// static
|
||||
ShadowRealm* ShadowRealm::New(Environment* 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
|
||||
// exceptions. If it does, exit the current Node.js instance.
|
||||
@ -32,6 +40,10 @@ MaybeLocal<Context> HostCreateShadowRealmContextCallback(
|
||||
Local<Context> initiator_context) {
|
||||
Environment* env = Environment::GetCurrent(initiator_context);
|
||||
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);
|
||||
if (realm != nullptr) {
|
||||
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_);
|
||||
}
|
||||
|
||||
|
15
test/fixtures/es-module-shadow-realm/custom-loaders.js
vendored
Normal file
15
test/fixtures/es-module-shadow-realm/custom-loaders.js
vendored
Normal 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();
|
9
test/fixtures/es-module-shadow-realm/preload-main.js
vendored
Normal file
9
test/fixtures/es-module-shadow-realm/preload-main.js
vendored
Normal 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);
|
1
test/fixtures/es-module-shadow-realm/preload.js
vendored
Normal file
1
test/fixtures/es-module-shadow-realm/preload.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
globalThis.preload = 42;
|
3
test/fixtures/es-module-shadow-realm/re-export-state-counter.mjs
vendored
Normal file
3
test/fixtures/es-module-shadow-realm/re-export-state-counter.mjs
vendored
Normal 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";
|
4
test/fixtures/es-module-shadow-realm/state-counter.mjs
vendored
Normal file
4
test/fixtures/es-module-shadow-realm/state-counter.mjs
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
let counter = 0;
|
||||
export const getCounter = () => {
|
||||
return counter++;
|
||||
};
|
21
test/parallel/test-shadow-realm-allowed-builtin-modules.js
Normal file
21
test/parallel/test-shadow-realm-allowed-builtin-modules.js
Normal 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());
|
26
test/parallel/test-shadow-realm-custom-loaders.js
Normal file
26
test/parallel/test-shadow-realm-custom-loaders.js
Normal 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());
|
20
test/parallel/test-shadow-realm-gc-module.js
Normal file
20
test/parallel/test-shadow-realm-gc-module.js
Normal 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());
|
28
test/parallel/test-shadow-realm-import-value-resolve.js
Normal file
28
test/parallel/test-shadow-realm-import-value-resolve.js
Normal 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());
|
29
test/parallel/test-shadow-realm-module.js
Normal file
29
test/parallel/test-shadow-realm-module.js
Normal 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());
|
20
test/parallel/test-shadow-realm-preload-module.js
Normal file
20
test/parallel/test-shadow-realm-preload-module.js
Normal 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());
|
Loading…
Reference in New Issue
Block a user