rustyscript/snapshot_builder.rs
1use std::{path::Path, rc::Rc, time::Duration};
2
3use deno_core::{JsRuntimeForSnapshot, PollEventLoopOptions};
4use tokio_util::sync::CancellationToken;
5
6use crate::{
7 async_bridge::{AsyncBridge, AsyncBridgeExt, TokioRuntime},
8 inner_runtime::{InnerRuntime, RuntimeOptions},
9 Error, Module, ModuleHandle,
10};
11
12/// A more restricted version of the `Runtime` struct that is used to create a snapshot of the runtime state
13/// This runtime should ONLY be used to create a snapshot, and not for normal use
14///
15/// Snapshots can be used to massively decrease the startup time of a Runtime instance (15ms -> 3ms) by pre-loading
16/// extensions and modules into the runtime state before it is created. A snapshot can be used on any runtime with
17/// the same set of extensions and options as the runtime that created it.
18///
19/// This struct is only available when the `snapshot_builder` feature is enabled
20/// Once you've set up the runtime, you can call `into_snapshot` to get the snapshot
21///
22/// You should save it to a file and load it with `include_bytes!` in order to use it
23/// in the `RuntimeOptions` struct's `startup_snapshot` field
24///
25/// # Example
26///
27/// ```rust
28/// use rustyscript::{SnapshotBuilder, Module, Error};
29/// use std::fs;
30///
31/// # fn main() -> Result<(), Error> {
32/// let module = Module::new("example.js", "export function example() { return 42; }");
33/// let snapshot = SnapshotBuilder::new(Default::default())?
34/// .with_module(&module)?
35/// .finish();
36///
37/// // Save the snapshot to a file
38/// fs::write("snapshot.bin", snapshot)?;
39///
40/// // To use the snapshot, load it with `include_bytes!` into the `RuntimeOptions` struct:
41/// // const STARTUP_SNAPSHOT: &[u8] = include_bytes!("snapshot.bin");
42/// // RuntimeOptions {
43/// // startup_snapshot: Some(STARTUP_SNAPSHOT),
44/// // ..Default::default()
45/// // };
46///
47/// # Ok(())
48/// # }
49/// ```
50pub struct SnapshotBuilder {
51 inner: InnerRuntime<deno_core::JsRuntimeForSnapshot>,
52 tokio: AsyncBridge,
53}
54impl SnapshotBuilder {
55 /// Creates a new instance of the runtime with the provided options.
56 ///
57 /// # Arguments
58 /// * `options` - A `RuntimeOptions` struct that specifies the configuration options for the runtime.
59 ///
60 /// # Returns
61 /// A `Result` containing either the initialized runtime instance on success (`Ok`) or an error on failure (`Err`).
62 ///
63 /// # Example
64 /// ```rust
65 /// use rustyscript::{ json_args, Runtime, RuntimeOptions, Module };
66 /// use std::time::Duration;
67 ///
68 /// # fn main() -> Result<(), rustyscript::Error> {
69 /// // Creates a runtime that will attempt to run function load() on start
70 /// // And which will time-out after 50ms
71 /// let mut runtime = Runtime::new(RuntimeOptions {
72 /// default_entrypoint: Some("load".to_string()),
73 /// timeout: Duration::from_millis(50),
74 /// ..Default::default()
75 /// })?;
76 ///
77 /// let module = Module::new("test.js", "
78 /// export const load = () => {
79 /// return 'Hello World!';
80 /// }
81 /// ");
82 ///
83 /// let module_handle = runtime.load_module(&module)?;
84 /// let value: String = runtime.call_entrypoint(&module_handle, json_args!())?;
85 /// assert_eq!("Hello World!", value);
86 /// # Ok(())
87 /// # }
88 /// ```
89 ///
90 /// # Errors
91 /// Can fail if the tokio runtime cannot be created,
92 /// Or if the deno runtime initialization fails (usually issues with extensions)
93 ///
94 pub fn new(options: RuntimeOptions) -> Result<Self, Error> {
95 let tokio = AsyncBridge::new(options.timeout)?;
96 let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
97 Ok(Self { inner, tokio })
98 }
99
100 /// Creates a new instance of the runtime with the provided options and a pre-configured tokio runtime.
101 /// See [`crate::Runtime::new`] for more information.
102 ///
103 /// # Errors
104 /// Can fail if the deno runtime initialization fails (usually issues with extensions)
105 pub fn with_tokio_runtime(
106 options: RuntimeOptions,
107 tokio: Rc<tokio::runtime::Runtime>,
108 ) -> Result<Self, Error> {
109 let tokio = AsyncBridge::with_tokio_runtime(options.timeout, tokio);
110 let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
111 Ok(Self { inner, tokio })
112 }
113
114 /// Creates a new instance of the runtime with the provided options and a borrowed tokio runtime handle.
115 /// See [`Runtime::new`] for more information.
116 ///
117 /// # Errors
118 /// Can fail if the deno runtime initialization fails (usually issues with extensions)
119 pub fn with_tokio_runtime_handle(
120 options: RuntimeOptions,
121 handle: tokio::runtime::Handle,
122 ) -> Result<Self, Error> {
123 let tokio = AsyncBridge::with_runtime_handle(options.timeout, handle);
124 let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
125 Ok(Self { inner, tokio })
126 }
127
128 /// Access the underlying deno runtime instance directly
129 pub fn deno_runtime(&mut self) -> &mut deno_core::JsRuntime {
130 self.inner.deno_runtime()
131 }
132
133 /// Access the underlying tokio runtime used for blocking operations
134 #[must_use]
135 pub fn tokio_runtime(&self) -> TokioRuntime {
136 self.tokio.tokio_runtime()
137 }
138
139 /// Returns the timeout for the runtime
140 #[must_use]
141 pub fn timeout(&self) -> std::time::Duration {
142 self.tokio.timeout()
143 }
144
145 /// Returns the heap exhausted token for the runtime
146 /// Used to detect when the runtime has run out of memory
147 #[must_use]
148 pub fn heap_exhausted_token(&self) -> CancellationToken {
149 self.tokio.heap_exhausted_token()
150 }
151
152 /// Destroy the v8 runtime, releasing all resources
153 /// Then the internal tokio runtime will be returned
154 #[must_use]
155 pub fn into_tokio_runtime(self) -> TokioRuntime {
156 self.tokio.into_tokio_runtime()
157 }
158
159 /// Set the current working directory for the runtime
160 /// This is used to resolve relative paths in the module loader
161 ///
162 /// The runtime will begin with the current working directory of the process
163 ///
164 /// # Errors
165 /// Can fail if the given path is not valid
166 pub fn set_current_dir(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
167 self.inner.set_current_dir(path)
168 }
169
170 /// Get the current working directory for the runtime
171 /// This is used to resolve relative paths in the module loader
172 ///
173 /// The runtime will begin with the current working directory of the process
174 #[must_use]
175 pub fn current_dir(&self) -> &Path {
176 self.inner.current_dir()
177 }
178
179 /// Advance the JS event loop by a single tick
180 /// See [`crate::Runtime::await_event_loop`] for fully running the event loop
181 ///
182 /// Returns true if the event loop has pending work, or false if it 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 fn advance_event_loop(&mut self, options: PollEventLoopOptions) -> Result<bool, Error> {
190 self.block_on(|runtime| async move { runtime.inner.advance_event_loop(options).await })
191 }
192
193 /// Run the JS event loop to completion, or until a timeout is reached
194 /// Required when using the `_immediate` variants of functions
195 ///
196 /// # Arguments
197 /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
198 /// * `timeout` - Optional timeout for the event loop
199 ///
200 /// # Errors
201 /// Can fail if a runtime error occurs during the event loop's execution
202 pub async fn await_event_loop(
203 &mut self,
204 options: PollEventLoopOptions,
205 timeout: Option<Duration>,
206 ) -> Result<(), Error> {
207 self.inner.await_event_loop(options, timeout).await
208 }
209
210 /// Run the JS event loop to completion, or until a timeout is reached
211 /// Required when using the `_immediate` variants of functions
212 ///
213 /// This is the blocking variant of [`crate::Runtime::await_event_loop`]
214 ///
215 /// # Arguments
216 /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
217 /// * `timeout` - Optional timeout for the event loop
218 ///
219 /// # Errors
220 /// Can fail if a runtime error occurs during the event loop's execution
221 pub fn block_on_event_loop(
222 &mut self,
223 options: deno_core::PollEventLoopOptions,
224 timeout: Option<Duration>,
225 ) -> Result<(), Error> {
226 self.block_on(|runtime| async move { runtime.await_event_loop(options, timeout).await })
227 }
228
229 /// Remove and return a value from the state, if one exists
230 /// ```rust
231 /// use rustyscript::{ Runtime };
232 ///
233 /// # fn main() -> Result<(), rustyscript::Error> {
234 /// let mut runtime = Runtime::new(Default::default())?;
235 /// runtime.put("test".to_string())?;
236 /// let value: String = runtime.take().unwrap();
237 /// assert_eq!(value, "test");
238 /// # Ok(())
239 /// # }
240 /// ```
241 pub fn take<T>(&mut self) -> Option<T>
242 where
243 T: 'static,
244 {
245 self.inner.take()
246 }
247
248 /// Add a value to the state
249 /// Only one value of each type is stored - additional calls to put overwrite the
250 /// old value
251 ///
252 /// # Errors
253 /// Can fail if the inner state cannot be borrowed mutably
254 ///
255 /// ```rust
256 /// use rustyscript::{ Runtime };
257 ///
258 /// # fn main() -> Result<(), rustyscript::Error> {
259 /// let mut runtime = Runtime::new(Default::default())?;
260 /// runtime.put("test".to_string())?;
261 /// let value: String = runtime.take().unwrap();
262 /// assert_eq!(value, "test");
263 /// # Ok(())
264 /// # }
265 /// ```
266 pub fn put<T>(&mut self, value: T) -> Result<(), Error>
267 where
268 T: 'static,
269 {
270 self.inner.put(value)
271 }
272
273 /// Evaluate a piece of non-ECMAScript-module JavaScript code
274 /// The expression is evaluated in the global context, so changes persist
275 ///
276 /// Blocks on promise resolution, and runs the event loop to completion
277 ///
278 /// Asynchronous code is supported, partially
279 /// - Top-level await is not supported
280 /// - The event loop will be run to completion after the expression is evaluated
281 ///
282 /// For top-level await support, use one of:
283 /// - `call_function_async`
284 /// - `call_stored_function_async`
285 /// - `load_module_async`
286 /// - `load_modules_async`
287 ///
288 /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
289 ///
290 /// # Arguments
291 /// * `expr` - A string representing the JavaScript expression to evaluate
292 ///
293 /// # Returns
294 /// A `Result` containing the deserialized result of the expression (`T`)
295 /// or an error (`Error`) if the expression cannot be evaluated or if the
296 /// result cannot be deserialized.
297 ///
298 /// # Errors
299 /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
300 ///
301 /// See [`crate::Runtime::eval`] for an example
302 pub fn eval<T>(&mut self, expr: impl ToString) -> Result<T, Error>
303 where
304 T: serde::de::DeserializeOwned,
305 {
306 self.block_on(|runtime| async move { runtime.eval_async(expr).await })
307 }
308
309 /// Evaluate a piece of non-ECMAScript-module JavaScript code
310 /// The expression is evaluated in the global context, so changes persist
311 ///
312 /// Awaits promise resolution, and runs the event loop to completion
313 ///
314 /// Asynchronous code is supported, partially
315 /// - Top-level await is not supported
316 /// - The event loop will be run to completion after the expression is evaluated
317 ///
318 /// For top-level await support, use one of:
319 /// - `call_function_async`
320 /// - `call_stored_function_async`
321 /// - `load_module_async`
322 /// - `load_modules_async`
323 ///
324 /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
325 ///
326 /// # Arguments
327 /// * `expr` - A string representing the JavaScript expression to evaluate
328 ///
329 /// # Returns
330 /// A `Result` containing the deserialized result of the expression (`T`)
331 /// or an error (`Error`) if the expression cannot be evaluated or if the
332 /// result cannot be deserialized.
333 ///
334 /// # Errors
335 /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
336 ///
337 /// # Example
338 /// For an example, see [`crate::Runtime::eval`]
339 pub async fn eval_async<T>(&mut self, expr: impl ToString) -> Result<T, Error>
340 where
341 T: serde::de::DeserializeOwned,
342 {
343 let result = self.inner.eval(expr.to_string()).await?;
344 let result = self.inner.resolve_with_event_loop(result).await?;
345 self.inner.decode_value(result)
346 }
347
348 /// Evaluate a piece of non-ECMAScript-module JavaScript code
349 /// The expression is evaluated in the global context, so changes persist
350 ///
351 /// Does not await promise resolution, or run the event loop
352 /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]
353 /// The event loop should be run using [`crate::Runtime::await_event_loop`]
354 ///
355 /// Note that this function needs to be async because calls to `setTimeout` must be evaluated from within an async runtime.
356 ///
357 /// Asynchronous code is supported, partially
358 /// - Top-level await is not supported
359 ///
360 /// For top-level await support, use one of:
361 /// - `call_function_async`
362 /// - `call_stored_function_async`
363 /// - `load_module_async`
364 /// - `load_modules_async`
365 ///
366 /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
367 ///
368 /// # Arguments
369 /// * `expr` - A string representing the JavaScript expression to evaluate
370 ///
371 /// # Returns
372 /// A `Result` containing the deserialized result of the expression (`T`)
373 /// or an error (`Error`) if the expression cannot be evaluated or if the
374 /// result cannot be deserialized.
375 ///
376 /// # Errors
377 /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
378 ///
379 /// # Example
380 /// For an example, see [`crate::Runtime::eval`]
381 pub async fn eval_immediate<T>(&mut self, expr: impl ToString) -> Result<T, Error>
382 where
383 T: serde::de::DeserializeOwned,
384 {
385 let result = self.inner.eval(expr.to_string()).await?;
386 self.inner.decode_value(result)
387 }
388
389 /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
390 /// Returns a future that resolves when:
391 /// - The event loop is resolved, and
392 /// - If the value is a promise, the promise is resolved
393 ///
394 /// Note that synchronous functions are run synchronously. Returned promises will be run asynchronously, however.
395 ///
396 /// See [`crate::Runtime::call_function`] for an example
397 ///
398 /// # Arguments
399 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
400 /// * `name` - A string representing the name of the javascript function to call.
401 /// * `args` - The arguments to pass to the function
402 ///
403 /// # Returns
404 /// A `Result` containing the deserialized result of the function call (`T`)
405 /// or an error (`Error`) if the function cannot be found, if there are issues with
406 /// calling the function, or if the result cannot be deserialized.
407 ///
408 /// # Errors
409 /// Fails if the function cannot be found, if there are issues with calling the function,
410 /// Or if the result cannot be deserialized into the requested type
411 pub async fn call_function_async<T>(
412 &mut self,
413 module_context: Option<&ModuleHandle>,
414 name: &str,
415 args: &impl serde::ser::Serialize,
416 ) -> Result<T, Error>
417 where
418 T: deno_core::serde::de::DeserializeOwned,
419 {
420 let function = self.inner.get_function_by_name(module_context, name)?;
421 let result = self
422 .inner
423 .call_function_by_ref(module_context, &function, args)?;
424 let result = self.inner.resolve_with_event_loop(result).await?;
425 self.inner.decode_value(result)
426 }
427
428 /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
429 /// Blocks until:
430 /// - The event loop is resolved, and
431 /// - If the value is a promise, the promise is resolved
432 ///
433 /// # Arguments
434 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
435 /// * `name` - A string representing the name of the javascript function to call.
436 /// * `args` - The arguments to pass to the function
437 ///
438 /// # Returns
439 /// A `Result` containing the deserialized result of the function call (`T`)
440 /// or an error (`Error`) if the function cannot be found, if there are issues with
441 /// calling the function, or if the result cannot be deserialized.
442 ///
443 /// # Errors
444 /// Fails if the function cannot be found, if there are issues with calling the function,
445 /// Or if the result cannot be deserialized into the requested type
446 ///
447 /// # Example
448 ///
449 /// ```rust
450 /// use rustyscript::{ json_args, Runtime, Module, Error };
451 ///
452 /// # fn main() -> Result<(), Error> {
453 /// let mut runtime = Runtime::new(Default::default())?;
454 /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
455 /// let module = runtime.load_module(&module)?;
456 /// let value: usize = runtime.call_function(Some(&module), "f", json_args!())?;
457 /// # Ok(())
458 /// # }
459 /// ```
460 pub fn call_function<T>(
461 &mut self,
462 module_context: Option<&ModuleHandle>,
463 name: &str,
464 args: &impl serde::ser::Serialize,
465 ) -> Result<T, Error>
466 where
467 T: deno_core::serde::de::DeserializeOwned,
468 {
469 self.block_on(|runtime| async move {
470 runtime
471 .call_function_async(module_context, name, args)
472 .await
473 })
474 }
475
476 /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
477 /// Will not attempt to resolve promises, or run the event loop
478 /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]
479 /// The event loop should be run using [`crate::Runtime::await_event_loop`]
480 ///
481 /// # Arguments
482 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
483 /// * `name` - A string representing the name of the javascript function to call.
484 /// * `args` - The arguments to pass to the function
485 ///
486 /// # Returns
487 /// A `Result` containing the deserialized result of the function call (`T`)
488 /// or an error (`Error`) if the function cannot be found, if there are issues with
489 /// calling the function, or if the result cannot be deserialized.
490 ///
491 /// # Errors
492 /// Fails if the function cannot be found, if there are issues with calling the function,
493 /// Or if the result cannot be deserialized into the requested type
494 ///
495 /// # Example
496 ///
497 /// ```rust
498 /// use rustyscript::{ json_args, Runtime, Module, Error };
499 ///
500 /// # fn main() -> Result<(), Error> {
501 /// let mut runtime = Runtime::new(Default::default())?;
502 /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
503 /// let module = runtime.load_module(&module)?;
504 /// let value: usize = runtime.call_function_immediate(Some(&module), "f", json_args!())?;
505 /// # Ok(())
506 /// # }
507 /// ```
508 pub fn call_function_immediate<T>(
509 &mut self,
510 module_context: Option<&ModuleHandle>,
511 name: &str,
512 args: &impl serde::ser::Serialize,
513 ) -> Result<T, Error>
514 where
515 T: deno_core::serde::de::DeserializeOwned,
516 {
517 let function = self.inner.get_function_by_name(module_context, name)?;
518 let result = self
519 .inner
520 .call_function_by_ref(module_context, &function, args)?;
521 self.inner.decode_value(result)
522 }
523
524 /// Get a value from a runtime instance
525 /// Blocks until:
526 /// - The event loop is resolved, and
527 /// - If the value is a promise, the promise is resolved
528 ///
529 /// # Arguments
530 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
531 /// * `name` - A string representing the name of the value to find
532 ///
533 /// # Returns
534 /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
535 /// Or if the result cannot be deserialized into the requested type
536 ///
537 /// # Errors
538 /// Can fail if the value cannot be found, or if the result cannot be deserialized.
539 ///
540 /// # Example
541 ///
542 /// ```rust
543 /// use rustyscript::{ Runtime, Module, Error };
544 ///
545 /// # fn main() -> Result<(), Error> {
546 /// let mut runtime = Runtime::new(Default::default())?;
547 /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
548 /// let module = runtime.load_module(&module)?;
549 /// let value: usize = runtime.get_value(Some(&module), "my_value")?;
550 /// # Ok(())
551 /// # }
552 /// ```
553 pub fn get_value<T>(
554 &mut self,
555 module_context: Option<&ModuleHandle>,
556 name: &str,
557 ) -> Result<T, Error>
558 where
559 T: serde::de::DeserializeOwned,
560 {
561 self.block_on(|runtime| async move { runtime.get_value_async(module_context, name).await })
562 }
563
564 /// Get a value from a runtime instance
565 /// Returns a future that resolves when:
566 /// - The event loop is resolved, and
567 /// - If the value is a promise, the promise is resolved
568 ///
569 /// See [`crate::Runtime::get_value`] for an example
570 ///
571 /// # Arguments
572 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
573 /// * `name` - A string representing the name of the value to find
574 ///
575 /// # Returns
576 /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
577 /// Or if the result cannot be deserialized into the requested type
578 ///
579 /// # Errors
580 /// Can fail if the value cannot be found, or if the result cannot be deserialized.
581 pub async fn get_value_async<T>(
582 &mut self,
583 module_context: Option<&ModuleHandle>,
584 name: &str,
585 ) -> Result<T, Error>
586 where
587 T: serde::de::DeserializeOwned,
588 {
589 let result = self.inner.get_value_ref(module_context, name)?;
590 let result = self.inner.resolve_with_event_loop(result).await?;
591 self.inner.decode_value(result)
592 }
593
594 /// Get a value from a runtime instance
595 /// Will not attempt to resolve promises, or run the event loop
596 /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]
597 /// The event loop should be run using [`crate::Runtime::await_event_loop`]
598 ///
599 /// # Arguments
600 /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
601 /// * `name` - A string representing the name of the value to find
602 ///
603 /// # Returns
604 /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
605 /// Or if the result cannot be deserialized into the requested type
606 ///
607 /// # Errors
608 /// Can fail if the value cannot be found, or if the result cannot be deserialized.
609 ///
610 /// # Example
611 ///
612 /// ```rust
613 /// use rustyscript::{ Runtime, Module, Error };
614 ///
615 /// # fn main() -> Result<(), Error> {
616 /// let mut runtime = Runtime::new(Default::default())?;
617 /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
618 /// let module = runtime.load_module(&module)?;
619 /// let value: usize = runtime.get_value_immediate(Some(&module), "my_value")?;
620 /// # Ok(())
621 /// # }
622 /// ```
623 pub fn get_value_immediate<T>(
624 &mut self,
625 module_context: Option<&ModuleHandle>,
626 name: &str,
627 ) -> Result<T, Error>
628 where
629 T: serde::de::DeserializeOwned,
630 {
631 let result = self.inner.get_value_ref(module_context, name)?;
632 self.inner.decode_value(result)
633 }
634
635 /// Executes the given module, and returns a handle allowing you to extract values
636 /// And call functions
637 ///
638 /// Blocks until the module has been executed AND the event loop has fully resolved
639 /// See [`crate::Runtime::load_module_async`] for a non-blocking variant, or use with async
640 /// background tasks
641 ///
642 /// # Arguments
643 /// * `module` - A `Module` object containing the module's filename and contents.
644 ///
645 /// # Returns
646 /// A `Result` containing a handle for the loaded module
647 /// or an error (`Error`) if there are issues with loading or executing the module
648 ///
649 /// # Errors
650 /// Can fail if the module cannot be loaded, or execution fails
651 ///
652 /// # Example
653 ///
654 /// ```rust
655 /// // Create a module with filename and contents
656 /// use rustyscript::{Runtime, Module, Error};
657 ///
658 /// # fn main() -> Result<(), Error> {
659 /// let mut runtime = Runtime::new(Default::default())?;
660 /// let module = Module::new("test.js", "export default () => 'test'");
661 /// runtime.load_module(&module);
662 /// # Ok(())
663 /// # }
664 /// ```
665 pub fn load_module(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
666 self.block_on(|runtime| async move {
667 let handle = runtime.load_module_async(module).await;
668 runtime
669 .await_event_loop(PollEventLoopOptions::default(), None)
670 .await?;
671 handle
672 })
673 }
674
675 /// Executes the given module, and returns a handle allowing you to extract values
676 /// And call functions
677 ///
678 /// Returns a future that resolves to the handle for the loaded module
679 /// Makes no attempt to fully resolve the event loop - call [`crate::Runtime::await_event_loop`]
680 /// to resolve background tasks and async listeners
681 ///
682 /// # Arguments
683 /// * `module` - A `Module` object containing the module's filename and contents.
684 ///
685 /// # Returns
686 /// A `Result` containing a handle for the loaded module
687 /// or an error (`Error`) if there are issues with loading or executing the module
688 ///
689 /// # Errors
690 /// Can fail if the module cannot be loaded, or execution fails
691 ///
692 /// See [`crate::Runtime::load_module`] for an example
693 pub async fn load_module_async(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
694 self.inner.load_modules(None, vec![module]).await
695 }
696
697 /// Executes the given module, and returns a handle allowing you to extract values
698 /// And call functions.
699 ///
700 /// Blocks until all modules have been executed AND the event loop has fully resolved
701 /// See [`crate::Runtime::load_module_async`] for a non-blocking variant, or use with async
702 /// background tasks
703 ///
704 /// This will load 'module' as the main module, and the others as side-modules.
705 /// Only one main module can be loaded per runtime
706 ///
707 /// # Arguments
708 /// * `module` - A `Module` object containing the module's filename and contents.
709 /// * `side_modules` - A set of additional modules to be loaded into memory for use
710 ///
711 /// # Returns
712 /// A `Result` containing a handle for the loaded module
713 /// or an error (`Error`) if there are issues with loading or executing the module
714 ///
715 /// # Errors
716 /// Can fail if the module cannot be loaded, or execution fails
717 ///
718 /// # Example
719 ///
720 /// ```rust
721 /// // Create a module with filename and contents
722 /// use rustyscript::{Runtime, Module, Error};
723 ///
724 /// # fn main() -> Result<(), Error> {
725 /// let mut runtime = Runtime::new(Default::default())?;
726 /// let module = Module::new("test.js", "export default () => 'test'");
727 /// runtime.load_modules(&module, vec![]);
728 /// # Ok(())
729 /// # }
730 /// ```
731 pub fn load_modules(
732 &mut self,
733 module: &Module,
734 side_modules: Vec<&Module>,
735 ) -> Result<ModuleHandle, Error> {
736 self.block_on(move |runtime| async move {
737 let handle = runtime.load_modules_async(module, side_modules).await;
738 runtime
739 .await_event_loop(PollEventLoopOptions::default(), None)
740 .await?;
741 handle
742 })
743 }
744
745 /// Executes the given module, and returns a handle allowing you to extract values
746 /// And call functions.
747 ///
748 /// Returns a future that resolves to the handle for the loaded module
749 /// Makes no attempt to resolve the event loop - call [`crate::Runtime::await_event_loop`] to
750 /// resolve background tasks and async listeners
751 ///
752 /// This will load 'module' as the main module, and the others as side-modules.
753 /// Only one main module can be loaded per runtime
754 ///
755 /// See [`crate::Runtime::load_modules`] for an example
756 ///
757 /// # Arguments
758 /// * `module` - A `Module` object containing the module's filename and contents.
759 /// * `side_modules` - A set of additional modules to be loaded into memory for use
760 ///
761 /// # Returns
762 /// A `Result` containing a handle for the loaded main module, or the last side-module
763 /// or an error (`Error`) if there are issues with loading or executing the modules
764 ///
765 /// # Errors
766 /// Can fail if the modules cannot be loaded, or execution fails
767 pub async fn load_modules_async(
768 &mut self,
769 module: &Module,
770 side_modules: Vec<&Module>,
771 ) -> Result<ModuleHandle, Error> {
772 self.inner.load_modules(Some(module), side_modules).await
773 }
774
775 /// Executes the given module, on the runtime, making it available to be
776 /// imported by other modules in this runtime, and those that will use the
777 /// snapshot
778 ///
779 /// This is a blocking operation, and will run the event loop to completion
780 /// For a non-blocking variant, see [`SnapshotBuilder::load_module_async`]
781 ///
782 /// # Arguments
783 /// * `module` - A `Module` object containing the module's filename and contents.
784 ///
785 /// # Errors
786 /// Can fail if the module cannot be loaded, or execution fails
787 pub fn with_module(mut self, module: &Module) -> Result<Self, Error> {
788 self.load_module(module)?;
789 Ok(self)
790 }
791
792 /// Executes a piece of non-ECMAScript-module JavaScript code on the runtime
793 /// This code can be used to set up the runtime state before creating the snapshot
794 ///
795 /// This is a blocking operation, and will run the event loop to completion
796 ///
797 /// # Arguments
798 /// * `expr` - A string representing the JavaScript expression to evaluate
799 ///
800 /// # Errors
801 /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized
802 pub fn with_expression(mut self, expr: &str) -> Result<Self, Error> {
803 self.eval::<()>(expr)?;
804 Ok(self)
805 }
806
807 /// Consumes the runtime and returns a snapshot of the runtime state
808 /// This is only available when the `snapshot_builder` feature is enabled
809 /// and will return a `Box<[u8]>` representing the snapshot
810 ///
811 /// To use the snapshot, provide it, as a static slice, in [`RuntimeOptions::startup_snapshot`]
812 /// Therefore, in order to use this snapshot, make sure you write it to a file and load it with
813 /// `include_bytes!`
814 ///
815 /// WARNING: In order to use the snapshot, make sure the runtime using it is
816 /// provided the same extensions and options as the original runtime.
817 #[must_use]
818 pub fn finish(self) -> Box<[u8]> {
819 let deno_rt: JsRuntimeForSnapshot = self.inner.into_inner();
820 deno_rt.snapshot()
821 }
822}
823
824impl AsyncBridgeExt for SnapshotBuilder {
825 fn bridge(&self) -> &AsyncBridge {
826 &self.tokio
827 }
828}