mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-04-02 02:11:06 +00:00
Merge pull request #1612 from Pauan/cache
Initial interning implementation
This commit is contained in:
commit
68a1519eaa
@ -25,6 +25,7 @@ spans = ["wasm-bindgen-macro/spans"]
|
|||||||
std = []
|
std = []
|
||||||
serde-serialize = ["serde", "serde_json", "std"]
|
serde-serialize = ["serde", "serde_json", "std"]
|
||||||
nightly = []
|
nightly = []
|
||||||
|
enable-interning = ["std"]
|
||||||
|
|
||||||
# Whether or not the `#[wasm_bindgen]` macro is strict and generates an error on
|
# Whether or not the `#[wasm_bindgen]` macro is strict and generates an error on
|
||||||
# all unused attributes
|
# all unused attributes
|
||||||
@ -38,6 +39,7 @@ xxx_debug_only_print_generated_code = ["wasm-bindgen-macro/xxx_debug_only_print_
|
|||||||
wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.48" }
|
wasm-bindgen-macro = { path = "crates/macro", version = "=0.2.48" }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
serde_json = { version = "1.0", optional = true }
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
cfg-if = "0.1.9"
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
|
||||||
js-sys = { path = 'crates/js-sys', version = '0.3.25' }
|
js-sys = { path = 'crates/js-sys', version = '0.3.25' }
|
||||||
|
@ -22,6 +22,8 @@ jobs:
|
|||||||
displayName: "Anyref test suite builds"
|
displayName: "Anyref test suite builds"
|
||||||
- script: cargo test --target wasm32-unknown-unknown --features serde-serialize
|
- script: cargo test --target wasm32-unknown-unknown --features serde-serialize
|
||||||
displayName: "Crate test suite (with serde)"
|
displayName: "Crate test suite (with serde)"
|
||||||
|
- script: cargo test --target wasm32-unknown-unknown --features enable-interning
|
||||||
|
displayName: "Crate test suite (with enable-interning)"
|
||||||
- script: cargo test --target wasm32-unknown-unknown -p no-std
|
- script: cargo test --target wasm32-unknown-unknown -p no-std
|
||||||
displayName: "Crate test suite (no_std)"
|
displayName: "Crate test suite (no_std)"
|
||||||
- script: cargo test -p wasm-bindgen-futures
|
- script: cargo test -p wasm-bindgen-futures
|
||||||
|
@ -24,6 +24,7 @@ tys! {
|
|||||||
BOOLEAN
|
BOOLEAN
|
||||||
FUNCTION
|
FUNCTION
|
||||||
CLOSURE
|
CLOSURE
|
||||||
|
CACHED_STRING
|
||||||
STRING
|
STRING
|
||||||
REF
|
REF
|
||||||
REFMUT
|
REFMUT
|
||||||
@ -58,6 +59,7 @@ pub enum Descriptor {
|
|||||||
RefMut(Box<Descriptor>),
|
RefMut(Box<Descriptor>),
|
||||||
Slice(Box<Descriptor>),
|
Slice(Box<Descriptor>),
|
||||||
Vector(Box<Descriptor>),
|
Vector(Box<Descriptor>),
|
||||||
|
CachedString,
|
||||||
String,
|
String,
|
||||||
Anyref,
|
Anyref,
|
||||||
Enum { hole: u32 },
|
Enum { hole: u32 },
|
||||||
@ -127,6 +129,7 @@ impl Descriptor {
|
|||||||
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data, clamped))),
|
SLICE => Descriptor::Slice(Box::new(Descriptor::_decode(data, clamped))),
|
||||||
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data, clamped))),
|
VECTOR => Descriptor::Vector(Box::new(Descriptor::_decode(data, clamped))),
|
||||||
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data, clamped))),
|
OPTIONAL => Descriptor::Option(Box::new(Descriptor::_decode(data, clamped))),
|
||||||
|
CACHED_STRING => Descriptor::CachedString,
|
||||||
STRING => Descriptor::String,
|
STRING => Descriptor::String,
|
||||||
ANYREF => Descriptor::Anyref,
|
ANYREF => Descriptor::Anyref,
|
||||||
ENUM => Descriptor::Enum { hole: get(data) },
|
ENUM => Descriptor::Enum { hole: get(data) },
|
||||||
@ -159,12 +162,12 @@ impl Descriptor {
|
|||||||
|
|
||||||
pub fn vector_kind(&self) -> Option<VectorKind> {
|
pub fn vector_kind(&self) -> Option<VectorKind> {
|
||||||
let inner = match *self {
|
let inner = match *self {
|
||||||
Descriptor::String => return Some(VectorKind::String),
|
Descriptor::String | Descriptor::CachedString => return Some(VectorKind::String),
|
||||||
Descriptor::Vector(ref d) => &**d,
|
Descriptor::Vector(ref d) => &**d,
|
||||||
Descriptor::Slice(ref d) => &**d,
|
Descriptor::Slice(ref d) => &**d,
|
||||||
Descriptor::Ref(ref d) => match **d {
|
Descriptor::Ref(ref d) => match **d {
|
||||||
Descriptor::Slice(ref d) => &**d,
|
Descriptor::Slice(ref d) => &**d,
|
||||||
Descriptor::String => return Some(VectorKind::String),
|
Descriptor::String | Descriptor::CachedString => return Some(VectorKind::String),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
},
|
},
|
||||||
Descriptor::RefMut(ref d) => match **d {
|
Descriptor::RefMut(ref d) => match **d {
|
||||||
|
@ -1088,6 +1088,34 @@ impl<'a> Context<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expose_get_cached_string_from_wasm(&mut self) -> Result<(), Error> {
|
||||||
|
if !self.should_write_global("get_cached_string_from_wasm") {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.expose_get_object();
|
||||||
|
self.expose_get_string_from_wasm()?;
|
||||||
|
|
||||||
|
// This has support for both `&str` and `Option<&str>`.
|
||||||
|
//
|
||||||
|
// If `ptr` is not `0` then we know that it's a `&str` or `Some(&str)`, so we just decode it.
|
||||||
|
//
|
||||||
|
// If `ptr` is `0` then the `len` is a pointer to the cached `JsValue`, so we return that.
|
||||||
|
//
|
||||||
|
// If `ptr` and `len` are both `0` then that means it's `None`, in that case we rely upon
|
||||||
|
// the fact that `getObject(0)` is guaranteed to be `undefined`.
|
||||||
|
self.global("
|
||||||
|
function getCachedStringFromWasm(ptr, len) {
|
||||||
|
if (ptr === 0) {
|
||||||
|
return getObject(len);
|
||||||
|
} else {
|
||||||
|
return getStringFromWasm(ptr, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn expose_get_array_js_value_from_wasm(&mut self) -> Result<(), Error> {
|
fn expose_get_array_js_value_from_wasm(&mut self) -> Result<(), Error> {
|
||||||
if !self.should_write_global("get_array_js_value_from_wasm") {
|
if !self.should_write_global("get_array_js_value_from_wasm") {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
|
@ -130,6 +130,33 @@ impl<'a, 'b> Outgoing<'a, 'b> {
|
|||||||
Ok(format!("v{}", i))
|
Ok(format!("v{}", i))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NonstandardOutgoing::CachedString {
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
owned,
|
||||||
|
optional,
|
||||||
|
} => {
|
||||||
|
let ptr = self.arg(*offset);
|
||||||
|
let len = self.arg(*length);
|
||||||
|
let tmp = self.js.tmp();
|
||||||
|
|
||||||
|
if *optional {
|
||||||
|
self.js.typescript_optional("string");
|
||||||
|
} else {
|
||||||
|
self.js.typescript_required("string");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cx.expose_get_cached_string_from_wasm()?;
|
||||||
|
|
||||||
|
self.js.prelude(&format!("const v{} = getCachedStringFromWasm({}, {});", tmp, ptr, len));
|
||||||
|
|
||||||
|
if *owned {
|
||||||
|
self.prelude_free_cached_string(&ptr, &len)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(format!("v{}", tmp))
|
||||||
|
}
|
||||||
|
|
||||||
NonstandardOutgoing::StackClosure {
|
NonstandardOutgoing::StackClosure {
|
||||||
a,
|
a,
|
||||||
b,
|
b,
|
||||||
@ -408,4 +435,14 @@ impl<'a, 'b> Outgoing<'a, 'b> {
|
|||||||
));
|
));
|
||||||
self.cx.require_internal_export("__wbindgen_free")
|
self.cx.require_internal_export("__wbindgen_free")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prelude_free_cached_string(&mut self, ptr: &str, len: &str) -> Result<(), Error> {
|
||||||
|
self.js.prelude(&format!(
|
||||||
|
"if ({ptr} !== 0) {{ wasm.__wbindgen_free({ptr}, {len}); }}",
|
||||||
|
ptr = ptr,
|
||||||
|
len = len,
|
||||||
|
));
|
||||||
|
|
||||||
|
self.cx.require_internal_export("__wbindgen_free")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ impl IncomingBuilder {
|
|||||||
Descriptor::RefMut(d) => self.process_ref(true, d)?,
|
Descriptor::RefMut(d) => self.process_ref(true, d)?,
|
||||||
Descriptor::Option(d) => self.process_option(d)?,
|
Descriptor::Option(d) => self.process_option(d)?,
|
||||||
|
|
||||||
Descriptor::String | Descriptor::Vector(_) => {
|
Descriptor::String | Descriptor::CachedString | Descriptor::Vector(_) => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!("unsupported argument type for calling Rust function from JS {:?}", arg)
|
format_err!("unsupported argument type for calling Rust function from JS {:?}", arg)
|
||||||
})? ;
|
})? ;
|
||||||
@ -256,7 +256,7 @@ impl IncomingBuilder {
|
|||||||
self.bindings
|
self.bindings
|
||||||
.push(NonstandardIncoming::BorrowedAnyref { val: expr });
|
.push(NonstandardIncoming::BorrowedAnyref { val: expr });
|
||||||
}
|
}
|
||||||
Descriptor::String | Descriptor::Slice(_) => {
|
Descriptor::String | Descriptor::CachedString | Descriptor::Slice(_) => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!(
|
format_err!(
|
||||||
"unsupported slice type for calling Rust function from JS {:?}",
|
"unsupported slice type for calling Rust function from JS {:?}",
|
||||||
@ -363,7 +363,7 @@ impl IncomingBuilder {
|
|||||||
self.webidl.push(ast::WebidlScalarType::Any);
|
self.webidl.push(ast::WebidlScalarType::Any);
|
||||||
}
|
}
|
||||||
|
|
||||||
Descriptor::String | Descriptor::Vector(_) => {
|
Descriptor::String | Descriptor::CachedString | Descriptor::Vector(_) => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!(
|
format_err!(
|
||||||
"unsupported optional slice type for calling Rust function from JS {:?}",
|
"unsupported optional slice type for calling Rust function from JS {:?}",
|
||||||
|
@ -60,6 +60,18 @@ pub enum NonstandardOutgoing {
|
|||||||
kind: VectorKind,
|
kind: VectorKind,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// A Rust String (or &str) which might be cached, or might be `None`.
|
||||||
|
///
|
||||||
|
/// If `offset` is 0 then it is cached, and the cached JsValue's index is in `length`.
|
||||||
|
///
|
||||||
|
/// If `offset` and `length` are both 0, then it is `None`.
|
||||||
|
CachedString {
|
||||||
|
offset: u32,
|
||||||
|
length: u32,
|
||||||
|
owned: bool,
|
||||||
|
optional: bool,
|
||||||
|
},
|
||||||
|
|
||||||
/// A `&[u64]` or `&[i64]` is being passed to JS, and the 64-bit sizes here
|
/// A `&[u64]` or `&[i64]` is being passed to JS, and the 64-bit sizes here
|
||||||
/// aren't supported by WebIDL bindings yet.
|
/// aren't supported by WebIDL bindings yet.
|
||||||
View64 {
|
View64 {
|
||||||
@ -240,6 +252,8 @@ impl OutgoingBuilder<'_> {
|
|||||||
Descriptor::Ref(d) => self.process_ref(false, d)?,
|
Descriptor::Ref(d) => self.process_ref(false, d)?,
|
||||||
Descriptor::RefMut(d) => self.process_ref(true, d)?,
|
Descriptor::RefMut(d) => self.process_ref(true, d)?,
|
||||||
|
|
||||||
|
Descriptor::CachedString => self.cached_string(false, true),
|
||||||
|
|
||||||
Descriptor::Vector(_) | Descriptor::String => {
|
Descriptor::Vector(_) | Descriptor::String => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!(
|
format_err!(
|
||||||
@ -281,6 +295,7 @@ impl OutgoingBuilder<'_> {
|
|||||||
self.bindings
|
self.bindings
|
||||||
.push(NonstandardOutgoing::BorrowedAnyref { idx });
|
.push(NonstandardOutgoing::BorrowedAnyref { idx });
|
||||||
}
|
}
|
||||||
|
Descriptor::CachedString => self.cached_string(false, false),
|
||||||
Descriptor::Slice(_) | Descriptor::String => {
|
Descriptor::Slice(_) | Descriptor::String => {
|
||||||
use wasm_webidl_bindings::ast::WebidlScalarType::*;
|
use wasm_webidl_bindings::ast::WebidlScalarType::*;
|
||||||
|
|
||||||
@ -422,6 +437,9 @@ impl OutgoingBuilder<'_> {
|
|||||||
}
|
}
|
||||||
Descriptor::Ref(d) => self.process_option_ref(false, d)?,
|
Descriptor::Ref(d) => self.process_option_ref(false, d)?,
|
||||||
Descriptor::RefMut(d) => self.process_option_ref(true, d)?,
|
Descriptor::RefMut(d) => self.process_option_ref(true, d)?,
|
||||||
|
|
||||||
|
Descriptor::CachedString => self.cached_string(true, true),
|
||||||
|
|
||||||
Descriptor::String | Descriptor::Vector(_) => {
|
Descriptor::String | Descriptor::Vector(_) => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!(
|
format_err!(
|
||||||
@ -455,6 +473,7 @@ impl OutgoingBuilder<'_> {
|
|||||||
self.bindings
|
self.bindings
|
||||||
.push(NonstandardOutgoing::BorrowedAnyref { idx });
|
.push(NonstandardOutgoing::BorrowedAnyref { idx });
|
||||||
}
|
}
|
||||||
|
Descriptor::CachedString => self.cached_string(true, false),
|
||||||
Descriptor::String | Descriptor::Slice(_) => {
|
Descriptor::String | Descriptor::Slice(_) => {
|
||||||
let kind = arg.vector_kind().ok_or_else(|| {
|
let kind = arg.vector_kind().ok_or_else(|| {
|
||||||
format_err!(
|
format_err!(
|
||||||
@ -505,6 +524,18 @@ impl OutgoingBuilder<'_> {
|
|||||||
.push(NonstandardOutgoing::Standard(binding.into()));
|
.push(NonstandardOutgoing::Standard(binding.into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn cached_string(&mut self, optional: bool, owned: bool) {
|
||||||
|
let offset = self.push_wasm(ValType::I32);
|
||||||
|
let length = self.push_wasm(ValType::I32);
|
||||||
|
self.webidl.push(ast::WebidlScalarType::DomString);
|
||||||
|
self.bindings.push(NonstandardOutgoing::CachedString {
|
||||||
|
offset,
|
||||||
|
length,
|
||||||
|
owned,
|
||||||
|
optional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn option_native(&mut self, signed: bool, ty: ValType) {
|
fn option_native(&mut self, signed: bool, ty: ValType) {
|
||||||
let present = self.push_wasm(ValType::I32);
|
let present = self.push_wasm(ValType::I32);
|
||||||
let val = self.push_wasm(ty);
|
let val = self.push_wasm(ty);
|
||||||
|
82
src/cache/intern.rs
vendored
Normal file
82
src/cache/intern.rs
vendored
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
use cfg_if::cfg_if;
|
||||||
|
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "enable-interning")] {
|
||||||
|
use std::thread_local;
|
||||||
|
use std::string::String;
|
||||||
|
use std::borrow::ToOwned;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::JsValue;
|
||||||
|
|
||||||
|
struct Cache {
|
||||||
|
entries: RefCell<HashMap<String, JsValue>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static CACHE: Cache = Cache {
|
||||||
|
entries: RefCell::new(HashMap::new()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This returns the raw index of the cached JsValue, so you must take care
|
||||||
|
/// so that you don't use it after it is freed.
|
||||||
|
pub(crate) fn unsafe_get_str(s: &str) -> Option<u32> {
|
||||||
|
CACHE.with(|cache| {
|
||||||
|
let cache = cache.entries.borrow();
|
||||||
|
|
||||||
|
cache.get(s).map(|x| x.idx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn intern_str(key: &str) {
|
||||||
|
CACHE.with(|cache| {
|
||||||
|
let mut cache = cache.entries.borrow_mut();
|
||||||
|
|
||||||
|
// Can't use `entry` because `entry` requires a `String`
|
||||||
|
if !cache.contains_key(key) {
|
||||||
|
cache.insert(key.to_owned(), JsValue::from(key));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Interns Rust strings so that it's much faster to send them to JS.
|
||||||
|
///
|
||||||
|
/// Sending strings from Rust to JS is slow, because it has to do a full `O(n)`
|
||||||
|
/// copy and *also* encode from UTF-8 to UTF-16. This must be done every single
|
||||||
|
/// time a string is sent to JS.
|
||||||
|
///
|
||||||
|
/// If you are sending the same string multiple times, you can call this `intern`
|
||||||
|
/// function, which simply returns its argument unchanged:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use wasm_bindgen::intern;
|
||||||
|
/// intern("foo") // returns "foo"
|
||||||
|
/// # ;
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// However, if you enable the `"enable-interning"` feature for wasm-bindgen,
|
||||||
|
/// then it will add the string into an internal cache.
|
||||||
|
///
|
||||||
|
/// When you send that cached string to JS, it will look it up in the cache,
|
||||||
|
/// which completely avoids the `O(n)` copy and encoding. This has a significant
|
||||||
|
/// speed boost (as high as 783%)!
|
||||||
|
///
|
||||||
|
/// However, there is a small cost to this caching, so you shouldn't cache every
|
||||||
|
/// string. Only cache strings which have a high likelihood of being sent
|
||||||
|
/// to JS multiple times.
|
||||||
|
///
|
||||||
|
/// Also, keep in mind that this function is a *performance hint*: it's not
|
||||||
|
/// *guaranteed* that the string will be cached, and the caching strategy
|
||||||
|
/// might change at any time, so don't rely upon it.
|
||||||
|
#[inline]
|
||||||
|
pub fn intern(s: &str) -> &str {
|
||||||
|
#[cfg(feature = "enable-interning")]
|
||||||
|
intern_str(s);
|
||||||
|
|
||||||
|
s
|
||||||
|
}
|
1
src/cache/mod.rs
vendored
Normal file
1
src/cache/mod.rs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod intern;
|
@ -4,6 +4,7 @@ use std::prelude::v1::*;
|
|||||||
use core::slice;
|
use core::slice;
|
||||||
use core::str;
|
use core::str;
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use crate::convert::OptionIntoWasmAbi;
|
use crate::convert::OptionIntoWasmAbi;
|
||||||
use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi};
|
use crate::convert::{FromWasmAbi, IntoWasmAbi, RefFromWasmAbi, RefMutFromWasmAbi, WasmAbi};
|
||||||
|
|
||||||
@ -123,6 +124,24 @@ vectors! {
|
|||||||
u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
|
u8 i8 u16 i16 u32 i32 u64 i64 usize isize f32 f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "enable-interning")] {
|
||||||
|
#[inline]
|
||||||
|
fn unsafe_get_cached_str(x: &str) -> Option<WasmSlice> {
|
||||||
|
// This uses 0 for the ptr as an indication that it is a JsValue and not a str.
|
||||||
|
crate::cache::intern::unsafe_get_str(x).map(|x| WasmSlice { ptr: 0, len: x })
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#[inline]
|
||||||
|
fn unsafe_get_cached_str(_x: &str) -> Option<WasmSlice> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if_std! {
|
if_std! {
|
||||||
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
impl<T> IntoWasmAbi for Vec<T> where Box<[T]>: IntoWasmAbi<Abi = WasmSlice> {
|
||||||
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
type Abi = <Box<[T]> as IntoWasmAbi>::Abi;
|
||||||
@ -153,12 +172,14 @@ if_std! {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
fn into_abi(self) -> Self::Abi {
|
||||||
self.into_bytes().into_abi()
|
// This is safe because the JsValue is immediately looked up in the heap and
|
||||||
|
// then returned, so use-after-free cannot occur.
|
||||||
|
unsafe_get_cached_str(&self).unwrap_or_else(|| self.into_bytes().into_abi())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OptionIntoWasmAbi for String {
|
impl OptionIntoWasmAbi for String {
|
||||||
fn none() -> WasmSlice { null_slice() }
|
fn none() -> Self::Abi { null_slice() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWasmAbi for String {
|
impl FromWasmAbi for String {
|
||||||
@ -180,12 +201,14 @@ impl<'a> IntoWasmAbi for &'a str {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn into_abi(self) -> Self::Abi {
|
fn into_abi(self) -> Self::Abi {
|
||||||
self.as_bytes().into_abi()
|
// This is safe because the JsValue is immediately looked up in the heap and
|
||||||
|
// then returned, so use-after-free cannot occur.
|
||||||
|
unsafe_get_cached_str(self).unwrap_or_else(|| self.as_bytes().into_abi())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptionIntoWasmAbi for &'a str {
|
impl<'a> OptionIntoWasmAbi for &'a str {
|
||||||
fn none() -> WasmSlice {
|
fn none() -> Self::Abi {
|
||||||
null_slice()
|
null_slice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#![doc(hidden)]
|
#![doc(hidden)]
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use crate::{Clamped, JsValue};
|
use crate::{Clamped, JsValue};
|
||||||
|
|
||||||
macro_rules! tys {
|
macro_rules! tys {
|
||||||
@ -29,6 +30,7 @@ tys! {
|
|||||||
BOOLEAN
|
BOOLEAN
|
||||||
FUNCTION
|
FUNCTION
|
||||||
CLOSURE
|
CLOSURE
|
||||||
|
CACHED_STRING
|
||||||
STRING
|
STRING
|
||||||
REF
|
REF
|
||||||
REFMUT
|
REFMUT
|
||||||
@ -75,10 +77,22 @@ simple! {
|
|||||||
f64 => F64
|
f64 => F64
|
||||||
bool => BOOLEAN
|
bool => BOOLEAN
|
||||||
char => CHAR
|
char => CHAR
|
||||||
str => STRING
|
|
||||||
JsValue => ANYREF
|
JsValue => ANYREF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "enable-interning")] {
|
||||||
|
simple! {
|
||||||
|
str => CACHED_STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
simple! {
|
||||||
|
str => STRING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> WasmDescribe for *const T {
|
impl<T> WasmDescribe for *const T {
|
||||||
fn describe() {
|
fn describe() {
|
||||||
inform(I32)
|
inform(I32)
|
||||||
@ -115,8 +129,17 @@ impl<'a, T: WasmDescribe + ?Sized> WasmDescribe for &'a mut T {
|
|||||||
if_std! {
|
if_std! {
|
||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
impl WasmDescribe for String {
|
cfg_if! {
|
||||||
fn describe() { inform(STRING) }
|
if #[cfg(feature = "enable-interning")] {
|
||||||
|
simple! {
|
||||||
|
String => CACHED_STRING
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
simple! {
|
||||||
|
String => STRING
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: WasmDescribe> WasmDescribe for Box<[T]> {
|
impl<T: WasmDescribe> WasmDescribe for Box<[T]> {
|
||||||
|
@ -57,6 +57,7 @@ pub mod prelude {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub mod convert;
|
pub mod convert;
|
||||||
pub mod describe;
|
pub mod describe;
|
||||||
|
|
||||||
@ -68,6 +69,9 @@ if_std! {
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
pub mod closure;
|
pub mod closure;
|
||||||
mod anyref;
|
mod anyref;
|
||||||
|
|
||||||
|
mod cache;
|
||||||
|
pub use cache::intern::intern;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of an object owned by JS.
|
/// Representation of an object owned by JS.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user