From 7f4f737ff2b7b1c7f0b81d61384fdea69fd320ae Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 17 Jun 2020 09:54:17 +0800 Subject: [PATCH] Gate WASM target. --- README.md | 36 ++++++++++++++++++++++++++---------- RELEASES.md | 4 ++-- src/api.rs | 14 ++++++++++++++ src/engine.rs | 11 ++++++++++- src/lib.rs | 2 ++ src/module.rs | 4 ++++ src/packages/mod.rs | 2 ++ src/result.rs | 14 +++++++++++++- tests/time.rs | 2 ++ 9 files changed, 75 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index d25637cd..34682ab7 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,11 @@ Rhai - Embedded Scripting for Rust Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way to add scripting to any application. +Supported targets +----------------- + +* All common targets, including [WASM] and `no-std`. + Features -------- @@ -27,13 +32,11 @@ Features * Sand-boxed - the scripting [`Engine`], if declared immutable, cannot mutate the containing environment unless explicitly permitted (e.g. via a `RefCell`). * Rugged - protection against malicious attacks (such as [stack-overflow](#maximum-call-stack-depth), [over-sized data](#maximum-length-of-strings), and [runaway scripts](#maximum-number-of-operations) etc.) that may come from untrusted third-party user-land scripts. * Track script evaluation [progress](#tracking-progress-and-force-terminate-script-run) and manually terminate a script run. -* [`no-std`](#optional-features) support. -* Supports compiling to `WASM`, optionally with [minimal builds](#minimal-builds). * [Function overloading](#function-overloading). * [Operator overloading](#operator-overloading). * Organize code base with dynamically-loadable [modules]. * Scripts are [optimized](#script-optimization) (useful for template-based machine-generated scripts) for repeated evaluations. -* Support for [minimal builds](#minimal-builds) by excluding unneeded language [features](#optional-features). +* Support for [minimal builds] by excluding unneeded language [features](#optional-features). * Very few additional dependencies (right now only [`num-traits`](https://crates.io/crates/num-traits/) to do checked arithmetic operations); for [`no-std`](#optional-features) builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`. @@ -142,8 +145,10 @@ Making [`Dynamic`] small helps performance due to better cache efficiency. ### Minimal builds +[minimal builds]: #minimal-builds + In order to compile a _minimal_build - i.e. a build optimized for size - perhaps for `no-std` embedded targets or for -compiling to `WASM`, it is essential that the correct linker flags are used in `cargo.toml`: +compiling to [WASM], it is essential that the correct linker flags are used in `cargo.toml`: ```toml [profile.release] @@ -167,8 +172,19 @@ A _raw_ engine supports, out of the box, only a very [restricted set](#built-in- Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint. Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once. -Related -------- +### Compiling to WebAssembly (WASM) + +[WASM]: #compiling-to-WebAssembly-wasm + +It is possible to use Rhai when compiling to WebAssembly (WASM), but certain features will not be available, +such as the script file API's and loading modules from external script files. + +Also look into [minimal builds] to reduce generated WASM size. As of this version, a typical, full-featured +Rhai scripting engine compiles to a single WASM file around 200KB gzipped. When excluding features that are +marginal in WASM environment, the gzipped payload can be further shrunk to 160KB. + +Related Resources +----------------- Other cool projects to check out: @@ -2453,10 +2469,10 @@ which simply loads a script file based on the path (with `.rhai` extension attac Built-in module resolvers are grouped under the `rhai::module_resolvers` module namespace. -| Module Resolver | Description | -| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `FileModuleResolver` | The default module resolution service, not available under [`no_std`]. Loads a script file (based off the current directory) with `.rhai` extension.
The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function.
`FileModuleResolver::create_module()` loads a script file and returns a module. | -| `StaticModuleResolver` | Loads modules that are statically added. This can be used under [`no_std`]. | +| Module Resolver | Description | +| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `FileModuleResolver` | The default module resolution service, not available under [`no_std`] or [WASM] builds. Loads a script file (based off the current directory) with `.rhai` extension.
The base directory can be changed via the `FileModuleResolver::new_with_path()` constructor function.
`FileModuleResolver::create_module()` loads a script file and returns a module. | +| `StaticModuleResolver` | Loads modules that are statically added. This can be used under [`no_std`]. | An [`Engine`]'s module resolver is set via a call to `Engine::set_module_resolver`: diff --git a/RELEASES.md b/RELEASES.md index fcd99454..4128b19b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -5,7 +5,7 @@ Version 0.15.1 ============== This is a minor release which enables updating indexers (via registered indexer setters) and supports functions -with `&str` parameters (maps transparently to `ImmutableString`). +with `&str` parameters (maps transparently to `ImmutableString`). WASM is also a tested target. Buf fix ------- @@ -28,7 +28,7 @@ New features * `Engine:register_fn` and `Engine:register_result_fn` accepts functions that take parameters of type `&str` (immutable string slice), which maps directly to `ImmutableString`. This is to avoid needing wrappers for functions taking string parameters. * Set maximum limit on data sizes: `Engine::set_max_string_size`, `Engine::set_max_array_size` and `Engine::set_max_map_size`. * Supports trailing commas on array literals, object map literals, function definitions and function calls. -* Supports compiling to `WASM`. +* Enhances support for compiling to WASM. Version 0.15.0 ============== diff --git a/src/api.rs b/src/api.rs index 4a6e04cc..1ff88316 100644 --- a/src/api.rs +++ b/src/api.rs @@ -555,6 +555,8 @@ impl Engine { /// Read the contents of a file into a string. #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] fn read_file(path: PathBuf) -> Result> { let mut f = File::open(path.clone()).map_err(|err| { Box::new(EvalAltResult::ErrorReadingScriptFile( @@ -598,6 +600,8 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn compile_file(&self, path: PathBuf) -> Result> { self.compile_file_with_scope(&Scope::new(), path) } @@ -634,6 +638,8 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn compile_file_with_scope( &self, scope: &Scope, @@ -775,6 +781,8 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn eval_file(&self, path: PathBuf) -> Result> { Self::read_file(path).and_then(|contents| self.eval::(&contents)) } @@ -799,6 +807,8 @@ impl Engine { /// # } /// ``` #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn eval_file_with_scope( &self, scope: &mut Scope, @@ -1004,6 +1014,8 @@ impl Engine { /// Evaluate a file, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn consume_file(&self, path: PathBuf) -> Result<(), Box> { Self::read_file(path).and_then(|contents| self.consume(&contents)) } @@ -1011,6 +1023,8 @@ impl Engine { /// Evaluate a file with own scope, but throw away the result and only return error (if any). /// Useful for when you don't need the result, but still need to keep track of possible errors. #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub fn consume_file_with_scope( &self, scope: &mut Scope, diff --git a/src/engine.rs b/src/engine.rs index 66f04f3b..9c4c65ec 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -292,8 +292,15 @@ impl Default for Engine { #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] module_resolver: Some(Box::new(resolvers::FileModuleResolver::new())), - #[cfg(any(feature = "no_module", feature = "no_std"))] + #[cfg(any( + feature = "no_module", + feature = "no_std", + target_arch = "wasm32", + target_arch = "wasm64" + ))] module_resolver: None, type_names: HashMap::new(), @@ -373,6 +380,8 @@ fn extract_prop_from_setter(fn_name: &str) -> Option<&str> { /// Print/debug to stdout fn default_print(s: &str) { #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] println!("{}", s); } diff --git a/src/lib.rs b/src/lib.rs index d4dedda3..f9c13150 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,8 @@ //! engine.register_fn("compute", compute_something); //! //! # #[cfg(not(feature = "no_std"))] +//! # #[cfg(not(target_arch = "wasm32"))] +//! # #[cfg(not(target_arch = "wasm64"))] //! assert_eq!( //! // Evaluate the script, expects a 'bool' return //! engine.eval_file::("my_script.rhai".into())?, diff --git a/src/module.rs b/src/module.rs index d1206b5d..21bf1d11 100644 --- a/src/module.rs +++ b/src/module.rs @@ -1054,6 +1054,8 @@ pub trait ModuleResolver: SendSync { #[cfg(not(feature = "no_module"))] pub mod resolvers { #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] pub use super::file::FileModuleResolver; pub use super::stat::StaticModuleResolver; } @@ -1063,6 +1065,8 @@ pub mod resolvers {} /// Script file-based module resolver. #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_std"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] mod file { use super::*; use crate::stdlib::path::PathBuf; diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 3b89b9f4..6161cd38 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -33,6 +33,8 @@ pub use pkg_std::StandardPackage; pub use string_basic::BasicStringPackage; pub use string_more::MoreStringPackage; #[cfg(not(feature = "no_std"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] pub use time_basic::BasicTimePackage; /// Trait that all packages must implement. diff --git a/src/result.rs b/src/result.rs index 83943df2..cfd96262 100644 --- a/src/result.rs +++ b/src/result.rs @@ -13,6 +13,8 @@ use crate::stdlib::{ }; #[cfg(not(feature = "no_std"))] +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(target_arch = "wasm64"))] use crate::stdlib::path::PathBuf; /// Evaluation result. @@ -29,6 +31,8 @@ pub enum EvalAltResult { /// /// Never appears under the `no_std` feature. #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] ErrorReadingScriptFile(PathBuf, Position, std::io::Error), /// Call to an unknown function. Wrapped value is the name of the function. @@ -101,7 +105,9 @@ impl EvalAltResult { pub(crate) fn desc(&self) -> &str { match self { #[cfg(not(feature = "no_std"))] - Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file", + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] + Self::ErrorReadingScriptFile(_, _, _) => "Cannot read from script file", Self::ErrorParsing(p, _) => p.desc(), Self::ErrorInFunctionCall(_, _, _) => "Error in called function", @@ -160,6 +166,8 @@ impl fmt::Display for EvalAltResult { match self { #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] Self::ErrorReadingScriptFile(path, _, err) => { write!(f, "{} '{}': {}", desc, path.display(), err)? } @@ -259,6 +267,8 @@ impl EvalAltResult { pub fn position(&self) -> Position { match self { #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] Self::ErrorReadingScriptFile(_, pos, _) => *pos, Self::ErrorParsing(_, pos) @@ -297,6 +307,8 @@ impl EvalAltResult { pub fn set_position(&mut self, new_position: Position) { match self { #[cfg(not(feature = "no_std"))] + #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(target_arch = "wasm64"))] Self::ErrorReadingScriptFile(_, pos, _) => *pos = new_position, Self::ErrorParsing(_, pos) diff --git a/tests/time.rs b/tests/time.rs index 2590e174..fd5a9182 100644 --- a/tests/time.rs +++ b/tests/time.rs @@ -1,4 +1,6 @@ #![cfg(not(feature = "no_std"))] +#![cfg(not(target_arch = "wasm32"))] +#![cfg(not(target_arch = "wasm64"))] use rhai::{Engine, EvalAltResult, INT};