From a04f277a4429f9b14dc8108334a92c50966a7b00 Mon Sep 17 00:00:00 2001 From: y21 <30553356+y21@users.noreply.github.com> Date: Fri, 20 Oct 2023 19:40:43 +0200 Subject: [PATCH] support recursive require --- Cargo.lock | 2 ++ crates/dash_node_impl/Cargo.toml | 1 + crates/dash_node_impl/src/lib.rs | 42 +++++++++++++++++++++++++++++--- crates/dash_vm/src/gc/trace.rs | 3 ++- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ddfe9eba..462c345e 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,8 +456,10 @@ dependencies = [ "dash_middle", "dash_optimizer", "dash_parser", + "dash_proc_macro", "dash_rt", "dash_vm", + "rustc-hash", "serde", "serde_json", "tokio", diff --git a/crates/dash_node_impl/Cargo.toml b/crates/dash_node_impl/Cargo.toml index c8ab57d9..d57218ec 100644 --- a/crates/dash_node_impl/Cargo.toml +++ b/crates/dash_node_impl/Cargo.toml @@ -16,3 +16,4 @@ serde_json = "1.0.103" serde = { version = "1.0", features = ["derive"] } tokio = { version = "1.24.0", features = ["full"] } dash_proc_macro = { path = "../dash_proc_macro" } +rustc-hash = "1.1.0" diff --git a/crates/dash_node_impl/src/lib.rs b/crates/dash_node_impl/src/lib.rs index 0a5f0474..05b5d50f 100644 --- a/crates/dash_node_impl/src/lib.rs +++ b/crates/dash_node_impl/src/lib.rs @@ -1,4 +1,6 @@ +use std::cell::RefCell; use std::path::{Path, PathBuf}; +use std::rc::Rc; use anyhow::Context; use anyhow::{anyhow, bail}; @@ -16,6 +18,7 @@ use dash_vm::value::object::PropertyValue; use dash_vm::value::Value; use dash_vm::{delegate, throw}; use package::Package; +use rustc_hash::FxHashMap; mod package; @@ -46,7 +49,16 @@ async fn run_inner_fallible(path: &str, opt: OptLevel, initial_gc_threshold: Opt let mut rt = Runtime::new(initial_gc_threshold).await; let scope = &mut rt.vm_mut().scope(); - execute_node_module(scope, path, &entry, opt).map_err(|err| match err { + + execute_node_module( + scope, + path, + path, + &entry, + opt, + Rc::new(RefCell::new(FxHashMap::default())), + ) + .map_err(|err| match err { (EvalError::Middle(errs), _) => anyhow!("{}", errs.formattable(&entry, true)), (EvalError::Exception(err), _) => anyhow!("{}", format_value(err.root(scope), scope).unwrap()), })?; @@ -57,14 +69,17 @@ async fn run_inner_fallible(path: &str, opt: OptLevel, initial_gc_threshold: Opt /// Returns the `module` object fn execute_node_module( scope: &mut LocalScope, - directory: &Path, + dir_path: &Path, + file_path: &Path, source: &str, opt: OptLevel, + ongoing_requires: Rc>>, ) -> Result { let exports = Value::Object(scope.register(NamedObject::new(scope))); let module = Value::Object(scope.register(NamedObject::new(scope))); let require = Value::Object(scope.register(RequireFunction { - dir: directory.to_owned(), + dir: dir_path.to_owned(), + ongoing_requires: ongoing_requires.clone(), object: NamedObject::new(scope), })); module @@ -83,6 +98,10 @@ fn execute_node_module( .set_property(scope, "require".into(), PropertyValue::static_default(require)) .unwrap(); + ongoing_requires + .borrow_mut() + .insert(file_path.to_owned(), module.clone()); + scope.eval(source, opt)?; Ok(module) @@ -91,6 +110,7 @@ fn execute_node_module( #[derive(Debug, Trace)] struct RequireFunction { dir: PathBuf, + ongoing_requires: Rc>>, object: NamedObject, } @@ -120,16 +140,23 @@ impl Object for RequireFunction { let Some(Value::String(arg)) = args.first() else { throw!(scope, Error, "require() expects a string argument"); }; + let is_path = matches!(arg.chars().next(), Some('.' | '/' | '~')); if is_path { let canonicalized_path = match self.dir.join(&**arg).canonicalize() { Ok(v) => v, Err(err) => throw!(scope, Error, err.to_string()), }; + + if let Some(module) = self.ongoing_requires.borrow().get(&canonicalized_path) { + return Ok(module.clone()); + } + let source = match std::fs::read_to_string(&canonicalized_path) { Ok(v) => v, Err(err) => throw!(scope, Error, err.to_string()), }; + let Some(parent_dir) = canonicalized_path.parent() else { throw!( scope, @@ -139,7 +166,14 @@ impl Object for RequireFunction { ); }; - let module = match execute_node_module(scope, parent_dir, &source, OptLevel::default()) { + let module = match execute_node_module( + scope, + parent_dir, + &canonicalized_path, + &source, + OptLevel::default(), + self.ongoing_requires.clone(), + ) { Ok(v) => v, Err((EvalError::Exception(value), _)) => return Err(value.root(scope)), Err((EvalError::Middle(errs), _)) => { diff --git a/crates/dash_vm/src/gc/trace.rs b/crates/dash_vm/src/gc/trace.rs index 64ce7f66..c6ed165b 100644 --- a/crates/dash_vm/src/gc/trace.rs +++ b/crates/dash_vm/src/gc/trace.rs @@ -1,5 +1,6 @@ use std::cell::Cell; use std::cell::RefCell; +use std::collections::HashMap; use std::collections::HashSet; use std::path::PathBuf; use std::rc::Rc; @@ -58,7 +59,7 @@ unsafe impl Trace for HashSet { } } -unsafe impl Trace for ahash::HashMap { +unsafe impl Trace for HashMap { fn trace(&self) { for (k, v) in self.iter() { k.trace();