Skip to content

Commit 486a694

Browse files
committed
feat: Runtime manager
1 parent 5b6494a commit 486a694

File tree

12 files changed

+352
-281
lines changed

12 files changed

+352
-281
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ default-run = "andromeda"
99
clap = { workspace = true }
1010
cliclack = { workspace = true }
1111
console = { workspace = true }
12-
miette = { workspace = true }
1312
nova_vm = { workspace = true }
1413
andromeda-core = { workspace = true }
1514
oxc_ast = { workspace = true }
1615
oxc_parser = { workspace = true }
1716
oxc_span = { workspace = true }
18-
oxc_diagnostics = { workspace = true }
1917
oxc_allocator = { workspace = true }
2018
oxc_semantic = { workspace = true }
2119
anymap = { workspace = true }

cli/src/helper.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

cli/src/main.rs

Lines changed: 37 additions & 207 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,15 @@
11
// This Source Code Form is subject to the terms of the Mozilla Public
22
// License, v. 2.0. If a copy of the MPL was not distributed with this
33
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4-
mod helper;
5-
mod runtime;
6-
mod theme;
4+
mod repl;
5+
mod repl_theme;
76

8-
use andromeda_core::{initialize_recommended_extensions, HostData, MacroTask};
7+
use andromeda_core::{Runtime, RuntimeConfig};
98
use clap::{Parser as ClapParser, Subcommand};
10-
use cliclack::{input, intro, set_theme};
11-
use helper::exit_with_parse_errors;
12-
use nova_vm::ecmascript::{
13-
builtins::promise_objects::promise_abstract_operations::promise_capability_records::PromiseCapability,
14-
execution::{
15-
agent::{HostHooks, Job, Options},
16-
initialize_host_defined_realm, Agent, Realm,
17-
},
18-
scripts_and_modules::script::{parse_script, script_evaluation},
19-
types::{Object, Value},
20-
};
21-
use runtime::attach_builtins;
22-
use std::{any::Any, cell::RefCell, collections::VecDeque, fmt::Debug, sync::atomic::Ordering};
23-
use theme::DefaultTheme;
9+
use repl::repl;
2410

2511
/// A JavaScript runtime
26-
#[derive(Debug, ClapParser)] // requires `derive` feature
12+
#[derive(Debug, ClapParser)]
2713
#[command(name = "andromeda")]
2814
#[command(
2915
about = "The coolest JavaScript Runtime",
@@ -50,48 +36,10 @@ enum Command {
5036
},
5137

5238
/// Runs the REPL
53-
Repl {},
54-
}
55-
56-
struct CliHostHooks {
57-
promise_job_queue: RefCell<VecDeque<Job>>,
58-
host_data: HostData,
59-
}
60-
61-
// RefCell doesn't implement Debug
62-
impl Debug for CliHostHooks {
63-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64-
f.debug_struct("CliHostHooks")
65-
//.field("promise_job_queue", &*self.promise_job_queue.borrow())
66-
.finish()
67-
}
68-
}
69-
70-
impl CliHostHooks {
71-
pub fn new(host_data: HostData) -> Self {
72-
Self {
73-
promise_job_queue: RefCell::default(),
74-
host_data,
75-
}
76-
}
77-
78-
fn pop_promise_job(&self) -> Option<Job> {
79-
self.promise_job_queue.borrow_mut().pop_front()
80-
}
81-
82-
fn any_pending_macro_tasks(&self) -> bool {
83-
self.host_data.macro_task_count.load(Ordering::Relaxed) > 0
84-
}
85-
}
86-
87-
impl HostHooks for CliHostHooks {
88-
fn enqueue_promise_job(&self, job: Job) {
89-
self.promise_job_queue.borrow_mut().push_back(job);
90-
}
91-
92-
fn get_host_data(&self) -> &dyn Any {
93-
&self.host_data
94-
}
39+
Repl {
40+
#[arg(short, long)]
41+
verbose: bool,
42+
},
9543
}
9644

9745
fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -102,163 +50,45 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
10250
.build()
10351
.unwrap();
10452

