Skip to content

Commit

Permalink
Merge pull request #73 from y21/node
Browse files Browse the repository at this point in the history
initial node-compat mode
  • Loading branch information
y21 authored Oct 22, 2023
2 parents c613d74 + cd6a1f8 commit c90d515
Show file tree
Hide file tree
Showing 15 changed files with 392 additions and 71 deletions.
18 changes: 18 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ members = [
"crates/dash_log",
"crates/dash_rt_net",
"crates/dash_typed_cfg",
"crates/dash_node_impl",
]
resolver = "2"

Expand Down
3 changes: 3 additions & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name = "dash-cli"
version = "0.3.0"
edition = "2018"

[features]
nodejs = []

[dependencies]
anyhow = "1.0"
Expand All @@ -17,6 +19,7 @@ dash_vm = { path = "../crates/dash_vm", features = [] }
dash_middle = { path = "../crates/dash_middle", features = ["format"] }
dash_optimizer = { path = "../crates/dash_optimizer" }
dash_decompiler = { path = "../crates/dash_decompiler" }
dash_node_impl = { path = "../crates/dash_node_impl" }
dash_rt_modules = { path = "../crates/dash_rt_modules", features = [
# "http",
# "fs",
Expand Down
7 changes: 5 additions & 2 deletions cli/src/cmd/eval.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use anyhow::Context;
use clap::ArgMatches;
use dash_middle::parser::error::IntoFormattableErrors;
use dash_rt::format_value;
use dash_vm::eval::EvalError;
use dash_vm::Vm;

Expand All @@ -14,8 +15,10 @@ pub fn eval(args: &ArgMatches) -> anyhow::Result<()> {
let mut scope = vm.scope();

match scope.eval(source, opt) {
Ok(value) => util::print_value(value.root(&mut scope), &mut scope).unwrap(),
Err((EvalError::Exception(value), _)) => util::print_value(value.root(&mut scope), &mut scope).unwrap(),
Ok(value) => println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()),
Err((EvalError::Exception(value), _)) => {
println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap())
}
Err((EvalError::Middle(errs), _)) => println!("{}", errs.formattable(source, true)),
};

Expand Down
9 changes: 5 additions & 4 deletions cli/src/cmd/repl.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use dash_middle::parser::error::IntoFormattableErrors;
use dash_optimizer::OptLevel;
use dash_rt::format_value;
use dash_vm::eval::EvalError;
use dash_vm::Vm;
use rustyline::Editor;

use crate::util;

pub fn repl() -> anyhow::Result<()> {
let mut rl = Editor::<()>::new();

Expand All @@ -20,8 +19,10 @@ pub fn repl() -> anyhow::Result<()> {
rl.add_history_entry(&input);

match scope.eval(&input, OptLevel::Aggressive) {
Ok(value) => util::print_value(value.root(&mut scope), &mut scope).unwrap(),
Err((EvalError::Exception(value), _)) => util::print_value(value.root(&mut scope), &mut scope).unwrap(),
Ok(value) => println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap()),
Err((EvalError::Exception(value), _)) => {
println!("{}", format_value(value.root(&mut scope), &mut scope).unwrap())
}
Err((EvalError::Middle(errs), _)) => println!("{}", errs.formattable(&input, true)),
}

Expand Down
31 changes: 25 additions & 6 deletions cli/src/cmd/run.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use dash_middle::parser::error::IntoFormattableErrors;
use dash_optimizer::OptLevel;
use dash_rt::format_value;
use dash_rt::runtime::Runtime;
use dash_rt::state::State;
use dash_vm::eval::EvalError;
Expand All @@ -14,18 +15,27 @@ use crate::util;

pub fn run(args: &ArgMatches) -> anyhow::Result<()> {
let path = args.value_of("file").context("Missing source")?;
let nodejs = args.is_present("node");
let initial_gc_threshold = args
.value_of("initial-gc-threshold")
.map(<usize as FromStr>::from_str)
.transpose()?;

let source = fs::read_to_string(path).context("Failed to read source")?;
let opt = util::opt_level_from_matches(args)?;

let before = args.is_present("timing").then(Instant::now);
let quiet = args.is_present("quiet");

let async_rt = tokio::runtime::Runtime::new()?;
async_rt.block_on(inner(source, opt, args.is_present("quiet"), initial_gc_threshold))?;
if nodejs {
#[cfg(feature = "nodejs")]
{
dash_node_impl::run_with_nodejs_mnemnoics(path, opt, initial_gc_threshold)?;
}
#[cfg(not(feature = "nodejs"))]
{
anyhow::bail!("dash needs to be compiled with the `nodejs` feature to support node-compat mode");
}
} else {
run_normal_mode(path, opt, quiet, initial_gc_threshold)?;
}

if let Some(before) = before {
println!("\n{:?}", before.elapsed());
Expand All @@ -34,6 +44,15 @@ pub fn run(args: &ArgMatches) -> anyhow::Result<()> {
Ok(())
}

fn run_normal_mode(path: &str, opt: OptLevel, quiet: bool, initial_gc_threshold: Option<usize>) -> anyhow::Result<()> {
let source = fs::read_to_string(path).context("Failed to read source")?;

let async_rt = tokio::runtime::Runtime::new()?;
async_rt.block_on(inner(source, opt, quiet, initial_gc_threshold))?;

Ok(())
}

async fn inner(source: String, opt: OptLevel, quiet: bool, initial_gc_threshold: Option<usize>) -> anyhow::Result<()> {
let mut rt = Runtime::new(initial_gc_threshold).await;

Expand All @@ -55,7 +74,7 @@ async fn inner(source: String, opt: OptLevel, quiet: bool, initial_gc_threshold:
// TODO: EvalError::VmError should probably bail too?

if !quiet {
util::print_value(value, &mut scope).unwrap();
println!("{}", format_value(value, &mut scope).unwrap());
}

let state = State::from_vm(&scope);
Expand Down
3 changes: 3 additions & 0 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ fn main() -> anyhow::Result<()> {
.default_value("1")
.possible_values(["0", "1", "2"]);

let nodejs = Arg::new("node").long("node").takes_value(false);

let initial_gc_threshold = Arg::new("initial-gc-threshold")
.help("Sets the initial GC object threshold, i.e. the object count at which the first GC cycle triggers.")
.long("initial-gc-threshold")
Expand All @@ -40,6 +42,7 @@ fn main() -> anyhow::Result<()> {
.arg(Arg::new("timing").short('t').long("timing").takes_value(false))
.arg(Arg::new("quiet").short('q').long("quiet").takes_value(false))
.arg(opt_level.clone())
.arg(nodejs)
.arg(initial_gc_threshold.clone()),
)
.subcommand(Command::new("repl").override_help("Enter a JavaScript REPL"))
Expand Down
50 changes: 0 additions & 50 deletions cli/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,59 +1,9 @@
use std::cell::OnceCell;

use anyhow::Context;
use clap::ArgMatches;
use dash_compiler::FunctionCompiler;
use dash_middle::compiler::CompileResult;
use dash_middle::interner::StringInterner;
use dash_optimizer::OptLevel;
use dash_vm::frame::Exports;
use dash_vm::frame::Frame;
use dash_vm::localscope::LocalScope;
use dash_vm::value::ops::abstractions::conversions::ValueConversion;
use dash_vm::value::Value;

pub fn opt_level_from_matches(args: &ArgMatches) -> anyhow::Result<OptLevel> {
args.value_of("opt")
.and_then(OptLevel::from_level)
.context("Invalid opt level")
}

pub fn print_value(value: Value, scope: &mut LocalScope) -> Result<(), Value> {
thread_local! {
// Cache bytecode so we can avoid recompiling it every time
// We can be even smarter if we need to -- cache the whole value at callsite
static INSPECT_BC: OnceCell<CompileResult> = const { OnceCell::new() };
}

let inspect_bc = INSPECT_BC.with(|tls| {
let inspect = tls.get_or_init(|| {
FunctionCompiler::compile_str(
// TODO: can reuse a string interner if worth it
&mut StringInterner::new(),
include_str!("../../crates/dash_rt/js/inspect.js"),
Default::default(),
)
.unwrap()
});
inspect.clone()
});

let Exports {
default: Some(inspect_fn),
..
} = scope.execute_module(Frame::from_compile_result(inspect_bc)).unwrap()
else {
panic!("inspect module did not have a default export");
};

let result = inspect_fn
.root(scope)
.apply(scope, Value::undefined(), vec![value])
.unwrap()
.to_string(scope)
.unwrap();

println!("{result}");

Ok(())
}
19 changes: 19 additions & 0 deletions crates/dash_node_impl/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "dash_node_impl"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
dash_middle = { path = "../dash_middle" }
dash_vm = { path = "../dash_vm" }
dash_parser = { path = "../dash_parser", features = ["from_string"] }
dash_rt = { path = "../dash_rt" }
dash_optimizer = { path = "../dash_optimizer" }
anyhow = "1.0"
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"
Loading

0 comments on commit c90d515

Please sign in to comment.