diff --git a/Cargo.toml b/Cargo.toml index a6a8a688..a216c0b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ spans = ["wasm-bindgen-macro/spans"] std = [] serde-serialize = ["serde", "serde_json", "std"] nightly = [] +disable-interning = [] # Whether or not the `#[wasm_bindgen]` macro is strict and generates an error on # all unused attributes @@ -38,6 +39,8 @@ xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_ wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.48" } serde = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true } +uluru = "0.3.0" +cfg-if = "0.1.9" [target.'cfg(target_arch = "wasm32")'.dev-dependencies] js-sys = { path = 'crates/js-sys', version = '0.3.25' } @@ -89,6 +92,5 @@ exclude = ['crates/typescript'] [patch.crates-io] wasm-bindgen = { path = '.' } wasm-bindgen-futures = { path = 'crates/futures' } -wasm-bindgen-cache = { path = 'crates/cache' } js-sys = { path = 'crates/js-sys' } web-sys = { path = 'crates/web-sys' } diff --git a/crates/cache/Cargo.toml b/crates/cache/Cargo.toml deleted file mode 100644 index 58145cec..00000000 --- a/crates/cache/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -authors = ["The wasm-bindgen Developers"] -description = "Utilities for caching JS objects to avoid expensive copying" -documentation = "https://docs.rs/wasm-bindgen-cache" -homepage = "https://rustwasm.github.io/wasm-bindgen/" -license = "MIT/Apache-2.0" -name = "wasm-bindgen-cache" -repository = "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/cache" -readme = "./README.md" -version = "0.1.0" -edition = "2018" - -[features] -disabled = [] - -[dependencies] -js-sys = { path = "../js-sys", version = '0.3.24' } -uluru = "0.3.0" - -[target.'cfg(target_arch = "wasm32")'.dev-dependencies] -wasm-bindgen-test = { path = '../test', version = '0.2.47' } diff --git a/crates/cache/src/lib.rs b/crates/cache/src/lib.rs deleted file mode 100644 index 789fdfcf..00000000 --- a/crates/cache/src/lib.rs +++ /dev/null @@ -1,100 +0,0 @@ -#![deny(missing_docs)] - -//! - - -/// -pub mod intern { - use js_sys::JsString; - use std::cell::{Cell, RefCell}; - use uluru::{LRUCache, Entry}; - - struct Pair { - key: String, - value: JsString, - } - - // TODO figure out a good default capacity - type Entries = LRUCache::<[Entry; 1_024]>; - - struct Cache { - enabled: Cell, - max_str_len: Cell, - entries: RefCell, - } - - // TODO figure out a good max_str_len - thread_local! { - static CACHE: Cache = Cache { - enabled: Cell::new(true), - max_str_len: Cell::new(128), - entries: RefCell::new(LRUCache::default()), - }; - } - - fn get_js_string(cache: &mut Entries, key: &str) -> JsString { - if let Some(p) = cache.find(|p| p.key == key) { - p.value.clone() - - } else { - let value = JsString::from(key); - - cache.insert(Pair { - key: key.to_owned(), - value: value.clone(), - }); - - value - } - } - - fn cache_str(s: &str) -> JsString { - CACHE.with(|cache| { - let should_cache = - cache.enabled.get() && - s.len() <= cache.max_str_len.get(); - - if should_cache { - get_js_string(&mut cache.entries.borrow_mut(), s) - - } else { - JsString::from(s) - } - }) - } - - /// - #[inline] - pub fn str(s: &str) -> JsString { - if cfg!(feature = "disabled") { - JsString::from(s) - - } else { - cache_str(s) - } - } - - /// - #[inline] - pub fn set_max_str_len(len: usize) { - if !cfg!(feature = "disabled") { - CACHE.with(|cache| cache.max_str_len.set(len)); - } - } - - /// - #[inline] - pub fn enable() { - if !cfg!(feature = "disabled") { - CACHE.with(|cache| cache.enabled.set(true)); - } - } - - /// - #[inline] - pub fn disable() { - if !cfg!(feature = "disabled") { - CACHE.with(|cache| cache.enabled.set(false)); - } - } -} diff --git a/src/cache/intern.rs b/src/cache/intern.rs new file mode 100644 index 00000000..1893017a --- /dev/null +++ b/src/cache/intern.rs @@ -0,0 +1,92 @@ +use std::thread_local; +use std::string::String; +use std::borrow::ToOwned; +use std::cell::{Cell, RefCell}; +use crate::JsValue; +use uluru::{LRUCache, Entry}; + + +struct Pair { + key: String, + value: JsValue, +} + +// TODO figure out a good default capacity +type Entries = LRUCache::<[Entry; 1_024]>; + +struct Cache { + enabled: Cell, + max_str_len: Cell, + entries: RefCell, +} + +// TODO figure out a good max_str_len +thread_local! { + static CACHE: Cache = Cache { + enabled: Cell::new(true), + max_str_len: Cell::new(128), + entries: RefCell::new(LRUCache::default()), + }; +} + +fn get_js_string(cache: &mut Entries, key: &str) -> JsValue { + if let Some(p) = cache.find(|p| p.key == key) { + p.value.clone() + + } else { + let value = JsValue::from(key); + + cache.insert(Pair { + key: key.to_owned(), + value: value.clone(), + }); + + value + } +} + +fn cache_str(s: &str) -> JsValue { + CACHE.with(|cache| { + let should_cache = + cache.enabled.get() && + s.len() <= cache.max_str_len.get(); + + if should_cache { + get_js_string(&mut cache.entries.borrow_mut(), s) + + } else { + JsValue::from(s) + } + }) +} + +#[inline] +pub fn str(s: &str) -> JsValue { + if cfg!(feature = "disable-interning") { + JsValue::from(s) + + } else { + cache_str(s) + } +} + +#[inline] +pub fn set_max_str_len(len: usize) { + if !cfg!(feature = "disable-interning") { + CACHE.with(|cache| cache.max_str_len.set(len)); + } +} + +#[inline] +pub fn enable() { + if !cfg!(feature = "disable-interning") { + CACHE.with(|cache| cache.enabled.set(true)); + } +} + +#[inline] +pub fn disable() { + if !cfg!(feature = "disable-interning") { + CACHE.with(|cache| cache.enabled.set(false)); + } +} diff --git a/src/cache/mod.rs b/src/cache/mod.rs new file mode 100644 index 00000000..96d1a492 --- /dev/null +++ b/src/cache/mod.rs @@ -0,0 +1 @@ +pub mod intern; diff --git a/src/convert/impls.rs b/src/convert/impls.rs index 4161e4a9..a55c84ac 100644 --- a/src/convert/impls.rs +++ b/src/convert/impls.rs @@ -315,6 +315,13 @@ impl IntoWasmAbi for JsValue { } } +impl OptionIntoWasmAbi for JsValue { + #[inline] + fn none() -> u32 { + std::u32::MAX + } +} + impl FromWasmAbi for JsValue { type Abi = u32; diff --git a/src/convert/slices.rs b/src/convert/slices.rs index 3cca7e28..2e87d688 100644 --- a/src/convert/slices.rs +++ b/src/convert/slices.rs @@ -4,6 +4,7 @@ use std::prelude::v1::*; use core::slice; use core::str; +use cfg_if::cfg_if; use crate::convert::OptionIntoWasmAbi; use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi}; @@ -148,19 +149,41 @@ if_std! { fn is_none(abi: &WasmSlice) -> bool { abi.ptr == 0 } } - impl IntoWasmAbi for String { - type Abi = as IntoWasmAbi>::Abi; + cfg_if! { + if #[cfg(feature = "disable-interning")] { + impl IntoWasmAbi for String { + type Abi = as IntoWasmAbi>::Abi; - #[inline] - fn into_abi(self) -> Self::Abi { - self.into_bytes().into_abi() + #[inline] + fn into_abi(self) -> Self::Abi { + self.into_bytes().into_abi() + } + } + + impl OptionIntoWasmAbi for String { + #[inline] + fn none() -> Self::Abi { null_slice() } + } + + } else { + impl IntoWasmAbi for String { + type Abi = ::Abi; + + #[inline] + fn into_abi(self, extra: &mut dyn Stack) -> Self::Abi { + crate::cache::intern::str(&self).into_abi(extra) + } + } + + impl OptionIntoWasmAbi for String { + #[inline] + fn none() -> Self::Abi { + ::none() + } + } } } - impl OptionIntoWasmAbi for String { - fn none() -> WasmSlice { null_slice() } - } - impl FromWasmAbi for String { type Abi = as FromWasmAbi>::Abi; @@ -175,18 +198,38 @@ if_std! { } } -impl<'a> IntoWasmAbi for &'a str { - type Abi = <&'a [u8] as IntoWasmAbi>::Abi; - #[inline] - fn into_abi(self) -> Self::Abi { - self.as_bytes().into_abi() - } -} +cfg_if! { + if #[cfg(feature = "disable-interning")] { + impl<'a> IntoWasmAbi for &'a str { + type Abi = <&'a [u8] as IntoWasmAbi>::Abi; -impl<'a> OptionIntoWasmAbi for &'a str { - fn none() -> WasmSlice { - null_slice() + #[inline] + fn into_abi(self) -> Self::Abi { + self.as_bytes().into_abi() + } + } + + impl<'a> OptionIntoWasmAbi for &'a str { + fn none() -> Self::Abi { null_slice() } + } + + } else { + impl<'a> IntoWasmAbi for &'a str { + type Abi = ::Abi; + + #[inline] + fn into_abi(self, extra: &mut dyn Stack) -> Self::Abi { + crate::cache::intern::str(self).into_abi(extra) + } + } + + impl<'a> OptionIntoWasmAbi for &'a str { + #[inline] + fn none() -> Self::Abi { + ::none() + } + } } } diff --git a/src/describe.rs b/src/describe.rs index 7e25bf6c..5df5d764 100644 --- a/src/describe.rs +++ b/src/describe.rs @@ -53,7 +53,7 @@ pub trait WasmDescribe { } macro_rules! simple { - ($($t:ident => $d:ident)*) => ($( + ($($t:ident => $d:expr)*) => ($( impl WasmDescribe for $t { fn describe() { inform($d) } } @@ -75,7 +75,7 @@ simple! { f64 => F64 bool => BOOLEAN char => CHAR - str => STRING + str => if cfg!(feature = "disable-interning") { STRING } else { ANYREF } JsValue => ANYREF } @@ -116,7 +116,7 @@ if_std! { use std::prelude::v1::*; impl WasmDescribe for String { - fn describe() { inform(STRING) } + fn describe() { inform(if cfg!(feature = "disable-interning") { STRING } else { ANYREF }) } } impl WasmDescribe for Box<[T]> { diff --git a/src/lib.rs b/src/lib.rs index 487652f9..2f044673 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -57,6 +57,8 @@ pub mod prelude { } } +#[allow(unused)] +mod cache; pub mod convert; pub mod describe;