mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-31 01:11:06 +00:00
Expose primitive information about JsObject
Adds bindings for wbindgen to fill in via JS bindings to read the various primitive properties of a JS value.
This commit is contained in:
parent
996c296de8
commit
0b81185c99
@ -525,58 +525,151 @@ impl Js {
|
|||||||
extra_imports_interface.push_str("}\n");
|
extra_imports_interface.push_str("}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.wasm_import_needed("__wbindgen_object_clone_ref", m) {
|
{
|
||||||
self.expose_add_heap_object();
|
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| {
|
||||||
self.expose_get_object();
|
if !self.wasm_import_needed(name, m) {
|
||||||
let bump_cnt = if self.debug {
|
return
|
||||||
String::from("
|
}
|
||||||
if (typeof(val) === 'number')
|
imports_object.push_str(&format!("
|
||||||
throw new Error('corrupt slab');
|
{}: {},
|
||||||
val.cnt += 1;
|
", m.import_name(name), f(self)));
|
||||||
")
|
|
||||||
} else {
|
|
||||||
String::from("(val as {cnt:number}).cnt += 1;")
|
|
||||||
};
|
};
|
||||||
imports_object.push_str(&format!("
|
|
||||||
{}: function(idx: number): number {{
|
|
||||||
// If this object is on the stack promote it to the heap.
|
|
||||||
if ((idx & 1) === 1)
|
|
||||||
return addHeapObject(getObject(idx));
|
|
||||||
|
|
||||||
// Otherwise if the object is on the heap just bump the
|
bind("__wbindgen_object_clone_ref", &|me| {
|
||||||
// refcount and move on
|
me.expose_add_heap_object();
|
||||||
const val = slab[idx >> 1];
|
me.expose_get_object();
|
||||||
{}
|
let bump_cnt = if me.debug {
|
||||||
return idx;
|
String::from("
|
||||||
}},
|
if (typeof(val) === 'number')
|
||||||
", m.import_name("__wbindgen_object_clone_ref"), bump_cnt));
|
throw new Error('corrupt slab');
|
||||||
}
|
val.cnt += 1;
|
||||||
|
")
|
||||||
|
} else {
|
||||||
|
String::from("(val as {cnt:number}).cnt += 1;")
|
||||||
|
};
|
||||||
|
format!("
|
||||||
|
function(idx: number): number {{
|
||||||
|
// If this object is on the stack promote it to the heap.
|
||||||
|
if ((idx & 1) === 1)
|
||||||
|
return addHeapObject(getObject(idx));
|
||||||
|
|
||||||
if self.wasm_import_needed("__wbindgen_object_drop_ref", m) {
|
// Otherwise if the object is on the heap just bump the
|
||||||
self.expose_drop_ref();
|
// refcount and move on
|
||||||
let name = m.import_name("__wbindgen_object_drop_ref");
|
const val = slab[idx >> 1];
|
||||||
imports_object.push_str(&format!("{}: dropRef,\n", name));
|
{}
|
||||||
}
|
return idx;
|
||||||
|
}}
|
||||||
|
", bump_cnt)
|
||||||
|
});
|
||||||
|
|
||||||
if self.wasm_import_needed("__wbindgen_string_new", m) {
|
bind("__wbindgen_object_drop_ref", &|me| {
|
||||||
self.expose_add_heap_object();
|
me.expose_drop_ref();
|
||||||
self.expose_get_string_from_wasm();
|
"dropRef".to_string()
|
||||||
imports_object.push_str(&format!("
|
});
|
||||||
{}: function(ptr: number, len: number): number {{
|
|
||||||
return addHeapObject(getStringFromWasm(ptr, len));
|
|
||||||
}},
|
|
||||||
", m.import_name("__wbindgen_string_new")));
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.wasm_import_needed("__wbindgen_throw", m) {
|
bind("__wbindgen_string_new", &|me| {
|
||||||
self.expose_get_string_from_wasm();
|
me.expose_add_heap_object();
|
||||||
imports_object.push_str(&format!("\
|
me.expose_get_string_from_wasm();
|
||||||
{}: function(ptr: number, len: number) {{
|
String::from("(p, l) => addHeapObject(getStringFromWasm(p, l))")
|
||||||
throw new Error(getStringFromWasm(ptr, len));
|
});
|
||||||
}},
|
|
||||||
",
|
bind("__wbindgen_number_new", &|me| {
|
||||||
m.import_name("__wbindgen_throw"),
|
me.expose_add_heap_object();
|
||||||
));
|
String::from("addHeapObject")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_number_get", &|me| {
|
||||||
|
me.expose_global_memory();
|
||||||
|
String::from("
|
||||||
|
function(n: number, invalid: number): number {
|
||||||
|
let obj = getObject(n);
|
||||||
|
if (typeof(obj) === 'number')
|
||||||
|
return obj;
|
||||||
|
(new Uint8Array(memory.buffer))[invalid] = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_undefined_new", &|me| {
|
||||||
|
me.expose_add_heap_object();
|
||||||
|
String::from("() => addHeapObject(undefined)")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_null_new", &|me| {
|
||||||
|
me.expose_add_heap_object();
|
||||||
|
String::from("() => addHeapObject(null)")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_is_null", &|me| {
|
||||||
|
me.expose_get_object();
|
||||||
|
String::from("(idx) => getObject(idx) === null ? 1 : 0")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_is_undefined", &|me| {
|
||||||
|
me.expose_get_object();
|
||||||
|
String::from("(idx) => getObject(idx) === undefined ? 1 : 0")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_boolean_new", &|me| {
|
||||||
|
me.expose_add_heap_object();
|
||||||
|
String::from("(v) => addHeapObject(v == 1)")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_boolean_get", &|me| {
|
||||||
|
me.expose_get_object();
|
||||||
|
String::from("(i) => {
|
||||||
|
let v = getObject(i);
|
||||||
|
if (typeof(v) == 'boolean') {
|
||||||
|
return v ? 1 : 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
}")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_symbol_new", &|me| {
|
||||||
|
me.expose_get_string_from_wasm();
|
||||||
|
me.expose_add_heap_object();
|
||||||
|
String::from("(ptr, len) => {
|
||||||
|
let a: Symbol;
|
||||||
|
console.log(ptr, len);
|
||||||
|
if (ptr === 0) {
|
||||||
|
a = Symbol();
|
||||||
|
} else {
|
||||||
|
a = Symbol(getStringFromWasm(ptr, len));
|
||||||
|
}
|
||||||
|
return addHeapObject(a);
|
||||||
|
}")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_is_symbol", &|me| {
|
||||||
|
me.expose_get_object();
|
||||||
|
String::from("(i) => typeof(getObject(i)) == 'symbol' ? 1 : 0")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_throw", &|me| {
|
||||||
|
me.expose_get_string_from_wasm();
|
||||||
|
String::from("
|
||||||
|
function(ptr: number, len: number) {
|
||||||
|
throw new Error(getStringFromWasm(ptr, len));
|
||||||
|
}
|
||||||
|
")
|
||||||
|
});
|
||||||
|
|
||||||
|
bind("__wbindgen_string_get", &|me| {
|
||||||
|
me.expose_pass_string_to_wasm(m);
|
||||||
|
me.expose_get_object();
|
||||||
|
me.expose_global_memory();
|
||||||
|
String::from("(i, len_ptr) => {
|
||||||
|
let obj = getObject(i);
|
||||||
|
if (typeof(obj) !== 'string')
|
||||||
|
return 0;
|
||||||
|
const [ptr, len] = passStringToWasm(obj);
|
||||||
|
(new Uint32Array(memory.buffer))[len_ptr / 4] = len;
|
||||||
|
return ptr;
|
||||||
|
}")
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut writes = String::new();
|
let mut writes = String::new();
|
||||||
|
155
src/lib.rs
155
src/lib.rs
@ -9,6 +9,7 @@
|
|||||||
extern crate wasm_bindgen_macro;
|
extern crate wasm_bindgen_macro;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
/// A module which is typically glob imported from:
|
/// A module which is typically glob imported from:
|
||||||
///
|
///
|
||||||
@ -31,7 +32,7 @@ pub struct JsObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl JsObject {
|
impl JsObject {
|
||||||
/// Creates a new JS object which is a string.
|
/// Creates a new JS value which is a string.
|
||||||
///
|
///
|
||||||
/// The utf-8 string provided is copied to the JS heap and the string will
|
/// The utf-8 string provided is copied to the JS heap and the string will
|
||||||
/// be owned by the JS garbage collector.
|
/// be owned by the JS garbage collector.
|
||||||
@ -41,6 +42,52 @@ impl JsObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new JS value which is a number.
|
||||||
|
///
|
||||||
|
/// This function creates a JS value representing a number (a heap
|
||||||
|
/// allocated number) and returns a handle to the JS version of it.
|
||||||
|
pub fn from_f64(n: f64) -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
JsObject::__from_idx(__wbindgen_number_new(n))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new JS value which is a boolean.
|
||||||
|
///
|
||||||
|
/// This function creates a JS object representing a boolean (a heap
|
||||||
|
/// allocated boolean) and returns a handle to the JS version of it.
|
||||||
|
pub fn from_bool(b: bool) -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
JsObject::__from_idx(__wbindgen_boolean_new(b as u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new JS value representing `undefined`.
|
||||||
|
pub fn undefined() -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
JsObject::__from_idx(__wbindgen_undefined_new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new JS value representing `null`.
|
||||||
|
pub fn null() -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
JsObject::__from_idx(__wbindgen_null_new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new JS symbol with the optional description specified.
|
||||||
|
///
|
||||||
|
/// This function will invoke the `Symbol` constructor in JS and return the
|
||||||
|
/// JS object corresponding to the symbol created.
|
||||||
|
pub fn symbol(description: Option<&str>) -> JsObject {
|
||||||
|
unsafe {
|
||||||
|
let ptr = description.map(|s| s.as_ptr()).unwrap_or(ptr::null());
|
||||||
|
let len = description.map(|s| s.len()).unwrap_or(0);
|
||||||
|
JsObject::__from_idx(__wbindgen_symbol_new(ptr, len))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn __from_idx(idx: u32) -> JsObject {
|
pub fn __from_idx(idx: u32) -> JsObject {
|
||||||
JsObject { idx }
|
JsObject { idx }
|
||||||
@ -57,6 +104,77 @@ impl JsObject {
|
|||||||
mem::forget(self);
|
mem::forget(self);
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the `f64` value of this JS value if it's an instance of a
|
||||||
|
/// number.
|
||||||
|
///
|
||||||
|
/// If this JS value is not an instance of a number then this returns
|
||||||
|
/// `None`.
|
||||||
|
pub fn as_f64(&self) -> Option<f64> {
|
||||||
|
let mut invalid = 0;
|
||||||
|
unsafe {
|
||||||
|
let ret = __wbindgen_number_get(self.idx, &mut invalid);
|
||||||
|
if invalid == 1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `String` of this JS value if it's an instance of a
|
||||||
|
/// string and it's valid utf-8.
|
||||||
|
///
|
||||||
|
/// If this JS value is not an instance of a string or if it's not valid
|
||||||
|
/// utf-8 then this returns `None`.
|
||||||
|
pub fn as_string(&self) -> Option<String> {
|
||||||
|
unsafe {
|
||||||
|
let mut len = 0;
|
||||||
|
let ptr = __wbindgen_string_get(self.idx, &mut len);
|
||||||
|
if ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let data = Vec::from_raw_parts(ptr, len, len);
|
||||||
|
Some(String::from_utf8_unchecked(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `bool` value of this JS value if it's an instance of a
|
||||||
|
/// boolean.
|
||||||
|
///
|
||||||
|
/// If this JS value is not an instance of a boolean then this returns
|
||||||
|
/// `None`.
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
unsafe {
|
||||||
|
match __wbindgen_boolean_get(self.idx) {
|
||||||
|
0 => Some(false),
|
||||||
|
1 => Some(true),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests whether this JS value is `null`
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
__wbindgen_is_null(self.idx) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests whether this JS value is `undefined`
|
||||||
|
pub fn is_undefined(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
__wbindgen_is_undefined(self.idx) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests whether the type of this JS value is `symbol`
|
||||||
|
pub fn is_symbol(&self) -> bool {
|
||||||
|
unsafe {
|
||||||
|
__wbindgen_is_symbol(self.idx) == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a str> for JsObject {
|
impl<'a> From<&'a str> for JsObject {
|
||||||
@ -65,10 +183,45 @@ impl<'a> From<&'a str> for JsObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a String> for JsObject {
|
||||||
|
fn from(s: &'a String) -> JsObject {
|
||||||
|
JsObject::from_str(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for JsObject {
|
||||||
|
fn from(s: bool) -> JsObject {
|
||||||
|
JsObject::from_bool(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! numbers {
|
||||||
|
($($n:ident)*) => ($(
|
||||||
|
impl From<$n> for JsObject {
|
||||||
|
fn from(n: $n) -> JsObject {
|
||||||
|
JsObject::from_f64(n.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*)
|
||||||
|
}
|
||||||
|
|
||||||
|
numbers! { i8 u8 i16 u16 i32 u32 f32 f64 }
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
|
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
|
||||||
fn __wbindgen_object_drop_ref(idx: u32);
|
fn __wbindgen_object_drop_ref(idx: u32);
|
||||||
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
fn __wbindgen_string_new(ptr: *const u8, len: usize) -> u32;
|
||||||
|
fn __wbindgen_number_new(f: f64) -> u32;
|
||||||
|
fn __wbindgen_number_get(idx: u32, invalid: *mut u8) -> f64;
|
||||||
|
fn __wbindgen_null_new() -> u32;
|
||||||
|
fn __wbindgen_undefined_new() -> u32;
|
||||||
|
fn __wbindgen_is_null(idx: u32) -> u32;
|
||||||
|
fn __wbindgen_is_undefined(idx: u32) -> u32;
|
||||||
|
fn __wbindgen_boolean_new(val: u32) -> u32;
|
||||||
|
fn __wbindgen_boolean_get(idx: u32) -> u32;
|
||||||
|
fn __wbindgen_symbol_new(ptr: *const u8, len: usize) -> u32;
|
||||||
|
fn __wbindgen_is_symbol(idx: u32) -> u32;
|
||||||
|
fn __wbindgen_string_get(idx: u32, len: *mut usize) -> *mut u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for JsObject {
|
impl Clone for JsObject {
|
||||||
|
97
tests/api.rs
97
tests/api.rs
@ -18,6 +18,82 @@ fn works() {
|
|||||||
pub fn bar(s: &str) -> JsObject {
|
pub fn bar(s: &str) -> JsObject {
|
||||||
JsObject::from(s)
|
JsObject::from(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn baz() -> JsObject {
|
||||||
|
JsObject::from(1.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn baz2(a: &JsObject, b: &JsObject) {
|
||||||
|
assert_eq!(a.as_f64(), Some(2.0));
|
||||||
|
assert_eq!(b.as_f64(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn js_null() -> JsObject {
|
||||||
|
JsObject::null()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn js_undefined() -> JsObject {
|
||||||
|
JsObject::undefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_is_null_undefined(
|
||||||
|
a: &JsObject,
|
||||||
|
b: &JsObject,
|
||||||
|
c: &JsObject,
|
||||||
|
) {
|
||||||
|
assert!(a.is_null());
|
||||||
|
assert!(!a.is_undefined());
|
||||||
|
|
||||||
|
assert!(!b.is_null());
|
||||||
|
assert!(b.is_undefined());
|
||||||
|
|
||||||
|
assert!(!c.is_null());
|
||||||
|
assert!(!c.is_undefined());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_true() -> JsObject {
|
||||||
|
JsObject::from(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_false() -> JsObject {
|
||||||
|
JsObject::from(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_bool(
|
||||||
|
a: &JsObject,
|
||||||
|
b: &JsObject,
|
||||||
|
c: &JsObject,
|
||||||
|
) {
|
||||||
|
assert_eq!(a.as_bool(), Some(true));
|
||||||
|
assert_eq!(b.as_bool(), Some(false));
|
||||||
|
assert_eq!(c.as_bool(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_symbol() -> JsObject {
|
||||||
|
let a = JsObject::symbol(None);
|
||||||
|
assert!(a.is_symbol());
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mk_symbol2(s: &str) -> JsObject {
|
||||||
|
let a = JsObject::symbol(Some(s));
|
||||||
|
assert!(a.is_symbol());
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assert_symbols(a: &JsObject, b: &JsObject) {
|
||||||
|
assert!(a.is_symbol());
|
||||||
|
assert!(!b.is_symbol());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire_string(a: &JsObject, b: &JsObject) {
|
||||||
|
assert_eq!(a.as_string().unwrap(), "foo");
|
||||||
|
assert_eq!(b.as_string(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn acquire_string2(a: &JsObject) -> String {
|
||||||
|
a.as_string().unwrap_or("wrong".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"#)
|
"#)
|
||||||
.file("test.ts", r#"
|
.file("test.ts", r#"
|
||||||
@ -29,6 +105,27 @@ fn works() {
|
|||||||
export function test(wasm: Exports) {
|
export function test(wasm: Exports) {
|
||||||
assert.strictEqual(wasm.foo(), 'foo');
|
assert.strictEqual(wasm.foo(), 'foo');
|
||||||
assert.strictEqual(wasm.bar('a'), 'a');
|
assert.strictEqual(wasm.bar('a'), 'a');
|
||||||
|
assert.strictEqual(wasm.baz(), 1);
|
||||||
|
wasm.baz2(2, 'a');
|
||||||
|
|
||||||
|
assert.strictEqual(wasm.js_null(), null);
|
||||||
|
assert.strictEqual(wasm.js_undefined(), undefined);
|
||||||
|
|
||||||
|
wasm.test_is_null_undefined(null, undefined, 1.0);
|
||||||
|
|
||||||
|
assert.strictEqual(wasm.get_true(), true);
|
||||||
|
assert.strictEqual(wasm.get_false(), false);
|
||||||
|
wasm.test_bool(true, false, 1.0);
|
||||||
|
|
||||||
|
assert.strictEqual(typeof(wasm.mk_symbol()), 'symbol');
|
||||||
|
assert.strictEqual(typeof(wasm.mk_symbol2('a')), 'symbol');
|
||||||
|
assert.strictEqual(Symbol.keyFor(wasm.mk_symbol()), undefined);
|
||||||
|
assert.strictEqual(Symbol.keyFor(wasm.mk_symbol2('b')), undefined);
|
||||||
|
|
||||||
|
wasm.assert_symbols(Symbol(), 'a');
|
||||||
|
wasm.acquire_string('foo', null)
|
||||||
|
assert.strictEqual(wasm.acquire_string2(''), '');
|
||||||
|
assert.strictEqual(wasm.acquire_string2('a'), 'a');
|
||||||
}
|
}
|
||||||
"#)
|
"#)
|
||||||
.test();
|
.test();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user