mirror of
https://github.com/fluencelabs/wasm-bindgen
synced 2025-03-16 02:00:51 +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");
|
||||
}
|
||||
|
||||
if self.wasm_import_needed("__wbindgen_object_clone_ref", m) {
|
||||
self.expose_add_heap_object();
|
||||
self.expose_get_object();
|
||||
let bump_cnt = if self.debug {
|
||||
String::from("
|
||||
if (typeof(val) === 'number')
|
||||
throw new Error('corrupt slab');
|
||||
val.cnt += 1;
|
||||
")
|
||||
} else {
|
||||
String::from("(val as {cnt:number}).cnt += 1;")
|
||||
{
|
||||
let mut bind = |name: &str, f: &Fn(&mut Self) -> String| {
|
||||
if !self.wasm_import_needed(name, m) {
|
||||
return
|
||||
}
|
||||
imports_object.push_str(&format!("
|
||||
{}: {},
|
||||
", m.import_name(name), f(self)));
|
||||
};
|
||||
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
|
||||
// refcount and move on
|
||||
const val = slab[idx >> 1];
|
||||
{}
|
||||
return idx;
|
||||
}},
|
||||
", m.import_name("__wbindgen_object_clone_ref"), bump_cnt));
|
||||
}
|
||||
bind("__wbindgen_object_clone_ref", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
me.expose_get_object();
|
||||
let bump_cnt = if me.debug {
|
||||
String::from("
|
||||
if (typeof(val) === 'number')
|
||||
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) {
|
||||
self.expose_drop_ref();
|
||||
let name = m.import_name("__wbindgen_object_drop_ref");
|
||||
imports_object.push_str(&format!("{}: dropRef,\n", name));
|
||||
}
|
||||
// Otherwise if the object is on the heap just bump the
|
||||
// refcount and move on
|
||||
const val = slab[idx >> 1];
|
||||
{}
|
||||
return idx;
|
||||
}}
|
||||
", bump_cnt)
|
||||
});
|
||||
|
||||
if self.wasm_import_needed("__wbindgen_string_new", m) {
|
||||
self.expose_add_heap_object();
|
||||
self.expose_get_string_from_wasm();
|
||||
imports_object.push_str(&format!("
|
||||
{}: function(ptr: number, len: number): number {{
|
||||
return addHeapObject(getStringFromWasm(ptr, len));
|
||||
}},
|
||||
", m.import_name("__wbindgen_string_new")));
|
||||
}
|
||||
bind("__wbindgen_object_drop_ref", &|me| {
|
||||
me.expose_drop_ref();
|
||||
"dropRef".to_string()
|
||||
});
|
||||
|
||||
if self.wasm_import_needed("__wbindgen_throw", m) {
|
||||
self.expose_get_string_from_wasm();
|
||||
imports_object.push_str(&format!("\
|
||||
{}: function(ptr: number, len: number) {{
|
||||
throw new Error(getStringFromWasm(ptr, len));
|
||||
}},
|
||||
",
|
||||
m.import_name("__wbindgen_throw"),
|
||||
));
|
||||
bind("__wbindgen_string_new", &|me| {
|
||||
me.expose_add_heap_object();
|
||||
me.expose_get_string_from_wasm();
|
||||
String::from("(p, l) => addHeapObject(getStringFromWasm(p, l))")
|
||||
});
|
||||
|
||||
bind("__wbindgen_number_new", &|me| {
|
||||
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();
|
||||
|
155
src/lib.rs
155
src/lib.rs
@ -9,6 +9,7 @@
|
||||
extern crate wasm_bindgen_macro;
|
||||
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
/// A module which is typically glob imported from:
|
||||
///
|
||||
@ -31,7 +32,7 @@ pub struct 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
|
||||
/// 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)]
|
||||
pub fn __from_idx(idx: u32) -> JsObject {
|
||||
JsObject { idx }
|
||||
@ -57,6 +104,77 @@ impl JsObject {
|
||||
mem::forget(self);
|
||||
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 {
|
||||
@ -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 {
|
||||
fn __wbindgen_object_clone_ref(idx: u32) -> u32;
|
||||
fn __wbindgen_object_drop_ref(idx: 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 {
|
||||
|
97
tests/api.rs
97
tests/api.rs
@ -18,6 +18,82 @@ fn works() {
|
||||
pub fn bar(s: &str) -> JsObject {
|
||||
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#"
|
||||
@ -29,6 +105,27 @@ fn works() {
|
||||
export function test(wasm: Exports) {
|
||||
assert.strictEqual(wasm.foo(), 'foo');
|
||||
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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user