Thanks to visit codestin.com
Credit goes to docs.rs

rustyscript/
runtime.rs

1use std::{path::Path, rc::Rc, time::Duration};
2
3use deno_core::PollEventLoopOptions;
4use tokio_util::sync::CancellationToken;
5
6use crate::{
7    async_bridge::{AsyncBridge, AsyncBridgeExt, TokioRuntime},
8    inner_runtime::{InnerRuntime, RsAsyncFunction, RsFunction},
9    js_value::Function,
10    Error, Module, ModuleHandle,
11};
12
13/// Represents the set of options accepted by the runtime constructor
14pub use crate::inner_runtime::RuntimeOptions;
15
16/// For functions returning nothing. Acts as a placeholder for the return type  
17/// Should accept any type of value from javascript
18///
19/// It is in fact an alias for [`crate::js_value::Value`]  
20/// Note: This used to be an alias for `serde_json::Value`, but was changed for performance reasons
21pub type Undefined = crate::js_value::Value;
22
23/// A runtime instance that can be used to execute JavaScript code and interact with it.  
24/// Most runtime functions have 3 variants - blocking, async, and immediate
25///
26/// For example:
27/// - `call_function` will block until the function is resolved and the event loop is empty
28/// - `call_function_async` will return a future that resolves when the function is resolved and the event loop is empty
29/// - `call_function_immediate` will return the result immediately, without resolving promises or running the event loop
30///   (See [`crate::js_value::Promise`])
31///
32/// Note: For multithreaded applications, you may need to call `init_platform` before creating a `Runtime`  
33/// (See [[`crate::init_platform`])
34pub struct Runtime {
35    inner: InnerRuntime<deno_core::JsRuntime>,
36    tokio: AsyncBridge,
37}
38
39impl Runtime {
40    /// Creates a new instance of the runtime with the provided options.
41    ///
42    /// # Arguments
43    /// * `options` - A `RuntimeOptions` struct that specifies the configuration options for the runtime.
44    ///
45    /// # Returns
46    /// A `Result` containing either the initialized runtime instance on success (`Ok`) or an error on failure (`Err`).
47    ///
48    /// # Example
49    /// ```rust
50    /// use rustyscript::{ json_args, Runtime, RuntimeOptions, Module };
51    /// use std::time::Duration;
52    ///
53    /// # fn main() -> Result<(), rustyscript::Error> {
54    /// // Creates a runtime that will attempt to run function load() on start
55    /// // And which will time-out after 50ms
56    /// let mut runtime = Runtime::new(RuntimeOptions {
57    ///     default_entrypoint: Some("load".to_string()),
58    ///     timeout: Duration::from_millis(50),
59    ///     ..Default::default()
60    /// })?;
61    ///
62    /// let module = Module::new("test.js", "
63    ///     export const load = () => {
64    ///         return 'Hello World!';
65    ///     }
66    /// ");
67    ///
68    /// let module_handle = runtime.load_module(&module)?;
69    /// let value: String = runtime.call_entrypoint(&module_handle, json_args!())?;
70    /// assert_eq!("Hello World!", value);
71    /// # Ok(())
72    /// # }
73    /// ```
74    ///
75    /// # Errors
76    /// Can fail if the tokio runtime cannot be created,  
77    /// Or if the deno runtime initialization fails (usually issues with extensions)
78    ///
79    pub fn new(options: RuntimeOptions) -> Result<Self, Error> {
80        let tokio = AsyncBridge::new(options.timeout)?;
81        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
82        Ok(Self { inner, tokio })
83    }
84
85    /// Creates a new instance of the runtime with the provided options and a pre-configured tokio runtime.  
86    /// See [`Runtime::new`] for more information.
87    ///
88    /// # Errors
89    /// Can fail if the deno runtime initialization fails (usually issues with extensions)
90    pub fn with_tokio_runtime(
91        options: RuntimeOptions,
92        tokio: Rc<tokio::runtime::Runtime>,
93    ) -> Result<Self, Error> {
94        let tokio = AsyncBridge::with_tokio_runtime(options.timeout, tokio);
95        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
96        Ok(Self { inner, tokio })
97    }
98
99    /// Creates a new instance of the runtime with the provided options and a borrowed tokio runtime handle.  
100    /// See [`Runtime::new`] for more information.
101    ///
102    /// # Errors
103    /// Can fail if the deno runtime initialization fails (usually issues with extensions)
104    pub fn with_tokio_runtime_handle(
105        options: RuntimeOptions,
106        handle: tokio::runtime::Handle,
107    ) -> Result<Self, Error> {
108        let tokio = AsyncBridge::with_runtime_handle(options.timeout, handle);
109        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
110        Ok(Self { inner, tokio })
111    }
112
113    /// Access the underlying deno runtime instance directly
114    pub fn deno_runtime(&mut self) -> &mut deno_core::JsRuntime {
115        self.inner.deno_runtime()
116    }
117
118    /// Access the underlying tokio runtime used for blocking operations
119    #[must_use]
120    pub fn tokio_runtime(&self) -> TokioRuntime {
121        self.tokio.tokio_runtime()
122    }
123
124    /// Returns the timeout for the runtime
125    #[must_use]
126    pub fn timeout(&self) -> std::time::Duration {
127        self.tokio.timeout()
128    }
129
130    /// Returns the heap exhausted token for the runtime  
131    /// Used to detect when the runtime has run out of memory
132    #[must_use]
133    pub fn heap_exhausted_token(&self) -> CancellationToken {
134        self.tokio.heap_exhausted_token()
135    }
136
137    /// Destroy the v8 runtime, releasing all resources  
138    /// Then the internal tokio runtime will be returned
139    #[must_use]
140    pub fn into_tokio_runtime(self) -> TokioRuntime {
141        self.tokio.into_tokio_runtime()
142    }
143
144    /// Set the current working directory for the runtime  
145    /// This is used to resolve relative paths in the module loader
146    ///
147    /// The runtime will begin with the current working directory of the process
148    ///
149    /// # Errors
150    /// Can fail if the given path is not valid
151    pub fn set_current_dir(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
152        self.inner.set_current_dir(path)
153    }
154
155    /// Get the current working directory for the runtime  
156    /// This is used to resolve relative paths in the module loader
157    ///
158    /// The runtime will begin with the current working directory of the process
159    #[must_use]
160    pub fn current_dir(&self) -> &Path {
161        self.inner.current_dir()
162    }
163
164    /// Advance the JS event loop by a single tick  
165    /// See [`Runtime::block_on_event_loop`] for fully running the event loop
166    ///
167    /// Returns true if the event loop has pending work, or false if it has completed
168    ///
169    /// # Arguments
170    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
171    ///
172    /// # Errors
173    /// Can fail if a runtime error occurs during the event loop's execution
174    pub fn advance_event_loop(&mut self, options: PollEventLoopOptions) -> Result<bool, Error> {
175        self.block_on(|runtime| async move { runtime.inner.advance_event_loop(options).await })
176    }
177
178    /// Advance the JS event loop by a single tick  
179    /// See [`Runtime::await_event_loop`] for fully running the event loop
180    ///
181    /// Returns a future that resolves true if the event loop has pending work, or false if it
182    /// has completed
183    ///
184    /// # Arguments
185    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
186    ///
187    /// # Errors
188    /// Can fail if a runtime error occurs during the event loop's execution
189    pub async fn advance_event_loop_async(
190        &mut self,
191        options: PollEventLoopOptions,
192    ) -> Result<bool, Error> {
193        self.inner.advance_event_loop(options).await
194    }
195
196    /// Run the JS event loop to completion, or until a timeout is reached  
197    /// Required when using the `_immediate` variants of functions
198    ///
199    /// # Arguments
200    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
201    /// * `timeout` - Optional timeout for the event loop
202    ///
203    /// # Errors
204    /// Can fail if a runtime error occurs during the event loop's execution
205    pub async fn await_event_loop(
206        &mut self,
207        options: PollEventLoopOptions,
208        timeout: Option<Duration>,
209    ) -> Result<(), Error> {
210        self.inner.await_event_loop(options, timeout).await
211    }
212
213    /// Run the JS event loop to completion, or until a timeout is reached  
214    /// Required when using the `_immediate` variants of functions
215    ///
216    /// This is the blocking variant of [`Runtime::await_event_loop`]
217    ///
218    /// # Arguments
219    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
220    /// * `timeout` - Optional timeout for the event loop
221    ///
222    /// # Errors
223    /// Can fail if a runtime error occurs during the event loop's execution
224    pub fn block_on_event_loop(
225        &mut self,
226        options: deno_core::PollEventLoopOptions,
227        timeout: Option<Duration>,
228    ) -> Result<(), Error> {
229        self.block_on(|runtime| async move { runtime.await_event_loop(options, timeout).await })
230    }
231
232    /// Remove and return a value from the state, if one exists
233    /// ```rust
234    /// use rustyscript::{ Runtime };
235    ///
236    /// # fn main() -> Result<(), rustyscript::Error> {
237    /// let mut runtime = Runtime::new(Default::default())?;
238    /// runtime.put("test".to_string())?;
239    /// let value: String = runtime.take().unwrap();
240    /// assert_eq!(value, "test");
241    /// # Ok(())
242    /// # }
243    /// ```
244    pub fn take<T>(&mut self) -> Option<T>
245    where
246        T: 'static,
247    {
248        self.inner.take()
249    }
250
251    /// Add a value to the state  
252    /// Only one value of each type is stored - additional calls to `put` overwrite the old value
253    ///
254    /// # Errors
255    /// Can fail if the inner state cannot be borrowed mutably
256    ///
257    /// ```rust
258    /// use rustyscript::{ Runtime };
259    ///
260    /// # fn main() -> Result<(), rustyscript::Error> {
261    /// let mut runtime = Runtime::new(Default::default())?;
262    /// runtime.put("test".to_string())?;
263    /// let value: String = runtime.take().unwrap();
264    /// assert_eq!(value, "test");
265    /// # Ok(())
266    /// # }
267    /// ```
268    pub fn put<T>(&mut self, value: T) -> Result<(), Error>
269    where
270        T: 'static,
271    {
272        self.inner.put(value)
273    }
274
275    /// Register a rust function to be callable from JS
276    /// - The [`crate::sync_callback`] macro can be used to simplify this process
277    ///
278    /// # Errors
279    /// Since this function borrows the state, it can fail if the state cannot be borrowed mutably
280    ///
281    /// ```rust
282    /// use rustyscript::{ Runtime, Module, serde_json::Value };
283    ///
284    /// # fn main() -> Result<(), rustyscript::Error> {
285    /// let module = Module::new("test.js", " rustyscript.functions.foo(); ");
286    /// let mut runtime = Runtime::new(Default::default())?;
287    /// runtime.register_function("foo", |args| {
288    ///     if let Some(value) = args.get(0) {
289    ///         println!("called with: {}", value);
290    ///     }
291    ///     Ok(Value::Null)
292    /// })?;
293    ///
294    /// # Ok(())
295    /// # }
296    /// ```
297    pub fn register_function<F>(&mut self, name: &str, callback: F) -> Result<(), Error>
298    where
299        F: RsFunction,
300    {
301        self.inner.register_function(name, callback)
302    }
303
304    /// Register a non-blocking rust function to be callable from JS
305    /// - The [`crate::async_callback`] macro can be used to simplify this process
306    ///
307    /// # Errors
308    /// Since this function borrows the state, it can fail if the state cannot be borrowed mutably
309    ///
310    /// ```rust
311    /// use rustyscript::{ Runtime, Module, serde_json::Value, async_callback, Error };
312    ///
313    /// # fn main() -> Result<(), rustyscript::Error> {
314    /// let module = Module::new("test.js", " rustyscript.async_functions.add(1, 2); ");
315    /// let mut runtime = Runtime::new(Default::default())?;
316    /// runtime.register_async_function("add", async_callback!(
317    ///     |a: i64, b: i64| async move {
318    ///         Ok::<i64, Error>(a + b)
319    ///     }
320    /// ))?;
321    ///
322    /// # Ok(())
323    /// # }
324    /// ```
325    pub fn register_async_function<F>(&mut self, name: &str, callback: F) -> Result<(), Error>
326    where
327        F: RsAsyncFunction,
328    {
329        self.inner.register_async_function(name, callback)
330    }
331
332    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
333    /// The expression is evaluated in the global context, so changes persist
334    ///
335    /// Blocks on promise resolution, and runs the event loop to completion
336    ///
337    /// Asynchronous code is supported, partially
338    /// - Top-level await is not supported
339    /// - The event loop will be run to completion after the expression is evaluated
340    ///
341    /// For top-level await support, use one of:
342    /// - `call_function_async`
343    /// - `call_stored_function_async`
344    /// - `load_module_async`
345    /// - `load_modules_async`
346    ///
347    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
348    ///
349    /// # Arguments
350    /// * `expr` - A string representing the JavaScript expression to evaluate
351    ///
352    /// # Returns
353    /// A `Result` containing the deserialized result of the expression (`T`)  
354    /// or an error (`Error`) if the expression cannot be evaluated or if the
355    /// result cannot be deserialized.
356    ///
357    /// # Errors
358    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
359    ///
360    /// # Example
361    /// ```rust
362    /// use rustyscript::{ Runtime, Error };
363    ///
364    /// # fn main() -> Result<(), Error> {
365    /// let mut runtime = Runtime::new(Default::default())?;
366    ///
367    /// let value: u32 = runtime.eval("2 + 2")?;
368    /// assert_eq!(4, value);
369    ///
370    /// let value: String = runtime.eval("new Promise(resolve => resolve('test'))")?;
371    /// assert_eq!("test", value);
372    ///
373    /// # Ok(())
374    /// # }
375    /// ```
376    pub fn eval<T>(&mut self, expr: impl ToString) -> Result<T, Error>
377    where
378        T: serde::de::DeserializeOwned,
379    {
380        self.block_on(|runtime| async move { runtime.eval_async(expr).await })
381    }
382
383    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
384    /// The expression is evaluated in the global context, so changes persist
385    ///
386    /// Awaits promise resolution, and runs the event loop to completion
387    ///
388    /// Asynchronous code is supported, partially
389    /// - Top-level await is not supported
390    /// - The event loop will be run to completion after the expression is evaluated
391    ///
392    /// For top-level await support, use one of:
393    /// - `call_function_async`
394    /// - `call_stored_function_async`
395    /// - `load_module_async`
396    /// - `load_modules_async`
397    ///
398    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
399    ///
400    /// # Arguments
401    /// * `expr` - A string representing the JavaScript expression to evaluate
402    ///
403    /// # Returns
404    /// A `Result` containing the deserialized result of the expression (`T`)  
405    /// or an error (`Error`) if the expression cannot be evaluated or if the
406    /// result cannot be deserialized.
407    ///
408    /// # Errors
409    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
410    ///
411    /// # Example
412    /// For an example, see [`Runtime::eval`]
413    pub async fn eval_async<T>(&mut self, expr: impl ToString) -> Result<T, Error>
414    where
415        T: serde::de::DeserializeOwned,
416    {
417        let result = self.inner.eval(expr.to_string()).await?;
418        let result = self.inner.resolve_with_event_loop(result).await?;
419        self.inner.decode_value(result)
420    }
421
422    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
423    /// The expression is evaluated in the global context, so changes persist
424    ///
425    /// Does not await promise resolution, or run the event loop  
426    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
427    /// The event loop should be run using [`Runtime::await_event_loop`]
428    ///
429    /// Note that this function needs to be async because calls to `setTimeout` must be evaluated from within an async runtime.
430    ///
431    /// Asynchronous code is supported, partially
432    /// - Top-level await is not supported
433    ///
434    /// For top-level await support, use one of:
435    /// - `call_function_async`
436    /// - `call_stored_function_async`
437    /// - `load_module_async`
438    /// - `load_modules_async`
439    ///
440    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
441    ///
442    /// # Arguments
443    /// * `expr` - A string representing the JavaScript expression to evaluate
444    ///
445    /// # Returns
446    /// A `Result` containing the deserialized result of the expression (`T`)  
447    /// or an error (`Error`) if the expression cannot be evaluated or if the
448    /// result cannot be deserialized.
449    ///
450    /// # Errors
451    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
452    ///
453    /// # Example
454    /// For an example, see [`Runtime::eval`]
455    pub async fn eval_immediate<T>(&mut self, expr: impl ToString) -> Result<T, Error>
456    where
457        T: serde::de::DeserializeOwned,
458    {
459        let result = self.inner.eval(expr.to_string()).await?;
460        self.inner.decode_value(result)
461    }
462
463    /// Calls a stored javascript function and deserializes its return value.
464    ///
465    /// Returns a future that resolves when:
466    /// - The event loop is resolved, and
467    /// - If the value is a promise, the promise is resolved
468    ///
469    /// See [`Runtime::call_function`] for an example
470    ///
471    /// Note that synchronous functions are run synchronously. Returned promises will be run asynchronously, however.
472    ///
473    /// # Arguments
474    /// * `module_context` - Optional handle to a module providing global context for the function
475    /// * `function` - A The function object
476    /// * `args` - The arguments to pass to the function
477    ///
478    /// # Returns
479    /// A `Result` containing the deserialized result of the function call (`T`)  
480    /// or an error (`Error`) if there are issues with calling the function,
481    /// or if the result cannot be deserialized.
482    ///
483    /// # Errors
484    /// Can fail if there are issues with calling the function, or if the result cannot be deserialized into the requested type
485    pub async fn call_stored_function_async<T>(
486        &mut self,
487        module_context: Option<&ModuleHandle>,
488        function: &Function,
489        args: &impl serde::ser::Serialize,
490    ) -> Result<T, Error>
491    where
492        T: serde::de::DeserializeOwned,
493    {
494        let function = function.as_global(&mut self.deno_runtime().handle_scope());
495        let result = self
496            .inner
497            .call_function_by_ref(module_context, &function, args)?;
498        let result = self.inner.resolve_with_event_loop(result).await?;
499        self.inner.decode_value(result)
500    }
501
502    /// Calls a stored javascript function and deserializes its return value.
503    ///
504    /// Blocks until:
505    /// - The event loop is resolved, and
506    /// - If the value is a promise, the promise is resolved
507    ///
508    /// See [`Runtime::call_function`] for an example
509    ///
510    /// # Arguments
511    /// * `module_context` - Optional handle to a module providing global context for the function
512    /// * `function` - A The function object
513    /// * `args` - The arguments to pass to the function
514    ///
515    /// # Returns
516    /// A `Result` containing the deserialized result of the function call (`T`)  
517    /// or an error (`Error`) if there are issues with calling the function,
518    /// or if the result cannot be deserialized.
519    ///
520    /// # Errors
521    /// Can fail if there are issues with calling the function, or if the result cannot be deserialized into the requested type
522    pub fn call_stored_function<T>(
523        &mut self,
524        module_context: Option<&ModuleHandle>,
525        function: &Function,
526        args: &impl serde::ser::Serialize,
527    ) -> Result<T, Error>
528    where
529        T: deno_core::serde::de::DeserializeOwned,
530    {
531        self.block_on(|runtime| async move {
532            runtime
533                .call_stored_function_async(module_context, function, args)
534                .await
535        })
536    }
537
538    /// Calls a stored javascript function and deserializes its return value.
539    ///
540    /// Will not attempt to resolve promises, or run the event loop  
541    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
542    /// The event loop should be run using [`Runtime::await_event_loop`]
543    ///
544    /// See [`Runtime::call_function`] for an example
545    ///
546    /// # Arguments
547    /// * `module_context` - Optional handle to a module providing global context for the function
548    /// * `function` - A The function object
549    /// * `args` - The arguments to pass to the function
550    ///
551    /// # Returns
552    /// A `Result` containing the deserialized result of the function call (`T`)  
553    /// or an error (`Error`) if there are issues with calling the function,
554    /// or if the result cannot be deserialized.
555    ///
556    /// # Errors
557    /// Can fail if there are issues with calling the function, or if the result cannot be deserialized into the requested type
558    pub fn call_stored_function_immediate<T>(
559        &mut self,
560        module_context: Option<&ModuleHandle>,
561        function: &Function,
562        args: &impl serde::ser::Serialize,
563    ) -> Result<T, Error>
564    where
565        T: deno_core::serde::de::DeserializeOwned,
566    {
567        let function = function.as_global(&mut self.deno_runtime().handle_scope());
568        let result = self
569            .inner
570            .call_function_by_ref(module_context, &function, args)?;
571        self.inner.decode_value(result)
572    }
573
574    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
575    ///
576    /// Returns a future that resolves when:
577    /// - The event loop is resolved, and
578    /// - If the value is a promise, the promise is resolved
579    ///
580    /// Note that synchronous functions are run synchronously. Returned promises will be run asynchronously, however.
581    ///
582    /// See [`Runtime::call_function`] for an example
583    ///
584    /// # Arguments
585    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
586    /// * `name` - A string representing the name of the javascript function to call.
587    /// * `args` - The arguments to pass to the function
588    ///
589    /// # Returns
590    /// A `Result` containing the deserialized result of the function call (`T`)  
591    /// or an error (`Error`) if the function cannot be found, if there are issues with
592    /// calling the function, or if the result cannot be deserialized.
593    ///
594    /// # Errors
595    /// Fails if the function cannot be found, if there are issues with calling the function,
596    /// Or if the result cannot be deserialized into the requested type
597    pub async fn call_function_async<T>(
598        &mut self,
599        module_context: Option<&ModuleHandle>,
600        name: &str,
601        args: &impl serde::ser::Serialize,
602    ) -> Result<T, Error>
603    where
604        T: deno_core::serde::de::DeserializeOwned,
605    {
606        let function = self.inner.get_function_by_name(module_context, name)?;
607        let result = self
608            .inner
609            .call_function_by_ref(module_context, &function, args)?;
610        let result = self.inner.resolve_with_event_loop(result).await?;
611        self.inner.decode_value(result)
612    }
613
614    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
615    ///
616    /// Blocks until:
617    /// - The event loop is resolved, and
618    /// - If the value is a promise, the promise is resolved
619    ///
620    /// # Arguments
621    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
622    /// * `name` - A string representing the name of the javascript function to call.
623    /// * `args` - The arguments to pass to the function
624    ///
625    /// # Returns
626    /// A `Result` containing the deserialized result of the function call (`T`)  
627    /// or an error (`Error`) if the function cannot be found, if there are issues with
628    /// calling the function, or if the result cannot be deserialized.
629    ///
630    /// # Errors
631    /// Fails if the function cannot be found, if there are issues with calling the function,  
632    /// Or if the result cannot be deserialized into the requested type
633    ///
634    /// # Example
635    ///
636    /// ```rust
637    /// use rustyscript::{ json_args, Runtime, Module, Error };
638    ///
639    /// # fn main() -> Result<(), Error> {
640    /// let mut runtime = Runtime::new(Default::default())?;
641    /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
642    /// let module = runtime.load_module(&module)?;
643    /// let value: usize = runtime.call_function(Some(&module), "f", json_args!())?;
644    /// # Ok(())
645    /// # }
646    /// ```
647    pub fn call_function<T>(
648        &mut self,
649        module_context: Option<&ModuleHandle>,
650        name: &str,
651        args: &impl serde::ser::Serialize,
652    ) -> Result<T, Error>
653    where
654        T: deno_core::serde::de::DeserializeOwned,
655    {
656        self.block_on(|runtime| async move {
657            runtime
658                .call_function_async(module_context, name, args)
659                .await
660        })
661    }
662
663    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
664    ///
665    /// Will not attempt to resolve promises, or run the event loop  
666    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
667    /// The event loop should be run using [`Runtime::await_event_loop`]
668    ///
669    /// # Arguments
670    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
671    /// * `name` - A string representing the name of the javascript function to call.
672    /// * `args` - The arguments to pass to the function
673    ///
674    /// # Returns
675    /// A `Result` containing the deserialized result of the function call (`T`)  
676    /// or an error (`Error`) if the function cannot be found, if there are issues with
677    /// calling the function, or if the result cannot be deserialized.
678    ///
679    /// # Errors
680    /// Fails if the function cannot be found, if there are issues with calling the function,  
681    /// Or if the result cannot be deserialized into the requested type
682    ///
683    /// # Example
684    ///
685    /// ```rust
686    /// use rustyscript::{ json_args, Runtime, Module, Error };
687    ///
688    /// # fn main() -> Result<(), Error> {
689    /// let mut runtime = Runtime::new(Default::default())?;
690    /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
691    /// let module = runtime.load_module(&module)?;
692    /// let value: usize = runtime.call_function_immediate(Some(&module), "f", json_args!())?;
693    /// # Ok(())
694    /// # }
695    /// ```
696    pub fn call_function_immediate<T>(
697        &mut self,
698        module_context: Option<&ModuleHandle>,
699        name: &str,
700        args: &impl serde::ser::Serialize,
701    ) -> Result<T, Error>
702    where
703        T: deno_core::serde::de::DeserializeOwned,
704    {
705        let function = self.inner.get_function_by_name(module_context, name)?;
706        let result = self
707            .inner
708            .call_function_by_ref(module_context, &function, args)?;
709        self.inner.decode_value(result)
710    }
711
712    /// Get a value from a runtime instance
713    ///
714    /// Blocks until:
715    /// - The event loop is resolved, and
716    /// - If the value is a promise, the promise is resolved
717    ///
718    /// # Arguments
719    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
720    /// * `name` - A string representing the name of the value to find
721    ///
722    /// # Returns
723    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
724    /// Or if the result cannot be deserialized into the requested type
725    ///
726    /// # Errors
727    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
728    ///
729    /// # Example
730    ///
731    /// ```rust
732    /// use rustyscript::{ Runtime, Module, Error };
733    ///
734    /// # fn main() -> Result<(), Error> {
735    /// let mut runtime = Runtime::new(Default::default())?;
736    /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
737    /// let module = runtime.load_module(&module)?;
738    /// let value: usize = runtime.get_value(Some(&module), "my_value")?;
739    /// # Ok(())
740    /// # }
741    /// ```
742    pub fn get_value<T>(
743        &mut self,
744        module_context: Option<&ModuleHandle>,
745        name: &str,
746    ) -> Result<T, Error>
747    where
748        T: serde::de::DeserializeOwned,
749    {
750        self.block_on(|runtime| async move { runtime.get_value_async(module_context, name).await })
751    }
752
753    /// Get a value from a runtime instance
754    ///
755    /// Returns a future that resolves when:
756    /// - The event loop is resolved, and
757    /// - If the value is a promise, the promise is resolved
758    ///
759    /// See [`Runtime::get_value`] for an example
760    ///
761    /// # Arguments
762    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
763    /// * `name` - A string representing the name of the value to find
764    ///
765    /// # Returns
766    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,  
767    /// Or if the result cannot be deserialized into the requested type
768    ///
769    /// # Errors
770    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
771    pub async fn get_value_async<T>(
772        &mut self,
773        module_context: Option<&ModuleHandle>,
774        name: &str,
775    ) -> Result<T, Error>
776    where
777        T: serde::de::DeserializeOwned,
778    {
779        let result = self.inner.get_value_ref(module_context, name)?;
780        let result = self.inner.resolve_with_event_loop(result).await?;
781        self.inner.decode_value(result)
782    }
783
784    /// Get a value from a runtime instance
785    ///
786    /// Will not attempt to resolve promises, or run the event loop  
787    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
788    /// The event loop should be run using [`Runtime::await_event_loop`]
789    ///
790    /// # Arguments
791    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
792    /// * `name` - A string representing the name of the value to find
793    ///
794    /// # Returns
795    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
796    /// Or if the result cannot be deserialized into the requested type
797    ///
798    /// # Errors
799    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
800    ///
801    /// # Example
802    ///
803    /// ```rust
804    /// use rustyscript::{ Runtime, Module, Error };
805    ///
806    /// # fn main() -> Result<(), Error> {
807    /// let mut runtime = Runtime::new(Default::default())?;
808    /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
809    /// let module = runtime.load_module(&module)?;
810    /// let value: usize = runtime.get_value_immediate(Some(&module), "my_value")?;
811    /// # Ok(())
812    /// # }
813    /// ```
814    pub fn get_value_immediate<T>(
815        &mut self,
816        module_context: Option<&ModuleHandle>,
817        name: &str,
818    ) -> Result<T, Error>
819    where
820        T: serde::de::DeserializeOwned,
821    {
822        let result = self.inner.get_value_ref(module_context, name)?;
823        self.inner.decode_value(result)
824    }
825
826    /// Executes the given module, and returns a handle allowing you to extract values
827    /// and call functions
828    ///
829    /// Blocks until the module has been executed AND the event loop has fully resolved  
830    /// See [`Runtime::load_module_async`] for a non-blocking variant, or use with async
831    /// background tasks
832    ///
833    /// # Arguments
834    /// * `module` - A `Module` object containing the module's filename and contents.
835    ///
836    /// # Returns
837    /// A `Result` containing a handle for the loaded module
838    /// or an error (`Error`) if there are issues with loading or executing the module
839    ///
840    /// # Errors
841    /// Can fail if the module cannot be loaded, or execution fails
842    ///
843    /// # Example
844    ///
845    /// ```rust
846    /// // Create a module with filename and contents
847    /// use rustyscript::{Runtime, Module, Error};
848    ///
849    /// # fn main() -> Result<(), Error> {
850    /// let mut runtime = Runtime::new(Default::default())?;
851    /// let module = Module::new("test.js", "export default () => 'test'");
852    /// runtime.load_module(&module);
853    /// # Ok(())
854    /// # }
855    /// ```
856    pub fn load_module(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
857        self.block_on(|runtime| async move {
858            let handle = runtime.load_module_async(module).await;
859            runtime
860                .await_event_loop(PollEventLoopOptions::default(), None)
861                .await?;
862            handle
863        })
864    }
865
866    /// Executes the given module, and returns a handle allowing you to extract values
867    /// and call functions
868    ///
869    /// Returns a future that resolves to the handle for the loaded module  
870    /// Makes no attempt to fully resolve the event loop - call [`Runtime::await_event_loop`]
871    /// to resolve background tasks and async listeners
872    ///
873    /// # Arguments
874    /// * `module` - A `Module` object containing the module's filename and contents.
875    ///
876    /// # Returns
877    /// A `Result` containing a handle for the loaded module
878    /// or an error (`Error`) if there are issues with loading or executing the module
879    ///
880    /// # Errors
881    /// Can fail if the module cannot be loaded, or execution fails
882    ///
883    /// See [`Runtime::load_module`] for an example
884    pub async fn load_module_async(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
885        self.inner.load_modules(None, vec![module]).await
886    }
887
888    /// Executes the given module, and returns a handle allowing you to extract values
889    /// and call functions.
890    ///
891    /// Blocks until all modules have been executed AND the event loop has fully resolved  
892    /// See [`Runtime::load_module_async`] for a non-blocking variant, or use with async
893    /// background tasks
894    ///
895    /// This will load 'module' as the main module, and the others as side-modules.  
896    /// Only one main module can be loaded per runtime
897    ///
898    /// # Arguments
899    /// * `module` - A `Module` object containing the module's filename and contents.
900    /// * `side_modules` - A set of additional modules to be loaded into memory for use
901    ///
902    /// # Returns
903    /// A `Result` containing a handle for the loaded module
904    /// or an error (`Error`) if there are issues with loading or executing the module
905    ///
906    /// # Errors
907    /// Can fail if the module cannot be loaded, or execution fails
908    ///
909    /// # Example
910    ///
911    /// ```rust
912    /// // Create a module with filename and contents
913    /// use rustyscript::{Runtime, Module, Error};
914    ///
915    /// # fn main() -> Result<(), Error> {
916    /// let mut runtime = Runtime::new(Default::default())?;
917    /// let module = Module::new("test.js", "export default () => 'test'");
918    /// runtime.load_modules(&module, vec![]);
919    /// # Ok(())
920    /// # }
921    /// ```
922    pub fn load_modules(
923        &mut self,
924        module: &Module,
925        side_modules: Vec<&Module>,
926    ) -> Result<ModuleHandle, Error> {
927        self.block_on(move |runtime| async move {
928            let handle = runtime.load_modules_async(module, side_modules).await;
929            runtime
930                .await_event_loop(PollEventLoopOptions::default(), None)
931                .await?;
932            handle
933        })
934    }
935
936    /// Executes the given module, and returns a handle allowing you to extract values
937    /// and call functions.
938    ///
939    /// Returns a future that resolves to the handle for the loaded module  
940    /// Makes no attempt to resolve the event loop - call [`Runtime::await_event_loop`] to
941    /// resolve background tasks and async listeners
942    ///
943    /// This will load 'module' as the main module, and the others as side-modules.  
944    /// Only one main module can be loaded per runtime
945    ///
946    /// See [`Runtime::load_modules`] for an example
947    ///
948    /// # Arguments
949    /// * `module` - A `Module` object containing the module's filename and contents.
950    /// * `side_modules` - A set of additional modules to be loaded into memory for use
951    ///
952    /// # Returns
953    /// A `Result` containing a handle for the loaded main module, or the last side-module
954    /// or an error (`Error`) if there are issues with loading or executing the modules
955    ///
956    /// # Errors
957    /// Can fail if the modules cannot be loaded, or execution fails
958    pub async fn load_modules_async(
959        &mut self,
960        module: &Module,
961        side_modules: Vec<&Module>,
962    ) -> Result<ModuleHandle, Error> {
963        self.inner.load_modules(Some(module), side_modules).await
964    }
965
966    /// Executes the entrypoint function of a module within the Deno runtime.
967    ///
968    /// Blocks until:
969    /// - The event loop is resolved, and
970    /// - If the value is a promise, the promise is resolved
971    ///
972    /// # Arguments
973    /// * `module_context` - A handle returned by loading a module into the runtime
974    ///
975    /// # Returns
976    /// A `Result` containing the deserialized result of the entrypoint execution (`T`)  
977    /// if successful, or an error (`Error`) if the entrypoint is missing, the execution fails,
978    /// or the result cannot be deserialized.
979    ///
980    /// # Errors
981    /// Can fail if the module cannot be loaded, if the entrypoint is missing, if the execution fails,  
982    /// Or if the result cannot be deserialized into the requested type
983    ///
984    /// # Example
985    ///
986    /// ```rust
987    /// use rustyscript::{json_args, Runtime, Module, Error};
988    ///
989    /// # fn main() -> Result<(), Error> {
990    /// let mut runtime = Runtime::new(Default::default())?;
991    /// let module = Module::new("test.js", "export default () => 'test'");
992    /// let module = runtime.load_module(&module)?;
993    ///
994    /// // Run the entrypoint and handle the result
995    /// let value: String = runtime.call_entrypoint(&module, json_args!())?;
996    /// # Ok(())
997    /// # }
998    /// ```
999    pub fn call_entrypoint<T>(
1000        &mut self,
1001        module_context: &ModuleHandle,
1002        args: &impl serde::ser::Serialize,
1003    ) -> Result<T, Error>
1004    where
1005        T: deno_core::serde::de::DeserializeOwned,
1006    {
1007        self.block_on(
1008            |runtime| async move { runtime.call_entrypoint_async(module_context, args).await },
1009        )
1010    }
1011
1012    /// Executes the entrypoint function of a module within the Deno runtime.
1013    ///
1014    /// Returns a future that resolves when:
1015    /// - The event loop is resolved, and
1016    /// - If the value is a promise, the promise is resolved
1017    ///
1018    /// Note that synchronous functions are run synchronously. Returned promises will be run asynchronously, however.
1019    ///
1020    /// See [`Runtime::call_entrypoint`] for an example
1021    ///
1022    /// # Arguments
1023    /// * `module_context` - A handle returned by loading a module into the runtime
1024    ///
1025    /// # Returns
1026    /// A `Result` containing the deserialized result of the entrypoint execution (`T`)  
1027    /// if successful, or an error (`Error`) if the entrypoint is missing, the execution fails,
1028    /// or the result cannot be deserialized.
1029    ///
1030    /// # Errors
1031    /// Can fail if the module cannot be loaded, if the entrypoint is missing, if the execution fails,  
1032    /// Or if the result cannot be deserialized into the requested type
1033    pub async fn call_entrypoint_async<T>(
1034        &mut self,
1035        module_context: &ModuleHandle,
1036        args: &impl serde::ser::Serialize,
1037    ) -> Result<T, Error>
1038    where
1039        T: deno_core::serde::de::DeserializeOwned,
1040    {
1041        if let Some(entrypoint) = module_context.entrypoint() {
1042            let result = self
1043                .inner
1044                .call_function_by_ref(Some(module_context), entrypoint, args)?;
1045            let result = self.inner.resolve_with_event_loop(result).await?;
1046            self.inner.decode_value(result)
1047        } else {
1048            Err(Error::MissingEntrypoint(module_context.module().clone()))
1049        }
1050    }
1051
1052    /// Executes the entrypoint function of a module within the Deno runtime.
1053    ///
1054    /// Will not attempt to resolve promises, or run the event loop  
1055    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
1056    /// The event loop should be run using [`Runtime::await_event_loop`]
1057    ///
1058    /// # Arguments
1059    /// * `module_context` - A handle returned by loading a module into the runtime
1060    ///
1061    /// # Returns
1062    /// A `Result` containing the deserialized result of the entrypoint execution (`T`)
1063    /// if successful, or an error (`Error`) if the entrypoint is missing, the execution fails,
1064    /// or the result cannot be deserialized.
1065    ///
1066    /// # Errors
1067    /// Can fail if the module cannot be loaded, if the entrypoint is missing, if the execution fails,
1068    /// Or if the result cannot be deserialized into the requested type
1069    ///
1070    /// # Example
1071    ///
1072    /// ```rust
1073    /// use rustyscript::{json_args, Runtime, Module, Error};
1074    ///
1075    /// # fn main() -> Result<(), Error> {
1076    /// let mut runtime = Runtime::new(Default::default())?;
1077    /// let module = Module::new("test.js", "export default () => 'test'");
1078    /// let module = runtime.load_module(&module)?;
1079    ///
1080    /// // Run the entrypoint and handle the result
1081    /// let value: String = runtime.call_entrypoint_immediate(&module, json_args!())?;
1082    /// # Ok(())
1083    /// # }
1084    /// ```
1085    pub fn call_entrypoint_immediate<T>(
1086        &mut self,
1087        module_context: &ModuleHandle,
1088        args: &impl serde::ser::Serialize,
1089    ) -> Result<T, Error>
1090    where
1091        T: deno_core::serde::de::DeserializeOwned,
1092    {
1093        if let Some(entrypoint) = module_context.entrypoint() {
1094            let result = self.block_on(|runtime| async move {
1095                runtime
1096                    .inner
1097                    .call_function_by_ref(Some(module_context), entrypoint, args)
1098            })?;
1099            self.inner.decode_value(result)
1100        } else {
1101            Err(Error::MissingEntrypoint(module_context.module().clone()))
1102        }
1103    }
1104
1105    /// Loads a module into a new runtime, executes the entry function and returns the
1106    /// result of the module's execution, deserialized into the specified Rust type (`T`).
1107    ///
1108    /// # Arguments
1109    /// * `module` - A `Module` object containing the module's filename and contents.
1110    /// * `side_modules` - A set of additional modules to be loaded into memory for use
1111    /// * `runtime_options` - Options for the creation of the runtime
1112    /// * `entrypoint_args` - Arguments to pass to the entrypoint function
1113    ///
1114    /// # Returns
1115    /// A `Result` containing the deserialized result of the entrypoint execution (`T`)  
1116    /// if successful, or an error (`Error`) if the entrypoint is missing, the execution fails,
1117    /// or the result cannot be deserialized.
1118    ///
1119    /// # Errors
1120    /// Can fail if the module cannot be loaded, if the entrypoint is missing, if the execution fails,  
1121    /// Or if the result cannot be deserialized into the requested type
1122    ///
1123    /// # Example
1124    ///
1125    /// ```rust
1126    /// // Create a module with filename and contents
1127    /// use rustyscript::{json_args, Runtime, Module, Error};
1128    ///
1129    /// # fn main() -> Result<(), Error> {
1130    /// let module = Module::new("test.js", "export default () => 2");
1131    /// let value: usize = Runtime::execute_module(&module, vec![], Default::default(), json_args!())?;
1132    /// # Ok(())
1133    /// # }
1134    /// ```
1135    pub fn execute_module<T>(
1136        module: &Module,
1137        side_modules: Vec<&Module>,
1138        runtime_options: RuntimeOptions,
1139        entrypoint_args: &impl serde::ser::Serialize,
1140    ) -> Result<T, Error>
1141    where
1142        T: deno_core::serde::de::DeserializeOwned,
1143    {
1144        let mut runtime = Runtime::new(runtime_options)?;
1145        let module = runtime.load_modules(module, side_modules)?;
1146        let value: T = runtime.call_entrypoint(&module, entrypoint_args)?;
1147        Ok(value)
1148    }
1149}
1150
1151impl AsyncBridgeExt for Runtime {
1152    fn bridge(&self) -> &AsyncBridge {
1153        &self.tokio
1154    }
1155}
1156
1157#[cfg(test)]
1158mod test_runtime {
1159    use crate::json_args;
1160    use std::time::Duration;
1161
1162    use super::*;
1163    use deno_core::extension;
1164
1165    #[test]
1166    fn test_new() {
1167        Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1168
1169        extension!(test_extension);
1170        Runtime::new(RuntimeOptions {
1171            extensions: vec![test_extension::init()],
1172            ..Default::default()
1173        })
1174        .expect("Could not create runtime with extensions");
1175    }
1176
1177    #[test]
1178    fn test_get_value() {
1179        let module = Module::new(
1180            "test.js",
1181            "
1182            globalThis.a = 2;
1183            export const b = 'test';
1184            export const fnc = null;
1185        ",
1186        );
1187
1188        let mut runtime =
1189            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1190        let module = runtime
1191            .load_modules(&module, vec![])
1192            .expect("Could not load module");
1193
1194        assert_eq!(
1195            2,
1196            runtime
1197                .get_value::<usize>(Some(&module), "a")
1198                .expect("Could not find global")
1199        );
1200        assert_eq!(
1201            "test",
1202            runtime
1203                .get_value::<String>(Some(&module), "b")
1204                .expect("Could not find export")
1205        );
1206        runtime
1207            .get_value::<Undefined>(Some(&module), "c")
1208            .expect_err("Could not detect null");
1209        runtime
1210            .get_value::<Undefined>(Some(&module), "d")
1211            .expect_err("Could not detect undeclared");
1212    }
1213
1214    #[test]
1215    fn test_load_module() {
1216        let mut runtime =
1217            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1218        let module = Module::new(
1219            "test.js",
1220            "
1221            export default () => 2;
1222        ",
1223        );
1224        let module = runtime
1225            .load_modules(&module, vec![])
1226            .expect("Could not load module");
1227        assert_ne!(0, module.id());
1228
1229        let mut runtime =
1230            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1231        let module1 = Module::new(
1232            "importme.js",
1233            "
1234            export const value = 2;
1235        ",
1236        );
1237        let module2 = Module::new(
1238            "test.js",
1239            "
1240            import { value } from './importme.js';
1241            rustyscript.register_entrypoint(() => value);
1242        ",
1243        );
1244        runtime
1245            .load_module(&module1)
1246            .expect("Could not load modules");
1247        let module = runtime
1248            .load_module(&module2)
1249            .expect("Could not load modules");
1250        let value: usize = runtime
1251            .call_entrypoint(&module, json_args!())
1252            .expect("Could not call exported fn");
1253        assert_eq!(2, value);
1254
1255        let mut runtime = Runtime::new(RuntimeOptions {
1256            timeout: Duration::from_millis(50),
1257            ..Default::default()
1258        })
1259        .expect("Could not create the runtime");
1260        let module = Module::new(
1261            "test.js",
1262            "
1263            await new Promise(r => setTimeout(r, 2000));
1264        ",
1265        );
1266        runtime
1267            .load_modules(&module, vec![])
1268            .expect_err("Did not interupt after timeout");
1269    }
1270
1271    #[test]
1272    fn test_load_modules() {
1273        let mut runtime =
1274            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1275        let module = Module::new(
1276            "test.js",
1277            "
1278            rustyscript.register_entrypoint(() => 2);
1279        ",
1280        );
1281        let module = runtime
1282            .load_modules(&module, vec![])
1283            .expect("Could not load module");
1284        assert_ne!(0, module.id());
1285
1286        let mut runtime =
1287            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1288        let module1 = Module::new(
1289            "importme.js",
1290            "
1291            export const value = 2;
1292        ",
1293        );
1294        let module2 = Module::new(
1295            "test.js",
1296            "
1297            import { value } from './importme.js';
1298            rustyscript.register_entrypoint(() => value);
1299        ",
1300        );
1301        let module = runtime
1302            .load_modules(&module2, vec![&module1])
1303            .expect("Could not load modules");
1304        let value: usize = runtime
1305            .call_entrypoint(&module, json_args!())
1306            .expect("Could not call exported fn");
1307        assert_eq!(2, value);
1308
1309        let mut runtime = Runtime::new(RuntimeOptions {
1310            timeout: Duration::from_millis(50),
1311            ..Default::default()
1312        })
1313        .expect("Could not create the runtime");
1314        let module = Module::new(
1315            "test.js",
1316            "
1317            await new Promise(r => setTimeout(r, 5000));
1318        ",
1319        );
1320        runtime
1321            .load_modules(&module, vec![])
1322            .expect_err("Did not interupt after timeout");
1323    }
1324
1325    #[test]
1326    fn test_call_entrypoint() {
1327        let mut runtime =
1328            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1329        let module = Module::new(
1330            "test.js",
1331            "
1332            rustyscript.register_entrypoint(() => 2);
1333        ",
1334        );
1335        let module = runtime
1336            .load_modules(&module, vec![])
1337            .expect("Could not load module");
1338        let value: usize = runtime
1339            .call_entrypoint(&module, json_args!())
1340            .expect("Could not call registered fn");
1341        assert_eq!(2, value);
1342
1343        let mut runtime = Runtime::new(RuntimeOptions {
1344            default_entrypoint: Some("load".to_string()),
1345            ..Default::default()
1346        })
1347        .expect("Could not create the runtime");
1348        let module = Module::new(
1349            "test.js",
1350            "
1351            export const load = () => 2;
1352        ",
1353        );
1354        let module = runtime
1355            .load_modules(&module, vec![])
1356            .expect("Could not load module");
1357        let value: usize = runtime
1358            .call_entrypoint(&module, json_args!())
1359            .expect("Could not call exported fn");
1360        assert_eq!(2, value);
1361
1362        let mut runtime =
1363            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1364        let module = Module::new(
1365            "test.js",
1366            "
1367            export const load = () => 2;
1368        ",
1369        );
1370        let module = runtime
1371            .load_modules(&module, vec![])
1372            .expect("Could not load module");
1373        runtime
1374            .call_entrypoint::<Undefined>(&module, json_args!())
1375            .expect_err("Did not detect no entrypoint");
1376    }
1377
1378    #[test]
1379    fn test_execute_module() {
1380        let module = Module::new(
1381            "test.js",
1382            "
1383            rustyscript.register_entrypoint(() => 2);
1384        ",
1385        );
1386        let value: usize =
1387            Runtime::execute_module(&module, vec![], RuntimeOptions::default(), json_args!())
1388                .expect("Could not exec module");
1389        assert_eq!(2, value);
1390
1391        let module = Module::new(
1392            "test.js",
1393            "
1394            function load() { return 2; }
1395        ",
1396        );
1397        Runtime::execute_module::<Undefined>(
1398            &module,
1399            vec![],
1400            RuntimeOptions::default(),
1401            json_args!(),
1402        )
1403        .expect_err("Could not detect no entrypoint");
1404    }
1405
1406    #[test]
1407    fn call_function() {
1408        let module = Module::new(
1409            "test.js",
1410            "
1411            globalThis.fna = (i) => i;
1412            export function fnb() { return 'test'; }
1413            export const fnc = 2;
1414            export const fne = () => {};
1415        ",
1416        );
1417
1418        let mut runtime =
1419            Runtime::new(RuntimeOptions::default()).expect("Could not create the runtime");
1420        let module = runtime
1421            .load_modules(&module, vec![])
1422            .expect("Could not load module");
1423
1424        let result: usize = runtime
1425            .call_function(Some(&module), "fna", json_args!(2))
1426            .expect("Could not call global");
1427        assert_eq!(2, result);
1428
1429        let result: String = runtime
1430            .call_function(Some(&module), "fnb", json_args!())
1431            .expect("Could not call export");
1432        assert_eq!("test", result);
1433
1434        runtime
1435            .call_function::<Undefined>(Some(&module), "fnc", json_args!())
1436            .expect_err("Did not detect non-function");
1437        runtime
1438            .call_function::<Undefined>(Some(&module), "fnd", json_args!())
1439            .expect_err("Did not detect undefined");
1440        runtime
1441            .call_function::<Undefined>(Some(&module), "fne", json_args!())
1442            .expect("Did not allow undefined return");
1443    }
1444
1445    #[test]
1446    fn test_heap_exhaustion_handled() {
1447        let mut runtime = Runtime::new(RuntimeOptions {
1448            max_heap_size: Some(100 * 1024 * 1024),
1449            ..Default::default()
1450        })
1451        .expect("Could not create the runtime");
1452        let module = Module::new(
1453            "test.js",
1454            "const largeArray = new Array(40 * 1024 * 1024).fill('a');",
1455        );
1456        runtime
1457            .load_modules(&module, vec![])
1458            .expect_err("Did not detect heap exhaustion");
1459    }
1460}