#![no_std]
extern crate no_std_compat as std;
pub use externref_polyfill::ExternRef;
use spin::Mutex;
use std::prelude::v1::*;
pub const JS_UNDEFINED: ExternRef = ExternRef { value: 0 };
pub const JS_NULL: ExternRef = ExternRef { value: 1 };
pub const DOM_SELF: ExternRef = ExternRef { value: 2 };
pub const DOM_WINDOW: ExternRef = ExternRef { value: 2 };
pub const DOM_DOCUMENT: ExternRef = ExternRef { value: 3 };
pub const DOM_BODY: ExternRef = ExternRef { value: 4 };
extern "C" {
fn js_register_function(start: f64, len: f64) -> f64;
fn js_invoke_function(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> f64;
fn js_invoke_function_and_return_object(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> i64;
fn js_invoke_function_and_return_bigint(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> i64;
fn js_invoke_function_and_return_string(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> usize;
fn js_invoke_function_and_return_array_buffer(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> usize;
fn js_invoke_function_and_return_bool(
fn_handle: f64,
parameters_start: *const u8,
parameters_length: usize,
) -> f64;
}
#[derive(Copy, Clone)]
pub struct JSFunction {
pub fn_handle: f64,
}
pub fn vec_into_raw_parts(v: Vec<u8>) -> (*mut u8, usize, usize) {
let mut me = std::mem::ManuallyDrop::new(v);
(me.as_mut_ptr(), me.len(), me.capacity())
}
pub enum InvokeParam<'a> {
Undefined,
Null,
Float64(f64),
BigInt(i64),
String(&'a str),
ExternRef(&'a ExternRef),
Float32Array(&'a [f32]),
Float64Array(&'a [f64]),
Bool(bool),
Uint32Array(&'a [u32]),
}
impl From<f64> for InvokeParam<'_> {
fn from(f: f64) -> Self {
InvokeParam::Float64(f)
}
}
impl From<i32> for InvokeParam<'_> {
fn from(i: i32) -> Self {
InvokeParam::Float64(i as f64)
}
}
impl From<usize> for InvokeParam<'_> {
fn from(i: usize) -> Self {
InvokeParam::Float64(i as f64)
}
}
impl From<i64> for InvokeParam<'_> {
fn from(i: i64) -> Self {
InvokeParam::BigInt(i)
}
}
impl<'a> From<&'a str> for InvokeParam<'a> {
fn from(s: &'a str) -> Self {
InvokeParam::String(s)
}
}
impl<'a> From<&'a ExternRef> for InvokeParam<'a> {
fn from(i: &'a ExternRef) -> Self {
InvokeParam::ExternRef(i)
}
}
impl<'a> From<&'a [f32]> for InvokeParam<'a> {
fn from(a: &'a [f32]) -> Self {
InvokeParam::Float32Array(a)
}
}
impl<'a> From<&'a [f64]> for InvokeParam<'a> {
fn from(a: &'a [f64]) -> Self {
InvokeParam::Float64Array(a)
}
}
impl From<bool> for InvokeParam<'_> {
fn from(b: bool) -> Self {
InvokeParam::Bool(b)
}
}
impl<'a> From<&'a [u32]> for InvokeParam<'a> {
fn from(a: &'a [u32]) -> Self {
InvokeParam::Uint32Array(a)
}
}
fn param_to_bytes(params: &[InvokeParam]) -> Vec<u8> {
let mut param_bytes = Vec::new();
for param in params {
match param {
InvokeParam::Undefined => {
param_bytes.push(0);
}
InvokeParam::Null => {
param_bytes.push(1);
}
InvokeParam::Float64(f) => {
param_bytes.push(2);
param_bytes.extend_from_slice(&f.to_le_bytes());
}
InvokeParam::BigInt(i) => {
param_bytes.push(3);
param_bytes.extend_from_slice(&i.to_le_bytes());
}
InvokeParam::String(s) => {
param_bytes.push(4);
let start = s.as_ptr() as usize;
let len = s.len();
param_bytes.extend_from_slice(&start.to_le_bytes());
param_bytes.extend_from_slice(&len.to_le_bytes());
}
InvokeParam::ExternRef(i) => {
param_bytes.push(5);
param_bytes.extend_from_slice(&i.value.to_le_bytes());
}
InvokeParam::Float32Array(a) => {
param_bytes.push(6);
let start = a.as_ptr() as usize;
let len = a.len();
param_bytes.extend_from_slice(&start.to_le_bytes());
param_bytes.extend_from_slice(&len.to_le_bytes());
}
InvokeParam::Bool(b) => {
if *b {
param_bytes.push(7);
} else {
param_bytes.push(8);
}
}
InvokeParam::Float64Array(a) => {
param_bytes.push(9);
let start = a.as_ptr() as usize;
let len = a.len();
param_bytes.extend_from_slice(&start.to_le_bytes());
param_bytes.extend_from_slice(&len.to_le_bytes());
}
InvokeParam::Uint32Array(a) => {
param_bytes.push(10);
let start = a.as_ptr() as usize;
let len = a.len();
param_bytes.extend_from_slice(&start.to_le_bytes());
param_bytes.extend_from_slice(&len.to_le_bytes());
}
}
}
param_bytes
}
impl JSFunction {
pub fn invoke(&self, params: &[InvokeParam]) -> f64
where {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
unsafe { js_invoke_function(self.fn_handle, ptr, length) }
}
pub fn invoke_and_return_object(&self, params: &[InvokeParam]) -> ExternRef
where {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
let handle = unsafe { js_invoke_function_and_return_object(self.fn_handle, ptr, length) };
ExternRef { value: handle }
}
pub fn invoke_and_return_bigint(&self, params: &[InvokeParam]) -> i64
where {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
unsafe { js_invoke_function_and_return_bigint(self.fn_handle, ptr, length) }
}
pub fn invoke_and_return_string(&self, params: &[InvokeParam]) -> String
where {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
let allocation_id =
unsafe { js_invoke_function_and_return_string(self.fn_handle, ptr, length) };
extract_string_from_memory(allocation_id)
}
pub fn invoke_and_return_array_buffer(&self, params: &[InvokeParam]) -> Vec<u8>
where {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
let allocation_id =
unsafe { js_invoke_function_and_return_array_buffer(self.fn_handle, ptr, length) };
extract_vec_from_memory(allocation_id)
}
pub fn invoke_and_return_bool(&self, params: &[InvokeParam]) -> bool {
let param_bytes = param_to_bytes(params);
let (ptr, length, _) = vec_into_raw_parts(param_bytes);
let ret = unsafe { js_invoke_function_and_return_bool(self.fn_handle, ptr, length) };
ret != 0.0
}
}
pub fn register_function(code: &str) -> JSFunction {
let start = code.as_ptr();
let len = code.len();
unsafe {
JSFunction {
fn_handle: js_register_function(start as usize as f64, len as f64),
}
}
}
static ALLOCATIONS: Mutex<Vec<Option<Vec<u8>>>> = Mutex::new(Vec::new());
pub fn extract_string_from_memory(allocation_id: usize) -> String {
let allocations = ALLOCATIONS.lock();
let allocation = allocations.get(allocation_id).unwrap();
let vec = allocation.as_ref().unwrap();
let s = String::from_utf8(vec.clone()).unwrap();
s
}
pub fn extract_vec_from_memory(allocation_id: usize) -> Vec<u8> {
let allocations = ALLOCATIONS.lock();
let allocation = allocations.get(allocation_id).unwrap();
let vec = allocation.as_ref().unwrap();
vec.clone()
}
#[no_mangle]
pub fn create_allocation(size: usize) -> usize {
let mut buf = Vec::with_capacity(size as usize);
buf.resize(size, 0);
let mut allocations = ALLOCATIONS.lock();
let i = allocations.len();
allocations.push(Some(buf));
i
}
#[no_mangle]
pub fn allocation_ptr(allocation_id: i32) -> *const u8 {
let allocations = ALLOCATIONS.lock();
let allocation = allocations.get(allocation_id as usize).unwrap();
let vec = allocation.as_ref().unwrap();
vec.as_ptr()
}
#[no_mangle]
pub fn allocation_len(allocation_id: i32) -> f64 {
let allocations = ALLOCATIONS.lock();
let allocation = allocations.get(allocation_id as usize).unwrap();
let vec = allocation.as_ref().unwrap();
vec.len() as f64
}
pub fn clear_allocation(allocation_id: usize) {
let mut allocations = ALLOCATIONS.lock();
allocations[allocation_id] = None;
}
#[macro_export]
macro_rules! js {
($e:expr) => {{
static mut FN: Option<f64> = None;
unsafe {
if FN.is_none() {
FN = Some(js::register_function($e).fn_handle);
}
JSFunction {
fn_handle: FN.unwrap(),
}
}
}};
}