1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

fix(core): allow esm extensions not included in snapshot (#18980)

Fixes #18979.

This changes the predicate for allowing `ext:` specifier resolution from
`snapshot_loaded_and_not_snapshotting` to `ext_resolution_allowed` which
is only set to true during the extension module loading phase. Module
loaders as used in core
are now declared as `ExtModuleLoader` rather than `dyn ModuleLoader`.
This commit is contained in:
Nayeem Rahman 2023-05-04 01:44:59 +01:00 committed by GitHub
parent e3276fbb71
commit 7a8bb3b611
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 221 additions and 365 deletions

View file

@ -10,9 +10,7 @@ use crate::profiling::is_profiling;
pub fn create_js_runtime(setup: impl FnOnce() -> Vec<Extension>) -> JsRuntime { pub fn create_js_runtime(setup: impl FnOnce() -> Vec<Extension>) -> JsRuntime {
JsRuntime::new(RuntimeOptions { JsRuntime::new(RuntimeOptions {
extensions: setup(), extensions: setup(),
module_loader: Some( module_loader: None,
std::rc::Rc::new(deno_core::ExtModuleLoader::default()),
),
..Default::default() ..Default::default()
}) })
} }

View file

@ -1,4 +1,4 @@
error: Uncaught TypeError: Cannot load extension module from external code error: Uncaught (in promise) TypeError: Cannot load extension module from external code
await import("ext:runtime/01_errors.js"); await import("ext:runtime/01_errors.js");
^ ^
at [WILDCARD]/extension_dynamic_import.ts:1:1 at [WILDCARD]/extension_dynamic_import.ts:1:1

View file

@ -10,7 +10,6 @@ use crate::error::is_instance_of_error;
use crate::error::JsStackFrame; use crate::error::JsStackFrame;
use crate::modules::get_asserted_module_type_from_assertions; use crate::modules::get_asserted_module_type_from_assertions;
use crate::modules::parse_import_assertions; use crate::modules::parse_import_assertions;
use crate::modules::resolve_helper;
use crate::modules::validate_import_assertions; use crate::modules::validate_import_assertions;
use crate::modules::ImportAssertionsKind; use crate::modules::ImportAssertionsKind;
use crate::modules::ModuleMap; use crate::modules::ModuleMap;
@ -259,46 +258,43 @@ pub fn host_import_module_dynamically_callback<'s>(
.unwrap() .unwrap()
.to_rust_string_lossy(scope); .to_rust_string_lossy(scope);
let is_ext_module = specifier_str.starts_with("ext:");
let resolver = v8::PromiseResolver::new(scope).unwrap(); let resolver = v8::PromiseResolver::new(scope).unwrap();
let promise = resolver.get_promise(scope); let promise = resolver.get_promise(scope);
if !is_ext_module { let assertions = parse_import_assertions(
let assertions = parse_import_assertions( scope,
scope, import_assertions,
import_assertions, ImportAssertionsKind::DynamicImport,
ImportAssertionsKind::DynamicImport, );
{
let tc_scope = &mut v8::TryCatch::new(scope);
validate_import_assertions(tc_scope, &assertions);
if tc_scope.has_caught() {
let e = tc_scope.exception().unwrap();
resolver.reject(tc_scope, e);
}
}
let asserted_module_type =
get_asserted_module_type_from_assertions(&assertions);
let resolver_handle = v8::Global::new(scope, resolver);
{
let state_rc = JsRuntime::state(scope);
let module_map_rc = JsRuntime::module_map(scope);
debug!(
"dyn_import specifier {} referrer {} ",
specifier_str, referrer_name_str
); );
ModuleMap::load_dynamic_import(
{ module_map_rc,
let tc_scope = &mut v8::TryCatch::new(scope); &specifier_str,
validate_import_assertions(tc_scope, &assertions); &referrer_name_str,
if tc_scope.has_caught() { asserted_module_type,
let e = tc_scope.exception().unwrap(); resolver_handle,
resolver.reject(tc_scope, e); );
} state_rc.borrow_mut().notify_new_dynamic_import();
}
let asserted_module_type =
get_asserted_module_type_from_assertions(&assertions);
let resolver_handle = v8::Global::new(scope, resolver);
{
let state_rc = JsRuntime::state(scope);
let module_map_rc = JsRuntime::module_map(scope);
debug!(
"dyn_import specifier {} referrer {} ",
specifier_str, referrer_name_str
);
ModuleMap::load_dynamic_import(
module_map_rc,
&specifier_str,
&referrer_name_str,
asserted_module_type,
resolver_handle,
);
state_rc.borrow_mut().notify_new_dynamic_import();
}
} }
// Map errors from module resolution (not JS errors from module execution) to // Map errors from module resolution (not JS errors from module execution) to
// ones rethrown from this scope, so they include the call stack of the // ones rethrown from this scope, so they include the call stack of the
@ -311,16 +307,6 @@ pub fn host_import_module_dynamically_callback<'s>(
let promise = promise.catch(scope, map_err).unwrap(); let promise = promise.catch(scope, map_err).unwrap();
if is_ext_module {
let message = v8::String::new_external_onebyte_static(
scope,
b"Cannot load extension module from external code",
)
.unwrap();
let exception = v8::Exception::type_error(scope, message);
resolver.reject(scope, exception);
}
Some(promise) Some(promise)
} }
@ -375,13 +361,7 @@ fn import_meta_resolve(
url_prop.to_rust_string_lossy(scope) url_prop.to_rust_string_lossy(scope)
}; };
let module_map_rc = JsRuntime::module_map(scope); let module_map_rc = JsRuntime::module_map(scope);
let (loader, snapshot_loaded_and_not_snapshotting) = { let loader = module_map_rc.borrow().loader.clone();
let module_map = module_map_rc.borrow();
(
module_map.loader.clone(),
module_map.snapshot_loaded_and_not_snapshotting,
)
};
let specifier_str = specifier.to_rust_string_lossy(scope); let specifier_str = specifier.to_rust_string_lossy(scope);
if specifier_str.starts_with("npm:") { if specifier_str.starts_with("npm:") {
@ -389,13 +369,8 @@ fn import_meta_resolve(
return; return;
} }
match resolve_helper( match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport)
snapshot_loaded_and_not_snapshotting, {
loader,
&specifier_str,
&referrer,
ResolutionKind::DynamicImport,
) {
Ok(resolved) => { Ok(resolved) => {
let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap(); let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
rv.set(resolved_val); rv.set(resolved_val);

View file

@ -471,6 +471,16 @@ impl Extension {
pub fn disable(self) -> Self { pub fn disable(self) -> Self {
self.enabled(false) self.enabled(false)
} }
pub(crate) fn find_esm(
&self,
specifier: &str,
) -> Option<&ExtensionFileSource> {
self
.get_esm_sources()?
.iter()
.find(|s| s.specifier == specifier)
}
} }
// Provides a convenient builder pattern to declare Extensions // Provides a convenient builder pattern to declare Extensions

View file

@ -9,6 +9,7 @@ use crate::module_specifier::ModuleSpecifier;
use crate::resolve_import; use crate::resolve_import;
use crate::resolve_url; use crate::resolve_url;
use crate::snapshot_util::SnapshottedData; use crate::snapshot_util::SnapshottedData;
use crate::Extension;
use crate::JsRuntime; use crate::JsRuntime;
use crate::OpState; use crate::OpState;
use anyhow::Error; use anyhow::Error;
@ -379,25 +380,6 @@ impl ModuleLoader for NoopModuleLoader {
} }
} }
/// Helper function, that calls into `loader.resolve()`, but denies resolution
/// of `ext` scheme if we are running with a snapshot loaded and not
/// creating a snapshot
pub(crate) fn resolve_helper(
snapshot_loaded_and_not_snapshotting: bool,
loader: Rc<dyn ModuleLoader>,
specifier: &str,
referrer: &str,
kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> {
if snapshot_loaded_and_not_snapshotting && specifier.starts_with("ext:") {
return Err(generic_error(
"Cannot load extension module from external code",
));
}
loader.resolve(specifier, referrer, kind)
}
/// Function that can be passed to the `ExtModuleLoader` that allows to /// Function that can be passed to the `ExtModuleLoader` that allows to
/// transpile sources before passing to V8. /// transpile sources before passing to V8.
pub type ExtModuleLoaderCb = pub type ExtModuleLoaderCb =
@ -405,7 +387,8 @@ pub type ExtModuleLoaderCb =
pub struct ExtModuleLoader { pub struct ExtModuleLoader {
module_loader: Rc<dyn ModuleLoader>, module_loader: Rc<dyn ModuleLoader>,
esm_sources: Vec<ExtensionFileSource>, extensions: Rc<RefCell<Vec<Extension>>>,
ext_resolution_allowed: RefCell<bool>,
used_esm_sources: RefCell<HashMap<String, bool>>, used_esm_sources: RefCell<HashMap<String, bool>>,
maybe_load_callback: Option<ExtModuleLoaderCb>, maybe_load_callback: Option<ExtModuleLoaderCb>,
} }
@ -414,7 +397,8 @@ impl Default for ExtModuleLoader {
fn default() -> Self { fn default() -> Self {
Self { Self {
module_loader: Rc::new(NoopModuleLoader), module_loader: Rc::new(NoopModuleLoader),
esm_sources: vec![], extensions: Default::default(),
ext_resolution_allowed: Default::default(),
used_esm_sources: RefCell::new(HashMap::default()), used_esm_sources: RefCell::new(HashMap::default()),
maybe_load_callback: None, maybe_load_callback: None,
} }
@ -424,70 +408,46 @@ impl Default for ExtModuleLoader {
impl ExtModuleLoader { impl ExtModuleLoader {
pub fn new( pub fn new(
module_loader: Option<Rc<dyn ModuleLoader>>, module_loader: Option<Rc<dyn ModuleLoader>>,
esm_sources: Vec<ExtensionFileSource>, extensions: Rc<RefCell<Vec<Extension>>>,
maybe_load_callback: Option<ExtModuleLoaderCb>, maybe_load_callback: Option<ExtModuleLoaderCb>,
) -> Self { ) -> Self {
let used_esm_sources: HashMap<String, bool> = esm_sources let used_esm_sources: HashMap<String, bool> = extensions
.borrow()
.iter() .iter()
.flat_map(|e| e.get_esm_sources())
.flatten()
.map(|file_source| (file_source.specifier.to_string(), false)) .map(|file_source| (file_source.specifier.to_string(), false))
.collect(); .collect();
ExtModuleLoader { ExtModuleLoader {
module_loader: module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)), module_loader: module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)),
esm_sources, extensions,
ext_resolution_allowed: Default::default(),
used_esm_sources: RefCell::new(used_esm_sources), used_esm_sources: RefCell::new(used_esm_sources),
maybe_load_callback, maybe_load_callback,
} }
} }
}
impl Drop for ExtModuleLoader { pub fn resolve(
fn drop(&mut self) {
let used_esm_sources = self.used_esm_sources.get_mut();
let unused_modules: Vec<_> = used_esm_sources
.iter()
.filter(|(_s, v)| !*v)
.map(|(s, _)| s)
.collect();
if !unused_modules.is_empty() {
let mut msg =
"Following modules were passed to ExtModuleLoader but never used:\n"
.to_string();
for m in unused_modules {
msg.push_str(" - ");
msg.push_str(m);
msg.push('\n');
}
panic!("{}", msg);
}
}
}
impl ModuleLoader for ExtModuleLoader {
fn resolve(
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
kind: ResolutionKind, kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
if let Ok(url_specifier) = ModuleSpecifier::parse(specifier) { if specifier.starts_with("ext:") {
if url_specifier.scheme() == "ext" { if !referrer.starts_with("ext:") && referrer != "."
let referrer_specifier = ModuleSpecifier::parse(referrer).ok(); || !*self.ext_resolution_allowed.borrow()
if referrer == "." || referrer_specifier.unwrap().scheme() == "ext" { {
return Ok(url_specifier); return Err(generic_error(
} else { "Cannot load extension module from external code",
return Err(generic_error( ));
"Cannot load extension module from external code",
));
};
} }
return Ok(ModuleSpecifier::parse(specifier)?);
} }
self.module_loader.resolve(specifier, referrer, kind) self.module_loader.resolve(specifier, referrer, kind)
} }
fn load( pub fn load(
&self, &self,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
@ -502,10 +462,10 @@ impl ModuleLoader for ExtModuleLoader {
} }
let specifier = module_specifier.to_string(); let specifier = module_specifier.to_string();
let maybe_file_source = self let extensions = self.extensions.borrow();
.esm_sources let maybe_file_source = extensions
.iter() .iter()
.find(|file_source| file_source.specifier == module_specifier.as_str()); .find_map(|e| e.find_esm(module_specifier.as_str()));
if let Some(file_source) = maybe_file_source { if let Some(file_source) = maybe_file_source {
{ {
@ -538,7 +498,7 @@ impl ModuleLoader for ExtModuleLoader {
.boxed_local() .boxed_local()
} }
fn prepare_load( pub fn prepare_load(
&self, &self,
op_state: Rc<RefCell<OpState>>, op_state: Rc<RefCell<OpState>>,
module_specifier: &ModuleSpecifier, module_specifier: &ModuleSpecifier,
@ -556,6 +516,37 @@ impl ModuleLoader for ExtModuleLoader {
is_dyn_import, is_dyn_import,
) )
} }
pub fn allow_ext_resolution(&self) {
*self.ext_resolution_allowed.borrow_mut() = true;
}
pub fn disallow_ext_resolution(&self) {
*self.ext_resolution_allowed.borrow_mut() = false;
}
}
impl Drop for ExtModuleLoader {
fn drop(&mut self) {
let used_esm_sources = self.used_esm_sources.get_mut();
let unused_modules: Vec<_> = used_esm_sources
.iter()
.filter(|(_s, v)| !*v)
.map(|(s, _)| s)
.collect();
if !unused_modules.is_empty() {
let mut msg =
"Following modules were passed to ExtModuleLoader but never used:\n"
.to_string();
for m in unused_modules {
msg.push_str(" - ");
msg.push_str(m);
msg.push('\n');
}
panic!("{}", msg);
}
}
} }
/// Basic file system module loader. /// Basic file system module loader.
@ -643,8 +634,7 @@ pub(crate) struct RecursiveModuleLoad {
// These three fields are copied from `module_map_rc`, but they are cloned // These three fields are copied from `module_map_rc`, but they are cloned
// ahead of time to avoid already-borrowed errors. // ahead of time to avoid already-borrowed errors.
op_state: Rc<RefCell<OpState>>, op_state: Rc<RefCell<OpState>>,
loader: Rc<dyn ModuleLoader>, loader: Rc<ExtModuleLoader>,
snapshot_loaded_and_not_snapshotting: bool,
} }
impl RecursiveModuleLoad { impl RecursiveModuleLoad {
@ -700,9 +690,6 @@ impl RecursiveModuleLoad {
init, init,
state: LoadState::Init, state: LoadState::Init,
module_map_rc: module_map_rc.clone(), module_map_rc: module_map_rc.clone(),
snapshot_loaded_and_not_snapshotting: module_map_rc
.borrow()
.snapshot_loaded_and_not_snapshotting,
op_state, op_state,
loader, loader,
pending: FuturesUnordered::new(), pending: FuturesUnordered::new(),
@ -731,29 +718,17 @@ impl RecursiveModuleLoad {
fn resolve_root(&self) -> Result<ModuleSpecifier, Error> { fn resolve_root(&self) -> Result<ModuleSpecifier, Error> {
match self.init { match self.init {
LoadInit::Main(ref specifier) => resolve_helper( LoadInit::Main(ref specifier) => {
self.snapshot_loaded_and_not_snapshotting, self
self.loader.clone(), .loader
specifier, .resolve(specifier, ".", ResolutionKind::MainModule)
".",
ResolutionKind::MainModule,
),
LoadInit::Side(ref specifier) => resolve_helper(
self.snapshot_loaded_and_not_snapshotting,
self.loader.clone(),
specifier,
".",
ResolutionKind::Import,
),
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
resolve_helper(
self.snapshot_loaded_and_not_snapshotting,
self.loader.clone(),
specifier,
referrer,
ResolutionKind::DynamicImport,
)
} }
LoadInit::Side(ref specifier) => {
self.loader.resolve(specifier, ".", ResolutionKind::Import)
}
LoadInit::DynamicImport(ref specifier, ref referrer, _) => self
.loader
.resolve(specifier, referrer, ResolutionKind::DynamicImport),
} }
} }
@ -762,29 +737,21 @@ impl RecursiveModuleLoad {
let (module_specifier, maybe_referrer) = match self.init { let (module_specifier, maybe_referrer) = match self.init {
LoadInit::Main(ref specifier) => { LoadInit::Main(ref specifier) => {
let spec = resolve_helper( let spec =
self.snapshot_loaded_and_not_snapshotting, self
self.loader.clone(), .loader
specifier, .resolve(specifier, ".", ResolutionKind::MainModule)?;
".",
ResolutionKind::MainModule,
)?;
(spec, None) (spec, None)
} }
LoadInit::Side(ref specifier) => { LoadInit::Side(ref specifier) => {
let spec = resolve_helper( let spec =
self.snapshot_loaded_and_not_snapshotting, self
self.loader.clone(), .loader
specifier, .resolve(specifier, ".", ResolutionKind::Import)?;
".",
ResolutionKind::Import,
)?;
(spec, None) (spec, None)
} }
LoadInit::DynamicImport(ref specifier, ref referrer, _) => { LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
let spec = resolve_helper( let spec = self.loader.resolve(
self.snapshot_loaded_and_not_snapshotting,
self.loader.clone(),
specifier, specifier,
referrer, referrer,
ResolutionKind::DynamicImport, ResolutionKind::DynamicImport,
@ -1093,7 +1060,7 @@ pub(crate) struct ModuleMap {
pub(crate) next_load_id: ModuleLoadId, pub(crate) next_load_id: ModuleLoadId,
// Handling of futures for loading module sources // Handling of futures for loading module sources
pub loader: Rc<dyn ModuleLoader>, pub loader: Rc<ExtModuleLoader>,
op_state: Rc<RefCell<OpState>>, op_state: Rc<RefCell<OpState>>,
pub(crate) dynamic_import_map: pub(crate) dynamic_import_map:
HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>, HashMap<ModuleLoadId, v8::Global<v8::PromiseResolver>>,
@ -1105,8 +1072,6 @@ pub(crate) struct ModuleMap {
// This store is used temporarly, to forward parsed JSON // This store is used temporarly, to forward parsed JSON
// value from `new_json_module` to `json_module_evaluation_steps` // value from `new_json_module` to `json_module_evaluation_steps`
json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>, json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>,
pub(crate) snapshot_loaded_and_not_snapshotting: bool,
} }
impl ModuleMap { impl ModuleMap {
@ -1381,9 +1346,8 @@ impl ModuleMap {
} }
pub(crate) fn new( pub(crate) fn new(
loader: Rc<dyn ModuleLoader>, loader: Rc<ExtModuleLoader>,
op_state: Rc<RefCell<OpState>>, op_state: Rc<RefCell<OpState>>,
snapshot_loaded_and_not_snapshotting: bool,
) -> ModuleMap { ) -> ModuleMap {
Self { Self {
handles: vec![], handles: vec![],
@ -1397,7 +1361,6 @@ impl ModuleMap {
preparing_dynamic_imports: FuturesUnordered::new(), preparing_dynamic_imports: FuturesUnordered::new(),
pending_dynamic_imports: FuturesUnordered::new(), pending_dynamic_imports: FuturesUnordered::new(),
json_value_store: HashMap::new(), json_value_store: HashMap::new(),
snapshot_loaded_and_not_snapshotting,
} }
} }
@ -1526,9 +1489,7 @@ impl ModuleMap {
return Err(ModuleError::Exception(exception)); return Err(ModuleError::Exception(exception));
} }
let module_specifier = match resolve_helper( let module_specifier = match self.loader.resolve(
self.snapshot_loaded_and_not_snapshotting,
self.loader.clone(),
&import_specifier, &import_specifier,
name.as_ref(), name.as_ref(),
if is_dynamic_import { if is_dynamic_import {
@ -1717,20 +1678,9 @@ impl ModuleMap {
.dynamic_import_map .dynamic_import_map
.insert(load.id, resolver_handle); .insert(load.id, resolver_handle);
let (loader, snapshot_loaded_and_not_snapshotting) = { let loader = module_map_rc.borrow().loader.clone();
let module_map = module_map_rc.borrow(); let resolve_result =
( loader.resolve(specifier, referrer, ResolutionKind::DynamicImport);
module_map.loader.clone(),
module_map.snapshot_loaded_and_not_snapshotting,
)
};
let resolve_result = resolve_helper(
snapshot_loaded_and_not_snapshotting,
loader,
specifier,
referrer,
ResolutionKind::DynamicImport,
);
let fut = match resolve_result { let fut = match resolve_result {
Ok(module_specifier) => { Ok(module_specifier) => {
if module_map_rc if module_map_rc
@ -1764,14 +1714,10 @@ impl ModuleMap {
referrer: &str, referrer: &str,
import_assertions: HashMap<String, String>, import_assertions: HashMap<String, String>,
) -> Option<v8::Local<'s, v8::Module>> { ) -> Option<v8::Local<'s, v8::Module>> {
let resolved_specifier = resolve_helper( let resolved_specifier = self
self.snapshot_loaded_and_not_snapshotting, .loader
self.loader.clone(), .resolve(specifier, referrer, ResolutionKind::Import)
specifier, .expect("Module should have been already resolved");
referrer,
ResolutionKind::Import,
)
.expect("Module should have been already resolved");
let module_type = let module_type =
get_asserted_module_type_from_assertions(&import_assertions); get_asserted_module_type_from_assertions(&import_assertions);
@ -3042,48 +2988,34 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
} }
#[test] #[test]
fn ext_module_loader() { fn ext_resolution() {
let loader = ExtModuleLoader::default(); let loader = ExtModuleLoader::default();
assert!(loader loader.allow_ext_resolution();
.resolve("ext:foo", "ext:bar", ResolutionKind::Import) loader
.is_ok()); .resolve("ext:core.js", "ext:referrer.js", ResolutionKind::Import)
.unwrap();
loader
.resolve("ext:core.js", ".", ResolutionKind::Import)
.unwrap();
}
#[test]
fn ext_resolution_failure() {
let loader = ExtModuleLoader::default();
loader.allow_ext_resolution();
assert_eq!( assert_eq!(
loader loader
.resolve("ext:foo", "file://bar", ResolutionKind::Import) .resolve("ext:core.js", "file://bar", ResolutionKind::Import,)
.err() .err()
.map(|e| e.to_string()), .map(|e| e.to_string()),
Some("Cannot load extension module from external code".to_string()) Some("Cannot load extension module from external code".to_string())
); );
loader.disallow_ext_resolution();
assert_eq!( assert_eq!(
loader loader
.resolve("file://foo", "file://bar", ResolutionKind::Import) .resolve("ext:core.js", "ext:referrer.js", ResolutionKind::Import,)
.err() .err()
.map(|e| e.to_string()), .map(|e| e.to_string()),
Some(
"Module loading is not supported; attempted to resolve: \"file://foo\" from \"file://bar\""
.to_string()
)
);
assert_eq!(
loader
.resolve("file://foo", "ext:bar", ResolutionKind::Import)
.err()
.map(|e| e.to_string()),
Some(
"Module loading is not supported; attempted to resolve: \"file://foo\" from \"ext:bar\""
.to_string()
)
);
assert_eq!(
resolve_helper(
true,
Rc::new(loader),
"ext:core.js",
"file://bar",
ResolutionKind::Import,
)
.err()
.map(|e| e.to_string()),
Some("Cannot load extension module from external code".to_string()) Some("Cannot load extension module from external code".to_string())
); );
} }

View file

@ -132,7 +132,7 @@ pub struct JsRuntime {
v8_isolate: Option<v8::OwnedIsolate>, v8_isolate: Option<v8::OwnedIsolate>,
snapshot_options: snapshot_util::SnapshotOptions, snapshot_options: snapshot_util::SnapshotOptions,
allocations: IsolateAllocations, allocations: IsolateAllocations,
extensions: Vec<Extension>, extensions: Rc<RefCell<Vec<Extension>>>,
event_loop_middlewares: Vec<Box<OpEventLoopFn>>, event_loop_middlewares: Vec<Box<OpEventLoopFn>>,
// Marks if this is considered the top-level runtime. Used only be inspector. // Marks if this is considered the top-level runtime. Used only be inspector.
is_main: bool, is_main: bool,
@ -416,7 +416,7 @@ impl JsRuntime {
let global_context; let global_context;
let mut maybe_snapshotted_data = None; let mut maybe_snapshotted_data = None;
let (mut isolate, snapshot_options) = if snapshot_options.will_snapshot() { let mut isolate = if snapshot_options.will_snapshot() {
let snapshot_creator = let snapshot_creator =
snapshot_util::create_snapshot_creator(refs, options.startup_snapshot); snapshot_util::create_snapshot_creator(refs, options.startup_snapshot);
let mut isolate = JsRuntime::setup_isolate(snapshot_creator); let mut isolate = JsRuntime::setup_isolate(snapshot_creator);
@ -433,7 +433,7 @@ impl JsRuntime {
global_context = v8::Global::new(scope, context); global_context = v8::Global::new(scope, context);
} }
(isolate, snapshot_options) isolate
} else { } else {
#[cfg(not(target_env = "msvc"))] #[cfg(not(target_env = "msvc"))]
let vtable: &'static v8::RustAllocatorVtable< let vtable: &'static v8::RustAllocatorVtable<
@ -492,7 +492,7 @@ impl JsRuntime {
global_context = v8::Global::new(scope, context); global_context = v8::Global::new(scope, context);
} }
(isolate, snapshot_options) isolate
}; };
// SAFETY: this is first use of `isolate_ptr` so we are sure we're // SAFETY: this is first use of `isolate_ptr` so we are sure we're
@ -521,61 +521,33 @@ impl JsRuntime {
None None
}; };
let loader = if snapshot_options != snapshot_util::SnapshotOptions::Load { let loader = options
let esm_sources = options .module_loader
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
#[cfg(feature = "include_js_files_for_snapshotting")]
if snapshot_options.will_snapshot() {
for source in options
.extensions .extensions
.iter() .iter()
.flat_map(|ext| match ext.get_esm_sources() { .flat_map(|e| vec![e.get_esm_sources(), e.get_js_sources()])
Some(s) => s.to_owned(), .flatten()
None => vec![], .flatten()
})
.collect::<Vec<ExtensionFileSource>>();
#[cfg(feature = "include_js_files_for_snapshotting")]
if snapshot_options != snapshot_util::SnapshotOptions::None {
for source in &esm_sources {
use crate::ExtensionFileSourceCode;
if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) =
&source.code
{
println!("cargo:rerun-if-changed={}", path.display())
}
}
}
#[cfg(feature = "include_js_files_for_snapshotting")]
{ {
let js_sources = options use crate::ExtensionFileSourceCode;
.extensions if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) =
.iter() &source.code
.flat_map(|ext| match ext.get_js_sources() { {
Some(s) => s.to_owned(), println!("cargo:rerun-if-changed={}", path.display())
None => vec![],
})
.collect::<Vec<ExtensionFileSource>>();
if snapshot_options != snapshot_util::SnapshotOptions::None {
for source in &js_sources {
use crate::ExtensionFileSourceCode;
if let ExtensionFileSourceCode::LoadedFromFsDuringSnapshot(path) =
&source.code
{
println!("cargo:rerun-if-changed={}", path.display())
}
}
} }
} }
}
Rc::new(crate::modules::ExtModuleLoader::new( let num_extensions = options.extensions.len();
options.module_loader, let extensions = Rc::new(RefCell::new(options.extensions));
esm_sources, let ext_loader = Rc::new(crate::modules::ExtModuleLoader::new(
options.snapshot_module_load_cb, Some(loader.clone()),
)) extensions.clone(),
} else { options.snapshot_module_load_cb,
options ));
.module_loader
.unwrap_or_else(|| Rc::new(NoopModuleLoader))
};
{ {
let mut state = state_rc.borrow_mut(); let mut state = state_rc.borrow_mut();
@ -589,12 +561,8 @@ impl JsRuntime {
Self::STATE_DATA_OFFSET, Self::STATE_DATA_OFFSET,
Rc::into_raw(state_rc.clone()) as *mut c_void, Rc::into_raw(state_rc.clone()) as *mut c_void,
); );
let module_map_rc =
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new( Rc::new(RefCell::new(ModuleMap::new(ext_loader, op_state)));
loader,
op_state,
snapshot_options == snapshot_util::SnapshotOptions::Load,
)));
if let Some(snapshotted_data) = maybe_snapshotted_data { if let Some(snapshotted_data) = maybe_snapshotted_data {
let scope = let scope =
&mut v8::HandleScope::with_context(&mut isolate, global_context); &mut v8::HandleScope::with_context(&mut isolate, global_context);
@ -610,10 +578,10 @@ impl JsRuntime {
v8_isolate: Some(isolate), v8_isolate: Some(isolate),
snapshot_options, snapshot_options,
allocations: IsolateAllocations::default(), allocations: IsolateAllocations::default(),
event_loop_middlewares: Vec::with_capacity(options.extensions.len()), event_loop_middlewares: Vec::with_capacity(num_extensions),
extensions: options.extensions, extensions,
state: state_rc, state: state_rc,
module_map: Some(module_map_rc), module_map: Some(module_map_rc.clone()),
is_main: options.is_main, is_main: options.is_main,
}; };
@ -621,7 +589,9 @@ impl JsRuntime {
// available during the initialization process. // available during the initialization process.
js_runtime.init_extension_ops().unwrap(); js_runtime.init_extension_ops().unwrap();
let realm = js_runtime.global_realm(); let realm = js_runtime.global_realm();
module_map_rc.borrow().loader.allow_ext_resolution();
js_runtime.init_extension_js(&realm).unwrap(); js_runtime.init_extension_js(&realm).unwrap();
module_map_rc.borrow().loader.disallow_ext_resolution();
js_runtime js_runtime
} }
@ -722,7 +692,21 @@ impl JsRuntime {
JsRealm::new(v8::Global::new(scope, context)) JsRealm::new(v8::Global::new(scope, context))
}; };
self
.module_map
.as_ref()
.unwrap()
.borrow()
.loader
.allow_ext_resolution();
self.init_extension_js(&realm)?; self.init_extension_js(&realm)?;
self
.module_map
.as_ref()
.unwrap()
.borrow()
.loader
.disallow_ext_resolution();
Ok(realm) Ok(realm)
} }
@ -790,7 +774,7 @@ impl JsRuntime {
// Take extensions to avoid double-borrow // Take extensions to avoid double-borrow
let extensions = std::mem::take(&mut self.extensions); let extensions = std::mem::take(&mut self.extensions);
for ext in &extensions { for ext in extensions.borrow().iter() {
{ {
if let Some(esm_files) = ext.get_esm_sources() { if let Some(esm_files) = ext.get_esm_sources() {
if let Some(entry_point) = ext.get_esm_entry_point() { if let Some(entry_point) = ext.get_esm_entry_point() {
@ -863,23 +847,15 @@ impl JsRuntime {
/// Initializes ops of provided Extensions /// Initializes ops of provided Extensions
fn init_extension_ops(&mut self) -> Result<(), Error> { fn init_extension_ops(&mut self) -> Result<(), Error> {
let op_state = self.op_state(); let op_state = self.op_state();
// Take extensions to avoid double-borrow // Setup state
{ for e in self.extensions.borrow_mut().iter_mut() {
let mut extensions: Vec<Extension> = std::mem::take(&mut self.extensions); // ops are already registered during in bindings::initialize_context();
e.init_state(&mut op_state.borrow_mut());
// Setup state // Setup event-loop middleware
for e in extensions.iter_mut() { if let Some(middleware) = e.init_event_loop_middleware() {
// ops are already registered during in bindings::initialize_context(); self.event_loop_middlewares.push(middleware);
e.init_state(&mut op_state.borrow_mut());
// Setup event-loop middleware
if let Some(middleware) = e.init_event_loop_middleware() {
self.event_loop_middlewares.push(middleware);
}
} }
// Restore extensions
self.extensions = extensions;
} }
Ok(()) Ok(())
} }

View file

@ -1,3 +1,4 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
console.log("Hello world!"); console.log("Hello world!");
console.log(Deno); console.log(Deno);
Extension.hello();

View file

@ -1,72 +1,31 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use deno_core::anyhow::Context;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::FsModuleLoader; use deno_core::FsModuleLoader;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::permissions::PermissionsContainer; use deno_runtime::permissions::PermissionsContainer;
use deno_runtime::worker::MainWorker; use deno_runtime::worker::MainWorker;
use deno_runtime::worker::WorkerOptions; use deno_runtime::worker::WorkerOptions;
use deno_runtime::BootstrapOptions;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
fn get_error_class_name(e: &AnyError) -> &'static str { deno_core::extension!(hello_runtime, esm = ["hello_runtime_bootstrap.js"]);
deno_runtime::errors::get_error_class_name(e).unwrap_or("Error")
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), AnyError> { async fn main() -> Result<(), AnyError> {
let module_loader = Rc::new(FsModuleLoader);
let create_web_worker_cb = Arc::new(|_| {
todo!("Web workers are not supported in the example");
});
let web_worker_event_cb = Arc::new(|_| {
todo!("Web workers are not supported in the example");
});
let options = WorkerOptions {
bootstrap: BootstrapOptions::default(),
extensions: vec![],
startup_snapshot: None,
unsafely_ignore_certificate_errors: None,
root_cert_store_provider: None,
seed: None,
source_map_getter: None,
format_js_error_fn: None,
web_worker_preload_module_cb: web_worker_event_cb.clone(),
web_worker_pre_execute_module_cb: web_worker_event_cb,
create_web_worker_cb,
maybe_inspector_server: None,
should_break_on_first_statement: false,
should_wait_for_inspector_session: false,
module_loader,
node_fs: None,
npm_resolver: None,
get_error_class_fn: Some(&get_error_class_name),
cache_storage_dir: None,
origin_storage_dir: None,
blob_store: BlobStore::default(),
broadcast_channel: InMemoryBroadcastChannel::default(),
shared_array_buffer_store: None,
compiled_wasm_module_store: None,
stdio: Default::default(),
};
let js_path = let js_path =
Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/hello_runtime.js"); Path::new(env!("CARGO_MANIFEST_DIR")).join("examples/hello_runtime.js");
let main_module = deno_core::resolve_path( let main_module = deno_core::resolve_path(
&js_path.to_string_lossy(), &js_path.to_string_lossy(),
&std::env::current_dir().context("Unable to get CWD")?, &std::env::current_dir()?,
)?; )?;
let permissions = PermissionsContainer::allow_all();
let mut worker = MainWorker::bootstrap_from_options( let mut worker = MainWorker::bootstrap_from_options(
main_module.clone(), main_module.clone(),
permissions, PermissionsContainer::allow_all(),
options, WorkerOptions {
module_loader: Rc::new(FsModuleLoader),
extensions: vec![hello_runtime::init_ops_and_esm()],
..Default::default()
},
); );
worker.execute_main_module(&main_module).await?; worker.execute_main_module(&main_module).await?;
worker.run_event_loop(false).await?; worker.run_event_loop(false).await?;

View file

@ -0,0 +1,5 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
function hello() {
console.log("Hello from extension!");
}
globalThis.Extension = { hello };