summaryrefslogtreecommitdiff
path: root/core/runtime.rs
diff options
context:
space:
mode:
Diffstat (limited to 'core/runtime.rs')
-rw-r--r--core/runtime.rs257
1 files changed, 242 insertions, 15 deletions
diff --git a/core/runtime.rs b/core/runtime.rs
index bd85e1a88..918049013 100644
--- a/core/runtime.rs
+++ b/core/runtime.rs
@@ -351,7 +351,8 @@ impl JsRuntime {
DENO_INIT.call_once(move || v8_init(v8_platform, options.will_snapshot));
// Add builtins extension
- if options.startup_snapshot.is_none() {
+ let has_startup_snapshot = options.startup_snapshot.is_some();
+ if !has_startup_snapshot {
options
.extensions_with_js
.insert(0, crate::ops_builtin::init_builtins());
@@ -423,6 +424,62 @@ impl JsRuntime {
// V8 takes ownership of external_references.
let refs: &'static v8::ExternalReferences = Box::leak(Box::new(refs));
let global_context;
+ let mut module_map_data = None;
+ let mut module_handles = vec![];
+
+ fn get_context_data(
+ scope: &mut v8::HandleScope<()>,
+ context: v8::Local<v8::Context>,
+ ) -> (Vec<v8::Global<v8::Module>>, v8::Global<v8::Object>) {
+ fn data_error_to_panic(err: v8::DataError) -> ! {
+ match err {
+ v8::DataError::BadType { actual, expected } => {
+ panic!(
+ "Invalid type for snapshot data: expected {}, got {}",
+ expected, actual
+ );
+ }
+ v8::DataError::NoData { expected } => {
+ panic!("No data for snapshot data: expected {}", expected);
+ }
+ }
+ }
+
+ let mut module_handles = vec![];
+ let mut scope = v8::ContextScope::new(scope, context);
+ // The 0th element is the module map itself, followed by X number of module
+ // handles. We need to deserialize the "next_module_id" field from the
+ // map to see how many module handles we expect.
+ match scope.get_context_data_from_snapshot_once::<v8::Object>(0) {
+ Ok(val) => {
+ let next_module_id = {
+ let next_module_id_str =
+ v8::String::new(&mut scope, "next_module_id").unwrap();
+ let next_module_id =
+ val.get(&mut scope, next_module_id_str.into()).unwrap();
+ assert!(next_module_id.is_int32());
+ let integer = next_module_id.to_integer(&mut scope).unwrap();
+ integer.int32_value(&mut scope).unwrap()
+ };
+ let no_of_modules = next_module_id - 1;
+
+ for i in 1..=no_of_modules {
+ match scope
+ .get_context_data_from_snapshot_once::<v8::Module>(i as usize)
+ {
+ Ok(val) => {
+ let module_global = v8::Global::new(&mut scope, val);
+ module_handles.push(module_global);
+ }
+ Err(err) => data_error_to_panic(err),
+ }
+ }
+
+ (module_handles, v8::Global::new(&mut scope, val))
+ }
+ Err(err) => data_error_to_panic(err),
+ }
+ }
let (mut isolate, snapshot_options) = if options.will_snapshot {
let (snapshot_creator, snapshot_loaded) =
@@ -468,6 +525,14 @@ impl JsRuntime {
let scope = &mut v8::HandleScope::new(&mut isolate);
let context =
bindings::initialize_context(scope, &op_ctxs, snapshot_options);
+
+ // Get module map data from the snapshot
+ if has_startup_snapshot {
+ let context_data = get_context_data(scope, context);
+ module_handles = context_data.0;
+ module_map_data = Some(context_data.1);
+ }
+
global_context = v8::Global::new(scope, context);
scope.set_default_context(context);
}
@@ -510,6 +575,13 @@ impl JsRuntime {
let context =
bindings::initialize_context(scope, &op_ctxs, snapshot_options);
+ // Get module map data from the snapshot
+ if has_startup_snapshot {
+ let context_data = get_context_data(scope, context);
+ module_handles = context_data.0;
+ module_map_data = Some(context_data.1);
+ }
+
global_context = v8::Global::new(scope, context);
}
@@ -544,7 +616,7 @@ impl JsRuntime {
state.inspector = inspector;
state
.known_realms
- .push(v8::Weak::new(&mut isolate, global_context));
+ .push(v8::Weak::new(&mut isolate, &global_context));
}
isolate.set_data(
Self::STATE_DATA_OFFSET,
@@ -552,6 +624,16 @@ impl JsRuntime {
);
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state)));
+ if let Some(module_map_data) = module_map_data {
+ let scope =
+ &mut v8::HandleScope::with_context(&mut isolate, global_context);
+ let mut module_map = module_map_rc.borrow_mut();
+ module_map.update_with_snapshot_data(
+ scope,
+ module_map_data,
+ module_handles,
+ );
+ }
isolate.set_data(
Self::MODULE_MAP_DATA_OFFSET,
Rc::into_raw(module_map_rc.clone()) as *mut c_void,
@@ -911,15 +993,37 @@ impl JsRuntime {
}
}
- self.state.borrow_mut().global_realm.take();
self.state.borrow_mut().inspector.take();
+ // Serialize the module map and store its data in the snapshot.
+ {
+ let module_map_rc = self.module_map.take().unwrap();
+ let module_map = module_map_rc.borrow();
+ let (module_map_data, module_handles) =
+ module_map.serialize_for_snapshotting(&mut self.handle_scope());
+
+ let context = self.global_context();
+ let mut scope = self.handle_scope();
+ let local_context = v8::Local::new(&mut scope, context);
+ let local_data = v8::Local::new(&mut scope, module_map_data);
+ let offset = scope.add_context_data(local_context, local_data);
+ assert_eq!(offset, 0);
+
+ for (index, handle) in module_handles.into_iter().enumerate() {
+ let module_handle = v8::Local::new(&mut scope, handle);
+ let offset = scope.add_context_data(local_context, module_handle);
+ assert_eq!(offset, index + 1);
+ }
+ }
+
// Drop existing ModuleMap to drop v8::Global handles
{
- self.module_map.take();
let v8_isolate = self.v8_isolate();
Self::drop_state_and_module_map(v8_isolate);
}
+
+ self.state.borrow_mut().global_realm.take();
+
// Drop other v8::Global handles before snapshotting
{
for weak_context in &self.state.clone().borrow().known_realms {
@@ -2605,10 +2709,13 @@ pub mod tests {
use super::*;
use crate::error::custom_error;
use crate::error::AnyError;
+ use crate::modules::AssertedModuleType;
+ use crate::modules::ModuleInfo;
use crate::modules::ModuleSource;
use crate::modules::ModuleSourceFuture;
use crate::modules::ModuleType;
use crate::modules::ResolutionKind;
+ use crate::modules::SymbolicModule;
use crate::ZeroCopyBuf;
use deno_ops::op;
use futures::future::lazy;
@@ -2618,6 +2725,7 @@ pub mod tests {
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
+
// deno_ops macros generate code assuming deno_core in scope.
mod deno_core {
pub use crate::*;
@@ -3422,8 +3530,6 @@ pub mod tests {
referrer: &str,
_kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> {
- assert_eq!(specifier, "file:///main.js");
- assert_eq!(referrer, ".");
let s = crate::resolve_import(specifier, referrer).unwrap();
Ok(s)
}
@@ -3434,29 +3540,150 @@ pub mod tests {
_maybe_referrer: Option<ModuleSpecifier>,
_is_dyn_import: bool,
) -> Pin<Box<ModuleSourceFuture>> {
+ eprintln!("load() should not be called");
unreachable!()
}
}
- let loader = std::rc::Rc::new(ModsLoader::default());
+ fn create_module(
+ runtime: &mut JsRuntime,
+ i: usize,
+ main: bool,
+ ) -> ModuleInfo {
+ let specifier = crate::resolve_url(&format!("file:///{i}.js")).unwrap();
+ let prev = i - 1;
+ let source_code = format!(
+ r#"
+ import {{ f{prev} }} from "file:///{prev}.js";
+ export function f{i}() {{ return f{prev}() }}
+ "#
+ );
+
+ let id = if main {
+ futures::executor::block_on(
+ runtime.load_main_module(&specifier, Some(source_code)),
+ )
+ .unwrap()
+ } else {
+ futures::executor::block_on(
+ runtime.load_side_module(&specifier, Some(source_code)),
+ )
+ .unwrap()
+ };
+ assert_eq!(i + 1, id as usize);
+
+ let _ = runtime.mod_evaluate(id);
+ futures::executor::block_on(runtime.run_event_loop(false)).unwrap();
+
+ ModuleInfo {
+ id,
+ main,
+ name: specifier.to_string(),
+ requests: vec![crate::modules::ModuleRequest {
+ specifier: crate::resolve_url(&format!("file:///{prev}.js")).unwrap(),
+ asserted_module_type: AssertedModuleType::JavaScriptOrWasm,
+ }],
+ module_type: ModuleType::JavaScript,
+ }
+ }
+
+ fn assert_module_map(runtime: &mut JsRuntime, modules: &Vec<ModuleInfo>) {
+ let module_map_rc = runtime.get_module_map();
+ let module_map = module_map_rc.borrow();
+ assert_eq!(module_map.ids_by_handle.len(), modules.len());
+ assert_eq!(module_map.handles_by_id.len(), modules.len());
+ assert_eq!(module_map.info.len(), modules.len());
+ assert_eq!(module_map.by_name.len(), modules.len());
+
+ assert_eq!(module_map.next_module_id, (modules.len() + 1) as ModuleId);
+ assert_eq!(module_map.next_load_id, (modules.len() + 1) as ModuleId);
+
+ let ids_by_handle = module_map.ids_by_handle.values().collect::<Vec<_>>();
+
+ for info in modules {
+ assert!(ids_by_handle.contains(&&info.id));
+ assert!(module_map.handles_by_id.contains_key(&info.id));
+ assert_eq!(module_map.info.get(&info.id).unwrap(), info);
+ assert_eq!(
+ module_map
+ .by_name
+ .get(&(info.name.clone(), AssertedModuleType::JavaScriptOrWasm))
+ .unwrap(),
+ &SymbolicModule::Mod(info.id)
+ );
+ }
+ }
+
+ let loader = Rc::new(ModsLoader::default());
let mut runtime = JsRuntime::new(RuntimeOptions {
- module_loader: Some(loader),
+ module_loader: Some(loader.clone()),
will_snapshot: true,
..Default::default()
});
- let specifier = crate::resolve_url("file:///main.js").unwrap();
- let source_code = "Deno.core.print('hello\\n')".to_string();
-
- let module_id = futures::executor::block_on(
- runtime.load_main_module(&specifier, Some(source_code)),
+ let specifier = crate::resolve_url("file:///0.js").unwrap();
+ let source_code =
+ r#"export function f0() { return "hello world" }"#.to_string();
+ let id = futures::executor::block_on(
+ runtime.load_side_module(&specifier, Some(source_code)),
)
.unwrap();
- let _ = runtime.mod_evaluate(module_id);
+ let _ = runtime.mod_evaluate(id);
futures::executor::block_on(runtime.run_event_loop(false)).unwrap();
- let _snapshot = runtime.snapshot();
+ let mut modules = vec![];
+ modules.push(ModuleInfo {
+ id,
+ main: false,
+ name: specifier.to_string(),
+ requests: vec![],
+ module_type: ModuleType::JavaScript,
+ });
+
+ modules.extend((1..200).map(|i| create_module(&mut runtime, i, false)));
+
+ assert_module_map(&mut runtime, &modules);
+
+ let snapshot = runtime.snapshot();
+
+ let mut runtime2 = JsRuntime::new(RuntimeOptions {
+ module_loader: Some(loader.clone()),
+ will_snapshot: true,
+ startup_snapshot: Some(Snapshot::JustCreated(snapshot)),
+ ..Default::default()
+ });
+
+ assert_module_map(&mut runtime2, &modules);
+
+ modules.extend((200..400).map(|i| create_module(&mut runtime2, i, false)));
+ modules.push(create_module(&mut runtime2, 400, true));
+
+ assert_module_map(&mut runtime2, &modules);
+
+ let snapshot2 = runtime2.snapshot();
+
+ let mut runtime3 = JsRuntime::new(RuntimeOptions {
+ module_loader: Some(loader),
+ startup_snapshot: Some(Snapshot::JustCreated(snapshot2)),
+ ..Default::default()
+ });
+
+ assert_module_map(&mut runtime3, &modules);
+
+ let source_code = r#"(async () => {
+ const mod = await import("file:///400.js");
+ return mod.f400();
+ })();"#
+ .to_string();
+ let val = runtime3.execute_script(".", &source_code).unwrap();
+ let val = futures::executor::block_on(runtime3.resolve_value(val)).unwrap();
+ {
+ let scope = &mut runtime3.handle_scope();
+ let value = v8::Local::new(scope, val);
+ let str_ = value.to_string(scope).unwrap().to_rust_string_lossy(scope);
+ assert_eq!(str_, "hello world");
+ }
}
#[test]