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 } ;
98use 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
9745fn 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