use std::ffi::c_char;
pub use crate::{emscripten_asm_const_double, emscripten_asm_const_int, emscripten_asm_const_ptr};
pub use emscripten_rs_macros::js_asm;
pub trait AsmSignature: Default {
const SIGNATURE: char;
}
macro_rules! int_like {
($($t:ty),*) => {
$(
impl AsmSignature for $t {
const SIGNATURE: char = 'i';
}
)*
};
}
int_like!(u8, i8, u16, i16, u32, i32, ());
impl AsmSignature for i64 {
const SIGNATURE: char = 'j';
}
impl AsmSignature for u64 {
const SIGNATURE: char = 'j';
}
impl<T> AsmSignature for *const T {
const SIGNATURE: char = 'p';
}
impl<T> AsmSignature for *mut T {
const SIGNATURE: char = 'p';
}
impl AsmSignature for f32 {
const SIGNATURE: char = 'f';
}
impl AsmSignature for f64 {
const SIGNATURE: char = 'd';
}
pub struct SignatureBuilder<const N: usize> {
sig: [c_char; N],
}
impl SignatureBuilder<1> {
pub const fn new<Ret: AsmSignature>() -> Self {
Self {
sig: [Ret::SIGNATURE as c_char],
}
}
pub const fn new_for<Ret: AsmSignature>(_: &Ret) -> Self {
Self {
sig: [Ret::SIGNATURE as c_char],
}
}
}
const NEXT_LEN<const N: usize>: usize = N + 1;
const fn push<T: Copy + [const] Default, const N: usize>(
arr: [T; N],
value: T,
) -> [T; core::direct_const_arg!(NEXT_LEN::<N>)] {
let mut out = [T::default(); core::direct_const_arg!(NEXT_LEN::<N>)];
let _ = &out[..N].copy_from_slice(&arr);
out[N] = value;
out
}
impl<const N: usize> SignatureBuilder<N> {
pub const fn add_param<Param: AsmSignature>(
self,
_: &Param,
) -> SignatureBuilder<core::direct_const_arg!(NEXT_LEN::<N>)> {
SignatureBuilder {
sig: push(self.sig, Param::SIGNATURE as c_char),
}
}
pub const fn finish(self) -> [c_char; core::direct_const_arg!(NEXT_LEN::<N>)] {
push(self.sig, '\0' as _)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn proof_of_concept() {
use crate::binding;
let result = unsafe {
#[unsafe(link_section = "em_asm")]
static CODE: [u8; 16] = *b"return $0 + $1;\0";
let x = 10;
let y = 20;
binding::emscripten_asm_const_int(
CODE.as_ptr() as _,
SignatureBuilder::new::<i32>()
.add_param(&x)
.add_param(&y)
.finish()
.as_ptr(),
x,
y,
)
};
assert_eq!(result, 30);
}
#[test]
fn basic() {
let number = 2;
js_asm! {
|number| {
console.log("Got: ", number);
}
}
assert_eq!(
js_asm! {
|| -> i32 {
return 1;
}
},
1
);
}
#[test]
fn mul_int() {
let x = 3;
let y = 2;
let result = js_asm! {
|x,y| -> i32 {
return x*y;
}
};
assert_eq!(result, 6);
}
#[test]
fn add_doubles() {
let a = 12.5;
let b = 52.2;
let result = js_asm! {
|a,b| -> f64 {
return a*b;
}
};
assert_eq!(result, 12.5 * 52.2);
}
#[test]
fn ptr_return() {
let string_ptr = c"Hello".as_ptr();
let returned_ptr = js_asm! { |string_ptr| -> *mut c_char { return string_ptr; } };
assert_eq!(string_ptr, returned_ptr as *const c_char);
}
}