105-
// Run the JS Engine in a secondary thread so tokio tasks can still run
106-
let engine_task = rt.spawn_blocking(|| {
107-
match args.command {
108-
Command::Run {
109-
verbose,
53+
// Run Nova in a secondary blocking thread so tokio tasks can still run
54+
let nova_thread = rt.spawn_blocking(|| match args.command {
55+
Command::Run {
56+
verbose,
57+
no_strict,
58+
paths,
59+
} => {
60+
let mut runtime = Runtime::new(RuntimeConfig {
11061
no_strict,
11162
paths,
112-
} => {
113-
let allocator = Default::default();
114-
115-
let (host_data, macro_task_rx) = HostData::new();
116-
let host_hooks: CliHostHooks = CliHostHooks::new(host_data);
117-
let host_hooks: &CliHostHooks = &*Box::leak(Box::new(host_hooks));
118-
let mut agent = Agent::new(
119-
Options {
120-
disable_gc: false,
121-
print_internals: verbose,
122-
},
123-
host_hooks,
124-
);
125-
{
126-
let create_global_object: Option<fn(&mut Realm) -> Object> = None;
127-
let create_global_this_value: Option<fn(&mut Realm) -> Object> = None;
128-
initialize_host_defined_realm(
129-
&mut agent,
130-
create_global_object,
131-
create_global_this_value,
132-
Some(initialize_recommended_extensions),
133-
);
134-
}
135-
let realm = agent.current_realm_id();
136-
137-
assert!(!paths.is_empty());
138-
attach_builtins(&allocator, &mut agent, no_strict);
139-
140-
let mut final_result = Ok(Value::Null);
141-
// Fetch the runtime mod.ts file using a macro and add it to the paths
142-
for path in paths {
143-
let file = std::fs::read_to_string(&path).unwrap();
144-
let script =
145-
match parse_script(&allocator, file.into(), realm, !no_strict, None) {
146-
Ok(script) => script,
147-
Err((file, errors)) => exit_with_parse_errors(errors, &path, &file),
148-
};
149-
final_result = script_evaluation(&mut agent, script);
150-
if final_result.is_err() {
151-
break;
152-
}
153-
}
154-
155-
if final_result.is_ok() {
156-
loop {
157-
while let Some(job) = host_hooks.pop_promise_job() {
158-
if let Err(err) = job.run(&mut agent) {
159-
final_result = Err(err);
160-
break;
161-
}
162-
}
163-
164-
// If both the microtasks and macrotasks queues are empty we can end the event loop
165-
if !host_hooks.any_pending_macro_tasks() {
166-
break;
167-
}
63+
verbose,
64+
});
65+
let runtime_result = runtime.run();
16866

169-
#[allow(clippy::single_match)]
170-
match macro_task_rx.recv() {
171-
Ok(MacroTask::ResolvePromise(root_value)) => {
172-
let value = root_value.take(&mut agent);
173-
if let Value::Promise(promise) = value {
174-
let promise_capability =
175-
PromiseCapability::from_promise(promise, false);
176-
promise_capability.resolve(&mut agent, Value::Undefined);
177-
} else {
178-
panic!("Attempted to resolve a non-promise value");
179-
}
180-
}
181-
_ => {}
182-
}
67+
match runtime_result {
68+
Ok(result) => {
69+
if verbose {
70+
println!("{:?}", result);
18371
}
18472
}
185-
186-
match final_result {
187-
Ok(result) => {
188-
if verbose {
189-
println!("{:?}", result);
190-
}
191-
}
192-
Err(error) => {
193-
eprintln!(
194-
"Uncaught exception: {}",
195-
error.value().string_repr(&mut agent).as_str(&agent)
196-
);
197-
std::process::exit(1);
198-
}
199-
}
200-
}
201-
Command::Repl {} => {
202-
let allocator = Default::default();
203-
let (host_data, _macro_task_rx) = HostData::new();
204-
let host_hooks: CliHostHooks = CliHostHooks::new(host_data);
205-
let host_hooks: &CliHostHooks = &*Box::leak(Box::new(host_hooks));
206-
let mut agent = Agent::new(
207-
Options {
208-
disable_gc: false,
209-
print_internals: true,
210-
},
211-
host_hooks,
212-
);
213-
{
214-
let create_global_object: Option<fn(&mut Realm) -> Object> = None;
215-
let create_global_this_value: Option<fn(&mut Realm) -> Object> = None;
216-
initialize_host_defined_realm(
217-
&mut agent,
218-
create_global_object,
219-
create_global_this_value,
220-
Some(initialize_recommended_extensions),
73+
Err(error) => {
74+
eprintln!(
75+
"Uncaught exception: {}",
76+
error
77+
.value()
78+
.string_repr(&mut runtime.agent)
79+
.as_str(&runtime.agent)
22180
);
222-
}
223-
let realm = agent.current_realm_id();
224-
attach_builtins(&allocator, &mut agent, false);
225-
226-
set_theme(DefaultTheme);
227-
println!("\n\n");
228-
let mut placeholder = "Enter a line of Javascript".to_string();
229-
230-
loop {
231-
intro("Nova Repl (type exit or ctrl+c to exit)").unwrap();
232-
let input: String = input("").placeholder(&placeholder).interact().unwrap();
233-
234-
if input.matches("exit").count() == 1 {
235-
std::process::exit(0);
236-
}
237-
placeholder = input.to_string();
238-
let script = match parse_script(&allocator, input.into(), realm, true, None) {
239-
Ok(script) => script,
240-
Err((file, errors)) => {
241-
exit_with_parse_errors(errors, "<stdin>", &file);
242-
}
243-
};
244-
let result = script_evaluation(&mut agent, script);
245-
match result {
246-
Ok(result) => {
247-
println!("{:?}\n", result);
248-
}
249-
Err(error) => {
250-
eprintln!(
251-
"Uncaught exception: {}",
252-
error.value().string_repr(&mut agent).as_str(&agent)
253-
);
254-
}
255-
}
81+
std::process::exit(1);
25682
}
25783
}
25884
}
85+
Command::Repl { verbose } => {
86+
repl(verbose);
87+
}
25988
});
26089

261-
rt.block_on(engine_task).unwrap();
90+
rt.block_on(nova_thread)
91+
.expect("oh no! Something went wrong when running Andromeda.");
26292

26393
Ok(())
26494
}

0 commit comments

Comments
 (0)