feat: copied from github.com/seddonm1/quickjs
This commit is contained in:
17
crates/quickjs-wasm/Cargo.toml
Normal file
17
crates/quickjs-wasm/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "quickjs-wasm"
|
||||
version = "0.1.0"
|
||||
authors = [""]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.68"
|
||||
once_cell = "1.17.0"
|
||||
quickjs-wasm-rs = { version = "0.1.3", features = ["json"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
console = []
|
||||
36
crates/quickjs-wasm/src/context.rs
Normal file
36
crates/quickjs-wasm/src/context.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
use anyhow::Result;
|
||||
use quickjs_wasm_rs::{Context, Value};
|
||||
use std::io::Write;
|
||||
|
||||
/// set quickjs globals
|
||||
pub fn set_quickjs_globals(context: &Context) -> anyhow::Result<()> {
|
||||
let global = context.global_object()?;
|
||||
let console_log_callback = context.wrap_callback(console_log_to(std::io::stdout()))?;
|
||||
let console_error_callback = context.wrap_callback(console_log_to(std::io::stderr()))?;
|
||||
let console_object = context.object_value()?;
|
||||
console_object.set_property("log", console_log_callback)?;
|
||||
console_object.set_property("error", console_error_callback)?;
|
||||
global.set_property("console", console_object)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// console_log_to is used to allow the javascript functions console.log and console.error to
|
||||
/// log to the stdout and stderr respectively.
|
||||
fn console_log_to<T>(mut stream: T) -> impl FnMut(&Context, &Value, &[Value]) -> Result<Value>
|
||||
where
|
||||
T: Write + 'static,
|
||||
{
|
||||
move |ctx: &Context, _this: &Value, args: &[Value]| {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i != 0 {
|
||||
write!(stream, " ")?;
|
||||
}
|
||||
|
||||
stream.write_all(arg.as_str()?.as_bytes())?;
|
||||
}
|
||||
|
||||
writeln!(stream)?;
|
||||
ctx.undefined_value()
|
||||
}
|
||||
}
|
||||
68
crates/quickjs-wasm/src/io.rs
Normal file
68
crates/quickjs-wasm/src/io.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use anyhow::Result;
|
||||
use quickjs_wasm_rs::{json, Context, Value};
|
||||
|
||||
#[link(wasm_import_module = "host")]
|
||||
extern "C" {
|
||||
fn get_input(ptr: i32);
|
||||
fn get_input_size() -> i32;
|
||||
fn get_data(ptr: i32);
|
||||
fn get_data_size() -> i32;
|
||||
fn set_output(ptr: i32, size: i32);
|
||||
}
|
||||
|
||||
/// gets the input from the host as a string
|
||||
pub fn get_input_string() -> Result<Option<String>> {
|
||||
let input_size = unsafe { get_input_size() } as usize;
|
||||
|
||||
if input_size == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(input_size);
|
||||
let ptr = buf.as_mut_ptr();
|
||||
unsafe { get_input(ptr as i32) };
|
||||
|
||||
let input_buf = unsafe { Vec::from_raw_parts(ptr, input_size, input_size) };
|
||||
|
||||
Ok(Some(String::from_utf8(input_buf.to_vec())?))
|
||||
}
|
||||
}
|
||||
|
||||
/// gets the input from the host as a string
|
||||
pub fn get_input_value(context: &Context) -> Result<Option<Value>> {
|
||||
let input_size = unsafe { get_data_size() } as usize;
|
||||
|
||||
if input_size == 0 {
|
||||
Ok(None)
|
||||
} else {
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(input_size);
|
||||
let ptr = buf.as_mut_ptr();
|
||||
unsafe { get_data(ptr as i32) };
|
||||
|
||||
let input_buf = unsafe { Vec::from_raw_parts(ptr, input_size, input_size) };
|
||||
|
||||
Ok(Some(json::transcode_input(context, &input_buf)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// sets the output value on the host
|
||||
pub fn set_output_value(output: Option<Value>) -> Result<()> {
|
||||
match output {
|
||||
Some(output) if !output.is_undefined() => {
|
||||
let output = json::transcode_output(output)?;
|
||||
|
||||
let size = output.len() as i32;
|
||||
let ptr = output.as_ptr();
|
||||
|
||||
unsafe {
|
||||
set_output(ptr as i32, size);
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
unsafe {
|
||||
set_output(0, 0);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
43
crates/quickjs-wasm/src/main.rs
Normal file
43
crates/quickjs-wasm/src/main.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
#[cfg(feature = "console")]
|
||||
mod context;
|
||||
mod io;
|
||||
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::OnceCell;
|
||||
use quickjs_wasm_rs::Context;
|
||||
|
||||
static mut JS_CONTEXT: OnceCell<Context> = OnceCell::new();
|
||||
static SCRIPT_NAME: &str = "script.js";
|
||||
|
||||
/// init() is executed by wizer to create a snapshot after the quickjs context has been initialized.
|
||||
///
|
||||
/// it also binds the console.log and console.error functions so they can be used for debugging in the
|
||||
/// user script.
|
||||
#[export_name = "wizer.initialize"]
|
||||
pub extern "C" fn init() {
|
||||
unsafe {
|
||||
let context = Context::default();
|
||||
|
||||
// add globals to the quickjs instance if enabled
|
||||
#[cfg(feature = "console")]
|
||||
context::set_quickjs_globals(&context).unwrap();
|
||||
|
||||
JS_CONTEXT.set(context).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
match io::get_input_string()? {
|
||||
Some(input) => {
|
||||
let context = unsafe { JS_CONTEXT.get_or_init(Context::default) };
|
||||
|
||||
if let Some(value) = io::get_input_value(context)? {
|
||||
context.global_object()?.set_property("data", value)?;
|
||||
}
|
||||
|
||||
let output = context.eval_global(SCRIPT_NAME, &input)?;
|
||||
io::set_output_value(Some(output))
|
||||
}
|
||||
None => io::set_output_value(None),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user