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

rustyscript/
inner_runtime.rs

1use std::{
2    collections::{HashMap, HashSet},
3    path::{Path, PathBuf},
4    pin::Pin,
5    rc::Rc,
6    sync::Arc,
7    task::Poll,
8    time::Duration,
9};
10
11use deno_core::{
12    futures::FutureExt, serde_json, serde_v8::from_v8, v8, JsRuntime, JsRuntimeForSnapshot,
13    PollEventLoopOptions,
14};
15use deno_features::FeatureChecker;
16use serde::de::DeserializeOwned;
17use tokio_util::sync::CancellationToken;
18
19use crate::{
20    ext,
21    module_loader::{LoaderOptions, RustyLoader},
22    traits::{ToDefinedValue, ToModuleSpecifier, ToV8String},
23    transpiler::transpile,
24    utilities, Error, ExtensionOptions, Module, ModuleHandle,
25};
26
27/// Wrapper trait to make the `InnerRuntime` generic over the runtime types
28pub trait RuntimeTrait {
29    fn try_new(options: deno_core::RuntimeOptions) -> Result<Self, Error>
30    where
31        Self: Sized;
32    fn rt_mut(&mut self) -> &mut JsRuntime;
33}
34impl RuntimeTrait for JsRuntime {
35    fn try_new(options: deno_core::RuntimeOptions) -> Result<Self, Error>
36    where
37        Self: Sized,
38    {
39        let rt = Self::try_new(options)?;
40        Ok(rt)
41    }
42    fn rt_mut(&mut self) -> &mut JsRuntime {
43        self
44    }
45}
46impl RuntimeTrait for JsRuntimeForSnapshot {
47    fn try_new(options: deno_core::RuntimeOptions) -> Result<Self, Error>
48    where
49        Self: Sized,
50    {
51        let rt = Self::try_new(options)?;
52        Ok(rt)
53    }
54    fn rt_mut(&mut self) -> &mut JsRuntime {
55        self
56    }
57}
58
59/// Represents a function that can be registered with the runtime
60pub trait RsFunction:
61    Fn(&[serde_json::Value]) -> Result<serde_json::Value, Error> + 'static
62{
63}
64impl<F> RsFunction for F where
65    F: Fn(&[serde_json::Value]) -> Result<serde_json::Value, Error> + 'static
66{
67}
68
69/// Represents an async function that can be registered with the runtime
70pub trait RsAsyncFunction:
71    Fn(
72        Vec<serde_json::Value>,
73    ) -> Pin<Box<dyn std::future::Future<Output = Result<serde_json::Value, Error>>>>
74    + 'static
75{
76}
77impl<F> RsAsyncFunction for F where
78    F: Fn(
79            Vec<serde_json::Value>,
80        ) -> Pin<Box<dyn std::future::Future<Output = Result<serde_json::Value, Error>>>>
81        + 'static
82{
83}
84
85/// Decodes a set of arguments into a vector of v8 values
86/// This is used to pass arguments to a javascript function
87/// And is faster and more flexible than using `json_args!`
88fn decode_args<'a>(
89    args: &impl serde::ser::Serialize,
90    scope: &mut v8::HandleScope<'a>,
91) -> Result<Vec<v8::Local<'a, v8::Value>>, Error> {
92    let args = deno_core::serde_v8::to_v8(scope, args)?;
93    match v8::Local::<v8::Array>::try_from(args) {
94        Ok(args) => {
95            let len = args.length();
96            let mut result = Vec::with_capacity(len as usize);
97            for i in 0..len {
98                let index = v8::Integer::new(
99                    scope,
100                    i.try_into().map_err(|_| {
101                        Error::Runtime(format!(
102                            "Could not decode {len} arguments - use `big_json_args`"
103                        ))
104                    })?,
105                );
106                let arg = args
107                    .get(scope, index.into())
108                    .ok_or_else(|| Error::Runtime(format!("Invalid argument at index {i}")))?;
109                result.push(arg);
110            }
111            Ok(result)
112        }
113        Err(_) if args.is_undefined() || args.is_null() => Ok(vec![]),
114        Err(_) => Ok(vec![args]),
115    }
116}
117
118/// Represents the set of options accepted by the runtime constructor
119pub struct RuntimeOptions {
120    /// A set of `deno_core` extensions to add to the runtime
121    pub extensions: Vec<deno_core::Extension>,
122
123    /// Additional options for the built-in extensions
124    pub extension_options: ext::ExtensionOptions,
125
126    /// Function to use as entrypoint if the module does not provide one
127    pub default_entrypoint: Option<String>,
128
129    /// Amount of time to run for before killing the thread
130    pub timeout: Duration,
131
132    /// Optional maximum heap size for the runtime
133    ///
134    /// If the heap size is exceeded, the runtime will return a `HeapExhausted` error.
135    ///
136    /// **WARNING** this is not a minimum heap size; the underlying V8 isolate will still crash if this number is too small for startup
137    /// (~5mb with default features)
138    pub max_heap_size: Option<usize>,
139
140    /// Optional cache provider for the module loader
141    #[allow(deprecated)]
142    pub module_cache: Option<Box<dyn crate::module_loader::ModuleCacheProvider>>,
143
144    /// Optional import provider for the module loader
145    pub import_provider: Option<Box<dyn crate::module_loader::ImportProvider>>,
146
147    /// Optional snapshot to load into the runtime
148    ///
149    /// This will reduce load times, but requires the same extensions to be loaded as when the snapshot was created  
150    ///
151    /// WARNING: Snapshots MUST be used on the same system they were created on
152    pub startup_snapshot: Option<&'static [u8]>,
153
154    /// Optional configuration parameters for building the underlying v8 isolate
155    ///
156    /// This can be used to alter the behavior of the runtime.
157    ///
158    /// See the `rusty_v8` documentation for more information
159    pub isolate_params: Option<v8::CreateParams>,
160
161    /// Optional shared array buffer store to use for the runtime.
162    ///
163    /// Allows data-sharing between runtimes across threads
164    pub shared_array_buffer_store: Option<deno_core::SharedArrayBufferStore>,
165
166    /// A whitelist of custom schema prefixes that are allowed to be loaded from javascript
167    ///
168    /// By default only `http`/`https` (`url_import` crate feature), and `file` (`fs_import` crate feature) are allowed
169    pub schema_whlist: HashSet<String>,
170}
171
172impl Default for RuntimeOptions {
173    fn default() -> Self {
174        Self {
175            extensions: Vec::default(),
176            default_entrypoint: None,
177            timeout: Duration::MAX,
178            max_heap_size: None,
179            module_cache: None,
180            import_provider: None,
181            startup_snapshot: None,
182            isolate_params: None,
183            shared_array_buffer_store: None,
184            schema_whlist: HashSet::default(),
185
186            extension_options: ExtensionOptions::default(),
187        }
188    }
189}
190
191/// Deno `JsRuntime` wrapper providing helper functions needed
192/// by the public-facing Runtime API
193///
194/// This struct is not intended to be used directly by the end user
195/// It provides a set of async functions that can be used to interact with the
196/// underlying deno runtime instance
197pub struct InnerRuntime<RT: RuntimeTrait> {
198    pub module_loader: Rc<RustyLoader>,
199    pub deno_runtime: RT,
200
201    pub cwd: PathBuf,
202    pub default_entrypoint: Option<String>,
203}
204impl<RT: RuntimeTrait> InnerRuntime<RT> {
205    pub fn new(
206        options: RuntimeOptions,
207        heap_exhausted_token: CancellationToken,
208    ) -> Result<Self, Error> {
209        let cwd = std::env::current_dir()?;
210        let module_loader = Rc::new(RustyLoader::new(LoaderOptions {
211            cache_provider: options.module_cache,
212            import_provider: options.import_provider,
213            schema_whlist: options.schema_whlist,
214            cwd: cwd.clone(),
215
216            #[cfg(feature = "node_experimental")]
217            node_resolver: options.extension_options.node_resolver.clone(),
218
219            ..Default::default()
220        }));
221
222        // If a snapshot is provided, do not reload ESM for extensions
223        let is_snapshot = options.startup_snapshot.is_some();
224        let extensions = ext::all_extensions(
225            options.extensions,
226            options.extension_options,
227            options.shared_array_buffer_store.clone(),
228            is_snapshot,
229        );
230
231        // If a heap size is provided, set the isolate params (preserving any user-provided params otherwise)
232        let isolate_params = match options.isolate_params {
233            Some(params) => {
234                if let Some(max_heap_size) = options.max_heap_size {
235                    Some(params.heap_limits(0, max_heap_size))
236                } else {
237                    Some(params)
238                }
239            }
240            None => {
241                if let Some(max_heap_size) = options.max_heap_size {
242                    let params = v8::Isolate::create_params().heap_limits(0, max_heap_size);
243                    Some(params)
244                } else {
245                    None
246                }
247            }
248        };
249
250        let mut deno_runtime = RT::try_new(deno_core::RuntimeOptions {
251            module_loader: Some(module_loader.clone()),
252
253            extension_transpiler: Some(module_loader.as_extension_transpiler()),
254            create_params: isolate_params,
255            shared_array_buffer_store: options.shared_array_buffer_store.clone(),
256
257            startup_snapshot: options.startup_snapshot,
258            extensions,
259
260            ..Default::default()
261        })?;
262
263        let mut feature_checker = FeatureChecker::default();
264        feature_checker.set_exit_cb(Box::new(|_, _| {}));
265        deno_runtime
266            .rt_mut()
267            .op_state()
268            .borrow_mut()
269            .put(Arc::new(feature_checker));
270
271        // Add a callback to terminate the runtime if the max_heap_size limit is approached
272        if options.max_heap_size.is_some() {
273            let isolate_handle = deno_runtime.rt_mut().v8_isolate().thread_safe_handle();
274
275            deno_runtime
276                .rt_mut()
277                .add_near_heap_limit_callback(move |current_value, _| {
278                    isolate_handle.terminate_execution();
279
280                    // Signal the outer runtime to cancel block_on future (avoid hanging) and return friendly error
281                    heap_exhausted_token.cancel();
282
283                    // Spike the heap limit while terminating to avoid segfaulting
284                    // Callback may fire multiple times if memory usage increases quicker then termination finalizes
285                    5 * current_value
286                });
287        }
288
289        let default_entrypoint = options.default_entrypoint;
290        Ok(Self {
291            module_loader,
292            deno_runtime,
293            cwd,
294            default_entrypoint,
295        })
296    }
297
298    /// Destroy the `RustyScript` runtime, returning the deno RT instance
299    #[allow(dead_code)]
300    pub fn into_inner(self) -> RT {
301        self.deno_runtime
302    }
303
304    /// Access the underlying deno runtime instance directly
305    pub fn deno_runtime(&mut self) -> &mut JsRuntime {
306        self.deno_runtime.rt_mut()
307    }
308
309    /// Set the current working directory for the runtime
310    /// This is used to resolve relative paths in the module loader
311    pub fn set_current_dir(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
312        let path = path.as_ref();
313        let path = utilities::resolve_path(path, Some(&self.cwd))?
314            .to_file_path()
315            .map_err(|()| Error::Runtime("Invalid path".to_string()))?;
316
317        self.cwd = path;
318        self.module_loader.set_current_dir(self.cwd.clone());
319        Ok(&self.cwd)
320    }
321
322    pub fn current_dir(&self) -> &Path {
323        &self.cwd
324    }
325
326    /// Remove and return a value from the state
327    pub fn take<T>(&mut self) -> Option<T>
328    where
329        T: 'static,
330    {
331        let state = self.deno_runtime().op_state();
332        if let Ok(mut state) = state.try_borrow_mut() {
333            if state.has::<T>() {
334                return Some(state.take());
335            }
336        }
337
338        None
339    }
340
341    /// Add a value to the state
342    /// Only one value of each type is stored
343    pub fn put<T>(&mut self, value: T) -> Result<(), Error>
344    where
345        T: 'static,
346    {
347        let state = self.deno_runtime().op_state();
348        let mut state = state.try_borrow_mut()?;
349        state.put(value);
350
351        Ok(())
352    }
353
354    /// Register an async rust function
355    /// The function must return a Future that resolves to a `serde_json::Value`
356    /// and accept a vec of `serde_json::Value` as arguments
357    pub fn register_async_function<F>(&mut self, name: &str, callback: F) -> Result<(), Error>
358    where
359        F: RsAsyncFunction,
360    {
361        let state = self.deno_runtime().op_state();
362        let mut state = state.try_borrow_mut()?;
363
364        if !state.has::<HashMap<String, Box<dyn RsAsyncFunction>>>() {
365            state.put(HashMap::<String, Box<dyn RsAsyncFunction>>::new());
366        }
367
368        // Insert the callback into the state
369        state
370            .borrow_mut::<HashMap<String, Box<dyn RsAsyncFunction>>>()
371            .insert(name.to_string(), Box::new(callback));
372
373        Ok(())
374    }
375
376    /// Register a rust function
377    /// The function must return a `serde_json::Value`
378    /// and accept a slice of `serde_json::Value` as arguments
379    pub fn register_function<F>(&mut self, name: &str, callback: F) -> Result<(), Error>
380    where
381        F: RsFunction,
382    {
383        let state = self.deno_runtime().op_state();
384        let mut state = state.try_borrow_mut()?;
385
386        if !state.has::<HashMap<String, Box<dyn RsFunction>>>() {
387            state.put(HashMap::<String, Box<dyn RsFunction>>::new());
388        }
389
390        // Insert the callback into the state
391        state
392            .borrow_mut::<HashMap<String, Box<dyn RsFunction>>>()
393            .insert(name.to_string(), Box::new(callback));
394
395        Ok(())
396    }
397
398    /// Runs the JS event loop to completion
399    pub async fn await_event_loop(
400        &mut self,
401        options: PollEventLoopOptions,
402        timeout: Option<Duration>,
403    ) -> Result<(), Error> {
404        if let Some(timeout) = timeout {
405            Ok(tokio::select! {
406                r = self.deno_runtime().run_event_loop(options) => r,
407                () = tokio::time::sleep(timeout) => Ok(()),
408            }?)
409        } else {
410            Ok(self.deno_runtime().run_event_loop(options).await?)
411        }
412    }
413
414    /// Advances the JS event loop by one tick
415    /// Return true if the event loop is pending
416    pub async fn advance_event_loop(
417        &mut self,
418        options: PollEventLoopOptions,
419    ) -> Result<bool, Error> {
420        let result = std::future::poll_fn(|cx| {
421            Poll::Ready(match self.deno_runtime().poll_event_loop(cx, options) {
422                Poll::Ready(t) => t.map(|()| false),
423                Poll::Pending => Ok(true),
424            })
425        })
426        .await?;
427
428        Ok(result)
429    }
430
431    /// Evaluate a piece of non-ECMAScript-module JavaScript code
432    /// The expression is evaluated in the global context, so changes persist
433    ///
434    /// Async because some expressions may require a tokio runtime
435    ///
436    /// # Arguments
437    /// * `expr` - A string representing the JavaScript expression to evaluate
438    ///
439    /// # Returns
440    /// A `Result` containing the deserialized result of the expression (`T`)
441    /// or an error (`Error`) if the expression cannot be evaluated or if the
442    /// result cannot be deserialized.
443    #[allow(clippy::unused_async, reason = "Prevent panic on sleep calls")]
444    pub async fn eval(&mut self, expr: impl ToString) -> Result<v8::Global<v8::Value>, Error> {
445        let result = self.deno_runtime().execute_script("", expr.to_string())?;
446        Ok(result)
447    }
448
449    /// Attempt to get a value out of the global context (globalThis.name)
450    ///
451    /// # Arguments
452    /// * `name` - Name of the object to extract
453    ///
454    /// # Returns
455    /// A `Result` containing the non-null value extracted or an error (`Error`)
456    pub fn get_global_value(&mut self, name: &str) -> Result<v8::Global<v8::Value>, Error> {
457        let context = self.deno_runtime().main_context();
458        let mut scope = self.deno_runtime().handle_scope();
459        let global = context.open(&mut scope).global(&mut scope);
460
461        let key = name.to_v8_string(&mut scope)?;
462        let value = global.get(&mut scope, key.into());
463
464        match value.if_defined() {
465            Some(v) => Ok(v8::Global::<v8::Value>::new(&mut scope, v)),
466            _ => Err(Error::ValueNotFound(name.to_string())),
467        }
468    }
469
470    /// Attempt to get a value out of a module context
471    ///     ///
472    /// # Arguments
473    /// * `module` - A handle to a loaded module
474    /// * `name` - Name of the object to extract
475    ///
476    /// # Returns
477    /// A `Result` containing the non-null value extracted or an error (`Error`)
478    pub fn get_module_export_value(
479        &mut self,
480        module_context: &ModuleHandle,
481        name: &str,
482    ) -> Result<v8::Global<v8::Value>, Error> {
483        let module_namespace = self
484            .deno_runtime()
485            .get_module_namespace(module_context.id())?;
486        let mut scope = self.deno_runtime().handle_scope();
487        let module_namespace = module_namespace.open(&mut scope);
488        assert!(module_namespace.is_module_namespace_object());
489
490        let key = name.to_v8_string(&mut scope)?;
491        let value = module_namespace.get(&mut scope, key.into());
492
493        match value.if_defined() {
494            Some(v) => Ok(v8::Global::<v8::Value>::new(&mut scope, v)),
495            _ => Err(Error::ValueNotFound(name.to_string())),
496        }
497    }
498
499    pub async fn resolve_with_event_loop(
500        &mut self,
501        value: v8::Global<v8::Value>,
502    ) -> Result<v8::Global<v8::Value>, Error> {
503        let future = self.deno_runtime().resolve(value);
504        let result = self
505            .deno_runtime()
506            .with_event_loop_future(future, PollEventLoopOptions::default())
507            .await?;
508        Ok(result)
509    }
510
511    pub fn decode_value<T>(&mut self, value: v8::Global<v8::Value>) -> Result<T, Error>
512    where
513        T: DeserializeOwned,
514    {
515        let mut scope = self.deno_runtime().handle_scope();
516        let result = v8::Local::<v8::Value>::new(&mut scope, value);
517        Ok(from_v8(&mut scope, result)?)
518    }
519
520    pub fn get_value_ref(
521        &mut self,
522        module_context: Option<&ModuleHandle>,
523        name: &str,
524    ) -> Result<v8::Global<v8::Value>, Error> {
525        // Try to get the value from the module context first
526        let result = module_context
527            .and_then(|module_context| self.get_module_export_value(module_context, name).ok());
528
529        // If it's not found, try the global context
530        match result {
531            Some(result) => Ok(result),
532            None => self
533                .get_global_value(name)
534                .map_err(|_| Error::ValueNotFound(name.to_string())),
535        }
536    }
537
538    /// Retrieves a javascript function by its name from the Deno runtime's global context.
539    ///
540    /// # Arguments
541    /// * `module_context` - A module handle to use for context, to find exports
542    /// * `name` - A string representing the name of the javascript function to retrieve.
543    ///
544    /// # Returns
545    /// A `Result` containing a `v8::Global<v8::Function>` if
546    /// the function is found, or an error (`Error`) if the function cannot be found or
547    /// if it is not a valid javascript function.
548    pub fn get_function_by_name(
549        &mut self,
550        module_context: Option<&ModuleHandle>,
551        name: &str,
552    ) -> Result<v8::Global<v8::Function>, Error> {
553        // Get the value
554        let value = self.get_value_ref(module_context, name)?;
555
556        // Convert it into a function
557        let mut scope = self.deno_runtime().handle_scope();
558        let local_value = v8::Local::<v8::Value>::new(&mut scope, value);
559        let f: v8::Local<v8::Function> = local_value
560            .try_into()
561            .or::<Error>(Err(Error::ValueNotCallable(name.to_string())))?;
562
563        // Return it as a global
564        Ok(v8::Global::<v8::Function>::new(&mut scope, f))
565    }
566
567    pub fn call_function_by_ref(
568        &mut self,
569        module_context: Option<&ModuleHandle>,
570        function: &v8::Global<v8::Function>,
571        args: &impl serde::ser::Serialize,
572    ) -> Result<v8::Global<v8::Value>, Error> {
573        // Namespace, if provided
574        let module_namespace = if let Some(module_context) = module_context {
575            Some(
576                self.deno_runtime()
577                    .get_module_namespace(module_context.id())?,
578            )
579        } else {
580            None
581        };
582
583        let mut scope = self.deno_runtime().handle_scope();
584        let mut scope = v8::TryCatch::new(&mut scope);
585
586        // Get the namespace
587        // Module-level if supplied, none otherwise
588        let namespace: v8::Local<v8::Value> = if let Some(namespace) = module_namespace {
589            v8::Local::<v8::Object>::new(&mut scope, namespace).into()
590        } else {
591            // Create a new object to use as the namespace if none is provided
592            //let obj: v8::Local<v8::Value> = v8::Object::new(&mut scope).into();
593            let obj: v8::Local<v8::Value> = v8::undefined(&mut scope).into();
594            obj
595        };
596
597        let function_instance = function.open(&mut scope);
598
599        // Prep arguments
600        let args = decode_args(args, &mut scope)?;
601
602        // Call the function
603        let result = function_instance.call(&mut scope, namespace, &args);
604        match result {
605            Some(value) => {
606                let value = v8::Global::new(&mut scope, value);
607                Ok(value)
608            }
609            None if scope.has_caught() => {
610                let e = scope
611                    .message()
612                    .ok_or_else(|| Error::Runtime("Unknown error".to_string()))?;
613
614                let filename = e.get_script_resource_name(&mut scope);
615                let linenumber = e.get_line_number(&mut scope).unwrap_or_default();
616                let filename = if let Some(v) = filename {
617                    let filename = v.to_rust_string_lossy(&mut scope);
618                    format!("{filename}:{linenumber}: ")
619                } else if let Some(module_context) = module_context {
620                    let filename = module_context.module().filename().to_string_lossy();
621                    format!("{filename}:{linenumber}: ")
622                } else {
623                    String::new()
624                };
625
626                let msg = e.get(&mut scope).to_rust_string_lossy(&mut scope);
627
628                let s = format!("{filename}{msg}");
629                Err(Error::Runtime(s))
630            }
631            None => Err(Error::Runtime(
632                "Unknown error during function execution".to_string(),
633            )),
634        }
635    }
636
637    /// A utility function that run provided future concurrently with the event loop.
638    ///
639    /// If the event loop resolves while polling the future, it will continue to be polled,
640    /// Unless it returned an error
641    ///
642    /// Useful for interacting with local inspector session.
643    pub async fn with_event_loop_future<'fut, T, E>(
644        &mut self,
645        mut fut: impl std::future::Future<Output = Result<T, E>> + Unpin + 'fut,
646        poll_options: PollEventLoopOptions,
647    ) -> Result<T, Error>
648    where
649        deno_core::error::AnyError: From<E>,
650        Error: std::convert::From<E>,
651    {
652        // Manually implement tokio::select
653        std::future::poll_fn(|cx| {
654            let evt_status = self.deno_runtime().poll_event_loop(cx, poll_options);
655            let fut_status = fut.poll_unpin(cx);
656
657            match (evt_status, fut_status) {
658                (Poll::Ready(Err(e)), _) => {
659                    // Event loop failed
660                    Poll::Ready(Err(e.into()))
661                }
662
663                (_, Poll::Pending) => {
664                    // Continue polling
665                    Poll::Pending
666                }
667
668                (_, Poll::Ready(t)) => {
669                    for _ in 0..100 {
670                        if let Poll::Ready(Err(e)) =
671                            self.deno_runtime().poll_event_loop(cx, poll_options)
672                        {
673                            return Poll::Ready(Err(e.into()));
674                        }
675                    }
676
677                    // Future resolved
678                    Poll::Ready(t.map_err(Into::into))
679                }
680            }
681        })
682        .await
683    }
684
685    /// Get the entrypoint function for a module
686    pub fn get_module_entrypoint(
687        &mut self,
688        module_context: &mut ModuleHandle,
689    ) -> Result<Option<v8::Global<v8::Function>>, Error> {
690        let default = self.default_entrypoint.clone();
691
692        // Try to get an entrypoint from a call to `rustyscript.register_entrypoint` first
693        let state = self.deno_runtime().op_state();
694        let mut deep_state = state.try_borrow_mut()?;
695        let entrypoint = deep_state.try_take::<v8::Global<v8::Function>>();
696        if let Some(entrypoint) = entrypoint {
697            return Ok(Some(entrypoint));
698        }
699
700        // Try to get an entrypoint from the default export next
701        if let Ok(default_export) = self.get_module_export_value(module_context, "default") {
702            let mut scope = self.deno_runtime().handle_scope();
703            let default_export = v8::Local::new(&mut scope, default_export);
704            if default_export.is_function() {
705                if let Ok(f) = v8::Local::<v8::Function>::try_from(default_export) {
706                    return Ok(Some(v8::Global::new(&mut scope, f)));
707                }
708            }
709        }
710
711        // Try to get an entrypoint from the default entrypoint
712        if let Some(default) = default.as_deref() {
713            if let Ok(f) = self.get_function_by_name(Some(module_context), default) {
714                return Ok(Some(f));
715            }
716        }
717
718        Ok(None)
719    }
720
721    /// Load one or more modules
722    /// Returns a future that resolves to a handle to the main module, or the last
723    /// side-module
724    ///
725    /// Will return a handle to the main module, or the last
726    /// side-module
727    pub async fn load_modules(
728        &mut self,
729        main_module: Option<&Module>,
730        side_modules: Vec<&Module>,
731    ) -> Result<ModuleHandle, Error> {
732        if main_module.is_none() && side_modules.is_empty() {
733            return Err(Error::Runtime(
734                "Internal error: attempt to load no modules".to_string(),
735            ));
736        }
737
738        let mut module_handle_stub = ModuleHandle::default();
739
740        // Get additional modules first
741        for side_module in side_modules {
742            let module_specifier = side_module.filename().to_module_specifier(&self.cwd)?;
743            let (code, sourcemap) = transpile(&module_specifier, side_module.contents())?;
744
745            // Now CJS translation, for node
746            #[cfg(feature = "node_experimental")]
747            let code = self
748                .module_loader
749                .translate_cjs(&module_specifier, &code)
750                .await?;
751
752            let fast_code = deno_core::FastString::from(code.clone());
753
754            let s_modid = self
755                .deno_runtime()
756                .load_side_es_module_from_code(&module_specifier, fast_code)
757                .await?;
758
759            // Update source map cache
760            self.module_loader.insert_source_map(
761                module_specifier.as_str(),
762                code,
763                sourcemap.map(|s| s.to_vec()),
764            );
765
766            let mod_load = self.deno_runtime().mod_evaluate(s_modid);
767            self.with_event_loop_future(mod_load, PollEventLoopOptions::default())
768                .await?;
769            module_handle_stub = ModuleHandle::new(side_module, s_modid, None);
770        }
771
772        // Load main module
773        if let Some(module) = main_module {
774            let module_specifier = module.filename().to_module_specifier(&self.cwd)?;
775            let (code, sourcemap) = transpile(&module_specifier, module.contents())?;
776
777            // Now CJS translation, for node
778            #[cfg(feature = "node_experimental")]
779            let code = self
780                .module_loader
781                .translate_cjs(&module_specifier, &code)
782                .await?;
783
784            let fast_code = deno_core::FastString::from(code.clone());
785
786            let module_id = self
787                .deno_runtime()
788                .load_main_es_module_from_code(&module_specifier, fast_code)
789                .await?;
790
791            // Update source map cache
792            self.module_loader.insert_source_map(
793                module_specifier.as_str(),
794                code,
795                sourcemap.map(|s| s.to_vec()),
796            );
797
798            // Finish execution
799            let mod_load = self.deno_runtime().mod_evaluate(module_id);
800            self.with_event_loop_future(mod_load, PollEventLoopOptions::default())
801                .await?;
802            module_handle_stub = ModuleHandle::new(module, module_id, None);
803        }
804
805        // Try to get the default entrypoint
806        let entrypoint = self.get_module_entrypoint(&mut module_handle_stub)?;
807
808        Ok(ModuleHandle::new(
809            module_handle_stub.module(),
810            module_handle_stub.id(),
811            entrypoint,
812        ))
813    }
814}
815
816#[cfg(test)]
817mod test_inner_runtime {
818    use serde::Deserialize;
819
820    use crate::{async_callback, big_json_args, js_value::Function, json_args, sync_callback};
821
822    #[cfg(any(feature = "web", feature = "web_stub"))]
823    use crate::js_value::Promise;
824
825    use super::*;
826
827    /// Used for blocking functions
828    fn run_async_task<T, F, U>(f: F) -> T
829    where
830        U: std::future::Future<Output = Result<T, Error>>,
831        F: FnOnce() -> U,
832    {
833        let timeout = Duration::from_secs(2);
834        let tokio = tokio::runtime::Builder::new_current_thread()
835            .enable_all()
836            .thread_keep_alive(timeout)
837            .build()
838            .unwrap();
839        tokio
840            .block_on(async move {
841                tokio::time::timeout(timeout, f())
842                    .await
843                    .expect("Test failed")
844            })
845            .expect("Timed out")
846    }
847
848    macro_rules! assert_v8 {
849        ($l:expr, $r:expr, $t:ty, $rt:expr) => {
850            assert_eq!($rt.decode_value::<$t>($l).expect("Wrong type"), $r,)
851        };
852    }
853
854    #[test]
855    fn test_decode_args() {
856        let mut runtime =
857            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
858                .expect("Could not load runtime");
859        let mut scope = runtime.deno_runtime.handle_scope();
860
861        // empty
862        let args = decode_args(&json_args!(), &mut scope).expect("Could not decode args");
863        assert_eq!(args.len(), 0);
864
865        // single
866        let args = decode_args(&json_args!(2), &mut scope).expect("Could not decode args");
867        assert_eq!(args.len(), 1);
868
869        // single raw
870        let args = decode_args(&2, &mut scope).expect("Could not decode args");
871        assert_eq!(args.len(), 1);
872
873        // multiple heterogeneous
874        let args = decode_args(&json_args!(2, "test"), &mut scope).expect("Could not decode args");
875        assert_eq!(args.len(), 2);
876
877        // multiple homogeneous
878        let args = decode_args(&json_args!(2, 3), &mut scope).expect("Could not decode args");
879        assert_eq!(args.len(), 2);
880
881        // 16 args
882        let args = decode_args(
883            &(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15),
884            &mut scope,
885        )
886        .expect("Could not decode args");
887        assert_eq!(args.len(), 16);
888
889        // 32 args
890        let args = decode_args(
891            &big_json_args!(
892                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
893                10, 11, 12, 13, 14, 15
894            ),
895            &mut scope,
896        )
897        .expect("Could not decode args");
898        assert_eq!(args.len(), 32);
899    }
900
901    #[test]
902    fn test_put_take() {
903        let mut runtime =
904            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
905                .expect("Could not load runtime");
906
907        runtime.put(2usize).expect("Could not put value");
908        let v = runtime.take::<usize>().expect("Could not take value");
909        assert_eq!(v, 2);
910    }
911
912    #[test]
913    fn test_register_async_function() {
914        let mut runtime =
915            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
916                .expect("Could not load runtime");
917        runtime
918            .register_async_function(
919                "test",
920                async_callback!(|a: i64, b: i64| async move { Ok::<i64, Error>(a + b) }),
921            )
922            .expect("Could not register function");
923
924        let module = Module::new(
925            "test.js",
926            "
927            globalThis.v = await rustyscript.async_functions.test(2, 3);
928            ",
929        );
930
931        let rt = &mut runtime;
932        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
933
934        let result = runtime
935            .get_value_ref(Some(&module), "v")
936            .expect("Could not find global");
937        assert_v8!(result, 5, usize, runtime);
938    }
939
940    #[test]
941    fn test_register_function() {
942        let mut runtime =
943            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
944                .expect("Could not load runtime");
945        runtime
946            .register_function(
947                "test",
948                sync_callback!(|a: i64, b: i64| { Ok::<i64, Error>(a + b) }),
949            )
950            .expect("Could not register function");
951
952        run_async_task(|| async move {
953            let v = runtime
954                .eval("rustyscript.functions.test(2, 3)")
955                .await
956                .expect("failed to eval");
957            assert_v8!(v, 5, usize, runtime);
958            Ok(())
959        });
960    }
961
962    #[cfg(any(feature = "web", feature = "web_stub"))]
963    #[test]
964    fn test_eval() {
965        let mut runtime =
966            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
967                .expect("Could not load runtime");
968
969        run_async_task(|| async move {
970            let v = runtime.eval("2 + 2").await.expect("failed to eval");
971            assert_v8!(v, 4, usize, runtime);
972            let result = runtime
973                .eval(
974                    "
975                let sleep = (ms) => new Promise((r) => setTimeout(r, ms));
976                sleep(500).then(() => 2);
977            ",
978                )
979                .await
980                .expect("failed to eval");
981
982            let result: Promise<u32> = runtime
983                .decode_value(result)
984                .expect("Could not decode promise");
985
986            let result: u32 = result.resolve(runtime.deno_runtime()).await?;
987            assert_eq!(result, 2);
988            Ok(())
989        });
990    }
991
992    #[cfg(feature = "web_stub")]
993    #[test]
994    fn test_base64() {
995        let mut runtime =
996            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
997                .expect("Could not load runtime");
998
999        run_async_task(|| async move {
1000            let result = runtime.eval("btoa('foo')").await.expect("failed to eval");
1001            assert_v8!(result, "Zm9v", String, runtime);
1002
1003            let result = runtime
1004                .eval("atob(btoa('foo'))")
1005                .await
1006                .expect("failed to eval");
1007            assert_v8!(result, "foo", String, runtime);
1008
1009            Ok(())
1010        });
1011    }
1012
1013    #[test]
1014    fn test_get_value_ref() {
1015        let module = Module::new(
1016            "test.js",
1017            "
1018            globalThis.a = 2;
1019            export const b = 'test';
1020            export const fnc = null;
1021        ",
1022        );
1023
1024        let mut runtime =
1025            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1026                .expect("Could not load runtime");
1027
1028        let rt = &mut runtime;
1029        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
1030
1031        let v = runtime
1032            .get_value_ref(None, "a")
1033            .expect("Could not find global");
1034        assert_v8!(v, 2, usize, runtime);
1035
1036        let v = runtime
1037            .get_value_ref(Some(&module), "a")
1038            .expect("Could not find global");
1039        assert_v8!(v, 2, usize, runtime);
1040
1041        let v = runtime
1042            .get_value_ref(Some(&module), "b")
1043            .expect("Could not find export");
1044        assert_v8!(v, "test", String, runtime);
1045
1046        runtime
1047            .get_value_ref(Some(&module), "c")
1048            .expect_err("Could not detect null");
1049
1050        runtime
1051            .get_value_ref(Some(&module), "d")
1052            .expect_err("Could not detect undeclared");
1053    }
1054
1055    #[test]
1056    fn test_get_function_by_name() {
1057        let module = Module::new(
1058            "test.js",
1059            "
1060            globalThis.fna = () => {};
1061            export function fnb() {}
1062            export const fnc = 2;
1063        ",
1064        );
1065
1066        let mut runtime =
1067            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1068                .expect("Could not load runtime");
1069
1070        let rt = &mut runtime;
1071        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
1072
1073        runtime
1074            .get_function_by_name(Some(&module), "fna")
1075            .expect("Did not find global");
1076        runtime
1077            .get_function_by_name(Some(&module), "fnb")
1078            .expect("Did not find export");
1079        runtime
1080            .get_function_by_name(Some(&module), "fnc")
1081            .expect_err("Did not detect non-function");
1082        runtime
1083            .get_function_by_name(Some(&module), "fnd")
1084            .expect_err("Did not detect undefined");
1085    }
1086
1087    #[test]
1088    fn test_call_function_by_ref() {
1089        let module = Module::new(
1090            "test.js",
1091            "
1092            globalThis.fna = (i) => i;
1093            export function fnb() {
1094                return 'test';
1095            }
1096            export const fnc = 2;
1097            export const fne = () => {};
1098
1099            export const will_err = () => {
1100                throw new Error('msg');
1101            }
1102        ",
1103        );
1104
1105        run_async_task(|| async move {
1106            let mut runtime =
1107                InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1108                    .expect("Could not load runtime");
1109            let handle = runtime.load_modules(Some(&module), vec![]).await?;
1110
1111            let f = runtime.get_function_by_name(None, "fna").unwrap();
1112            let result = runtime
1113                .call_function_by_ref(Some(&handle), &f, json_args!(2))
1114                .expect("Could not call global");
1115            assert_v8!(result, 2, usize, runtime);
1116
1117            let f = runtime.get_function_by_name(Some(&handle), "fnb").unwrap();
1118            let result = runtime
1119                .call_function_by_ref(Some(&handle), &f, json_args!())
1120                .expect("Could not call export");
1121            assert_v8!(result, "test", String, runtime);
1122
1123            let f = runtime.get_function_by_name(Some(&handle), "fne").unwrap();
1124            runtime
1125                .call_function_by_ref(Some(&handle), &f, json_args!())
1126                .expect("Did not allow undefined return");
1127
1128            let f = runtime
1129                .get_function_by_name(Some(&handle), "will_err")
1130                .unwrap();
1131            runtime
1132                .call_function_by_ref(Some(&handle), &f, json_args!())
1133                .expect_err("Did not catch error");
1134
1135            Ok(())
1136        });
1137    }
1138
1139    #[test]
1140    fn test_ts_loader() {
1141        let module = Module::new(
1142            "test.ts",
1143            "
1144            export function test(left:number, right:number): number {
1145                return left + right;
1146            }
1147        ",
1148        );
1149
1150        let mut runtime =
1151            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1152                .expect("Could not load runtime");
1153
1154        let rt = &mut runtime;
1155        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
1156
1157        let f = runtime.get_function_by_name(Some(&module), "test").unwrap();
1158        let rt = &mut runtime;
1159        let result = run_async_task(|| async move {
1160            rt.call_function_by_ref(Some(&module), &f, json_args!(2, 3))
1161        });
1162        assert_v8!(result, 5, usize, runtime);
1163    }
1164
1165    #[cfg(any(feature = "web", feature = "web_stub"))]
1166    #[test]
1167    fn test_toplevel_await() {
1168        let module = Module::new(
1169            "test.js",
1170            "
1171            const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1172            await sleep(100);
1173            export function test() {
1174                return 2;
1175            }
1176        ",
1177        );
1178
1179        let mut runtime =
1180            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1181                .expect("Could not load runtime");
1182
1183        let rt = &mut runtime;
1184        let module = run_async_task(|| async move {
1185            let h = rt.load_modules(Some(&module), vec![]).await;
1186            rt.await_event_loop(PollEventLoopOptions::default(), None)
1187                .await?;
1188            h
1189        });
1190
1191        let f = runtime.get_function_by_name(Some(&module), "test").unwrap();
1192        let rt = &mut runtime;
1193        let result =
1194            run_async_task(
1195                || async move { rt.call_function_by_ref(Some(&module), &f, json_args!()) },
1196            );
1197        assert_v8!(result, 2, usize, runtime);
1198    }
1199
1200    #[cfg(any(feature = "web", feature = "web_stub"))]
1201    #[test]
1202    fn test_promise() {
1203        let module = Module::new(
1204            "test.js",
1205            "
1206            export const test = () => {
1207                return new Promise((resolve) => {
1208                    setTimeout(() => {
1209                        resolve(2);
1210                    }, 50);
1211                });
1212            }
1213        ",
1214        );
1215
1216        let mut runtime =
1217            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1218                .expect("Could not load runtime");
1219
1220        let rt = &mut runtime;
1221        run_async_task(|| async move {
1222            let module = rt.load_modules(Some(&module), vec![]).await?;
1223
1224            let f = rt.get_function_by_name(Some(&module), "test").unwrap();
1225            let result = rt.call_function_by_ref(Some(&module), &f, json_args!())?;
1226
1227            let result = rt.resolve_with_event_loop(result).await?;
1228            assert_v8!(result, 2, usize, rt);
1229
1230            Ok(())
1231        });
1232    }
1233
1234    #[cfg(any(feature = "web", feature = "web_stub"))]
1235    #[test]
1236    fn test_async_fn() {
1237        let module = Module::new(
1238            "test.js",
1239            "
1240            const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
1241            export async function test() {
1242                await sleep(100);
1243                return 2;
1244            }
1245        ",
1246        );
1247
1248        let mut runtime =
1249            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1250                .expect("Could not load runtime");
1251
1252        let rt = &mut runtime;
1253        run_async_task(|| async move {
1254            let module = rt.load_modules(Some(&module), vec![]).await?;
1255
1256            let f = rt.get_function_by_name(Some(&module), "test")?;
1257            let result = rt.call_function_by_ref(Some(&module), &f, json_args!())?;
1258            let result: Promise<usize> = rt.decode_value(result).expect("Could not deserialize");
1259            let result: usize = result.resolve(rt.deno_runtime()).await?;
1260            assert_eq!(2, result);
1261
1262            Ok(())
1263        });
1264    }
1265
1266    #[test]
1267    fn test_deep_error() {
1268        let module = Module::new(
1269            "test.js",
1270            "
1271            await new Promise(r => setTimeout(r)); throw 'huh';
1272        ",
1273        );
1274
1275        let mut runtime =
1276            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1277                .expect("Could not load runtime");
1278
1279        let rt = &mut runtime;
1280        run_async_task(|| async move {
1281            let result = rt.load_modules(Some(&module), vec![]).await;
1282            assert!(result.is_err());
1283            Ok(())
1284        });
1285    }
1286
1287    #[test]
1288    fn test_serialize_deep_fn() {
1289        let module = Module::new(
1290            "test.js",
1291            "
1292            let a = 2;
1293            export const test = {
1294                'name': 'test',
1295                'func': (x) => x + a
1296            }
1297        ",
1298        );
1299
1300        let mut runtime =
1301            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1302                .expect("Could not load runtime");
1303
1304        let rt = &mut runtime;
1305        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
1306
1307        #[derive(Deserialize)]
1308        #[allow(clippy::items_after_statements)]
1309        struct TestStruct {
1310            #[allow(dead_code)]
1311            name: String,
1312            func: Function,
1313        }
1314
1315        let structure = runtime.get_value_ref(Some(&module), "test").unwrap();
1316        let structure: TestStruct = runtime
1317            .decode_value(structure)
1318            .expect("Could not deserialize");
1319
1320        let function = structure
1321            .func
1322            .as_global(&mut runtime.deno_runtime().handle_scope());
1323
1324        run_async_task(|| async move {
1325            let value = runtime
1326                .call_function_by_ref(Some(&module), &function, json_args!(2))
1327                .expect("could not call function");
1328            assert_v8!(value, 4, usize, runtime);
1329
1330            let value = runtime
1331                .call_function_by_ref(Some(&module), &function, json_args!(3))
1332                .expect("could not call function twice");
1333            assert_v8!(value, 5, usize, runtime);
1334
1335            Ok(())
1336        });
1337    }
1338
1339    #[test]
1340    fn test_async_load_errors() {
1341        let module = Module::new(
1342            "test.js",
1343            "
1344            throw new Error('msg');
1345        ",
1346        );
1347
1348        let mut runtime =
1349            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1350                .expect("Could not load runtime");
1351
1352        let rt = &mut runtime;
1353        let module_ = module.clone();
1354        let result =
1355            run_async_task(
1356                || async move { Ok(rt.load_modules(Some(&module_), vec![]).await.is_err()) },
1357            );
1358        assert!(result);
1359
1360        let mut runtime =
1361            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1362                .expect("Could not load runtime");
1363
1364        let rt = &mut runtime;
1365        let result =
1366            run_async_task(
1367                || async move { Ok(rt.load_modules(None, vec![&module]).await.is_err()) },
1368            );
1369        assert!(result);
1370    }
1371
1372    #[test]
1373    fn test_serialize_fn() {
1374        let module = Module::new(
1375            "test.js",
1376            "
1377            export const test = (x) => 2*x;
1378        ",
1379        );
1380
1381        let mut runtime =
1382            InnerRuntime::<JsRuntime>::new(RuntimeOptions::default(), CancellationToken::new())
1383                .expect("Could not load runtime");
1384
1385        let rt = &mut runtime;
1386        let module = run_async_task(|| async move { rt.load_modules(Some(&module), vec![]).await });
1387
1388        let function = runtime
1389            .get_function_by_name(Some(&module), "test")
1390            .expect("Could not get function");
1391
1392        run_async_task(|| async move {
1393            let value = runtime
1394                .call_function_by_ref(Some(&module), &function, json_args!(2))
1395                .expect("could not call function");
1396            assert_v8!(value, 4, usize, runtime);
1397
1398            let value = runtime
1399                .call_function_by_ref(None, &function, json_args!(2))
1400                .expect("could not call function");
1401            assert_v8!(value, 4, usize, runtime);
1402
1403            Ok(())
1404        });
1405    }
1406}