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};