From b73d27ba5cccf31834ce4a4e9c1dc5b80c6b82b6 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Fri, 3 May 2024 07:03:35 +0530 Subject: [PATCH 01/13] Cleaned javy and wizen the input js file --- JS/wasm/crates/apis/Cargo.toml | 1 + JS/wasm/crates/apis/src/http/mod.rs | 99 +++++- JS/wasm/crates/apis/src/http/shims/index.js | 59 +++- .../crates/apis/src/http/shims/package.json | 1 + JS/wasm/crates/arakoo-core/Cargo.toml | 9 +- JS/wasm/crates/arakoo-core/src/execution.rs | 148 ++++---- JS/wasm/crates/arakoo-core/src/lib.rs | 95 ++++-- JS/wasm/crates/arakoo-core/src/main.rs | 122 ------- JS/wasm/crates/cli/Cargo.toml | 35 +- JS/wasm/crates/cli/build.rs | 54 +-- JS/wasm/crates/cli/src/bytecode.rs | 79 ----- JS/wasm/crates/cli/src/commands.rs | 48 --- JS/wasm/crates/cli/src/exports.rs | 48 --- JS/wasm/crates/cli/src/js.rs | 317 ------------------ JS/wasm/crates/cli/src/main.rs | 119 ++++--- .../crates/cli/src/wasm_generator/dynamic.rs | 193 ----------- JS/wasm/crates/cli/src/wasm_generator/mod.rs | 3 - .../crates/cli/src/wasm_generator/static.rs | 143 -------- .../cli/src/wasm_generator/transform.rs | 47 --- JS/wasm/crates/cli/src/wit.rs | 35 -- 20 files changed, 403 insertions(+), 1252 deletions(-) delete mode 100644 JS/wasm/crates/arakoo-core/src/main.rs delete mode 100644 JS/wasm/crates/cli/src/bytecode.rs delete mode 100644 JS/wasm/crates/cli/src/commands.rs delete mode 100644 JS/wasm/crates/cli/src/exports.rs delete mode 100644 JS/wasm/crates/cli/src/js.rs delete mode 100644 JS/wasm/crates/cli/src/wasm_generator/dynamic.rs delete mode 100644 JS/wasm/crates/cli/src/wasm_generator/mod.rs delete mode 100644 JS/wasm/crates/cli/src/wasm_generator/static.rs delete mode 100644 JS/wasm/crates/cli/src/wasm_generator/transform.rs delete mode 100644 JS/wasm/crates/cli/src/wit.rs diff --git a/JS/wasm/crates/apis/Cargo.toml b/JS/wasm/crates/apis/Cargo.toml index 0711d4b0e..df4850da1 100644 --- a/JS/wasm/crates/apis/Cargo.toml +++ b/JS/wasm/crates/apis/Cargo.toml @@ -19,3 +19,4 @@ javy = { workspace = true } tokio = "1.36.0" serde = { workspace = true } serde_json = { workspace = true } +quickjs-wasm-rs = "3.0.0" diff --git a/JS/wasm/crates/apis/src/http/mod.rs b/JS/wasm/crates/apis/src/http/mod.rs index 324646a1f..aaa0f6781 100644 --- a/JS/wasm/crates/apis/src/http/mod.rs +++ b/JS/wasm/crates/apis/src/http/mod.rs @@ -6,7 +6,6 @@ use anyhow::Result; use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; use crate::{fetch, get_response, get_response_len, http::types::Request, JSApiSet}; - pub(super) struct Http; impl JSApiSet for Http { @@ -16,6 +15,14 @@ impl JSApiSet for Http { let global = context.global_object()?; global.set_property("arakoo", context.value_from_bool(true)?)?; global.set_property("fetch_internal", context.wrap_callback(fetch_callback())?)?; + // global.set_property( + // "__internal_http_send", + // context.wrap_callback( + // |context: &JSContextRef, _this: JSValueRef<'_>, args: &[JSValueRef<'_>]| { + // send_http_request(context, &_this, args) + // }, + // )?, + // )?; Ok(()) } } @@ -96,3 +103,93 @@ fn fetch_callback( Ok(response_obj) } } + +// fn send_http_request(context: &Context, _this: &Value, args: &[Value]) -> Result { +// match args { +// [request] => { +// let deserializer = &mut Deserializer::from(request.clone()); +// let request = HttpRequest::deserialize(deserializer)?; + +// let mut builder = request::Builder::new() +// .method(request.method.deref()) +// .uri(request.uri.deref()); + +// if let Some(headers) = builder.headers_mut() { +// for (key, value) in &request.headers { +// headers.insert( +// HeaderName::from_bytes(key.as_bytes())?, +// HeaderValue::from_bytes(value.as_bytes())?, +// ); +// } +// } + +// let response = arakoo_send_request( +// builder.body(request.body.map(|buffer| buffer.into_vec().into()))?, +// )?; + +// let response = HttpResponse { +// status: response.status().as_u16(), +// headers: response +// .headers() +// .iter() +// .map(|(key, value)| { +// Ok(( +// key.as_str().to_owned(), +// str::from_utf8(value.as_bytes())?.to_owned(), +// )) +// }) +// .collect::>()?, +// body: response +// .into_body() +// .map(|bytes| ByteBuf::from(bytes.deref())), +// }; + +// let mut serializer = Serializer::from_context(context)?; +// response.serialize(&mut serializer)?; +// Ok(serializer.value) +// } + +// _ => Err(anyhow!("expected 1 argument, got {}", args.len())), +// } +// } + +// pub fn arakoo_send_request(req: HttpRequest) -> Result { +// let (req, body) = req.into_parts(); + +// let method = req.method.try_into()?; + +// let uri = req.uri.to_string(); + +// let params = vec![]; + +// let headers = &req +// .headers +// .iter() +// .map(try_header_to_strs) +// .collect::>>()?; + +// let body = body.as_ref().map(|bytes| bytes.as_ref()); + +// let out_req = OutboundRequest { +// method, +// uri: &uri, +// params: ¶ms, +// headers, +// body, +// }; + +// let OutboundResponse { +// status, +// headers, +// body, +// } = spin_http::send_request(out_req)?; + +// let resp_builder = http_types::response::Builder::new().status(status); +// let resp_builder = headers +// .into_iter() +// .flatten() +// .fold(resp_builder, |b, (k, v)| b.header(k, v)); +// resp_builder +// .body(body.map(Into::into)) +// .map_err(|_| OutboundHttpError::RuntimeError) +// } \ No newline at end of file diff --git a/JS/wasm/crates/apis/src/http/shims/index.js b/JS/wasm/crates/apis/src/http/shims/index.js index e9d2919a2..764b334fb 100644 --- a/JS/wasm/crates/apis/src/http/shims/index.js +++ b/JS/wasm/crates/apis/src/http/shims/index.js @@ -1,7 +1,12 @@ -import { TextEncoder, TextDecoder } from "@sinonjs/text-encoding"; import httpStatus from "http-status"; import Url from "url-parse"; import _queryString from "query-string"; +import "fast-text-encoding" + + +let encoder = new TextEncoder() +let decoder = new TextDecoder() + class URL { constructor(urlStr, base = undefined) { @@ -302,7 +307,51 @@ globalThis.entrypoint = requestToHandler; globalThis.result = {}; globalThis.error = null; -globalThis.fetch = async (resource, options = { method: "GET" }) => { - let response = await fetch_internal(resource, options); - return Promise.resolve(new Response(response.body, response)); -}; +// globalThis.fetch = async (resource, options = { method: "GET" }) => { +// let response = await fetch_internal(resource, options); +// return Promise.resolve(new Response(response.body, response)); +// }; + +function encodeBody(body) { + if (typeof (body) == "string") { + return encoder.encode(body).buffer + } else if (ArrayBuffer.isView(body)) { + return body.buffer + } else { + return body + } +} + + +globalThis.fetch = async (uri, options) => { + let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer + let fetchOptions = { + method: (options && options.method) || "GET", + uri: (uri instanceof URL) ? uri.toString() : uri, + headers: (options && options.headers) || {}, + body: encodedBodyData, + }; + console.log(JSON.stringify(fetchOptions)) + const { status, headers, body } = __internal_http_send({ + method: (options && options.method) || "GET", + uri: (uri instanceof URL) ? uri.toString() : uri, + headers: (options && options.headers) || {}, + body: encodedBodyData, + }) + return Promise.resolve({ + status, + headers: { + entries: () => Object.entries(headers || {}), + get: (key) => (headers && headers[key]) || null, + has: (key) => (headers && headers[key]) ? true : false + }, + arrayBuffer: () => Promise.resolve(body), + ok: (status > 199 && status < 300), + statusText: statusTextList[status], + text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), + json: () => { + let text = new TextDecoder().decode(body || new Uint8Array()) + return Promise.resolve(JSON.parse(text)) + } + }) +} diff --git a/JS/wasm/crates/apis/src/http/shims/package.json b/JS/wasm/crates/apis/src/http/shims/package.json index 732dbd3fc..a4166608e 100644 --- a/JS/wasm/crates/apis/src/http/shims/package.json +++ b/JS/wasm/crates/apis/src/http/shims/package.json @@ -11,6 +11,7 @@ "dependencies": { "@sinonjs/text-encoding": "^0.7.2", "esbuild": "^0.20.0", + "fast-text-encoding": "^1.0.6", "http-status": "^1.7.3", "query-string": "^8.1.0", "url-parse": "^1.5.10" diff --git a/JS/wasm/crates/arakoo-core/Cargo.toml b/JS/wasm/crates/arakoo-core/Cargo.toml index 92dc76e1a..4745f1a9a 100644 --- a/JS/wasm/crates/arakoo-core/Cargo.toml +++ b/JS/wasm/crates/arakoo-core/Cargo.toml @@ -1,13 +1,9 @@ [package] -name = "arakoo-core" +name = "arakoo-js-engine" edition.workspace = true version.workspace = true -[[bin]] -name = "arakoo-core" -path = "src/main.rs" [lib] -name = "javy_quickjs_provider" crate-type = ["cdylib"] [dependencies] @@ -21,5 +17,4 @@ apis = { path = "../apis", features = [ "stream_io", ] } serde_json = { workspace = true } -[features] -experimental_event_loop = [] +wit-bindgen = "0.24.0" \ No newline at end of file diff --git a/JS/wasm/crates/arakoo-core/src/execution.rs b/JS/wasm/crates/arakoo-core/src/execution.rs index d03da0752..c79e7407a 100644 --- a/JS/wasm/crates/arakoo-core/src/execution.rs +++ b/JS/wasm/crates/arakoo-core/src/execution.rs @@ -1,86 +1,86 @@ -use std::process; +// use std::process; -use anyhow::{bail, Error, Result}; -use apis::http::types::Response; -use javy::{json, quickjs::JSContextRef, Runtime}; +// use anyhow::{bail, Error, Result}; +// use apis::http::types::Response; +// use javy::{json, quickjs::JSContextRef, Runtime}; -pub fn run_bytecode(runtime: &Runtime, bytecode: &[u8]) { - let context = runtime.context(); +// pub fn run_bytecode(runtime: &Runtime, bytecode: &[u8]) { +// let context = runtime.context(); - context - .eval_binary(bytecode) - .and_then(|_| process_event_loop(context)) - .unwrap_or_else(handle_error); -} +// context +// .eval_binary(bytecode) +// .and_then(|_| process_event_loop(context)) +// .unwrap_or_else(handle_error); +// } -#[allow(dead_code)] -pub fn invoke_entrypoint( - runtime: &Runtime, - bytecode: &[u8], - input: String, -) -> anyhow::Result { - let context = runtime.context(); +// #[allow(dead_code)] +// pub fn invoke_entrypoint( +// runtime: &Runtime, +// bytecode: &[u8], +// input: String, +// ) -> anyhow::Result { +// let context = runtime.context(); - match context.eval_binary(bytecode) { - Ok(_) => {} - Err(e) => { - eprintln!("error"); - eprintln!("Error while running bytecode: {e}"); - return Err(e); - } - } - let global = context.global_object().unwrap(); - let entry_point = global.get_property("entrypoint").unwrap(); +// match context.eval_binary(bytecode) { +// Ok(_) => {} +// Err(e) => { +// eprintln!("error"); +// eprintln!("Error while running bytecode: {e}"); +// return Err(e); +// } +// } +// let global = context.global_object().unwrap(); +// let entry_point = global.get_property("entrypoint").unwrap(); - let request = input; - let input_bytes = request.as_bytes(); - let input_value = json::transcode_input(context, input_bytes).unwrap_or_else(|e| { - eprintln!("Error when transcoding input: {e}"); - process::abort(); - }); - entry_point - .call(&global, &[input_value]) - .and_then(|_| process_event_loop(context)) - .unwrap_or_else(handle_error); +// let request = input; +// let input_bytes = request.as_bytes(); +// let input_value = json::transcode_input(context, input_bytes).unwrap_or_else(|e| { +// eprintln!("Error when transcoding input: {e}"); +// process::abort(); +// }); +// entry_point +// .call(&global, &[input_value]) +// .and_then(|_| process_event_loop(context)) +// .unwrap_or_else(handle_error); - let global = context.global_object().unwrap(); +// let global = context.global_object().unwrap(); - let error = global.get_property("error").unwrap(); - let output = global.get_property("result").unwrap(); +// let error = global.get_property("error").unwrap(); +// let output = global.get_property("result").unwrap(); - if !error.is_null_or_undefined() { - let error = error.to_string(); - eprintln!("Error while running JS: {error}"); - process::abort(); - } - let output = json::transcode_output(output).unwrap(); - let response: Response = serde_json::from_slice(&output).unwrap(); - Ok(response) -} +// if !error.is_null_or_undefined() { +// let error = error.to_string(); +// eprintln!("Error while running JS: {error}"); +// process::abort(); +// } +// let output = json::transcode_output(output).unwrap(); +// let response: Response = serde_json::from_slice(&output).unwrap(); +// Ok(response) +// } -pub fn invoke_function(runtime: &Runtime, fn_module: &str, fn_name: &str) { - let context = runtime.context(); - let js = if fn_name == "default" { - format!("import {{ default as defaultFn }} from '{fn_module}'; defaultFn();") - } else { - format!("import {{ {fn_name} }} from '{fn_module}'; {fn_name}();") - }; - context - .eval_module("runtime.mjs", &js) - .and_then(|_| process_event_loop(context)) - .unwrap_or_else(handle_error); -} +// pub fn invoke_function(runtime: &Runtime, fn_module: &str, fn_name: &str) { +// let context = runtime.context(); +// let js = if fn_name == "default" { +// format!("import {{ default as defaultFn }} from '{fn_module}'; defaultFn();") +// } else { +// format!("import {{ {fn_name} }} from '{fn_module}'; {fn_name}();") +// }; +// context +// .eval_module("runtime.mjs", &js) +// .and_then(|_| process_event_loop(context)) +// .unwrap_or_else(handle_error); +// } -fn process_event_loop(context: &JSContextRef) -> Result<()> { - if cfg!(feature = "experimental_event_loop") { - context.execute_pending()?; - } else if context.is_pending() { - bail!("Adding tasks to the event queue is not supported"); - } - Ok(()) -} +// fn process_event_loop(context: &JSContextRef) -> Result<()> { +// if cfg!(feature = "experimental_event_loop") { +// context.execute_pending()?; +// } else if context.is_pending() { +// bail!("Adding tasks to the event queue is not supported"); +// } +// Ok(()) +// } -fn handle_error(e: Error) { - eprintln!("Error while running JS: {e}"); - process::abort(); -} +// fn handle_error(e: Error) { +// eprintln!("Error while running JS: {e}"); +// process::abort(); +// } diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index 567aed64d..15ec0fbc7 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -1,20 +1,55 @@ use javy::Runtime; use once_cell::sync::OnceCell; +use std::io; +use std::io::Read; use std::slice; use std::str; -mod execution; +// mod execution; mod runtime; -const FUNCTION_MODULE_NAME: &str = "function.mjs"; +// mod wit { +// use wit_bindgen::generate; -static mut COMPILE_SRC_RET_AREA: [u32; 2] = [0; 2]; +// use crate::Guest; + +// generate!({ +// path:"../../wit/demo.wit", +// world:"arakoo" +// }); + +// export!(Guest); +// } + +// struct Guest; + +// impl wit::exports::arakoo::demo::guest::Guest for Guest { +// fn get_data() -> u8 { +// 105 +// } +// } + +// const FUNCTION_MODULE_NAME: &str = "function.mjs"; + +// static mut COMPILE_SRC_RET_AREA: [u32; 2] = [0; 2]; static mut RUNTIME: OnceCell = OnceCell::new(); /// Used by Wizer to preinitialize the module #[export_name = "wizer.initialize"] pub extern "C" fn init() { + let mut contents = String::new(); + let res = io::stdin().read_to_string(&mut contents); + match res { + Ok(_) => (), + Err(err) => println!("Error : no input file specified or corrupted input js file supplied \n{}", err), + } + // println!("Contents : {}",contents); let runtime = runtime::new_runtime().unwrap(); + let context = runtime.context(); + match context.eval_global("function.js", &contents) { + Ok(_) => (), + Err(err) => println!("Error in evaluating script function.js : {:?}", err), + }; unsafe { RUNTIME.set(runtime).unwrap() }; } @@ -31,34 +66,34 @@ pub extern "C" fn init() { /// # Safety /// /// * `js_src_ptr` must reference a valid array of unsigned bytes of `js_src_len` length -#[export_name = "compile_src"] -pub unsafe extern "C" fn compile_src(js_src_ptr: *const u8, js_src_len: usize) -> *const u32 { - // Use fresh runtime to avoid depending on Wizened runtime - let runtime = runtime::new_runtime().unwrap(); - let js_src = str::from_utf8(slice::from_raw_parts(js_src_ptr, js_src_len)).unwrap(); - let bytecode = runtime - .context() - .compile_module(FUNCTION_MODULE_NAME, js_src) - .unwrap(); - let bytecode_len = bytecode.len(); - // We need the bytecode buffer to live longer than this function so it can be read from memory - let bytecode_ptr = Box::leak(bytecode.into_boxed_slice()).as_ptr(); - COMPILE_SRC_RET_AREA[0] = bytecode_ptr as u32; - COMPILE_SRC_RET_AREA[1] = bytecode_len.try_into().unwrap(); - COMPILE_SRC_RET_AREA.as_ptr() -} +// #[export_name = "compile_src"] +// pub unsafe extern "C" fn compile_src(js_src_ptr: *const u8, js_src_len: usize) -> *const u32 { +// // Use fresh runtime to avoid depending on Wizened runtime +// let runtime = runtime::new_runtime().unwrap(); +// let js_src = str::from_utf8(slice::from_raw_parts(js_src_ptr, js_src_len)).unwrap(); +// let bytecode = runtime +// .context() +// .compile_module(FUNCTION_MODULE_NAME, js_src) +// .unwrap(); +// let bytecode_len = bytecode.len(); +// // We need the bytecode buffer to live longer than this function so it can be read from memory +// let bytecode_ptr = Box::leak(bytecode.into_boxed_slice()).as_ptr(); +// COMPILE_SRC_RET_AREA[0] = bytecode_ptr as u32; +// COMPILE_SRC_RET_AREA[1] = bytecode_len.try_into().unwrap(); +// COMPILE_SRC_RET_AREA.as_ptr() +// } /// Evaluates QuickJS bytecode /// /// # Safety /// /// * `bytecode_ptr` must reference a valid array of unsigned bytes of `bytecode_len` length -#[export_name = "eval_bytecode"] -pub unsafe extern "C" fn eval_bytecode(bytecode_ptr: *const u8, bytecode_len: usize) { - let runtime = RUNTIME.get().unwrap(); - let bytecode = slice::from_raw_parts(bytecode_ptr, bytecode_len); - execution::run_bytecode(runtime, bytecode); -} +// #[export_name = "eval_bytecode"] +// pub unsafe extern "C" fn eval_bytecode(bytecode_ptr: *const u8, bytecode_len: usize) { +// let runtime = RUNTIME.get().unwrap(); +// let bytecode = slice::from_raw_parts(bytecode_ptr, bytecode_len); +// execution::run_bytecode(runtime, bytecode); +// } /// Evaluates QuickJS bytecode and invokes the exported JS function name. /// @@ -75,9 +110,9 @@ pub unsafe extern "C" fn invoke( fn_name_ptr: *const u8, fn_name_len: usize, ) { - let runtime = RUNTIME.get().unwrap(); - let bytecode = slice::from_raw_parts(bytecode_ptr, bytecode_len); - let fn_name = str::from_utf8_unchecked(slice::from_raw_parts(fn_name_ptr, fn_name_len)); - execution::run_bytecode(runtime, bytecode); - execution::invoke_function(runtime, FUNCTION_MODULE_NAME, fn_name); + // let runtime = RUNTIME.get().unwrap(); + // let bytecode = slice::from_raw_parts(bytecode_ptr, bytecode_len); + // let fn_name = str::from_utf8_unchecked(slice::from_raw_parts(fn_name_ptr, fn_name_len)); + // execution::run_bytecode(runtime, bytecode); + // execution::invoke_function(runtime, FUNCTION_MODULE_NAME, fn_name); } diff --git a/JS/wasm/crates/arakoo-core/src/main.rs b/JS/wasm/crates/arakoo-core/src/main.rs deleted file mode 100644 index 70bc1b337..000000000 --- a/JS/wasm/crates/arakoo-core/src/main.rs +++ /dev/null @@ -1,122 +0,0 @@ -use javy::Runtime; -use once_cell::sync::OnceCell; - -use std::io; -use std::io::Read; -use std::slice; -use std::str; -use std::string::String; - -mod execution; -mod runtime; -use apis::http::types::Request; -const FUNCTION_MODULE_NAME: &str = "function.mjs"; - -static mut RUNTIME: OnceCell = OnceCell::new(); -static mut BYTECODE: OnceCell> = OnceCell::new(); - -#[link(wasm_import_module = "arakoo")] -extern "C" { - fn set_output(ptr: *const u8, len: i32); - fn get_request_len() -> i32; - fn get_request(ptr: *mut u8); -} - -#[export_name = "wizer.initialize"] -pub extern "C" fn init() { - let _wasm_ctx = WasmCtx::new(); - let runtime = runtime::new_runtime().unwrap(); - - let mut contents = String::new(); - io::stdin().read_to_string(&mut contents).unwrap(); - let bytecode = runtime - .context() - .compile_module("function.mjs", &contents) - .unwrap(); - - unsafe { - RUNTIME.set(runtime).unwrap(); - BYTECODE.set(bytecode).unwrap(); - } -} - -fn main() { - let bytecode = unsafe { BYTECODE.take().unwrap() }; - let runtime = unsafe { RUNTIME.take().unwrap() }; - execution::run_bytecode(&runtime, &bytecode); -} - -#[export_name = "run_entrypoint"] -pub fn run_entrypoint() { - let runtime = unsafe { RUNTIME.take().unwrap() }; - let bytecode = unsafe { BYTECODE.take().unwrap() }; - - let input_len = unsafe { get_request_len() }; - let mut input_buffer = Vec::with_capacity(input_len as usize); - let input_ptr = input_buffer.as_mut_ptr(); - - let input_buffer = unsafe { - get_request(input_ptr); - Vec::from_raw_parts(input_ptr, input_len as usize, input_len as usize) - }; - let request: Request = serde_json::from_slice(&input_buffer).unwrap(); - let request_string = serde_json::to_string(&request).unwrap(); - let result = execution::invoke_entrypoint(&runtime, &bytecode, request_string).unwrap(); - - let result_string = serde_json::to_string(&result).unwrap(); - - let len = result_string.len() as i32; - let result_ptr = result_string.as_ptr(); - unsafe { - set_output(result_ptr, len); - }; -} - -// Removed in post-processing. -/// Evaluates QuickJS bytecode and invokes the exported JS function name. -/// -/// # Safety -/// -/// * `fn_name_ptr` must reference a UTF-8 string with `fn_name_size` byte -/// length. -#[export_name = "javy.invoke"] -pub unsafe extern "C" fn invoke(fn_name_ptr: *mut u8, fn_name_size: usize) { - let _wasm_ctx = WasmCtx::new(); - - let js_fn_name = str::from_utf8_unchecked(slice::from_raw_parts(fn_name_ptr, fn_name_size)); - let runtime = unsafe { RUNTIME.take().unwrap() }; - execution::invoke_function(&runtime, FUNCTION_MODULE_NAME, js_fn_name); -} - -// RAII abstraction for calling Wasm ctors and dtors for exported non-main functions. -struct WasmCtx; - -impl WasmCtx { - #[must_use = "Failing to assign the return value will result in the wasm dtors being run immediately"] - fn new() -> Self { - unsafe { __wasm_call_ctors() }; - Self - } -} - -impl Drop for WasmCtx { - fn drop(&mut self) { - unsafe { __wasm_call_dtors() }; - } -} - -extern "C" { - // `__wasm_call_ctors` is generated by `wasm-ld` and invokes all of the global constructors. - // In a Rust bin crate, the `_start` function will invoke this implicitly but no other exported - // Wasm functions will invoke this. - // If this is not invoked, access to environment variables and directory preopens will not be - // available. - // This should only be invoked at the start of exported Wasm functions that are not the `main` - // function. - // References: - // - [Rust 1.67.0 stopped initializing the WASI environment for exported functions](https://github.com/rust-lang/rust/issues/107635) - // - [Wizer header in Fastly's JS compute runtime](https://github.com/fastly/js-compute-runtime/blob/main/runtime/js-compute-runtime/third_party/wizer.h#L92) - fn __wasm_call_ctors(); - - fn __wasm_call_dtors(); -} diff --git a/JS/wasm/crates/cli/Cargo.toml b/JS/wasm/crates/cli/Cargo.toml index fe47acc48..4948e1650 100644 --- a/JS/wasm/crates/cli/Cargo.toml +++ b/JS/wasm/crates/cli/Cargo.toml @@ -6,43 +6,14 @@ version.workspace = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] -name = "javy" +name = "arakoo-compiler" path = "src/main.rs" -[features] -dump_wat = ["dep:wasmprinter"] -experimental_event_loop = [] - [dependencies] wizer = { workspace = true } -structopt = "0.3" anyhow = { workspace = true } -binaryen = "0.13.0" -# binaryen = { git = "https://github.com/pepyakin/binaryen-rs", rev = "00c98174843f957681ba0bc5cdcc9d15f5d0cb23" } -brotli = "3.4.0" -wasmprinter = { version = "0.2.75", optional = true } -wasmtime = { workspace = true } -wasmtime-wasi = { workspace = true } -wasi-common = { workspace = true } -walrus = "0.20.3" -swc_core = { version = "0.87.19", features = [ - "common_sourcemap", - "ecma_ast", - "ecma_parser", -] } -wit-parser = "0.13.0" -convert_case = "0.6.0" -wat = "1.0.85" - -[dev-dependencies] -serde_json = "1.0" -uuid = { version = "1.6", features = ["v4"] } -lazy_static = "1.4" -serde = { version = "1.0", default-features = false, features = ["derive"] } -criterion = "0.5" -num-format = "0.4.4" -tempfile = "3.9.0" -wasmparser = "0.118.1" +clap = { version = "4.1.4", features = [ "derive" ] } +binaryen = { git = "https://github.com/pepyakin/binaryen-rs" } [build-dependencies] anyhow = "1.0.79" diff --git a/JS/wasm/crates/cli/build.rs b/JS/wasm/crates/cli/build.rs index 3bd70e472..925eb8fdd 100644 --- a/JS/wasm/crates/cli/build.rs +++ b/JS/wasm/crates/cli/build.rs @@ -20,25 +20,25 @@ fn main() -> Result<()> { fn stub_javy_core_for_clippy() -> Result<()> { let out_dir = PathBuf::from(env::var("OUT_DIR")?); let engine_path = out_dir.join("engine.wasm"); - let provider_path = out_dir.join("provider.wasm"); + // let provider_path = out_dir.join("provider.wasm"); if !engine_path.exists() { std::fs::write(engine_path, [])?; println!("cargo:warning=using stubbed engine.wasm for static analysis purposes..."); } - if !provider_path.exists() { - std::fs::write(provider_path, [])?; - println!("cargo:warning=using stubbed provider.wasm for static analysis purposes..."); - } + // if !provider_path.exists() { + // std::fs::write(provider_path, [])?; + // println!("cargo:warning=using stubbed provider.wasm for static analysis purposes..."); + // } Ok(()) } -fn read_file(path: impl AsRef) -> Result> { - let mut buf: Vec = vec![]; - fs::File::open(path.as_ref())?.read_to_end(&mut buf)?; - Ok(buf) -} +// fn read_file(path: impl AsRef) -> Result> { +// let mut buf: Vec = vec![]; +// fs::File::open(path.as_ref())?.read_to_end(&mut buf)?; +// Ok(buf) +// } // Copy the engine binary build from the `core` crate fn copy_javy_core() -> Result<()> { @@ -61,31 +61,31 @@ fn copy_javy_core() -> Result<()> { .join("../../target/wasm32-wasi/release/") }; - let engine_path = module_path.join("arakoo-core.wasm"); - let quickjs_provider_path = module_path.join("javy_quickjs_provider.wasm"); - let quickjs_provider_wizened_path = module_path.join("javy_quickjs_provider_wizened.wasm"); + let engine_path = module_path.join("arakoo_js_engine.wasm"); + // let quickjs_provider_path = module_path.join("javy_quickjs_provider.wasm"); + // let quickjs_provider_wizened_path = module_path.join("javy_quickjs_provider_wizened.wasm"); - let mut wizer = wizer::Wizer::new(); - let wizened = wizer - .allow_wasi(true)? - .wasm_bulk_memory(true) - .run(read_file(&quickjs_provider_path)?.as_slice())?; - fs::File::create(&quickjs_provider_wizened_path)?.write_all(&wizened)?; + // let mut wizer = wizer::Wizer::new(); + // let wizened = wizer + // .allow_wasi(true)? + // .wasm_bulk_memory(true) + // .run(read_file(&quickjs_provider_path)?.as_slice())?; + // fs::File::create(&quickjs_provider_wizened_path)?.write_all(&wizened)?; - println!("cargo:rerun-if-changed={}", engine_path.to_str().unwrap()); - println!( - "cargo:rerun-if-changed={}", - quickjs_provider_path.to_str().unwrap() - ); - println!("cargo:rerun-if-changed=build.rs"); + // println!("cargo:rerun-if-changed={}", engine_path.to_str().unwrap()); + // println!( + // "cargo:rerun-if-changed={}", + // quickjs_provider_path.to_str().unwrap() + // ); + // println!("cargo:rerun-if-changed=build.rs"); if engine_path.exists() { let out_dir = env::var("OUT_DIR")?; let copied_engine_path = Path::new(&out_dir).join("engine.wasm"); - let copied_provider_path = Path::new(&out_dir).join("provider.wasm"); + // let copied_provider_path = Path::new(&out_dir).join("provider.wasm"); fs::copy(&engine_path, copied_engine_path)?; - fs::copy(&quickjs_provider_wizened_path, copied_provider_path)?; + // fs::copy(&quickjs_provider_wizened_path, copied_provider_path)?; } Ok(()) } diff --git a/JS/wasm/crates/cli/src/bytecode.rs b/JS/wasm/crates/cli/src/bytecode.rs deleted file mode 100644 index abe4ec4ee..000000000 --- a/JS/wasm/crates/cli/src/bytecode.rs +++ /dev/null @@ -1,79 +0,0 @@ -use anyhow::{anyhow, Result}; -use wasmtime::{Engine, Instance, Linker, Memory, Module, Store}; -use wasmtime_wasi::{WasiCtx, WasiCtxBuilder}; - -pub const QUICKJS_PROVIDER_MODULE: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/provider.wasm")); - -pub fn compile_source(js_source_code: &[u8]) -> Result> { - let (mut store, instance, memory) = create_wasm_env()?; - let (js_src_ptr, js_src_len) = - copy_source_code_into_instance(js_source_code, &mut store, &instance, &memory)?; - let ret_ptr = call_compile(js_src_ptr, js_src_len, &mut store, &instance)?; - let bytecode = copy_bytecode_from_instance(ret_ptr, &mut store, &memory)?; - Ok(bytecode) -} - -fn create_wasm_env() -> Result<(Store, Instance, Memory)> { - let engine = Engine::default(); - let module = Module::new(&engine, QUICKJS_PROVIDER_MODULE)?; - let mut linker = Linker::new(&engine); - wasmtime_wasi::snapshots::preview_1::add_wasi_snapshot_preview1_to_linker(&mut linker, |s| s)?; - let wasi = WasiCtxBuilder::new().inherit_stderr().build(); - let mut store = Store::new(&engine, wasi); - let instance = linker.instantiate(&mut store, &module)?; - let memory = instance.get_memory(&mut store, "memory").unwrap(); - Ok((store, instance, memory)) -} - -fn copy_source_code_into_instance( - js_source_code: &[u8], - mut store: &mut Store, - instance: &Instance, - memory: &Memory, -) -> Result<(u32, u32)> { - let realloc_fn = instance - .get_typed_func::<(u32, u32, u32, u32), u32>(&mut store, "canonical_abi_realloc")?; - let js_src_len = js_source_code.len().try_into()?; - - let original_ptr = 0; - let original_size = 0; - let alignment = 1; - let size = js_src_len; - let js_source_ptr = - realloc_fn.call(&mut store, (original_ptr, original_size, alignment, size))?; - - memory.write(&mut store, js_source_ptr.try_into()?, js_source_code)?; - - Ok((js_source_ptr, js_src_len)) -} - -fn call_compile( - js_src_ptr: u32, - js_src_len: u32, - mut store: &mut Store, - instance: &Instance, -) -> Result { - let compile_src_fn = instance.get_typed_func::<(u32, u32), u32>(&mut store, "compile_src")?; - let ret_ptr = compile_src_fn - .call(&mut store, (js_src_ptr, js_src_len)) - .map_err(|_| anyhow!("JS compilation failed"))?; - Ok(ret_ptr) -} - -fn copy_bytecode_from_instance( - ret_ptr: u32, - mut store: &mut Store, - memory: &Memory, -) -> Result> { - let mut ret_buffer = [0; 8]; - memory.read(&mut store, ret_ptr.try_into()?, &mut ret_buffer)?; - - let bytecode_ptr = u32::from_le_bytes(ret_buffer[0..4].try_into()?); - let bytecode_len = u32::from_le_bytes(ret_buffer[4..8].try_into()?); - - let mut bytecode = vec![0; bytecode_len.try_into()?]; - memory.read(&mut store, bytecode_ptr.try_into()?, &mut bytecode)?; - - Ok(bytecode) -} diff --git a/JS/wasm/crates/cli/src/commands.rs b/JS/wasm/crates/cli/src/commands.rs deleted file mode 100644 index ad335f09c..000000000 --- a/JS/wasm/crates/cli/src/commands.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::path::PathBuf; -use structopt::StructOpt; - -#[derive(Debug, StructOpt)] -#[structopt(name = "javy", about = "JavaScript to WebAssembly toolchain")] -pub enum Command { - /// Compiles JavaScript to WebAssembly. - Compile(CompileCommandOpts), - /// Emits the provider binary that is required to run dynamically - /// linked WebAssembly modules. - EmitProvider(EmitProviderCommandOpts), -} - -#[derive(Debug, StructOpt)] -pub struct CompileCommandOpts { - #[structopt(parse(from_os_str))] - /// Path of the JavaScript input file. - pub input: PathBuf, - - #[structopt(short = "o", parse(from_os_str), default_value = "index.wasm")] - /// Desired path of the WebAssembly output file. - pub output: PathBuf, - - #[structopt(short = "d")] - /// Creates a smaller module that requires a dynamically linked QuickJS provider Wasm - /// module to execute (see `emit-provider` command). - pub dynamic: bool, - - #[structopt(long = "wit")] - /// Optional path to WIT file describing exported functions. - /// Only supports function exports with no arguments and no return values. - pub wit: Option, - - #[structopt(short = "n")] - /// Optional WIT world name for WIT file. Must be specified if WIT is file path is specified. - pub wit_world: Option, - - #[structopt(long = "no-source-compression")] - /// Disable source code compression, which reduces compile time at the expense of generating larger WebAssembly files. - pub no_source_compression: bool, -} - -#[derive(Debug, StructOpt)] -pub struct EmitProviderCommandOpts { - #[structopt(long = "out", short = "o")] - /// Output path for the provider binary (default is stdout). - pub out: Option, -} diff --git a/JS/wasm/crates/cli/src/exports.rs b/JS/wasm/crates/cli/src/exports.rs deleted file mode 100644 index 19e5eb52d..000000000 --- a/JS/wasm/crates/cli/src/exports.rs +++ /dev/null @@ -1,48 +0,0 @@ -use anyhow::{anyhow, Result}; -use convert_case::{Case, Casing}; -use std::{env, path::Path}; - -use crate::{js::JS, wit}; - -pub struct Export { - pub wit: String, - pub js: String, -} - -pub fn process_exports(js: &JS, wit: &Path, wit_world: &str) -> Result> { - let js_exports = js.exports()?; - parse_wit_exports(wit, wit_world)? - .into_iter() - .map(|wit_export| { - let export = wit_export.from_case(Case::Kebab).to_case(Case::Camel); - if !js_exports.contains(&export) { - Err(anyhow!("JS module does not export {export}")) - } else { - Ok(Export { - wit: wit_export, - js: export, - }) - } - }) - .collect::>>() -} - -fn parse_wit_exports(wit: &Path, wit_world: &str) -> Result> { - // Configure wit-parser to not require semicolons but only if the relevant - // environment variable is not already set. - const SEMICOLONS_OPTIONAL_ENV_VAR: &str = "WIT_REQUIRE_SEMICOLONS"; - let semicolons_env_var_already_set = env::var(SEMICOLONS_OPTIONAL_ENV_VAR).is_ok(); - if !semicolons_env_var_already_set { - env::set_var(SEMICOLONS_OPTIONAL_ENV_VAR, "0"); - } - - let exports = wit::parse_exports(wit, wit_world); - - // If we set the environment variable to not require semicolons, remove - // that environment variable now that we no longer need it set. - if !semicolons_env_var_already_set { - env::remove_var(SEMICOLONS_OPTIONAL_ENV_VAR); - } - - exports -} diff --git a/JS/wasm/crates/cli/src/js.rs b/JS/wasm/crates/cli/src/js.rs deleted file mode 100644 index c0f47fbfd..000000000 --- a/JS/wasm/crates/cli/src/js.rs +++ /dev/null @@ -1,317 +0,0 @@ -/// Higher-level representation of JavaScript. -/// -/// This is intended to be used to derive different representations of source -/// code. For example, as a byte array, a string, QuickJS bytecode, compressed -/// bytes, or attributes of the source code like what it exports. -use std::{ - collections::HashMap, - fs::File, - io::{Cursor, Read}, - path::Path, - rc::Rc, -}; - -use anyhow::{anyhow, bail, Context, Result}; -use brotli::enc::{self, BrotliEncoderParams}; -use swc_core::{ - common::{FileName, SourceMap}, - ecma::{ - ast::{ - Decl, EsVersion, ExportDecl, ExportSpecifier, Module, ModuleDecl, ModuleExportName, - ModuleItem, Stmt, - }, - parser::{self, EsConfig, Syntax}, - }, -}; - -use crate::bytecode; - -#[derive(Clone, Debug)] -pub struct JS { - source_code: Rc, -} - -impl JS { - fn from_string(source_code: String) -> JS { - JS { - source_code: Rc::new(source_code), - } - } - - pub fn from_file(path: &Path) -> Result { - let mut input_file = File::open(path) - .with_context(|| format!("Failed to open input file {}", path.display()))?; - let mut contents: Vec = vec![]; - input_file.read_to_end(&mut contents)?; - Ok(Self::from_string(String::from_utf8(contents)?)) - } - - pub fn as_bytes(&self) -> &[u8] { - self.source_code.as_bytes() - } - - pub fn compile(&self) -> Result> { - bytecode::compile_source(self.source_code.as_bytes()) - } - - pub fn compress(&self) -> Result> { - let mut compressed_source_code: Vec = vec![]; - enc::BrotliCompress( - &mut Cursor::new(&self.source_code.as_bytes()), - &mut compressed_source_code, - &BrotliEncoderParams { - quality: 11, - ..Default::default() - }, - )?; - Ok(compressed_source_code) - } - - pub fn exports(&self) -> Result> { - let module = self.parse_module()?; - - // function foo() ... - let mut functions = HashMap::new(); - // export { foo, bar as baz } - let mut named_exports = vec![]; - // export function foo() ... - let mut exported_functions = vec![]; - for item in module.body { - match item { - ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { - decl: Decl::Fn(f), - .. - })) => { - if !f.function.params.is_empty() { - bail!("Exported functions with parameters are not supported"); - } - if f.function.is_generator { - bail!("Exported generators are not supported"); - } - exported_functions.push(f.ident.sym); - } - ModuleItem::ModuleDecl(ModuleDecl::ExportNamed(e)) => { - for specifier in e.specifiers { - if let ExportSpecifier::Named(n) = specifier { - let orig = match n.orig { - ModuleExportName::Ident(i) => i.sym, - ModuleExportName::Str(s) => s.value, - }; - let exported_name = n.exported.map(|e| match e { - ModuleExportName::Ident(i) => i.sym, - ModuleExportName::Str(s) => s.value, - }); - named_exports.push((orig, exported_name)); - } - } - } - ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultDecl(e)) if e.decl.is_fn_expr() => { - exported_functions.push("default".into()) - } - ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(e)) if e.expr.is_arrow() => { - exported_functions.push("default".into()) - } - ModuleItem::Stmt(Stmt::Decl(Decl::Fn(f))) => { - functions.insert( - f.ident.sym, - (f.function.params.is_empty(), f.function.is_generator), - ); - } - _ => continue, - } - } - - let mut named_exported_functions = named_exports - .into_iter() - .filter_map(|(orig, exported)| { - if let Some((no_params, is_generator)) = functions.get(&orig) { - if !no_params { - Some(Err(anyhow!( - "Exported functions with parameters are not supported" - ))) - } else if *is_generator { - Some(Err(anyhow!("Exported generators are not supported"))) - } else { - Some(Ok(exported.unwrap_or(orig))) - } - } else { - None - } - }) - .collect::, _>>()?; - exported_functions.append(&mut named_exported_functions); - Ok(exported_functions - .into_iter() - .map(|f| f.to_string()) - .collect()) - } - - fn parse_module(&self) -> Result { - let source_map: SourceMap = Default::default(); - let file = source_map.new_source_file_from(FileName::Anon, self.source_code.clone()); - let mut errors = vec![]; - parser::parse_file_as_module( - &file, - Syntax::Es(EsConfig::default()), - EsVersion::Es2020, - None, - &mut errors, - ) - .map_err(|e| anyhow!(e.into_kind().msg())) - .with_context(|| "Invalid JavaScript") - } -} - -#[cfg(test)] -mod tests { - use crate::js::JS; - - use anyhow::Result; - - #[test] - fn parse_no_exports() -> Result<()> { - let exports = parse("function foo() {}")?; - assert_eq!(Vec::<&str>::default(), exports); - Ok(()) - } - - #[test] - fn parse_invalid_js() -> Result<()> { - let res = parse("fun foo() {}"); - assert_eq!("Invalid JavaScript", res.err().unwrap().to_string()); - Ok(()) - } - - #[test] - fn parse_one_func_export() -> Result<()> { - let exports = parse("export function foo() {}")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_func_export_with_parameter() -> Result<()> { - let res = parse("export function foo(bar) {}"); - assert_eq!( - "Exported functions with parameters are not supported", - res.err().unwrap().to_string() - ); - Ok(()) - } - - #[test] - fn parse_generator_export() -> Result<()> { - let res = parse("export function *foo() {}"); - assert_eq!( - "Exported generators are not supported", - res.err().unwrap().to_string() - ); - Ok(()) - } - - #[test] - fn parse_two_func_exports() -> Result<()> { - let exports = parse("export function foo() {}; export function bar() {};")?; - assert_eq!(vec!["foo", "bar"], exports); - Ok(()) - } - - #[test] - fn parse_const_export() -> Result<()> { - let exports = parse("export const x = 1;")?; - let expected_exports: Vec<&str> = vec![]; - assert_eq!(expected_exports, exports); - Ok(()) - } - - #[test] - fn parse_const_export_and_func_export() -> Result<()> { - let exports = parse("export const x = 1; export function foo() {}")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_named_func_export() -> Result<()> { - let exports = parse("function foo() {}; export { foo };")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_named_func_export_with_arg() -> Result<()> { - let res = parse("function foo(bar) {}; export { foo };"); - assert_eq!( - "Exported functions with parameters are not supported", - res.err().unwrap().to_string() - ); - Ok(()) - } - - #[test] - fn parse_funcs_with_args() -> Result<()> { - let exports = parse("function foo(bar) {}")?; - assert_eq!(Vec::<&str>::default(), exports); - Ok(()) - } - - #[test] - fn parse_named_func_export_and_const_export() -> Result<()> { - let exports = parse("function foo() {}; const bar = 1; export { foo, bar };")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_func_export_and_named_func_export() -> Result<()> { - let exports = parse("export function foo() {}; function bar() {}; export { bar };")?; - assert_eq!(vec!["foo", "bar"], exports); - Ok(()) - } - - #[test] - fn parse_renamed_func_export() -> Result<()> { - let exports = parse("function foo() {}; export { foo as bar };")?; - assert_eq!(vec!["bar"], exports); - Ok(()) - } - - #[test] - fn parse_hoisted_func_export() -> Result<()> { - let exports = parse("export { foo }; function foo() {}")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_renamed_hosted_func_export() -> Result<()> { - let exports = parse("export { foo as bar }; function foo() {}")?; - assert_eq!(vec!["bar"], exports); - Ok(()) - } - - #[test] - fn parse_hoisted_exports_with_func_and_const() -> Result<()> { - let exports = parse("export { foo, bar }; function foo() {}; const bar = 1;")?; - assert_eq!(vec!["foo"], exports); - Ok(()) - } - - #[test] - fn parse_default_arrow_export() -> Result<()> { - let exports = parse("export default () => {}")?; - assert_eq!(vec!["default"], exports); - Ok(()) - } - - #[test] - fn parse_default_function_export() -> Result<()> { - let exports = parse("export default function() {}")?; - assert_eq!(vec!["default"], exports); - Ok(()) - } - - fn parse(js: &str) -> Result> { - JS::from_string(js.to_string()).exports() - } -} diff --git a/JS/wasm/crates/cli/src/main.rs b/JS/wasm/crates/cli/src/main.rs index a98dd9a50..eb60b643c 100644 --- a/JS/wasm/crates/cli/src/main.rs +++ b/JS/wasm/crates/cli/src/main.rs @@ -1,49 +1,86 @@ -mod bytecode; -mod commands; -mod exports; -mod js; -mod wasm_generator; -mod wit; - -use crate::commands::{Command, EmitProviderCommandOpts}; -use crate::wasm_generator::r#static as static_generator; -use anyhow::{bail, Result}; -use js::JS; +use anyhow::bail; +use anyhow::Context; +use anyhow::Result; +use binaryen::CodegenConfig; +use binaryen::Module; +use clap::Parser; use std::fs; -use std::fs::File; -use std::io::Write; -use structopt::StructOpt; -use wasm_generator::dynamic as dynamic_generator; +use std::io::Read; +use std::process::Command; +use std::{env, fs::File, path::PathBuf}; +use wizer::Wizer; + +const VERSION: &str = env!("CARGO_PKG_VERSION"); + +#[derive(Debug, Parser)] +#[clap( + about = "A utility to convert js to arakoo runtime compatible wasm component", + version = VERSION +)] +pub struct Options { + pub input: PathBuf, + #[arg(short = 'o', default_value = "index.wasm")] + pub output: PathBuf, +} fn main() -> Result<()> { - let cmd = Command::from_args(); - - match &cmd { - Command::EmitProvider(opts) => emit_provider(opts), - Command::Compile(opts) => { - let js = JS::from_file(&opts.input)?; - let exports = match (&opts.wit, &opts.wit_world) { - (None, None) => Ok(vec![]), - (None, Some(_)) => Ok(vec![]), - (Some(_), None) => bail!("Must provide WIT world when providing WIT file"), - (Some(wit), Some(world)) => exports::process_exports(&js, wit, world), - }?; - let wasm = if opts.dynamic { - dynamic_generator::generate(&js, exports, opts.no_source_compression)? - } else { - static_generator::generate(&js, exports, opts.no_source_compression)? - }; - fs::write(&opts.output, wasm)?; - Ok(()) + let opts = Options::parse(); + + if env::var("EDECHAINS_JS_WIZEN").eq(&Ok("1".into())) { + env::remove_var("EDECHAINS_JS_WIZEN"); + + println!("\nStarting to build arakoo compatible module"); + + let wasm: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/engine.wasm")); + + println!("Preinitializing using Wizer"); + + let mut wasm = Wizer::new() + .allow_wasi(true)? + .inherit_stdio(true) + .wasm_bulk_memory(true) + .run(wasm)?; + + let codegen_config = CodegenConfig { + optimization_level: 3, + shrink_level: 0, + debug_info: false, + }; + + println!("Optimizing wasm binary using wasm-opt"); + + if let Ok(mut module) = Module::read(&wasm) { + module.optimize(&codegen_config); + module + .run_optimization_passes(vec!["strip"], &codegen_config) + .expect("Unable to optimize"); + wasm = module.write(); + } else { + bail!("Unable to read wasm binary for wasm-opt optimizations"); } + + fs::write(&opts.output, wasm)?; + return Ok(()); } -} -fn emit_provider(opts: &EmitProviderCommandOpts) -> Result<()> { - let mut file: Box = match opts.out.as_ref() { - Some(path) => Box::new(File::create(path)?), - _ => Box::new(std::io::stdout()), - }; - file.write_all(bytecode::QUICKJS_PROVIDER_MODULE)?; + let script = File::open(&opts.input) + .with_context(|| format!("Failed to open input file {}", opts.input.display()))?; + + let self_cmd = env::args().next().expect("No self command"); + env::set_var("EDECHAINS_JS_WIZEN", "1"); + + let status = Command::new(self_cmd) + .arg(&opts.input) + .arg("-o") + .arg(&opts.output) + .stdin(script) + .status()?; + + if !status.success() { + anyhow::bail!("Failed to convert js to wasm"); + } + + println!("Arakoo compatible module built successfully"); + Ok(()) } diff --git a/JS/wasm/crates/cli/src/wasm_generator/dynamic.rs b/JS/wasm/crates/cli/src/wasm_generator/dynamic.rs deleted file mode 100644 index a89455a1e..000000000 --- a/JS/wasm/crates/cli/src/wasm_generator/dynamic.rs +++ /dev/null @@ -1,193 +0,0 @@ -use crate::{exports::Export, js::JS}; - -use super::transform::{self, SourceCodeSection}; -use anyhow::Result; -use walrus::{DataKind, FunctionBuilder, Module, ValType}; - -// Run the calling code with the `dump_wat` feature enabled to print the WAT to stdout -// -// For the example generated WAT, the `bytecode_len` is 137 -// (module -// (type (;0;) (func)) -// (type (;1;) (func (param i32 i32))) -// (type (;2;) (func (param i32 i32 i32 i32))) -// (type (;3;) (func (param i32 i32 i32 i32) (result i32))) -// (import "javy_quickjs_provider_v1" "canonical_abi_realloc" (func (;0;) (type 3))) -// (import "javy_quickjs_provider_v1" "eval_bytecode" (func (;1;) (type 1))) -// (import "javy_quickjs_provider_v1" "memory" (memory (;0;) 0)) -// (import "javy_quickjs_provider_v1" "invoke" (func (;2;) (type 2))) -// (func (;3;) (type 0) -// (local i32 i32) -// i32.const 0 -// i32.const 0 -// i32.const 1 -// i32.const 137 -// call 0 -// local.tee 0 -// i32.const 0 -// i32.const 137 -// memory.init 0 -// data.drop 0 -// i32.const 0 -// i32.const 0 -// i32.const 1 -// i32.const 3 -// call 0 -// local.tee 1 -// i32.const 0 -// i32.const 3 -// memory.init 1 -// data.drop 1 -// local.get 0 -// i32.const 137 -// local.get 1 -// i32.const 3 -// call 2 -// ) -// (func (;4;) (type 0) -// (local i32) -// i32.const 0 -// i32.const 0 -// i32.const 1 -// i32.const 137 -// call 0 -// local.tee 0 -// i32.const 0 -// i32.const 137 -// memory.init 0 -// local.get 0 -// i32.const 137 -// call 1 -// ) -// (export "_start" (func 4)) -// (export "foo" (func 3)) -// (data (;0;) "\02\05\18function.mjs\06foo\0econsole\06log\06bar\0f\bc\03\00\01\00\00\be\03\00\00\0e\00\06\01\a0\01\00\00\00\03\01\01\1a\00\be\03\00\01\08\ea\05\c0\00\e1)8\e0\00\00\00B\e1\00\00\00\04\e2\00\00\00$\01\00)\bc\03\01\04\01\00\07\0a\0eC\06\01\be\03\00\00\00\03\00\00\13\008\e0\00\00\00B\e1\00\00\00\04\df\00\00\00$\01\00)\bc\03\01\02\03]") -// (data (;1;) "foo") -// ) -pub fn generate( - js: &JS, - exported_functions: Vec, - no_source_compression: bool, -) -> Result> { - let mut module = Module::with_config(transform::module_config()); - - const IMPORT_NAMESPACE: &str = "javy_quickjs_provider_v1"; - - let canonical_abi_realloc_type = module.types.add( - &[ValType::I32, ValType::I32, ValType::I32, ValType::I32], - &[ValType::I32], - ); - let (canonical_abi_realloc_fn, _) = module.add_import_func( - IMPORT_NAMESPACE, - "canonical_abi_realloc", - canonical_abi_realloc_type, - ); - - let eval_bytecode_type = module.types.add(&[ValType::I32, ValType::I32], &[]); - let (eval_bytecode_fn, _) = - module.add_import_func(IMPORT_NAMESPACE, "eval_bytecode", eval_bytecode_type); - - let (memory, _) = module.add_import_memory(IMPORT_NAMESPACE, "memory", false, 0, None); - - transform::add_producers_section(&mut module.producers); - if no_source_compression { - module.customs.add(SourceCodeSection::uncompressed(js)?); - } else { - module.customs.add(SourceCodeSection::compressed(js)?); - } - - let bytecode = js.compile()?; - let bytecode_len: i32 = bytecode.len().try_into()?; - let bytecode_data = module.data.add(DataKind::Passive, bytecode); - - let mut main = FunctionBuilder::new(&mut module.types, &[], &[]); - let bytecode_ptr_local = module.locals.add(ValType::I32); - main.func_body() - // Allocate memory in javy_quickjs_provider for bytecode array. - .i32_const(0) // orig ptr - .i32_const(0) // orig size - .i32_const(1) // alignment - .i32_const(bytecode_len) // new size - .call(canonical_abi_realloc_fn) - // Copy bytecode array into allocated memory. - .local_tee(bytecode_ptr_local) // save returned address to local and set as dest addr for mem.init - .i32_const(0) // offset into data segment for mem.init - .i32_const(bytecode_len) // size to copy from data segment - // top-2: dest addr, top-1: offset into source, top-0: size of memory region in bytes. - .memory_init(memory, bytecode_data) - // Evaluate bytecode. - .local_get(bytecode_ptr_local) // ptr to bytecode - .i32_const(bytecode_len) - .call(eval_bytecode_fn); - let main = main.finish(vec![], &mut module.funcs); - - module.exports.add("_start", main); - - if !exported_functions.is_empty() { - let invoke_type = module.types.add( - &[ValType::I32, ValType::I32, ValType::I32, ValType::I32], - &[], - ); - let (invoke_fn, _) = module.add_import_func(IMPORT_NAMESPACE, "invoke", invoke_type); - - let fn_name_ptr_local = module.locals.add(ValType::I32); - for export in exported_functions { - // For each JS function export, add an export that copies the name of the function into memory and invokes it. - let js_export_bytes = export.js.as_bytes(); - let js_export_len: i32 = js_export_bytes.len().try_into().unwrap(); - let fn_name_data = module.data.add(DataKind::Passive, js_export_bytes.to_vec()); - - let mut export_fn = FunctionBuilder::new(&mut module.types, &[], &[]); - export_fn - .func_body() - // Copy bytecode. - .i32_const(0) // orig ptr - .i32_const(0) // orig len - .i32_const(1) // alignment - .i32_const(bytecode_len) // size to copy - .call(canonical_abi_realloc_fn) - .local_tee(bytecode_ptr_local) - .i32_const(0) // offset into data segment - .i32_const(bytecode_len) // size to copy - .memory_init(memory, bytecode_data) // copy bytecode into allocated memory - .data_drop(bytecode_data) - // Copy function name. - .i32_const(0) // orig ptr - .i32_const(0) // orig len - .i32_const(1) // alignment - .i32_const(js_export_len) // new size - .call(canonical_abi_realloc_fn) - .local_tee(fn_name_ptr_local) - .i32_const(0) // offset into data segment - .i32_const(js_export_len) // size to copy - .memory_init(memory, fn_name_data) // copy fn name into allocated memory - .data_drop(fn_name_data) - // Call invoke. - .local_get(bytecode_ptr_local) - .i32_const(bytecode_len) - .local_get(fn_name_ptr_local) - .i32_const(js_export_len) - .call(invoke_fn); - let export_fn = export_fn.finish(vec![], &mut module.funcs); - module.exports.add(&export.wit, export_fn); - } - } - - let wasm = module.emit_wasm(); - print_wat(&wasm)?; - Ok(wasm) -} - -#[cfg(feature = "dump_wat")] -fn print_wat(wasm_binary: &[u8]) -> Result<()> { - println!( - "Generated WAT: \n{}", - wasmprinter::print_bytes(&wasm_binary)? - ); - Ok(()) -} - -#[cfg(not(feature = "dump_wat"))] -fn print_wat(_wasm_binary: &[u8]) -> Result<()> { - Ok(()) -} diff --git a/JS/wasm/crates/cli/src/wasm_generator/mod.rs b/JS/wasm/crates/cli/src/wasm_generator/mod.rs deleted file mode 100644 index 186453527..000000000 --- a/JS/wasm/crates/cli/src/wasm_generator/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod dynamic; -pub mod r#static; -mod transform; diff --git a/JS/wasm/crates/cli/src/wasm_generator/static.rs b/JS/wasm/crates/cli/src/wasm_generator/static.rs deleted file mode 100644 index 14440b494..000000000 --- a/JS/wasm/crates/cli/src/wasm_generator/static.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::{collections::HashMap, rc::Rc, sync::OnceLock}; - -use anyhow::{anyhow, Result}; -use binaryen::{CodegenConfig, Module}; -use walrus::{DataKind, ExportItem, FunctionBuilder, FunctionId, MemoryId, ValType}; -use wasi_common::{pipe::ReadPipe, WasiCtx}; -use wasmtime::Linker; -use wasmtime_wasi::WasiCtxBuilder; -use wizer::Wizer; - -use crate::{exports::Export, js::JS}; - -use super::transform::{self, SourceCodeSection}; - -static mut WASI: OnceLock = OnceLock::new(); - -pub fn generate(js: &JS, exports: Vec, no_source_compression: bool) -> Result> { - let wasm = include_bytes!(concat!(env!("OUT_DIR"), "/engine.wasm")); - - let wasi = WasiCtxBuilder::new() - .stdin(Box::new(ReadPipe::from(js.as_bytes()))) - .inherit_stdout() - .inherit_stderr() - .build(); - // We can't move the WasiCtx into `make_linker` since WasiCtx doesn't implement the `Copy` trait. - // So we move the WasiCtx into a mutable static OnceLock instead. - // Setting the value in the `OnceLock` and getting the reference back from it should be safe given - // we're never executing this code concurrently. This code will also fail if `generate` is invoked - // more than once per execution. - if unsafe { WASI.set(wasi) }.is_err() { - panic!("Failed to set WASI static variable") - } - - let wasm = Wizer::new() - .make_linker(Some(Rc::new(|engine| { - let mut linker = Linker::new(engine); - wasmtime_wasi::add_to_linker(&mut linker, |_ctx: &mut Option| { - unsafe { WASI.get_mut() }.unwrap() - })?; - Ok(linker) - })))? - .wasm_bulk_memory(true) - .run(wasm) - .map_err(|_| anyhow!("JS compilation failed"))?; - - let mut module = transform::module_config().parse(&wasm)?; - - let (realloc, free, invoke, memory) = { - let mut exports = HashMap::new(); - for export in module.exports.iter() { - exports.insert(export.name.as_str(), export); - } - ( - *exports.get("canonical_abi_realloc").unwrap(), - *exports.get("canonical_abi_free").unwrap(), - *exports.get("javy.invoke").unwrap(), - *exports.get("memory").unwrap(), - ) - }; - - let realloc_export = realloc.id(); - let free_export = free.id(); - let invoke_export = invoke.id(); - - if !exports.is_empty() { - let ExportItem::Function(realloc_fn) = realloc.item else { - unreachable!() - }; - let ExportItem::Function(invoke_fn) = invoke.item else { - unreachable!() - }; - let ExportItem::Memory(memory) = memory.item else { - unreachable!() - }; - export_exported_js_functions(&mut module, realloc_fn, invoke_fn, memory, exports); - } - - // We no longer need these exports so remove them. - module.exports.delete(realloc_export); - module.exports.delete(free_export); - module.exports.delete(invoke_export); - - let wasm = module.emit_wasm(); - - let codegen_cfg = CodegenConfig { - optimization_level: 3, // Aggressively optimize for speed. - shrink_level: 0, // Don't optimize for size at the expense of performance. - debug_info: false, - }; - - let mut module = Module::read(&wasm) - .map_err(|_| anyhow!("Unable to read wasm binary for wasm-opt optimizations"))?; - module.optimize(&codegen_cfg); - module - .run_optimization_passes(vec!["strip"], &codegen_cfg) - .map_err(|_| anyhow!("Running wasm-opt optimization passes failed"))?; - let wasm = module.write(); - - let mut module = transform::module_config().parse(&wasm)?; - if no_source_compression { - module.customs.add(SourceCodeSection::uncompressed(js)?); - } else { - module.customs.add(SourceCodeSection::compressed(js)?); - } - transform::add_producers_section(&mut module.producers); - Ok(module.emit_wasm()) -} - -fn export_exported_js_functions( - module: &mut walrus::Module, - realloc_fn: FunctionId, - invoke_fn: FunctionId, - memory: MemoryId, - js_exports: Vec, -) { - let ptr_local = module.locals.add(ValType::I32); - for export in js_exports { - println!("Exporting JS function: {}", export.js); - // For each JS function export, add an export that copies the name of the function into memory and invokes it. - let js_export_bytes = export.js.as_bytes(); - let js_export_len: i32 = js_export_bytes.len().try_into().unwrap(); - let fn_name_data = module.data.add(DataKind::Passive, js_export_bytes.to_vec()); - - let mut export_fn = FunctionBuilder::new(&mut module.types, &[], &[]); - export_fn - .func_body() - .i32_const(0) // orig ptr - .i32_const(0) // orig len - .i32_const(1) // alignment - .i32_const(js_export_len) // new size - .call(realloc_fn) - .local_tee(ptr_local) - .i32_const(0) // offset into data segment - .i32_const(js_export_len) // size to copy - .memory_init(memory, fn_name_data) // copy fn name into allocated memory - .data_drop(fn_name_data) - .local_get(ptr_local) - .i32_const(js_export_len) - .call(invoke_fn); - let export_fn = export_fn.finish(vec![], &mut module.funcs); - module.exports.add(&export.wit, export_fn); - } -} diff --git a/JS/wasm/crates/cli/src/wasm_generator/transform.rs b/JS/wasm/crates/cli/src/wasm_generator/transform.rs deleted file mode 100644 index 68023dcbd..000000000 --- a/JS/wasm/crates/cli/src/wasm_generator/transform.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::borrow::Cow; - -use anyhow::Result; -use walrus::{CustomSection, IdsToIndices, ModuleConfig, ModuleProducers}; - -use crate::js::JS; - -#[derive(Debug)] -pub struct SourceCodeSection { - source_code: Vec, -} - -impl SourceCodeSection { - pub fn compressed(js: &JS) -> Result { - Ok(SourceCodeSection { - source_code: js.compress()?, - }) - } - - pub fn uncompressed(js: &JS) -> Result { - Ok(SourceCodeSection { - source_code: js.as_bytes().to_vec(), - }) - } -} - -impl CustomSection for SourceCodeSection { - fn name(&self) -> &str { - "javy_source" - } - - fn data(&self, _ids_to_indices: &IdsToIndices) -> Cow<[u8]> { - (&self.source_code).into() - } -} - -pub fn module_config() -> ModuleConfig { - let mut config = ModuleConfig::new(); - config.generate_name_section(false); - config -} - -pub fn add_producers_section(producers: &mut ModuleProducers) { - producers.clear(); // removes Walrus and Rust - producers.add_language("JavaScript", "ES2020"); - producers.add_processed_by("Javy", env!("CARGO_PKG_VERSION")); -} diff --git a/JS/wasm/crates/cli/src/wit.rs b/JS/wasm/crates/cli/src/wit.rs deleted file mode 100644 index 027dc22da..000000000 --- a/JS/wasm/crates/cli/src/wit.rs +++ /dev/null @@ -1,35 +0,0 @@ -use std::path::Path; - -use anyhow::{bail, Result}; - -use wit_parser::{Resolve, UnresolvedPackage, WorldItem}; - -pub fn parse_exports(wit: impl AsRef, world: &str) -> Result> { - let mut resolve = Resolve::default(); - let package = UnresolvedPackage::parse_path(wit.as_ref())?; - resolve.push(package)?; - let (_, package_id) = resolve.package_names.first().unwrap(); - let world_id = resolve.select_world(*package_id, Some(world))?; - let world = resolve.worlds.get(world_id).unwrap(); - - if !world.imports.is_empty() { - bail!("Imports in WIT file are not supported"); - } - let mut exported_functions = vec![]; - for (_, export) in &world.exports { - match export { - WorldItem::Interface(_) => bail!("Exported interfaces are not supported"), - WorldItem::Function(f) => { - if !f.params.is_empty() { - bail!("Exported functions with parameters are not supported") - } else if f.results.len() != 0 { - bail!("Exported functions with return values are not supported") - } else { - exported_functions.push(f.name.clone()) - } - } - WorldItem::Type(_) => bail!("Exported types are not supported"), - } - } - Ok(exported_functions) -} From e7afa1f763951adcdb1402854cdcdba9f90509ec Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Tue, 7 May 2024 17:03:12 +0530 Subject: [PATCH 02/13] Update wizer and compatible wasmtime --- Cargo.toml | 10 ++++++---- JS/wasi_snapshot_preview1.reactor.wasm | Bin 0 -> 81039 bytes JS/wasm/crates/serve/Cargo.toml | 8 +++++--- 3 files changed, 11 insertions(+), 7 deletions(-) create mode 100644 JS/wasi_snapshot_preview1.reactor.wasm diff --git a/Cargo.toml b/Cargo.toml index 535224e5c..875ab0861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,15 +13,17 @@ edition = "2021" version = "0.0.1" [workspace.dependencies] -wizer = "4.0.0" -wasmtime = "16" -wasmtime-wasi = "16" -wasi-common = "16" +wizer = "6.0.0" +wasmtime = { features = ["async"], version = "19" } +wasmtime-wasi = "19" javy = { version = "2.1.0" } anyhow = "1.0.79" once_cell = "1.19.0" serde = { version = "1", features = ["derive"] } serde_json = "1" +serde_bytes = "0.11" +http = "1.1.0" + [profile.release] lto = true opt-level = 's' diff --git a/JS/wasi_snapshot_preview1.reactor.wasm b/JS/wasi_snapshot_preview1.reactor.wasm new file mode 100644 index 0000000000000000000000000000000000000000..8149eb955a57c9f93ab39fc2ecf4b57bf0b161f0 GIT binary patch literal 81039 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 Date: Tue, 7 May 2024 17:08:42 +0530 Subject: [PATCH 03/13] wizen input js file and optimize it with binaryen --- JS/wasm/crates/cli/Cargo.toml | 1 + JS/wasm/crates/cli/build.rs | 6 ++++-- JS/wasm/crates/cli/src/main.rs | 10 +++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/JS/wasm/crates/cli/Cargo.toml b/JS/wasm/crates/cli/Cargo.toml index 4948e1650..de325a491 100644 --- a/JS/wasm/crates/cli/Cargo.toml +++ b/JS/wasm/crates/cli/Cargo.toml @@ -2,6 +2,7 @@ name = "cli" edition.workspace = true version.workspace = true +build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/JS/wasm/crates/cli/build.rs b/JS/wasm/crates/cli/build.rs index 925eb8fdd..9d2752189 100644 --- a/JS/wasm/crates/cli/build.rs +++ b/JS/wasm/crates/cli/build.rs @@ -53,6 +53,7 @@ fn copy_javy_core() -> Result<()> { .unwrap() .join("../../target/wasm32-wasi/debug/") } else { + println!("cargo:warning=using release build for arakoo_js_engine..."); PathBuf::from(&cargo_manifest_dir) .parent() .unwrap() @@ -72,12 +73,12 @@ fn copy_javy_core() -> Result<()> { // .run(read_file(&quickjs_provider_path)?.as_slice())?; // fs::File::create(&quickjs_provider_wizened_path)?.write_all(&wizened)?; - // println!("cargo:rerun-if-changed={}", engine_path.to_str().unwrap()); + println!("cargo:rerun-if-changed={}", engine_path.to_str().unwrap()); // println!( // "cargo:rerun-if-changed={}", // quickjs_provider_path.to_str().unwrap() // ); - // println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=build.rs"); if engine_path.exists() { let out_dir = env::var("OUT_DIR")?; @@ -85,6 +86,7 @@ fn copy_javy_core() -> Result<()> { // let copied_provider_path = Path::new(&out_dir).join("provider.wasm"); fs::copy(&engine_path, copied_engine_path)?; + println!("cargo:warning=copied engine.wasm to OUT_DIR"); // fs::copy(&quickjs_provider_wizened_path, copied_provider_path)?; } Ok(()) diff --git a/JS/wasm/crates/cli/src/main.rs b/JS/wasm/crates/cli/src/main.rs index eb60b643c..37816c94c 100644 --- a/JS/wasm/crates/cli/src/main.rs +++ b/JS/wasm/crates/cli/src/main.rs @@ -30,8 +30,12 @@ fn main() -> Result<()> { env::remove_var("EDECHAINS_JS_WIZEN"); println!("\nStarting to build arakoo compatible module"); - - let wasm: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/engine.wasm")); + let wasm: Vec = if let Ok(wasm_bytes) = std::fs::read(concat!(env!("OUT_DIR"), "/engine.wasm")) { + wasm_bytes + } else { + // Provide a fallback wasm binary if the file is not found + panic!("Engine wasm not found"); + }; println!("Preinitializing using Wizer"); @@ -39,7 +43,7 @@ fn main() -> Result<()> { .allow_wasi(true)? .inherit_stdio(true) .wasm_bulk_memory(true) - .run(wasm)?; + .run(wasm.as_slice())?; let codegen_config = CodegenConfig { optimization_level: 3, From 44d547259b81301c50feaacaf1147316f200c141 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Tue, 7 May 2024 17:12:32 +0530 Subject: [PATCH 04/13] Migrate Guest(wasm engine) to component model and support hono --- JS/wasm/crates/apis/Cargo.toml | 5 + JS/wasm/crates/apis/src/fetch/mod.rs | 86 +++++++ JS/wasm/crates/apis/src/http/mod.rs | 186 +-------------- JS/wasm/crates/apis/src/http/shims/index.js | 156 ++++++------ JS/wasm/crates/apis/src/lib.rs | 27 ++- JS/wasm/crates/arakoo-core/Cargo.toml | 5 +- JS/wasm/crates/arakoo-core/src/lib.rs | 251 ++++++++++++++++++-- JS/wasm/wit/arakoo.wit | 8 +- JS/wasm/wit/http-types.wit | 87 ++++--- JS/wasm/wit/http.wit | 5 + 10 files changed, 477 insertions(+), 339 deletions(-) create mode 100644 JS/wasm/crates/apis/src/fetch/mod.rs create mode 100644 JS/wasm/wit/http.wit diff --git a/JS/wasm/crates/apis/Cargo.toml b/JS/wasm/crates/apis/Cargo.toml index df4850da1..d2d419c48 100644 --- a/JS/wasm/crates/apis/Cargo.toml +++ b/JS/wasm/crates/apis/Cargo.toml @@ -19,4 +19,9 @@ javy = { workspace = true } tokio = "1.36.0" serde = { workspace = true } serde_json = { workspace = true } +serde_bytes = { workspace = true } +http = { workspace = true } quickjs-wasm-rs = "3.0.0" +wit-bindgen = "0.24.0" +bytes = { version = "1.6.0", features = ["serde"] } + diff --git a/JS/wasm/crates/apis/src/fetch/mod.rs b/JS/wasm/crates/apis/src/fetch/mod.rs new file mode 100644 index 000000000..aa64ec7e7 --- /dev/null +++ b/JS/wasm/crates/apis/src/fetch/mod.rs @@ -0,0 +1,86 @@ +use anyhow::{anyhow, Result}; +use http::{request, HeaderName, HeaderValue}; +use quickjs_wasm_rs::{JSContextRef, JSValueRef, Serializer}; +use serde_bytes::ByteBuf; +use crate::{types::{HttpRequest, HttpResponse}, JSApiSet}; +use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; +use quickjs_wasm_rs::Deserializer; +use serde::{Deserialize, Serialize}; +use std::ops::Deref; + +mod outbound_http; + + +pub(super) struct Fetch; + +impl JSApiSet for Fetch { + fn register(&self, runtime: &javy::Runtime, _config: &crate::APIConfig) -> Result<()> { + let context = runtime.context(); + let global = context.global_object()?; + global.set_property("arakoo", context.value_from_bool(true)?)?; + + global.set_property( + "__internal_http_send", + context.wrap_callback( + |context: &JSContextRef, _this: JSValueRef<'_>, args: &[JSValueRef<'_>]| { + send_http_request(context, &_this, args) + }, + )?, + )?; + + Ok(()) + } +} + +fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValueRef]) -> Result { + match args { + [request] => { + let deserializer = &mut Deserializer::from(request.clone()); + let request = HttpRequest::deserialize(deserializer)?; + + let mut builder = request::Builder::new() + .method(request.method.deref()) + .uri(request.uri.deref()); + + if let Some(headers) = builder.headers_mut() { + for (key, value) in &request.headers { + headers.insert( + HeaderName::from_bytes(key.as_bytes())?, + HeaderValue::from_bytes(value.as_bytes())?, + ); + } + } + + let outbound_request = builder.body(request.body.map(|buffer| buffer.into_vec().into())).unwrap(); + + let response = outbound_http::send_request( + outbound_request + )?; + + let response = HttpResponse { + status: response.status().as_u16(), + headers: response + .headers() + .iter() + .map(|(key, value)| { + Ok(( + key.as_str().to_owned(), + str::from_utf8(value.as_bytes())?.to_owned(), + )) + }) + .collect::>()?, + body: response + .into_body() + .map(|bytes| ByteBuf::from(bytes.deref())), + status_text: response.status().canonical_reason().unwrap_or("").to_owned(), + }; + + let mut serializer = Serializer::from_context(context)?; + response.serialize(&mut serializer)?; + Ok(serializer.value) + } + + _ => Err(anyhow!("expected 1 argument, got {}", args.len())), + } +} + diff --git a/JS/wasm/crates/apis/src/http/mod.rs b/JS/wasm/crates/apis/src/http/mod.rs index aaa0f6781..6477ec0cc 100644 --- a/JS/wasm/crates/apis/src/http/mod.rs +++ b/JS/wasm/crates/apis/src/http/mod.rs @@ -1,195 +1,17 @@ pub mod types; -use std::collections::HashMap; - use anyhow::Result; -use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; +use crate::JSApiSet; + +// use crate::{fetch, get_response, get_response_len, http::types::Request, JSApiSet}; + -use crate::{fetch, get_response, get_response_len, http::types::Request, JSApiSet}; pub(super) struct Http; impl JSApiSet for Http { fn register(&self, runtime: &javy::Runtime, _config: &crate::APIConfig) -> Result<()> { let context = runtime.context(); context.eval_global("http.js", include_str!("shims/dist/index.js"))?; - let global = context.global_object()?; - global.set_property("arakoo", context.value_from_bool(true)?)?; - global.set_property("fetch_internal", context.wrap_callback(fetch_callback())?)?; - // global.set_property( - // "__internal_http_send", - // context.wrap_callback( - // |context: &JSContextRef, _this: JSValueRef<'_>, args: &[JSValueRef<'_>]| { - // send_http_request(context, &_this, args) - // }, - // )?, - // )?; Ok(()) } } - -fn fetch_callback( -) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { - move |_ctx, _this, args| { - if args.len() < 1 { - return Err(anyhow::anyhow!( - "Expected at least 1 argument, got {}", - args.len() - )); - } - let uri = args.get(0).unwrap().to_string(); - let opts: HashMap = args[1].try_into()?; - let method = opts.get("method").unwrap_or(&"GET".into()).to_string(); - let headers = match opts.get("headers") { - Some(JSValue::Object(headers)) => headers - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - _ => HashMap::default(), - }; - let body = opts.get("body").unwrap_or(&"".into()).to_string(); - let params = match opts.get("params") { - Some(JSValue::Object(params)) => params - .iter() - .map(|(k, v)| (k.to_string(), v.to_string())) - .collect(), - _ => HashMap::default(), - }; - - let request = - serde_json::to_string(&Request::new(uri, method, headers, body, params)).unwrap(); - let request_ptr = request.as_ptr(); - let request_len = request.len() as i32; - unsafe { fetch(request_ptr, request_len) } - let response_len = unsafe { get_response_len() }; - let mut response_buffer = Vec::with_capacity(response_len as usize); - let response_ptr = response_buffer.as_mut_ptr(); - let response_buffer = unsafe { - get_response(response_ptr); - Vec::from_raw_parts(response_ptr, response_len as usize, response_len as usize) - }; - let response: serde_json::Value = match serde_json::from_slice(&response_buffer) { - Ok(response) => response, - Err(e) => { - eprintln!("Failed to parse fetch response: {}", e); - return Err(anyhow::anyhow!( - "Failed to parse fetch response: {}", - e.to_string() - )); - } - }; - let mut headers_map: HashMap = HashMap::new(); - for (key, value) in response["headers"].as_object().unwrap() { - headers_map.insert( - key.to_string(), - JSValue::String(value.as_str().unwrap().to_string()), - ); - } - let mut response_map: HashMap = HashMap::new(); - response_map.insert( - "status".to_string(), - JSValue::Int(response["status"].as_i64().unwrap().try_into().unwrap()), - ); - response_map.insert( - "statusText".to_string(), - JSValue::String(response["statusText"].as_str().unwrap().to_string()), - ); - response_map.insert( - "body".to_string(), - JSValue::String(response["body"].as_str().unwrap().to_string()), - ); - response_map.insert("headers".to_string(), JSValue::Object(headers_map)); - let response_obj = JSValue::Object(response_map); - // todo!("fetch"); - Ok(response_obj) - } -} - -// fn send_http_request(context: &Context, _this: &Value, args: &[Value]) -> Result { -// match args { -// [request] => { -// let deserializer = &mut Deserializer::from(request.clone()); -// let request = HttpRequest::deserialize(deserializer)?; - -// let mut builder = request::Builder::new() -// .method(request.method.deref()) -// .uri(request.uri.deref()); - -// if let Some(headers) = builder.headers_mut() { -// for (key, value) in &request.headers { -// headers.insert( -// HeaderName::from_bytes(key.as_bytes())?, -// HeaderValue::from_bytes(value.as_bytes())?, -// ); -// } -// } - -// let response = arakoo_send_request( -// builder.body(request.body.map(|buffer| buffer.into_vec().into()))?, -// )?; - -// let response = HttpResponse { -// status: response.status().as_u16(), -// headers: response -// .headers() -// .iter() -// .map(|(key, value)| { -// Ok(( -// key.as_str().to_owned(), -// str::from_utf8(value.as_bytes())?.to_owned(), -// )) -// }) -// .collect::>()?, -// body: response -// .into_body() -// .map(|bytes| ByteBuf::from(bytes.deref())), -// }; - -// let mut serializer = Serializer::from_context(context)?; -// response.serialize(&mut serializer)?; -// Ok(serializer.value) -// } - -// _ => Err(anyhow!("expected 1 argument, got {}", args.len())), -// } -// } - -// pub fn arakoo_send_request(req: HttpRequest) -> Result { -// let (req, body) = req.into_parts(); - -// let method = req.method.try_into()?; - -// let uri = req.uri.to_string(); - -// let params = vec![]; - -// let headers = &req -// .headers -// .iter() -// .map(try_header_to_strs) -// .collect::>>()?; - -// let body = body.as_ref().map(|bytes| bytes.as_ref()); - -// let out_req = OutboundRequest { -// method, -// uri: &uri, -// params: ¶ms, -// headers, -// body, -// }; - -// let OutboundResponse { -// status, -// headers, -// body, -// } = spin_http::send_request(out_req)?; - -// let resp_builder = http_types::response::Builder::new().status(status); -// let resp_builder = headers -// .into_iter() -// .flatten() -// .fold(resp_builder, |b, (k, v)| b.header(k, v)); -// resp_builder -// .body(body.map(Into::into)) -// .map_err(|_| OutboundHttpError::RuntimeError) -// } \ No newline at end of file diff --git a/JS/wasm/crates/apis/src/http/shims/index.js b/JS/wasm/crates/apis/src/http/shims/index.js index 764b334fb..ee209c758 100644 --- a/JS/wasm/crates/apis/src/http/shims/index.js +++ b/JS/wasm/crates/apis/src/http/shims/index.js @@ -167,10 +167,13 @@ class Headers { class Request { constructor(input) { - this.url = input.url; + // console.log("In constructor of request input body len",input.body.byteLength); + this.url = input.uri; this.method = input.method; this.headers = new Headers(input.headers || {}); - this.body = input.body; + let bodyArray = new Uint8Array(input.body); + let bodyString = decoder.decode(bodyArray); + this.body = JSON.parse(bodyString); this.params = input.params || {}; this.geo = input.geo || {}; } @@ -267,45 +270,45 @@ class Response { return this.body; } } -let handlerFunction; -globalThis.addEventListener = (_eventName, handler) => { - handlerFunction = handler; -}; +// let handlerFunction; +// globalThis.addEventListener = (_eventName, handler) => { +// handlerFunction = handler; +// }; -const requestToHandler = (input) => { - const request = new Request(input); - const event = { - request, - response: {}, - respondWith(res) { - this.response = res; - }, - }; +// const requestToHandler = (input) => { +// const request = new Request(input); +// const event = { +// request, +// response: {}, +// respondWith(res) { +// this.response = res; +// }, +// }; + +// try { +// handlerFunction(event); + +// Promise.resolve(event.response) +// .then((res) => { +// console.log("res: ", res); +// result = { +// body: res.body, +// headers: res.headers.headers, +// status: res.status, +// statusText: res.statusText, +// }; +// }) +// .catch((err) => { +// error = `err: \n${err}`; +// }); +// } catch (err) { +// error = `err: ${err}\n${err.stack}`; +// } +// }; - try { - handlerFunction(event); - - Promise.resolve(event.response) - .then((res) => { - console.log("res: ", res); - result = { - body: res.body, - headers: res.headers.headers, - status: res.status, - statusText: res.statusText, - }; - }) - .catch((err) => { - error = `err: \n${err}`; - }); - } catch (err) { - error = `err: ${err}\n${err.stack}`; - } -}; - -globalThis.entrypoint = requestToHandler; -globalThis.result = {}; -globalThis.error = null; +// globalThis.entrypoint = requestToHandler; +// globalThis.result = {}; +// globalThis.error = null; // globalThis.fetch = async (resource, options = { method: "GET" }) => { // let response = await fetch_internal(resource, options); @@ -322,36 +325,49 @@ function encodeBody(body) { } } - -globalThis.fetch = async (uri, options) => { - let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer - let fetchOptions = { - method: (options && options.method) || "GET", - uri: (uri instanceof URL) ? uri.toString() : uri, - headers: (options && options.headers) || {}, - body: encodedBodyData, - }; - console.log(JSON.stringify(fetchOptions)) - const { status, headers, body } = __internal_http_send({ - method: (options && options.method) || "GET", - uri: (uri instanceof URL) ? uri.toString() : uri, - headers: (options && options.headers) || {}, - body: encodedBodyData, - }) - return Promise.resolve({ - status, - headers: { - entries: () => Object.entries(headers || {}), - get: (key) => (headers && headers[key]) || null, - has: (key) => (headers && headers[key]) ? true : false +globalThis.requestToEvent = (inputReq) => { + const request = new Request(inputReq); + const event = { + request, + response: {}, + respondWith(res) { + console.log("Response recieved ",res); + this.response = res; }, - arrayBuffer: () => Promise.resolve(body), - ok: (status > 199 && status < 300), - statusText: statusTextList[status], - text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), - json: () => { - let text = new TextDecoder().decode(body || new Uint8Array()) - return Promise.resolve(JSON.parse(text)) - } - }) + }; + console.log("event: ", JSON.stringify(event)) + return event; } + +// globalThis.fetch = async (uri, options) => { +// let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer +// let fetchOptions = { +// method: (options && options.method) || "GET", +// uri: (uri instanceof URL) ? uri.toString() : uri, +// headers: (options && options.headers) || {}, +// body: encodedBodyData, +// }; +// console.log(JSON.stringify(fetchOptions)) +// const { status, headers, body } = __internal_http_send({ +// method: (options && options.method) || "GET", +// uri: (uri instanceof URL) ? uri.toString() : uri, +// headers: (options && options.headers) || {}, +// body: encodedBodyData, +// }) +// return Promise.resolve({ +// status, +// headers: { +// entries: () => Object.entries(headers || {}), +// get: (key) => (headers && headers[key]) || null, +// has: (key) => (headers && headers[key]) ? true : false +// }, +// arrayBuffer: () => Promise.resolve(body), +// ok: (status > 199 && status < 300), +// statusText: statusTextList[status], +// text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), +// json: () => { +// let text = new TextDecoder().decode(body || new Uint8Array()) +// return Promise.resolve(JSON.parse(text)) +// } +// }) +// } diff --git a/JS/wasm/crates/apis/src/lib.rs b/JS/wasm/crates/apis/src/lib.rs index 357a34106..1003996d2 100644 --- a/JS/wasm/crates/apis/src/lib.rs +++ b/JS/wasm/crates/apis/src/lib.rs @@ -62,20 +62,21 @@ mod stream_io; mod text_encoding; pub mod http; -mod jsonnet; +mod pdfparse; -#[link(wasm_import_module = "arakoo")] -extern "C" { - fn jsonnet_evaluate(var_ptr: *const u8, var_len: i32, code_ptr: *const u8, code_len: i32); - fn jsonnet_evaluate_file(var_ptr: *const u8, var_len: i32, path_ptr: *const u8, path_len: i32); - fn jsonnet_output_len() -> i32; - fn jsonnet_output(ptr: *mut u8); - fn fetch(request_pointer: *const u8, request_len: i32); - fn get_response_len() -> i32; - fn get_response(ptr: *mut u8); -} +// mod jsonnet; + +// #[link(wasm_import_module = "arakoo")] +// extern "C" { +// fn jsonnet_evaluate(var_ptr: *const u8, var_len: i32, code_ptr: *const u8, code_len: i32); +// fn jsonnet_evaluate_file(var_ptr: *const u8, var_len: i32, path_ptr: *const u8, path_len: i32); +// fn jsonnet_output_len() -> i32; +// fn jsonnet_output(ptr: *mut u8); +// fn fetch(request_pointer: *const u8, request_len: i32); +// fn get_response_len() -> i32; +// fn get_response(ptr: *mut u8); +// } -mod pdfparse; pub(crate) trait JSApiSet { fn register(&self, runtime: &Runtime, config: &APIConfig) -> Result<()>; @@ -102,7 +103,7 @@ pub fn add_to_runtime(runtime: &Runtime, config: APIConfig) -> Result<()> { #[cfg(feature = "text_encoding")] text_encoding::TextEncoding.register(runtime, &config)?; http::Http.register(runtime, &config)?; - jsonnet::Jsonnet.register(runtime, &config)?; + // jsonnet::Jsonnet.register(runtime, &config)?; pdfparse::PDFPARSER.register(runtime, &config)?; Ok(()) } diff --git a/JS/wasm/crates/arakoo-core/Cargo.toml b/JS/wasm/crates/arakoo-core/Cargo.toml index 4745f1a9a..b0b6767fd 100644 --- a/JS/wasm/crates/arakoo-core/Cargo.toml +++ b/JS/wasm/crates/arakoo-core/Cargo.toml @@ -17,4 +17,7 @@ apis = { path = "../apis", features = [ "stream_io", ] } serde_json = { workspace = true } -wit-bindgen = "0.24.0" \ No newline at end of file +serde = { workspace = true } +serde_bytes = { workspace = true } +send_wrapper = "0.6.0" +wit-bindgen = "0.24.0" diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index 15ec0fbc7..6c82bc16a 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -1,38 +1,106 @@ +use anyhow::anyhow; +use anyhow::Result; +use javy::quickjs::from_qjs_value; +use javy::quickjs::to_qjs_value; +use javy::quickjs::Deserializer; +use javy::quickjs::JSContextRef; +use javy::quickjs::JSValue; +use javy::quickjs::JSValueRef; use javy::Runtime; use once_cell::sync::OnceCell; +use send_wrapper::SendWrapper; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use std::io; use std::io::Read; -use std::slice; -use std::str; +use std::ops::Deref; +use std::sync::Mutex; -// mod execution; -mod runtime; +use serde_bytes::ByteBuf; + +#[derive(Serialize, Deserialize, Debug)] +pub struct HttpRequest { + pub method: String, + pub uri: String, + #[serde(default)] + pub headers: HashMap, + pub params: HashMap, + pub body: Option, +} -// mod wit { -// use wit_bindgen::generate; +#[derive(Serialize, Deserialize, Debug)] +pub struct HttpResponse { + pub status: u16, + #[serde(default)] + pub headers: HashMap, + pub body: Option, + pub status_text: String, -// use crate::Guest; +} -// generate!({ -// path:"../../wit/demo.wit", -// world:"arakoo" -// }); +pub mod wit { + use wit_bindgen::generate; -// export!(Guest); -// } + generate!({ + path:"../../wit", + world:"reactor", + }); -// struct Guest; + use super::Guest; + export!(Guest); + pub use self::arakoo::edgechains::http_types::{Method, Request, Response}; + pub use self::exports::arakoo::edgechains::inbound_http; + pub use self::arakoo::edgechains; +} -// impl wit::exports::arakoo::demo::guest::Guest for Guest { -// fn get_data() -> u8 { -// 105 -// } -// } + +struct Guest; + +// mod execution; +mod runtime; // const FUNCTION_MODULE_NAME: &str = "function.mjs"; // static mut COMPILE_SRC_RET_AREA: [u32; 2] = [0; 2]; -static mut RUNTIME: OnceCell = OnceCell::new(); +// static mut RUNTIME: OnceCell = OnceCell::new(); +static CONTEXT: OnceCell> = OnceCell::new(); +static HANDLER: OnceCell> = OnceCell::new(); +static GLOBAL: OnceCell> = OnceCell::new(); +static mut RUNTIME_INSTANCE: Option = None; +static ON_RESOLVE: OnceCell> = OnceCell::new(); +static ON_REJECT: OnceCell> = OnceCell::new(); +static RESPONSE: Mutex> = Mutex::new(None); +static EXCEPTION: Mutex> = Mutex::new(None); + +fn on_resolve(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { + // (*args).clone_into(&mut cloned_args); + let mut qjs_value = Option::None; + if args.len() > 0 { + for arg in args { + qjs_value = Some(from_qjs_value(*arg).unwrap()); + // println!("Arg resolve: {:?}", qjs_value.as_ref().unwrap()); + } + RESPONSE.lock().unwrap().replace(qjs_value.unwrap()); + Ok(JSValue::Undefined) + } else { + Err(anyhow!("expected 1 argument, got {}", args.len())) + } +} + +fn on_reject(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { + // (*args).clone_into(&mut cloned_args); + let mut qjs_value = Option::None; + if (args.len() > 0) { + for arg in args { + qjs_value = Some(from_qjs_value(*arg).unwrap()); + println!("Arg reject : {:?}", qjs_value.as_ref().unwrap()); + } + EXCEPTION.lock().unwrap().replace(qjs_value.unwrap()); + Ok(JSValue::Undefined) + } else { + Err(anyhow!("expected 1 argument, got {}", args.len())) + } +} /// Used by Wizer to preinitialize the module #[export_name = "wizer.initialize"] @@ -40,17 +108,150 @@ pub extern "C" fn init() { let mut contents = String::new(); let res = io::stdin().read_to_string(&mut contents); match res { - Ok(_) => (), - Err(err) => println!("Error : no input file specified or corrupted input js file supplied \n{}", err), + Ok(len) => println!("Read {} bytes", len), + Err(err) => println!( + "Error : no input file specified or corrupted input js file supplied \n{}", + err + ), } // println!("Contents : {}",contents); - let runtime = runtime::new_runtime().unwrap(); + unsafe { + if RUNTIME_INSTANCE.is_none() { + RUNTIME_INSTANCE = Some(runtime::new_runtime().unwrap()); + } + } + let runtime = unsafe { RUNTIME_INSTANCE.as_ref().unwrap() }; let context = runtime.context(); - match context.eval_global("function.js", &contents) { + CONTEXT.set(SendWrapper::new(context)).unwrap(); + match context.eval_global("javascriptCode.js", &contents) { Ok(_) => (), Err(err) => println!("Error in evaluating script function.js : {:?}", err), }; - unsafe { RUNTIME.set(runtime).unwrap() }; + + let global = context + .global_object() + .expect("Unable to get global object"); + GLOBAL.set(SendWrapper::new(global)).unwrap(); + + let hono = global.get_property("_export").unwrap(); + let handle_event = hono.get_property("fetch").expect("Hono app not exported"); + HANDLER.set(SendWrapper::new(handle_event)).unwrap(); + + let on_resolve = context.wrap_callback(on_resolve).unwrap(); + ON_RESOLVE.set(SendWrapper::new(on_resolve)).unwrap(); + let on_reject = context.wrap_callback(on_reject).unwrap(); + ON_REJECT.set(SendWrapper::new(on_reject)).unwrap(); +} + +impl wit::inbound_http::Guest for Guest { + fn handle_request(req: wit::Request) -> wit::Response { + println!("{:?}", req); + let context = **CONTEXT.get().unwrap(); + let mut serializer = + javy::quickjs::Serializer::from_context(context).expect("Unable to create serializer"); + let handler = **HANDLER.get().unwrap(); + let request = HttpRequest { + method: match req.method { + wit::Method::Get => "GET".to_string(), + wit::Method::Post => "POST".to_string(), + wit::Method::Put => "PUT".to_string(), + wit::Method::Delete => "DELETE".to_string(), + wit::Method::Patch => "PATCH".to_string(), + wit::Method::Head => "HEAD".to_string(), + wit::Method::Options => "OPTIONS".to_string(), + }, + uri: req.uri, + headers: req + .headers + .iter() + .map(|(k, v)| Ok((k.as_str().to_owned(), v.as_str().to_owned()))) + .collect::>>() + .unwrap(), + params: req + .params + .iter() + .map(|(k, v)| Ok((k.as_str().to_owned(), v.as_str().to_owned()))) + .collect::>>() + .unwrap(), + body: req.body.map(|bytes| ByteBuf::from::>(bytes)), + }; + // let hono_event = + // hono_event.serialize(&mut serializer).unwrap(); + request + .serialize(&mut serializer) + .expect("unable to serialize httprequest"); + let request_value = serializer.value; + // println!("body of httpRequest : {:?}", from_qjs_value(request_value).unwrap()); + let global = GLOBAL.get().unwrap() ; + let request_to_event = global + .get_property("requestToEvent") + .expect("Unable to get requestToEvent"); + let event = request_to_event + .call(global, &[request_value]) + .expect("Unable to call requestToEvent"); + let event_request = event + .get_property("request") + .expect("Unable to get request from event"); + let promise = handler + .call(global, &[event_request, event]) + .expect("Unable to call handler"); + + let on_resolve = ON_RESOLVE.get().unwrap().clone() ; + let on_reject = ON_REJECT.get().unwrap().clone() ; + let then_func = promise.get_property("then").unwrap(); + if then_func.is_function() { + then_func + .call( + &promise, + &[on_resolve.deref().clone(), on_reject.deref().clone()], + ) + .unwrap(); + } else { + RESPONSE + .lock() + .unwrap() + .replace(from_qjs_value(promise).unwrap()); + } + + context + .execute_pending() + .expect("Unable to execute pending tasks"); + + // let response = to_qjs_value(context, &RESPONSE.lock().unwrap().take().unwrap()).unwrap(); + let response = RESPONSE.lock().unwrap().take().unwrap(); + + // let deserializer = &mut Deserializer::from(response); + // let response = HttpResponse::deserialize(deserializer).unwrap(); + // println!("Http Response {:?}", response); + if let JSValue::Object(obj) = response { + let status_code_ref = to_qjs_value(context, obj.get("status").unwrap()).unwrap(); + let status_code = status_code_ref.as_i32_unchecked(); + let status_text_ref = to_qjs_value(context, obj.get("statusText").unwrap()).unwrap(); + let status_text = status_text_ref.as_str().unwrap(); + let body_ref = to_qjs_value(context, obj.get("body").unwrap()).unwrap(); + let headers = obj.get("headers").unwrap(); + let mut headers_vec = Vec::new(); + let headers_obj = match headers { + JSValue::Object(obj) => obj, + _ => panic!("Headers is not object {:?}", headers), + }; + if let JSValue::Object(headers_obj) = headers_obj.get("headers").unwrap() { + for (k, v) in headers_obj.iter() { + let key = k.clone(); + let value = (*v).to_string(); + headers_vec.push((key.to_string(), value.to_string())); + } + } + wit::Response { + status: status_code as u16, + headers: Some(headers_vec), + body: Some(body_ref.as_str().unwrap().as_bytes().to_vec()), + status_text: status_text.to_string(), + } + } else { + panic!("Response is not object {:?}", response); + } + } } /// Compiles JS source code to QuickJS bytecode. diff --git a/JS/wasm/wit/arakoo.wit b/JS/wasm/wit/arakoo.wit index f4ea1ddba..f44acdad8 100644 --- a/JS/wasm/wit/arakoo.wit +++ b/JS/wasm/wit/arakoo.wit @@ -1,5 +1,7 @@ -use * from http-types +package arakoo:edgechains; -fetch-request: func(request: http-request) -> expected - +world reactor { + import http; + export inbound-http; +} \ No newline at end of file diff --git a/JS/wasm/wit/http-types.wit b/JS/wasm/wit/http-types.wit index 168cafc4a..869fecd36 100644 --- a/JS/wasm/wit/http-types.wit +++ b/JS/wasm/wit/http-types.wit @@ -1,45 +1,42 @@ - type uri = string - type http-status = u16 - type http-header = tuple - type http-headers = list - enum http-method { - get, - post, - put, - patch, - delete, - options, - head - } - type http-param = tuple - type http-params = list - type http-body = list - record http-request { - body: option, - headers: http-headers, - method: http-method, - params: http-params, - uri: uri, - } - record http-request-error { - error: http-error, - message: string - } - record http-response { - body: option, - headers: http-headers, - status: http-status, - } - enum http-error { - invalid-request, - invalid-request-body, - invalid-response-body, - not-allowed, - internal-error, - timeout, - redirect-loop, - } - enum file-error { - not-found, - invalid-path, - } +interface http-types { + type http-status = u16; + type body = list; + type headers = list>; + type params = list>; + type uri = string; + + enum method { + get, + post, + put, + delete, + patch, + head, + options, + } + + record request { + method: method, + uri: uri, + headers: headers, + params: params, + body: option, + } + + record response { + status: http-status, + headers: option, + body: option, + status-text: string, + } + + enum http-error { + success, + destination-not-allowed, + invalid-url, + request-error, + runtime-error, + too-many-requests, + } +} + diff --git a/JS/wasm/wit/http.wit b/JS/wasm/wit/http.wit new file mode 100644 index 000000000..6ca943cfd --- /dev/null +++ b/JS/wasm/wit/http.wit @@ -0,0 +1,5 @@ +interface http { + use http-types.{request, response, http-error}; + + send-request: func(req: request) -> result; +} From d5e003a26bf9d500b5ea28bde949d773f0025971 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Tue, 7 May 2024 17:13:05 +0530 Subject: [PATCH 05/13] Migrate Host(serve runtime) to component model and support hono --- JS/wasm/crates/serve/src/binding.rs | 627 ++++++++++++++-------------- JS/wasm/crates/serve/src/io.rs | 99 +++-- JS/wasm/crates/serve/src/lib.rs | 278 ++++++++---- 3 files changed, 567 insertions(+), 437 deletions(-) diff --git a/JS/wasm/crates/serve/src/binding.rs b/JS/wasm/crates/serve/src/binding.rs index b1ca79d59..5ed79f596 100644 --- a/JS/wasm/crates/serve/src/binding.rs +++ b/JS/wasm/crates/serve/src/binding.rs @@ -1,336 +1,335 @@ -use std::{ - env, - sync::{Arc, Mutex}, -}; +// use std::{ +// env, +// sync::{Arc, Mutex}, +// }; -use arakoo_jsonnet::{ - ext_string, jsonnet_destroy, jsonnet_evaluate_file, jsonnet_evaluate_snippet, jsonnet_make, -}; +// use arakoo_jsonnet::{ +// ext_string, jsonnet_destroy, jsonnet_evaluate_file, jsonnet_evaluate_snippet, jsonnet_make, +// }; -use std::{fs, io}; -use tokio::runtime::Builder; -use tracing::error; -use wasi_common::WasiCtx; -use wasmtime::*; +// use std::{fs, io}; +// use tokio::runtime::Builder; +// use tracing::error; +// use wasmtime::*; -use crate::io::{WasmInput, WasmOutput}; +// use crate::io::{WasmInput, WasmOutput}; -// pub struct VM { -// state: State, -// manifest_format: Box, -// trace_format: Box, -// tla_args: GcHashMap, -// } +// // pub struct VM { +// // state: State, +// // manifest_format: Box, +// // trace_format: Box, +// // tla_args: GcHashMap, +// // } -/// Adds exported functions to the Wasm linker. -/// -/// This function wraps the `jsonnet_evaluate`, `jsonnet_output_len`, and `jsonnet_output` -/// functions to be called from WebAssembly. It sets up the necessary state and -/// memory management for evaluating Jsonnet code and writing the output back to -/// WebAssembly memory. -pub fn add_jsonnet_to_linker(linker: &mut Linker) -> anyhow::Result<()> { - // Create a shared output buffer that will be used to store the result of the Jsonnet evaluation. - let output: Arc> = Arc::new(Mutex::new(String::new())); - let mut output_clone = output.clone(); +// /// Adds exported functions to the Wasm linker. +// /// +// /// This function wraps the `jsonnet_evaluate`, `jsonnet_output_len`, and `jsonnet_output` +// /// functions to be called from WebAssembly. It sets up the necessary state and +// /// memory management for evaluating Jsonnet code and writing the output back to +// /// WebAssembly memory. +// pub fn add_jsonnet_to_linker(linker: &mut Linker) -> anyhow::Result<()> { +// // Create a shared output buffer that will be used to store the result of the Jsonnet evaluation. +// let output: Arc> = Arc::new(Mutex::new(String::new())); +// let mut output_clone = output.clone(); - // Wrap the `jsonnet_evaluate` function to be called from WebAssembly. - linker.func_wrap( - "arakoo", - "jsonnet_evaluate", - move |mut caller: Caller<'_, WasiCtx>, - var_ptr: i32, - var_len: i32, - path_ptr: i32, - code_len: i32| { - // Clone the output buffer for use within the closure. - let output = output_clone.clone(); - // Get the WebAssembly memory instance. - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - // Calculate the offsets for the variable and path buffers in WebAssembly memory. - let var_offset = var_ptr as u32 as usize; - let path_offset = path_ptr as u32 as usize; - // Create buffers to read the variable and path data from WebAssembly memory. - let mut var_buffer = vec![0; var_len as usize]; - let mut path_buffer = vec![0; code_len as usize]; +// // Wrap the `jsonnet_evaluate` function to be called from WebAssembly. +// linker.func_wrap( +// "arakoo", +// "jsonnet_evaluate", +// move |mut caller: Caller<'_, WasiCtx>, +// var_ptr: i32, +// var_len: i32, +// path_ptr: i32, +// code_len: i32| { +// // Clone the output buffer for use within the closure. +// let output = output_clone.clone(); +// // Get the WebAssembly memory instance. +// let mem = match caller.get_export("memory") { +// Some(Extern::Memory(mem)) => mem, +// _ => return Err(Trap::NullReference.into()), +// }; +// // Calculate the offsets for the variable and path buffers in WebAssembly memory. +// let var_offset = var_ptr as u32 as usize; +// let path_offset = path_ptr as u32 as usize; +// // Create buffers to read the variable and path data from WebAssembly memory. +// let mut var_buffer = vec![0; var_len as usize]; +// let mut path_buffer = vec![0; code_len as usize]; - // Read the path data from WebAssembly memory and convert it to a string. - let path = match mem.read(&caller, path_offset, &mut path_buffer) { - Ok(_) => match std::str::from_utf8(&path_buffer) { - Ok(s) => s, - Err(_) => return Err(Trap::BadSignature.into()), - }, - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - // Read the variable data from WebAssembly memory and convert it to a string. - let var = match mem.read(&caller, var_offset, &mut var_buffer) { - Ok(_) => match std::str::from_utf8(&var_buffer) { - Ok(s) => s, - Err(_) => return Err(Trap::BadSignature.into()), - }, - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - // Parse the variable data as JSON. - let var_json: serde_json::Value = match serde_json::from_str(var) { - Ok(v) => v, - Err(e) => { - error!("Error parsing var: {}", e); - return Err(Trap::BadSignature.into()); - } - }; +// // Read the path data from WebAssembly memory and convert it to a string. +// let path = match mem.read(&caller, path_offset, &mut path_buffer) { +// Ok(_) => match std::str::from_utf8(&path_buffer) { +// Ok(s) => s, +// Err(_) => return Err(Trap::BadSignature.into()), +// }, +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// // Read the variable data from WebAssembly memory and convert it to a string. +// let var = match mem.read(&caller, var_offset, &mut var_buffer) { +// Ok(_) => match std::str::from_utf8(&var_buffer) { +// Ok(s) => s, +// Err(_) => return Err(Trap::BadSignature.into()), +// }, +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// // Parse the variable data as JSON. +// let var_json: serde_json::Value = match serde_json::from_str(var) { +// Ok(v) => v, +// Err(e) => { +// error!("Error parsing var: {}", e); +// return Err(Trap::BadSignature.into()); +// } +// }; - // Initialize the Jsonnet VM state with default settings. - let vm = jsonnet_make(); +// // Initialize the Jsonnet VM state with default settings. +// let vm = jsonnet_make(); - // Evaluate the Jsonnet code snippet using the provided path and variables. - let code = path; - for (key, value) in var_json.as_object().unwrap() { - // context.add_ext_var(key.into(), Val::Str(value.as_str().unwrap().into())); - ext_string( - vm, - key, - value.as_str().expect("ext_string value is not a string"), - ); - } - let out = jsonnet_evaluate_snippet(vm, "deleteme", code); - // Store the output of the Jsonnet evaluation in the shared output buffer. - let mut output = output.lock().unwrap(); - *output = out; - jsonnet_destroy(vm); - Ok(()) - }, - )?; +// // Evaluate the Jsonnet code snippet using the provided path and variables. +// let code = path; +// for (key, value) in var_json.as_object().unwrap() { +// // context.add_ext_var(key.into(), Val::Str(value.as_str().unwrap().into())); +// ext_string( +// vm, +// key, +// value.as_str().expect("ext_string value is not a string"), +// ); +// } +// let out = jsonnet_evaluate_snippet(vm, "deleteme", code); +// // Store the output of the Jsonnet evaluation in the shared output buffer. +// let mut output = output.lock().unwrap(); +// *output = out; +// jsonnet_destroy(vm); +// Ok(()) +// }, +// )?; - output_clone = output.clone(); - // Wrap the `jsonnet_evaluate_file` function to be called from WebAssembly. - linker.func_wrap( - "arakoo", - "jsonnet_evaluate_file", - move |mut caller: Caller<'_, WasiCtx>, - var_ptr: i32, - var_len: i32, - path_ptr: i32, - code_len: i32| { - // Clone the output buffer for use within the closure. - let output = output_clone.clone(); - // Get the WebAssembly memory instance. - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - // Calculate the offsets for the variable and path buffers in WebAssembly memory. - let var_offset = var_ptr as u32 as usize; - let path_offset = path_ptr as u32 as usize; - // Create buffers to read the variable and path data from WebAssembly memory. - let mut var_buffer = vec![0; var_len as usize]; - let mut path_buffer = vec![0; code_len as usize]; +// output_clone = output.clone(); +// // Wrap the `jsonnet_evaluate_file` function to be called from WebAssembly. +// linker.func_wrap( +// "arakoo", +// "jsonnet_evaluate_file", +// move |mut caller: Caller<'_, WasiCtx>, +// var_ptr: i32, +// var_len: i32, +// path_ptr: i32, +// code_len: i32| { +// // Clone the output buffer for use within the closure. +// let output = output_clone.clone(); +// // Get the WebAssembly memory instance. +// let mem = match caller.get_export("memory") { +// Some(Extern::Memory(mem)) => mem, +// _ => return Err(Trap::NullReference.into()), +// }; +// // Calculate the offsets for the variable and path buffers in WebAssembly memory. +// let var_offset = var_ptr as u32 as usize; +// let path_offset = path_ptr as u32 as usize; +// // Create buffers to read the variable and path data from WebAssembly memory. +// let mut var_buffer = vec![0; var_len as usize]; +// let mut path_buffer = vec![0; code_len as usize]; - // Read the path data from WebAssembly memory and convert it to a string. - let path = match mem.read(&caller, path_offset, &mut path_buffer) { - Ok(_) => match std::str::from_utf8(&path_buffer) { - Ok(s) => s, - Err(_) => return Err(Trap::BadSignature.into()), - }, - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - // Read the variable data from WebAssembly memory and convert it to a string. - let var = match mem.read(&caller, var_offset, &mut var_buffer) { - Ok(_) => match std::str::from_utf8(&var_buffer) { - Ok(s) => s, - Err(_) => return Err(Trap::BadSignature.into()), - }, - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - // Parse the variable data as JSON. - let var_json: serde_json::Value = match serde_json::from_str(var) { - Ok(v) => v, - Err(e) => { - error!("Error parsing var: {}", e); - return Err(Trap::BadSignature.into()); - } - }; - // println!("var_json: {:?}", var_json); +// // Read the path data from WebAssembly memory and convert it to a string. +// let path = match mem.read(&caller, path_offset, &mut path_buffer) { +// Ok(_) => match std::str::from_utf8(&path_buffer) { +// Ok(s) => s, +// Err(_) => return Err(Trap::BadSignature.into()), +// }, +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// // Read the variable data from WebAssembly memory and convert it to a string. +// let var = match mem.read(&caller, var_offset, &mut var_buffer) { +// Ok(_) => match std::str::from_utf8(&var_buffer) { +// Ok(s) => s, +// Err(_) => return Err(Trap::BadSignature.into()), +// }, +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// // Parse the variable data as JSON. +// let var_json: serde_json::Value = match serde_json::from_str(var) { +// Ok(v) => v, +// Err(e) => { +// error!("Error parsing var: {}", e); +// return Err(Trap::BadSignature.into()); +// } +// }; +// // println!("var_json: {:?}", var_json); - let vm = jsonnet_make(); +// let vm = jsonnet_make(); - for (key, value) in var_json.as_object().unwrap() { - ext_string( - vm, - key, - value.as_str().expect("ext_string value is not a string"), - ); - } - let code = fs::read_to_string(path).expect("File not found"); - let out = jsonnet_evaluate_snippet(vm, "deleteme", &code); - let mut output: std::sync::MutexGuard<'_, String> = output.lock().unwrap(); - *output = out; +// for (key, value) in var_json.as_object().unwrap() { +// ext_string( +// vm, +// key, +// value.as_str().expect("ext_string value is not a string"), +// ); +// } +// let code = fs::read_to_string(path).expect("File not found"); +// let out = jsonnet_evaluate_snippet(vm, "deleteme", &code); +// let mut output: std::sync::MutexGuard<'_, String> = output.lock().unwrap(); +// *output = out; - Ok(()) - }, - )?; +// Ok(()) +// }, +// )?; - // Wrap the `jsonnet_output_len` function to be called from WebAssembly. - // This function returns the length of the output string. - let output_clone = output.clone(); - linker.func_wrap("arakoo", "jsonnet_output_len", move || -> i32 { - let output_clone = output_clone.clone(); - let output = output_clone.lock().unwrap().clone(); - output.len() as i32 - })?; +// // Wrap the `jsonnet_output_len` function to be called from WebAssembly. +// // This function returns the length of the output string. +// let output_clone = output.clone(); +// linker.func_wrap("arakoo", "jsonnet_output_len", move || -> i32 { +// let output_clone = output_clone.clone(); +// let output = output_clone.lock().unwrap().clone(); +// output.len() as i32 +// })?; - // Wrap the `jsonnet_output` function to be called from WebAssembly. - // This function writes the output string to the specified memory location. - linker.func_wrap( - "arakoo", - "jsonnet_output", - move |mut caller: Caller<'_, WasiCtx>, ptr: i32| { - let output_clone = output.clone(); - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - let offset = ptr as u32 as usize; - let out = output_clone.lock().unwrap().clone(); - match mem.write(&mut caller, offset, out.as_bytes()) { - Ok(_) => {} - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - Ok(()) - }, - )?; +// // Wrap the `jsonnet_output` function to be called from WebAssembly. +// // This function writes the output string to the specified memory location. +// linker.func_wrap( +// "arakoo", +// "jsonnet_output", +// move |mut caller: Caller<'_, WasiCtx>, ptr: i32| { +// let output_clone = output.clone(); +// let mem = match caller.get_export("memory") { +// Some(Extern::Memory(mem)) => mem, +// _ => return Err(Trap::NullReference.into()), +// }; +// let offset = ptr as u32 as usize; +// let out = output_clone.lock().unwrap().clone(); +// match mem.write(&mut caller, offset, out.as_bytes()) { +// Ok(_) => {} +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// Ok(()) +// }, +// )?; - Ok(()) -} +// Ok(()) +// } -pub fn add_fetch_to_linker(linker: &mut Linker) -> anyhow::Result<()> { - let response: Arc> = Arc::new(Mutex::new(String::new())); - let error: Arc> = Arc::new(Mutex::new(String::new())); +// pub fn add_fetch_to_linker(linker: &mut Linker) -> anyhow::Result<()> { +// let response: Arc> = Arc::new(Mutex::new(String::new())); +// let error: Arc> = Arc::new(Mutex::new(String::new())); - let response_clone = response.clone(); - let error_clone = error.clone(); - linker.func_wrap( - "arakoo", - "fetch", - move |mut caller: Caller<'_, WasiCtx>, request_ptr: i32, request_len: i32| { - let response_arc = response_clone.clone(); - let error = error_clone.clone(); - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => { - let mut error = error.lock().unwrap(); - *error = "Memory not found".to_string(); - return Err(Trap::NullReference.into()); - } - }; - let request_offset = request_ptr as u32 as usize; - let mut request_buffer = vec![0; request_len as usize]; - let request = match mem.read(&caller, request_offset, &mut request_buffer) { - Ok(_) => match std::str::from_utf8(&request_buffer) { - Ok(s) => s.to_string(), // Clone the string here - Err(_) => { - let mut error = error.lock().unwrap(); - *error = "Bad signature".to_string(); - return Err(Trap::BadSignature.into()); - } - }, - _ => { - let mut error = error.lock().unwrap(); - *error = "Memory out of bounds".to_string(); - return Err(Trap::MemoryOutOfBounds.into()); - } - }; +// let response_clone = response.clone(); +// let error_clone = error.clone(); +// linker.func_wrap( +// "arakoo", +// "fetch", +// move |mut caller: Caller<'_, WasiCtx>, request_ptr: i32, request_len: i32| { +// let response_arc = response_clone.clone(); +// let error = error_clone.clone(); +// let mem = match caller.get_export("memory") { +// Some(Extern::Memory(mem)) => mem, +// _ => { +// let mut error = error.lock().unwrap(); +// *error = "Memory not found".to_string(); +// return Err(Trap::NullReference.into()); +// } +// }; +// let request_offset = request_ptr as u32 as usize; +// let mut request_buffer = vec![0; request_len as usize]; +// let request = match mem.read(&caller, request_offset, &mut request_buffer) { +// Ok(_) => match std::str::from_utf8(&request_buffer) { +// Ok(s) => s.to_string(), // Clone the string here +// Err(_) => { +// let mut error = error.lock().unwrap(); +// *error = "Bad signature".to_string(); +// return Err(Trap::BadSignature.into()); +// } +// }, +// _ => { +// let mut error = error.lock().unwrap(); +// *error = "Memory out of bounds".to_string(); +// return Err(Trap::MemoryOutOfBounds.into()); +// } +// }; - let thread_result = std::thread::spawn(move || { - Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(async { - let request: WasmInput = match serde_json::from_str(&request) { - Ok(r) => r, - Err(e) => { - return Err(anyhow::anyhow!("Error parsing request: {}", e)); - } - }; - let method: reqwest::Method = match request.method().parse() { - Ok(m) => m, - Err(_) => { - return Err(anyhow::anyhow!( - "Invalid method: {}", - request.method() - )); - } - }; - let client = reqwest::Client::new(); - let mut builder = client.request(method, request.url()); - let header = request.headers(); - for (k, v) in header { - builder = builder.header(k, v); - } - builder = builder.body(request.body().to_string()); - match builder.send().await { - Ok(r) => { - let response = WasmOutput::from_reqwest_response(r).await?; - Ok(response) - } - Err(e) => { - error!("Error sending request: {}", e); - return Err(anyhow::anyhow!("Error sending request: {}", e)); - } - } - }) - }) - .join(); - let response = match thread_result { - Ok(Ok(r)) => r, - Ok(Err(e)) => { - let mut error = error.lock().unwrap(); - *error = format!("Error sending request: {}", e); - error!("Error sending request: {}", e); - return Err(Trap::BadSignature.into()); - } - Err(_) => { - let mut error = error.lock().unwrap(); - *error = "Error sending request: thread join error".to_string(); - error!("Error sending request: thread join error"); - return Err(Trap::BadSignature.into()); - } - }; +// let thread_result = std::thread::spawn(move || { +// Builder::new_current_thread() +// .enable_all() +// .build() +// .unwrap() +// .block_on(async { +// let request: WasmInput = match serde_json::from_str(&request) { +// Ok(r) => r, +// Err(e) => { +// return Err(anyhow::anyhow!("Error parsing request: {}", e)); +// } +// }; +// let method: reqwest::Method = match request.method().parse() { +// Ok(m) => m, +// Err(_) => { +// return Err(anyhow::anyhow!( +// "Invalid method: {}", +// request.method() +// )); +// } +// }; +// let client = reqwest::Client::new(); +// let mut builder = client.request(method, request.url()); +// let header = request.headers(); +// for (k, v) in header { +// builder = builder.header(k, v); +// } +// builder = builder.body(request.body().to_string()); +// match builder.send().await { +// Ok(r) => { +// let response = WasmOutput::from_reqwest_response(r).await?; +// Ok(response) +// } +// Err(e) => { +// error!("Error sending request: {}", e); +// return Err(anyhow::anyhow!("Error sending request: {}", e)); +// } +// } +// }) +// }) +// .join(); +// let response = match thread_result { +// Ok(Ok(r)) => r, +// Ok(Err(e)) => { +// let mut error = error.lock().unwrap(); +// *error = format!("Error sending request: {}", e); +// error!("Error sending request: {}", e); +// return Err(Trap::BadSignature.into()); +// } +// Err(_) => { +// let mut error = error.lock().unwrap(); +// *error = "Error sending request: thread join error".to_string(); +// error!("Error sending request: thread join error"); +// return Err(Trap::BadSignature.into()); +// } +// }; - let res = serde_json::to_string(&response).unwrap(); - let mut response = response_arc.lock().unwrap(); - *response = res; - Ok(()) - }, - )?; - // add the fetch_output_len and fetch_output functions here - let response_clone = response.clone(); - linker.func_wrap("arakoo", "get_response_len", move || -> i32 { - let response_clone = response_clone.clone(); - let response = response_clone.lock().unwrap().clone(); - response.len() as i32 - })?; +// let res = serde_json::to_string(&response).unwrap(); +// let mut response = response_arc.lock().unwrap(); +// *response = res; +// Ok(()) +// }, +// )?; +// // add the fetch_output_len and fetch_output functions here +// let response_clone = response.clone(); +// linker.func_wrap("arakoo", "get_response_len", move || -> i32 { +// let response_clone = response_clone.clone(); +// let response = response_clone.lock().unwrap().clone(); +// response.len() as i32 +// })?; - // also add the fetch_error_len and fetch_error functions here - linker.func_wrap( - "arakoo", - "get_response", - move |mut _caller: Caller<'_, WasiCtx>, ptr: i32| { - let response_clone = response.clone(); - let mem = match _caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - let offset = ptr as u32 as usize; - let response = response_clone.lock().unwrap().clone(); - match mem.write(&mut _caller, offset, response.as_bytes()) { - Ok(_) => {} - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - Ok(()) - }, - )?; - Ok(()) -} +// // also add the fetch_error_len and fetch_error functions here +// linker.func_wrap( +// "arakoo", +// "get_response", +// move |mut _caller: Caller<'_, WasiCtx>, ptr: i32| { +// let response_clone = response.clone(); +// let mem = match _caller.get_export("memory") { +// Some(Extern::Memory(mem)) => mem, +// _ => return Err(Trap::NullReference.into()), +// }; +// let offset = ptr as u32 as usize; +// let response = response_clone.lock().unwrap().clone(); +// match mem.write(&mut _caller, offset, response.as_bytes()) { +// Ok(_) => {} +// _ => return Err(Trap::MemoryOutOfBounds.into()), +// }; +// Ok(()) +// }, +// )?; +// Ok(()) +// } diff --git a/JS/wasm/crates/serve/src/io.rs b/JS/wasm/crates/serve/src/io.rs index de7883cd5..30668a23c 100644 --- a/JS/wasm/crates/serve/src/io.rs +++ b/JS/wasm/crates/serve/src/io.rs @@ -3,13 +3,24 @@ use std::collections::HashMap; use hyper::{header::HOST, http::request::Parts, HeaderMap, Uri}; use serde::{Deserialize, Serialize}; +#[derive(Serialize,Deserialize,Debug,Clone)] +pub enum Method { + GET, + POST, + PUT, + DELETE, + PATCH, + HEAD, + OPTIONS, +} + #[derive(Serialize, Deserialize, Debug)] -pub struct WasmInput<'a> { - url: String, - method: &'a str, - headers: HashMap, - body: String, - params: HashMap, +pub struct WasmInput { + pub uri: String, + pub method: Method, + pub headers: Vec<(String, String)>, + pub body: Option>, + pub params: Vec<(String, String)>, } #[derive(Deserialize, Debug, Clone, Serialize)] @@ -18,51 +29,35 @@ pub struct WasmOutput { pub status: u16, #[serde(rename = "statusText")] pub status_text: String, - body: Option, + pub body: Option, } -impl<'a> WasmInput<'a> { - pub fn new(request: &'a Parts, body: String) -> Self { - let mut params = HashMap::new(); +impl WasmInput { + pub fn new(request: &Parts, body: String) -> Self { + let mut params: Vec<(String, String)> = vec![]; if let Some(query) = request.uri.query() { for pair in query.split('&') { let mut parts = pair.split('='); let key = parts.next().unwrap(); let value = parts.next().unwrap(); - params.insert(key.to_string(), value.to_string()); + params.push((key.to_string(), value.to_string())); } } - let url = Self::build_url(request); + let uri = Self::build_uri(request); Self { - url, - method: request.method.as_str(), - headers: Self::build_headers_hash(&request.headers), - body, + uri, + method: Self::build_method(&request.method), + headers: Self::build_headers(&request.headers), + body: Self::build_body(body), params, } } - pub fn url(&self) -> &str { - &self.url - } - - pub fn method(&self) -> &str { - self.method - } - pub fn headers(&self) -> &HashMap { - &self.headers - } - - pub fn body(&self) -> &str { - &self.body - } - - - fn build_url(request: &Parts) -> String { + fn build_uri(request: &Parts) -> String { Uri::builder() .scheme("http") .authority(request.headers.get(HOST).unwrap().to_str().unwrap()) @@ -72,19 +67,43 @@ impl<'a> WasmInput<'a> { .to_string() } - fn build_headers_hash(headers: &HeaderMap) -> HashMap { - let mut parsed_headers = HashMap::new(); + fn build_headers(req_headers: &HeaderMap) -> Vec<(String, String)> { + let mut headers: Vec<(String, String)> = vec![]; - for (key, value) in headers.iter() { - parsed_headers.insert( + for (key, value) in req_headers.iter() { + headers.push(( String::from(key.as_str()), - String::from(value.to_str().unwrap()), - ); + String::from(value.to_str().unwrap().to_string()), + )); } - parsed_headers + headers + } + + fn build_method(method: &hyper::Method) -> Method { + let method: Method = match method { + &hyper::Method::GET => Method::GET, + &hyper::Method::POST => Method::POST, + &hyper::Method::PUT => Method::PUT, + &hyper::Method::DELETE => Method::DELETE, + &hyper::Method::PATCH => Method::PATCH, + &hyper::Method::HEAD => Method::HEAD, + &hyper::Method::OPTIONS => Method::OPTIONS, + _ => Method::GET, + }; + method + } + + fn build_body(body:String) -> Option> { + if body.is_empty() { + None + } else { + Some(body.into_bytes()) + } } } + + impl WasmOutput { pub fn body(&self) -> String { self.body.clone().unwrap_or_default() diff --git a/JS/wasm/crates/serve/src/lib.rs b/JS/wasm/crates/serve/src/lib.rs index 6ec3c0d15..ee4b49640 100644 --- a/JS/wasm/crates/serve/src/lib.rs +++ b/JS/wasm/crates/serve/src/lib.rs @@ -3,6 +3,7 @@ mod binding; mod io; use std::{ + collections::HashMap, convert::Infallible, env, future::Future, @@ -14,7 +15,8 @@ use std::{ task::{self, Poll}, }; -use binding::add_fetch_to_linker; +use anyhow::Context; +// use binding::add_fetch_to_linker; // use binding::add_exports_to_linker; use futures::future::{self, Ready}; use hyper::{ @@ -27,16 +29,28 @@ use hyper::{ use tracing::{error, event, info, Level}; use tracing_subscriber::{filter::EnvFilter, FmtSubscriber}; -use wasi_common::WasiCtx; -use wasmtime_wasi::WasiCtxBuilder; +// use wasi_common::WasiCtx; +use wasmtime_wasi::{bindings, ResourceTable, WasiCtx, WasiCtxBuilder, WasiView}; -use wasmtime::{Caller, Config, Engine, Extern, Linker, Module, Store, Trap, WasmBacktraceDetails}; +use async_trait::async_trait; +use wasmtime::component::{Component, Linker}; +use wasmtime::{Config, Engine, Store, WasmBacktraceDetails}; +use wit::arakoo::edgechains::http_types; +use wit::exports::arakoo::edgechains::inbound_http::{self}; use crate::{ - binding::add_jsonnet_to_linker, + // binding::add_jsonnet_to_linker, io::{WasmInput, WasmOutput}, }; +mod wit { + wasmtime::component::bindgen!({ + path:"../../wit", + world:"reactor", + async:true + }); +} + #[derive(Clone)] pub struct RequestService { worker_ctx: WorkerCtx, @@ -52,15 +66,41 @@ impl RequestService { #[derive(Clone)] pub struct WorkerCtx { engine: Engine, - module: Module, + component: Component, +} + +struct Host { + table: ResourceTable, + wasi: WasiCtx, +} + +impl WasiView for Host { + fn table(&mut self) -> &mut ResourceTable { + &mut self.table + } + + fn ctx(&mut self) -> &mut wasmtime_wasi::WasiCtx { + &mut self.wasi + } +} + +use wit::arakoo::edgechains::http as outbound_http; +#[async_trait] +impl outbound_http::Host for Host { + async fn send_request( + &mut self, + request: http_types::Request, + ) -> wasmtime::Result> { + todo!() + } } impl WorkerCtx { - pub fn new(module_path: impl AsRef) -> anyhow::Result { + pub fn new(component_path: impl AsRef) -> anyhow::Result { tracing_subscriber(); - info!("Loading module from {:?}", module_path.as_ref()); + info!("Loading Component from {:?}", component_path.as_ref()); let mut binding = Config::default(); - let config = binding.async_support(true); + let config = binding.async_support(true).wasm_component_model(true); // check if env has debug flag if env::var("DEBUG").is_ok() { config @@ -71,18 +111,20 @@ impl WorkerCtx { } let engine = Engine::new(&config)?; - let module = Module::from_file(&engine, module_path)?; + let component = Component::from_file(&engine, component_path) + .with_context(|| format!("Failed to load component : invalid path"))?; - Ok(Self { engine, module }) + Ok(Self { engine, component }) } - pub fn module(&self) -> &Module { - &self.module + pub fn component(&self) -> &Component { + &self.component } pub fn engine(&self) -> &Engine { &self.engine } + pub async fn serve(self, addr: SocketAddr) -> Result<(), hyper::Error> { info!("Starting server ..."); let server = hyper::Server::bind(&addr).serve(self); @@ -100,6 +142,7 @@ impl WorkerCtx { let body = hyper::body::to_bytes(body).await.unwrap(); let body_str = String::from_utf8_lossy(&body).to_string(); let result = self.run(&parts, body_str).await; + match result { Ok(output) => { let mut response = Response::builder(); @@ -139,105 +182,174 @@ impl WorkerCtx { /// functions to be called from WebAssembly. async fn run(&self, parts: &Parts, body: String) -> anyhow::Result { // Serialize the request parts and body into a JSON input for the WebAssembly module. - let input = serde_json::to_vec(&WasmInput::new(parts, body)).unwrap(); - let mem_len = input.len() as i32; + // let input = serde_json::to_vec(&WasmInput::new(parts, body)).unwrap(); + // let mem_len = input.len() as i32; // Create a new linker with the WASI context. - let mut linker: Linker = Linker::new(self.engine()); - wasmtime_wasi::add_to_linker(&mut linker, |ctx| ctx)?; + let mut linker = Linker::new(self.engine()); + // wasmtime_wasi::add_to_linker(&mut linker, |ctx| ctx)?; + bindings::cli::environment::add_to_linker(&mut linker, |x| x) + .expect("Unable to add environment"); + bindings::cli::exit::add_to_linker(&mut linker, |x| x).expect("Unable to add cli"); + bindings::io::error::add_to_linker(&mut linker, |x| x).expect("Unable to add io error"); + // bindings::sync::io::streams::add_to_linker(&mut linker, |x| x) + // .expect("Unable to add io streams"); + bindings::io::streams::add_to_linker(&mut linker, |x| x).expect("Unable to add io streams"); + bindings::cli::stdin::add_to_linker(&mut linker, |x| x).expect("Unable to add cli stdin"); + bindings::cli::stdout::add_to_linker(&mut linker, |x| x).expect("Unable to add cli stdout"); + bindings::cli::stderr::add_to_linker(&mut linker, |x| x).expect("Unable to add cli stderr"); + bindings::cli::terminal_input::add_to_linker(&mut linker, |x| x) + .expect("Unable to add cli terminal input"); + bindings::cli::terminal_output::add_to_linker(&mut linker, |x| x) + .expect("Unable to add cli terminal output"); + bindings::cli::terminal_stdin::add_to_linker(&mut linker, |x| x) + .expect("Unable to add cli terminal stdin"); + bindings::cli::terminal_stdout::add_to_linker(&mut linker, |x| x) + .expect("Unable to add cli terminal stdout"); + bindings::cli::terminal_stderr::add_to_linker(&mut linker, |x| x) + .expect("Unable to add cli terminal stderr"); + bindings::clocks::monotonic_clock::add_to_linker(&mut linker, |x| x) + .expect("Unable to add clocks monotonic clock"); + bindings::clocks::wall_clock::add_to_linker(&mut linker, |x| x) + .expect("Unable to add clocks wallclock"); + // bindings::sync::filesystem::types::add_to_linker(&mut linker, |x| x) + // .expect("Unable to add filesystem types"); + bindings::filesystem::types::add_to_linker(&mut linker, |x| x) + .expect("Unable to add filesystem types"); + bindings::filesystem::preopens::add_to_linker(&mut linker, |x| x) + .expect("Unable to add filesystem preopens"); + bindings::random::random::add_to_linker(&mut linker, |x| x).expect("Unable to add random"); // Wrap the `get_request_len` function to be called from WebAssembly. // This function returns the length of the input buffer. - linker.func_wrap("arakoo", "get_request_len", move || -> i32 { mem_len })?; + // linker.func_wrap("arakoo", "get_request_len", move || -> i32 { mem_len })?; // Wrap the `get_request` function to be called from WebAssembly. // This function writes the input buffer to the specified memory location. - match linker.func_wrap( - "arakoo", - "get_request", - move |mut caller: Caller<'_, WasiCtx>, ptr: i32| { - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - let offset = ptr as u32 as usize; - match mem.write(&mut caller, offset, &input) { - Ok(_) => {} - _ => return Err(Trap::MemoryOutOfBounds.into()), - }; - Ok(()) - }, - ) { - Ok(_) => {} - Err(e) => { - println!("Error adding get_request: {}", e); - } - } + // match linker.func_wrap( + // "arakoo", + // "get_request", + // move |mut caller: Caller<'_, WasiCtx>, ptr: i32| { + // let mem = match caller.get_export("memory") { + // Some(Extern::Memory(mem)) => mem, + // _ => return Err(Trap::NullReference.into()), + // }; + // let offset = ptr as u32 as usize; + // match mem.write(&mut caller, offset, &input) { + // Ok(_) => {} + // _ => return Err(Trap::MemoryOutOfBounds.into()), + // }; + // Ok(()) + // }, + // ) { + // Ok(_) => {} + // Err(e) => { + // println!("Error adding get_request: {}", e); + // } + // } // Create a shared output buffer that will be used to store the result of the WebAssembly execution. - let output: Arc> = Arc::new(Mutex::new(WasmOutput::new())); - let output_clone = output.clone(); + // let output: Arc> = Arc::new(Mutex::new(WasmOutput::new())); + // let output_clone = output.clone(); // Wrap the `set_output` function to be called from WebAssembly. // This function reads the output buffer from the specified memory location and updates the shared output buffer. - linker.func_wrap( - "arakoo", - "set_output", - move |mut caller: Caller<'_, WasiCtx>, ptr: i32, len: i32| { - let output = output_clone.clone(); - let mem = match caller.get_export("memory") { - Some(Extern::Memory(mem)) => mem, - _ => return Err(Trap::NullReference.into()), - }; - let offset = ptr as u32 as usize; - let mut buffer = vec![0; len as usize]; - match mem.read(&caller, offset, &mut buffer) { - Ok(_) => match serde_json::from_slice::(&buffer) { - Ok(parsed_output) => { - let mut output = output.lock().unwrap(); - *output = parsed_output; - Ok(()) - } - Err(_e) => Err(Trap::BadSignature.into()), - }, - _ => Err(Trap::MemoryOutOfBounds.into()), - } - }, - )?; + // linker.func_wrap( + // "arakoo", + // "set_output", + // move |mut caller: Caller<'_, WasiCtx>, ptr: i32, len: i32| { + // let output = output_clone.clone(); + // let mem = match caller.get_export("memory") { + // Some(Extern::Memory(mem)) => mem, + // _ => return Err(Trap::NullReference.into()), + // }; + // let offset = ptr as u32 as usize; + // let mut buffer = vec![0; len as usize]; + // match mem.read(&caller, offset, &mut buffer) { + // Ok(_) => match serde_json::from_slice::(&buffer) { + // Ok(parsed_output) => { + // let mut output = output.lock().unwrap(); + // *output = parsed_output; + // Ok(()) + // } + // Err(_e) => Err(Trap::BadSignature.into()), + // }, + // _ => Err(Trap::MemoryOutOfBounds.into()), + // } + // }, + // )?; // Add additional exports to the linker, such as Jsonnet evaluation functions. - add_jsonnet_to_linker(&mut linker)?; - add_fetch_to_linker(&mut linker)?; + // add_jsonnet_to_linker(&mut linker)?; + // add_fetch_to_linker(&mut linker)?; // Create a WASI context builder with inherited standard output and error streams. - let wasi_builder = WasiCtxBuilder::new() + let wasi = WasiCtxBuilder::new() .inherit_stdout() .inherit_stderr() .build(); + let table: ResourceTable = ResourceTable::new(); + // Create a new store with the WASI context. - let mut store = Store::new(self.engine(), wasi_builder); + let mut store = Store::new(self.engine(), Host { table, wasi }); // Instantiate the WebAssembly module with the linker and store. - linker.module(&mut store, "", self.module())?; + // linker.module(&mut store, "", self.module())?; // Get the entrypoint function from the WebAssembly instance and call it. - let instance = linker - .instantiate_async(&mut store, self.module()) - .await - .map_err(anyhow::Error::msg)?; - let run_entrypoint_fn = instance.get_typed_func::<(), ()>(&mut store, "run_entrypoint")?; - run_entrypoint_fn - .call_async(&mut store, ()) - .await - .map_err(anyhow::Error::msg)?; + // let instance = linker + // .instantiate_async(&mut store, self.module()) + // .await + // .map_err(anyhow::Error::msg)?; + // let run_entrypoint_fn = instance.get_typed_func::<(), ()>(&mut store, "run_entrypoint")?; + // run_entrypoint_fn + // .call_async(&mut store, ()) + // .await + // .map_err(anyhow::Error::msg)?; + let wasm_input = WasmInput::new(parts, body); + let request = inbound_http::Request { + method: match wasm_input.method { + io::Method::GET => http_types::Method::Get, + io::Method::POST => http_types::Method::Post, + io::Method::PUT => http_types::Method::Put, + io::Method::DELETE => http_types::Method::Delete, + io::Method::PATCH => http_types::Method::Patch, + io::Method::HEAD => http_types::Method::Head, + io::Method::OPTIONS => http_types::Method::Options, + }, + uri: wasm_input.uri, + headers: wasm_input.headers, + params: wasm_input.params, + body: wasm_input.body, + }; + let (reactor, instance) = + wit::Reactor::instantiate_async(&mut store, self.component(), &linker).await?; + let guest = reactor.arakoo_edgechains_inbound_http(); + let result: Result = + guest.call_handle_request(&mut store, &request).await; + let mut wasm_output = WasmOutput::new(); + // println!("Result of guest calling: {:?}", &result); + match result { + Ok(res) => { + wasm_output.status = res.status; + wasm_output.status_text = res.status_text; + let mut headers_map = HashMap::new(); + for (key, val) in res.headers.unwrap().iter() { + headers_map.insert(key.to_owned(), val.to_owned()); + } + wasm_output.headers = headers_map; + let body_vec = res.body.unwrap(); + if body_vec.len() > 0 { + wasm_output.body = Some(String::from_utf8(body_vec).unwrap()); + } + } + Err(err) => println!("Error occured : {:?}", err), + }; // Drop the store to release resources. drop(store); - - // Lock the output buffer and return a clone of the result. - let output = output.lock().unwrap().clone(); - Ok(output) + Ok(wasm_output) } fn make_service(&self) -> RequestService { RequestService::new(self.clone()) From d3376c036dc606a4df5f73d3e2fd5437218f8940 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Tue, 7 May 2024 17:14:27 +0530 Subject: [PATCH 06/13] Minor change in build.js for ec-wasm-js example --- JS/wasm/examples/ec-wasmjs-hono/build.js | 15 ++++++++++++++- JS/wasm/examples/ec-wasmjs-hono/package.json | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/JS/wasm/examples/ec-wasmjs-hono/build.js b/JS/wasm/examples/ec-wasmjs-hono/build.js index 57c3e469d..3967761da 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/build.js +++ b/JS/wasm/examples/ec-wasmjs-hono/build.js @@ -1,4 +1,5 @@ import { build } from "esbuild"; +import textReplace from "esbuild-plugin-text-replace"; import fs from "fs"; let runtime = process.argv[2]; @@ -20,9 +21,21 @@ build({ // entryPoints: ["src/index.js"], bundle: true, // minify: true, - minifySyntax: true, + // minifySyntax: true, // outfile: "bin/app.js", outdir: "bin", + // inject:["shim.js"], + // define:{ + // "export":"_export" + // }, + plugins:[ + textReplace({ + include:/src\/index.js$/, + pattern:[ + ["export default","_export = "] + ] + }) + ], format: "esm", target: "esnext", platform: "node", diff --git a/JS/wasm/examples/ec-wasmjs-hono/package.json b/JS/wasm/examples/ec-wasmjs-hono/package.json index df62e5ce1..3fdd21070 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/package.json +++ b/JS/wasm/examples/ec-wasmjs-hono/package.json @@ -8,13 +8,14 @@ "devDependencies": { "@planetscale/database": "^1.4.0", "esbuild": "^0.19", + "esbuild-plugin-text-replace": "^1.3.0", "hono": "^3.9" }, "dependencies": { + "@arakoodev/jsonnet": "file:../../../jsonnet/", "@hono/node-server": "^1.3.1", "axios": "^1.6.2", "crypto": "^1.0.1", - "@arakoodev/jsonnet": "file:../../../jsonnet/", "http": "^0.0.1-security", "stream": "^0.0.2" } From 530bcee55955a58c36b7d5984874362ed389f4c1 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Tue, 7 May 2024 18:48:32 +0530 Subject: [PATCH 07/13] Move apis crate to apis folder in arakoo-core --- Cargo.toml | 1 - JS/wasm/crates/apis/Cargo.toml | 27 ------------------- JS/wasm/crates/arakoo-core/Cargo.toml | 13 ++++----- .../src/apis}/api_config.rs | 3 +-- .../src/apis}/console/config.rs | 2 +- .../src/apis}/console/mod.rs | 7 ++--- .../src => arakoo-core/src/apis}/fetch/mod.rs | 0 .../src => arakoo-core/src/apis}/http/mod.rs | 4 +-- .../src/apis}/http/shims/.gitignore | 0 .../src/apis}/http/shims/build.js | 0 .../src/apis}/http/shims/index.js | 0 .../src/apis}/http/shims/package.json | 0 .../src/apis}/http/types.rs | 0 .../src/apis}/jsonnet/mod.rs | 0 .../lib.rs => arakoo-core/src/apis/mod.rs} | 9 ------- .../src/apis}/pdfparse/mod.rs | 2 +- .../src/apis}/random/mod.rs | 6 +++-- .../src/apis}/runtime_ext.rs | 4 +-- .../src/apis}/stream_io/io.js | 0 .../src/apis}/stream_io/mod.rs | 2 +- .../src/apis}/text_encoding/mod.rs | 4 +-- .../src/apis}/text_encoding/text-encoding.js | 0 JS/wasm/crates/arakoo-core/src/lib.rs | 1 + JS/wasm/crates/arakoo-core/src/runtime.rs | 2 +- 24 files changed, 27 insertions(+), 60 deletions(-) delete mode 100644 JS/wasm/crates/apis/Cargo.toml rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/api_config.rs (71%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/console/config.rs (98%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/console/mod.rs (97%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/fetch/mod.rs (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/mod.rs (80%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/shims/.gitignore (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/shims/build.js (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/shims/index.js (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/shims/package.json (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/http/types.rs (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/jsonnet/mod.rs (100%) rename JS/wasm/crates/{apis/src/lib.rs => arakoo-core/src/apis/mod.rs} (92%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/pdfparse/mod.rs (97%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/random/mod.rs (89%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/runtime_ext.rs (93%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/stream_io/io.js (100%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/stream_io/mod.rs (98%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/text_encoding/mod.rs (97%) rename JS/wasm/crates/{apis/src => arakoo-core/src/apis}/text_encoding/text-encoding.js (100%) diff --git a/Cargo.toml b/Cargo.toml index 875ab0861..7b2fad61d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,5 @@ [workspace] members = [ - "JS/wasm/crates/apis", "JS/wasm/crates/arakoo-core", "JS/wasm/crates/cli", "JS/wasm/crates/serve", diff --git a/JS/wasm/crates/apis/Cargo.toml b/JS/wasm/crates/apis/Cargo.toml deleted file mode 100644 index d2d419c48..000000000 --- a/JS/wasm/crates/apis/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "apis" -edition.workspace = true -version.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[features] -console = [] -random = ["dep:fastrand"] -stream_io = [] -text_encoding = [] - -[dependencies] -anyhow = { workspace = true } -fastrand = { version = "2.0.1", optional = true } -javy = { workspace = true } -# wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", tag = "v0.2.0" } -tokio = "1.36.0" -serde = { workspace = true } -serde_json = { workspace = true } -serde_bytes = { workspace = true } -http = { workspace = true } -quickjs-wasm-rs = "3.0.0" -wit-bindgen = "0.24.0" -bytes = { version = "1.6.0", features = ["serde"] } - diff --git a/JS/wasm/crates/arakoo-core/Cargo.toml b/JS/wasm/crates/arakoo-core/Cargo.toml index b0b6767fd..af6c0ba2c 100644 --- a/JS/wasm/crates/arakoo-core/Cargo.toml +++ b/JS/wasm/crates/arakoo-core/Cargo.toml @@ -10,14 +10,15 @@ crate-type = ["cdylib"] anyhow = { workspace = true } javy = { workspace = true, features = ["export_alloc_fns", "json"] } once_cell = { workspace = true } -apis = { path = "../apis", features = [ - "console", - "text_encoding", - "random", - "stream_io", -] } serde_json = { workspace = true } serde = { workspace = true } serde_bytes = { workspace = true } send_wrapper = "0.6.0" wit-bindgen = "0.24.0" +# fastrand = { version = "2.0.1", optional = true } +# wit-bindgen-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", tag = "v0.2.0" } +tokio = "1.36.0" +http = { workspace = true } +quickjs-wasm-rs = "3.0.0" +bytes = { version = "1.6.0", features = ["serde"] } +fastrand = "2.1.0" diff --git a/JS/wasm/crates/apis/src/api_config.rs b/JS/wasm/crates/arakoo-core/src/apis/api_config.rs similarity index 71% rename from JS/wasm/crates/apis/src/api_config.rs rename to JS/wasm/crates/arakoo-core/src/apis/api_config.rs index 0b0955adc..1237a427d 100644 --- a/JS/wasm/crates/apis/src/api_config.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/api_config.rs @@ -7,6 +7,5 @@ /// ``` #[derive(Debug, Default)] pub struct APIConfig { - #[cfg(feature = "console")] - pub(crate) console: crate::console::ConsoleConfig, + pub(crate) console: super::console::ConsoleConfig, } diff --git a/JS/wasm/crates/apis/src/console/config.rs b/JS/wasm/crates/arakoo-core/src/apis/console/config.rs similarity index 98% rename from JS/wasm/crates/apis/src/console/config.rs rename to JS/wasm/crates/arakoo-core/src/apis/console/config.rs index 3eb2152fb..1c2d05647 100644 --- a/JS/wasm/crates/apis/src/console/config.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/console/config.rs @@ -1,6 +1,6 @@ use std::io::{self, Write}; -use crate::APIConfig; +use super::APIConfig; /// A selection of possible destination streams for `console.log` and /// `console.error`. diff --git a/JS/wasm/crates/apis/src/console/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs similarity index 97% rename from JS/wasm/crates/apis/src/console/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/console/mod.rs index eb75d3427..f67c8ae6e 100644 --- a/JS/wasm/crates/apis/src/console/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs @@ -6,7 +6,7 @@ use javy::{ Runtime, }; -use crate::{APIConfig, JSApiSet}; +use super::{APIConfig, JSApiSet}; pub(super) use config::ConsoleConfig; pub use config::LogStream; @@ -79,8 +79,9 @@ mod tests { use std::rc::Rc; use std::{cmp, io}; - use crate::console::register_console; - use crate::{APIConfig, JSApiSet}; + use crate::apis::console::register_console; + + use super::{APIConfig, JSApiSet}; use super::Console; diff --git a/JS/wasm/crates/apis/src/fetch/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs similarity index 100% rename from JS/wasm/crates/apis/src/fetch/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs diff --git a/JS/wasm/crates/apis/src/http/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs similarity index 80% rename from JS/wasm/crates/apis/src/http/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/http/mod.rs index 6477ec0cc..0019f00ac 100644 --- a/JS/wasm/crates/apis/src/http/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs @@ -1,7 +1,7 @@ pub mod types; use anyhow::Result; -use crate::JSApiSet; +use super::JSApiSet; // use crate::{fetch, get_response, get_response_len, http::types::Request, JSApiSet}; @@ -9,7 +9,7 @@ use crate::JSApiSet; pub(super) struct Http; impl JSApiSet for Http { - fn register(&self, runtime: &javy::Runtime, _config: &crate::APIConfig) -> Result<()> { + fn register(&self, runtime: &javy::Runtime, _config: &super::APIConfig) -> Result<()> { let context = runtime.context(); context.eval_global("http.js", include_str!("shims/dist/index.js"))?; Ok(()) diff --git a/JS/wasm/crates/apis/src/http/shims/.gitignore b/JS/wasm/crates/arakoo-core/src/apis/http/shims/.gitignore similarity index 100% rename from JS/wasm/crates/apis/src/http/shims/.gitignore rename to JS/wasm/crates/arakoo-core/src/apis/http/shims/.gitignore diff --git a/JS/wasm/crates/apis/src/http/shims/build.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/build.js similarity index 100% rename from JS/wasm/crates/apis/src/http/shims/build.js rename to JS/wasm/crates/arakoo-core/src/apis/http/shims/build.js diff --git a/JS/wasm/crates/apis/src/http/shims/index.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js similarity index 100% rename from JS/wasm/crates/apis/src/http/shims/index.js rename to JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js diff --git a/JS/wasm/crates/apis/src/http/shims/package.json b/JS/wasm/crates/arakoo-core/src/apis/http/shims/package.json similarity index 100% rename from JS/wasm/crates/apis/src/http/shims/package.json rename to JS/wasm/crates/arakoo-core/src/apis/http/shims/package.json diff --git a/JS/wasm/crates/apis/src/http/types.rs b/JS/wasm/crates/arakoo-core/src/apis/http/types.rs similarity index 100% rename from JS/wasm/crates/apis/src/http/types.rs rename to JS/wasm/crates/arakoo-core/src/apis/http/types.rs diff --git a/JS/wasm/crates/apis/src/jsonnet/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs similarity index 100% rename from JS/wasm/crates/apis/src/jsonnet/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs diff --git a/JS/wasm/crates/apis/src/lib.rs b/JS/wasm/crates/arakoo-core/src/apis/mod.rs similarity index 92% rename from JS/wasm/crates/apis/src/lib.rs rename to JS/wasm/crates/arakoo-core/src/apis/mod.rs index 1003996d2..e74bf75df 100644 --- a/JS/wasm/crates/apis/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/mod.rs @@ -46,19 +46,14 @@ use anyhow::Result; use javy::Runtime; pub use api_config::APIConfig; -#[cfg(feature = "console")] pub use console::LogStream; pub use runtime_ext::RuntimeExt; mod api_config; -#[cfg(feature = "console")] mod console; -#[cfg(feature = "random")] mod random; mod runtime_ext; -#[cfg(feature = "stream_io")] mod stream_io; -#[cfg(feature = "text_encoding")] mod text_encoding; pub mod http; @@ -94,13 +89,9 @@ pub(crate) trait JSApiSet { /// # Ok::<(), Error>(()) /// ``` pub fn add_to_runtime(runtime: &Runtime, config: APIConfig) -> Result<()> { - #[cfg(feature = "console")] console::Console::new().register(runtime, &config)?; - #[cfg(feature = "random")] random::Random.register(runtime, &config)?; - #[cfg(feature = "stream_io")] stream_io::StreamIO.register(runtime, &config)?; - #[cfg(feature = "text_encoding")] text_encoding::TextEncoding.register(runtime, &config)?; http::Http.register(runtime, &config)?; // jsonnet::Jsonnet.register(runtime, &config)?; diff --git a/JS/wasm/crates/apis/src/pdfparse/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/pdfparse/mod.rs similarity index 97% rename from JS/wasm/crates/apis/src/pdfparse/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/pdfparse/mod.rs index 6840fd0c1..7ff29f852 100644 --- a/JS/wasm/crates/apis/src/pdfparse/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/pdfparse/mod.rs @@ -1,4 +1,4 @@ -use crate::{APIConfig, JSApiSet}; +use super::{APIConfig, JSApiSet}; use anyhow::Result; use javy::{ quickjs::{from_qjs_value, to_qjs_value, JSContextRef, JSValue, JSValueRef}, diff --git a/JS/wasm/crates/apis/src/random/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/random/mod.rs similarity index 89% rename from JS/wasm/crates/apis/src/random/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/random/mod.rs index e7bbb5708..65346115e 100644 --- a/JS/wasm/crates/apis/src/random/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/random/mod.rs @@ -1,7 +1,7 @@ use anyhow::Result; use javy::{quickjs::JSValue, Runtime}; -use crate::{APIConfig, JSApiSet}; +use super::{APIConfig, JSApiSet}; pub struct Random; @@ -19,7 +19,9 @@ impl JSApiSet for Random { #[cfg(test)] mod tests { - use crate::{random::Random, APIConfig, JSApiSet}; + use crate::apis::random::Random; + + use super::{ APIConfig, JSApiSet}; use anyhow::Result; use javy::Runtime; diff --git a/JS/wasm/crates/apis/src/runtime_ext.rs b/JS/wasm/crates/arakoo-core/src/apis/runtime_ext.rs similarity index 93% rename from JS/wasm/crates/apis/src/runtime_ext.rs rename to JS/wasm/crates/arakoo-core/src/apis/runtime_ext.rs index d64915741..703767685 100644 --- a/JS/wasm/crates/apis/src/runtime_ext.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/runtime_ext.rs @@ -1,7 +1,7 @@ use anyhow::Result; use javy::{Config, Runtime}; -use crate::APIConfig; +use super::APIConfig; /// A extension trait for [`Runtime`] that creates a [`Runtime`] with APIs /// provided in this crate. @@ -25,7 +25,7 @@ pub trait RuntimeExt { impl RuntimeExt for Runtime { fn new_with_apis(config: Config, api_config: APIConfig) -> Result { let runtime = Runtime::new(config)?; - crate::add_to_runtime(&runtime, api_config)?; + super::add_to_runtime(&runtime, api_config)?; Ok(runtime) } diff --git a/JS/wasm/crates/apis/src/stream_io/io.js b/JS/wasm/crates/arakoo-core/src/apis/stream_io/io.js similarity index 100% rename from JS/wasm/crates/apis/src/stream_io/io.js rename to JS/wasm/crates/arakoo-core/src/apis/stream_io/io.js diff --git a/JS/wasm/crates/apis/src/stream_io/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/stream_io/mod.rs similarity index 98% rename from JS/wasm/crates/apis/src/stream_io/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/stream_io/mod.rs index abdf12176..d6afd6726 100644 --- a/JS/wasm/crates/apis/src/stream_io/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/stream_io/mod.rs @@ -3,7 +3,7 @@ use std::io::{Read, Write}; use javy::Runtime; -use crate::{APIConfig, JSApiSet}; +use super::{APIConfig, JSApiSet}; pub(super) struct StreamIO; diff --git a/JS/wasm/crates/apis/src/text_encoding/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/text_encoding/mod.rs similarity index 97% rename from JS/wasm/crates/apis/src/text_encoding/mod.rs rename to JS/wasm/crates/arakoo-core/src/apis/text_encoding/mod.rs index 7d8007647..ae595b1e3 100644 --- a/JS/wasm/crates/apis/src/text_encoding/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/text_encoding/mod.rs @@ -6,7 +6,7 @@ use javy::{ Runtime, }; -use crate::{APIConfig, JSApiSet}; +use super::{APIConfig, JSApiSet}; pub(super) struct TextEncoding; @@ -81,7 +81,7 @@ fn encode_js_string_to_utf8_buffer( #[cfg(test)] mod tests { - use crate::{APIConfig, JSApiSet}; + use super::{APIConfig, JSApiSet}; use anyhow::Result; use javy::Runtime; diff --git a/JS/wasm/crates/apis/src/text_encoding/text-encoding.js b/JS/wasm/crates/arakoo-core/src/apis/text_encoding/text-encoding.js similarity index 100% rename from JS/wasm/crates/apis/src/text_encoding/text-encoding.js rename to JS/wasm/crates/arakoo-core/src/apis/text_encoding/text-encoding.js diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index 6c82bc16a..7b7cd8432 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -58,6 +58,7 @@ struct Guest; // mod execution; mod runtime; +mod apis; // const FUNCTION_MODULE_NAME: &str = "function.mjs"; diff --git a/JS/wasm/crates/arakoo-core/src/runtime.rs b/JS/wasm/crates/arakoo-core/src/runtime.rs index 51c7591c0..1fd3745d6 100644 --- a/JS/wasm/crates/arakoo-core/src/runtime.rs +++ b/JS/wasm/crates/arakoo-core/src/runtime.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use apis::{APIConfig, LogStream, RuntimeExt}; use javy::{Config, Runtime}; +use super::apis::{APIConfig, LogStream, RuntimeExt}; pub fn new_runtime() -> Result { let mut api_config = APIConfig::default(); From 9932988ccac6450d133110b71bf666d8d24e5556 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Wed, 8 May 2024 11:40:29 +0530 Subject: [PATCH 08/13] Implemented fetch in component model --- Cargo.toml | 3 + .../arakoo-core/src/apis/console/mod.rs | 48 ++++- .../crates/arakoo-core/src/apis/fetch/mod.rs | 29 +-- .../src/apis/fetch/outbound_http.rs | 83 +++++++ .../crates/arakoo-core/src/apis/http/mod.rs | 2 - .../arakoo-core/src/apis/http/shims/index.js | 69 +++--- .../crates/arakoo-core/src/apis/http/types.rs | 38 ---- JS/wasm/crates/arakoo-core/src/apis/mod.rs | 10 +- JS/wasm/crates/arakoo-core/src/apis/types.rs | 48 +++++ JS/wasm/crates/arakoo-core/src/lib.rs | 22 +- JS/wasm/crates/serve/Cargo.toml | 6 +- JS/wasm/crates/serve/src/binding.rs | 202 +++++++----------- JS/wasm/crates/serve/src/lib.rs | 42 +++- JS/wasm/wit/inbound-http.wit | 5 + 14 files changed, 364 insertions(+), 243 deletions(-) create mode 100644 JS/wasm/crates/arakoo-core/src/apis/fetch/outbound_http.rs delete mode 100644 JS/wasm/crates/arakoo-core/src/apis/http/types.rs create mode 100644 JS/wasm/crates/arakoo-core/src/apis/types.rs create mode 100644 JS/wasm/wit/inbound-http.wit diff --git a/Cargo.toml b/Cargo.toml index 7b2fad61d..2d288a0ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,9 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" serde_bytes = "0.11" http = "1.1.0" +reqwest = { version = "0.12.4", features = [ + "blocking","json" +] } [profile.release] lto = true diff --git a/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs index f67c8ae6e..6611f6a4e 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/console/mod.rs @@ -5,6 +5,7 @@ use javy::{ quickjs::{JSContextRef, JSValue, JSValueRef}, Runtime, }; +use quickjs_wasm_rs::from_qjs_value; use super::{APIConfig, JSApiSet}; @@ -58,11 +59,22 @@ where // will invoke a hostcall. let mut log_line = String::new(); for (i, arg) in args.iter().enumerate() { + let val: JSValue = from_qjs_value(*arg)?; if i != 0 { log_line.push(' '); } - let line = arg.to_string(); - log_line.push_str(&line); + if !arg.is_undefined() { + let proto = arg.get_property("__proto__").unwrap().to_string(); + if proto.contains("rror") { + log_line.push_str(&format!("__proto__ is {} Error in js evaluation : {:?}", proto,val)); + } else { + let line: String = log_js_value(&val); + log_line.push_str(&line); + } + } else { + let line: String = log_js_value(&val); + log_line.push_str(&line); + } } writeln!(stream, "{log_line}")?; @@ -71,6 +83,38 @@ where } } +fn log_js_value(arg: &JSValue) -> String { + match arg { + JSValue::String(s) => s.to_string(), + JSValue::Int(n) => n.to_string(), + JSValue::Bool(b) => b.to_string(), + JSValue::Object(o) => { + let flatten_obj = o + .iter() + .map(|(k, v)| format!("{}: {}", k, log_js_value(v))) + .collect::>() + .join(", "); + format!("Object = {{ {} }}", flatten_obj) + } + JSValue::Null => "null".to_string(), + JSValue::Undefined => "undefined".to_string(), + JSValue::Float(f) => f.to_string(), + JSValue::Array(arr) => { + let flatten_vec = arr + .iter() + .map(|v| log_js_value(v)) + .collect::>() + .join(", "); + format!("Array = [{}]", flatten_vec) + } + JSValue::ArrayBuffer(buff) => buff + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(", "), + } +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs index aa64ec7e7..bfd2df2f8 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs @@ -1,12 +1,15 @@ use anyhow::{anyhow, Result}; use http::{request, HeaderName, HeaderValue}; -use quickjs_wasm_rs::{JSContextRef, JSValueRef, Serializer}; use serde_bytes::ByteBuf; -use crate::{types::{HttpRequest, HttpResponse}, JSApiSet}; +// use crate::{types::{HttpRequest, HttpResponse}, JSApiSet}; use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; -use quickjs_wasm_rs::Deserializer; +use quickjs_wasm_rs::{from_qjs_value, Deserializer, Serializer}; use serde::{Deserialize, Serialize}; use std::ops::Deref; +use super::{types::{HttpRequest, HttpResponse}, APIConfig, JSApiSet}; +use super::types::arakoo_http; +use super::wit; +use std::str; mod outbound_http; @@ -14,11 +17,9 @@ mod outbound_http; pub(super) struct Fetch; impl JSApiSet for Fetch { - fn register(&self, runtime: &javy::Runtime, _config: &crate::APIConfig) -> Result<()> { + fn register(&self, runtime: &javy::Runtime, _config: &APIConfig) -> Result<()> { let context = runtime.context(); let global = context.global_object()?; - global.set_property("arakoo", context.value_from_bool(true)?)?; - global.set_property( "__internal_http_send", context.wrap_callback( @@ -35,8 +36,9 @@ impl JSApiSet for Fetch { fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValueRef]) -> Result { match args { [request] => { + println!("Request recieved in send_http_request: {:?}", from_qjs_value(*request).unwrap()); let deserializer = &mut Deserializer::from(request.clone()); - let request = HttpRequest::deserialize(deserializer)?; + let request = HttpRequest::deserialize(deserializer).expect("Unable to deserialize request"); let mut builder = request::Builder::new() .method(request.method.deref()) @@ -45,17 +47,18 @@ fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValue if let Some(headers) = builder.headers_mut() { for (key, value) in &request.headers { headers.insert( - HeaderName::from_bytes(key.as_bytes())?, - HeaderValue::from_bytes(value.as_bytes())?, + HeaderName::from_bytes(key.as_bytes()).expect("Unable to convert key to HeaderName"), + HeaderValue::from_bytes(value.as_bytes()).expect("Unable to convert value to HeaderValue"), ); } } - let outbound_request = builder.body(request.body.map(|buffer| buffer.into_vec().into())).unwrap(); - + let outbound_request = builder.body(request.body.map(|buffer| buffer.into_vec().into())).expect("Unable to build request body"); + println!("outbound_request in wrap_callback: {:?}", outbound_request); let response = outbound_http::send_request( outbound_request )?; + println!("outbound_response in wrap_callback: {:?}", response); let response = HttpResponse { status: response.status().as_u16(), @@ -69,7 +72,7 @@ fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValue )) }) .collect::>()?, - body: response + body: response.clone() .into_body() .map(|bytes| ByteBuf::from(bytes.deref())), status_text: response.status().canonical_reason().unwrap_or("").to_owned(), @@ -77,7 +80,7 @@ fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValue let mut serializer = Serializer::from_context(context)?; response.serialize(&mut serializer)?; - Ok(serializer.value) + Ok(from_qjs_value(serializer.value)?) } _ => Err(anyhow!("expected 1 argument, got {}", args.len())), diff --git a/JS/wasm/crates/arakoo-core/src/apis/fetch/outbound_http.rs b/JS/wasm/crates/arakoo-core/src/apis/fetch/outbound_http.rs new file mode 100644 index 000000000..e2405ff0b --- /dev/null +++ b/JS/wasm/crates/arakoo-core/src/apis/fetch/outbound_http.rs @@ -0,0 +1,83 @@ +use std::ops::Deref; + +use crate::wit::Method; + +use super::arakoo_http::{Request, Response}; +use super::wit::edgechains::http::{Request as OutboundRequest, Response as OutboundResponse}; +use super::wit::edgechains::http_types::HttpError as OutboundHttpError; +use super::wit::edgechains::http::send_request as outbound_send_request; +use http::{header::HeaderName, HeaderValue}; + + +pub fn send_request(req: Request) -> Result { + let (req, body) = req.into_parts(); + + let method = req.method.try_into()?; + + let uri = req.uri.to_string(); + + let params = vec![]; + + let headers = req + .headers + .iter() + .map(try_header_to_strs) + .collect::, OutboundHttpError>>()?; + + let body = body.as_ref().map(|bytes| bytes.as_ref()); + + let out_req = OutboundRequest { + method, + uri, + params, + headers, + body:Some(body.unwrap().to_vec()), + }; + + let OutboundResponse { + status, + headers, + body, + status_text, + } = outbound_send_request(&out_req)?; + + let resp_builder = http::response::Builder::new().status(status); + let resp_builder = headers + .into_iter() + .flatten() + .fold(resp_builder, |b, (k, v)| b.header(k, v)); + resp_builder + .body(body.map(Into::into)) + .map_err(|_| OutboundHttpError::RuntimeError) +} + +fn try_header_to_strs<'k, 'v>( + header: (&'k HeaderName, &'v HeaderValue), +) -> Result<(String, String), OutboundHttpError> { + Ok(( + header.0.as_str().to_string(), + header + .1 + .to_str() + .map_err(|_| OutboundHttpError::InvalidUrl)?.to_string(), + )) +} + +impl TryFrom for Method { + type Error = OutboundHttpError; + + fn try_from(method: http::Method) -> Result { + use http::Method; + use super::wit::Method::*; + Ok(match method { + Method::GET => Get, + Method::POST => Post, + Method::PUT => Put, + Method::DELETE => Delete, + Method::PATCH => Patch, + Method::HEAD => Head, + Method::OPTIONS => Options, + _ => return Err(super::wit::edgechains::http_types::HttpError::RequestError), + }) + } +} diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs index 0019f00ac..4cd7ee999 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/http/mod.rs @@ -1,5 +1,3 @@ -pub mod types; - use anyhow::Result; use super::JSApiSet; diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js index ee209c758..be0150745 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js +++ b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js @@ -331,7 +331,7 @@ globalThis.requestToEvent = (inputReq) => { request, response: {}, respondWith(res) { - console.log("Response recieved ",res); + console.log("Response recieved ", res); this.response = res; }, }; @@ -339,35 +339,38 @@ globalThis.requestToEvent = (inputReq) => { return event; } -// globalThis.fetch = async (uri, options) => { -// let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer -// let fetchOptions = { -// method: (options && options.method) || "GET", -// uri: (uri instanceof URL) ? uri.toString() : uri, -// headers: (options && options.headers) || {}, -// body: encodedBodyData, -// }; -// console.log(JSON.stringify(fetchOptions)) -// const { status, headers, body } = __internal_http_send({ -// method: (options && options.method) || "GET", -// uri: (uri instanceof URL) ? uri.toString() : uri, -// headers: (options && options.headers) || {}, -// body: encodedBodyData, -// }) -// return Promise.resolve({ -// status, -// headers: { -// entries: () => Object.entries(headers || {}), -// get: (key) => (headers && headers[key]) || null, -// has: (key) => (headers && headers[key]) ? true : false -// }, -// arrayBuffer: () => Promise.resolve(body), -// ok: (status > 199 && status < 300), -// statusText: statusTextList[status], -// text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), -// json: () => { -// let text = new TextDecoder().decode(body || new Uint8Array()) -// return Promise.resolve(JSON.parse(text)) -// } -// }) -// } +function fetch(uri, options) { + console.log("In fetch function", uri, options) + let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer + const { status, headers, body } = __internal_http_send({ + method: (options && options.method) || "GET", + uri: (uri instanceof URL) ? uri.toString() : uri, + headers: (options && options.headers) || {}, + body: encodedBodyData, + params: (options && options.params) || {}, + }) + console.log("Response from fetch", status, headers, body) + let obj; + try { + obj = { + status, + headers: { + entries: () => Object.entries(headers || {}), + get: (key) => (headers && headers[key]) || null, + has: (key) => (headers && headers[key]) ? true : false + }, + arrayBuffer: () => Promise.resolve(body), + ok: (status > 199 && status < 300), + statusText:httpStatus[status], + text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), + json: () => { + let text = new TextDecoder().decode(body || new Uint8Array()) + return Promise.resolve(JSON.parse(text)) + } + } + } catch (error) { + console.log("Error occured in sending response from fetch") + console.log(error) + } + return Promise.resolve(obj); +} \ No newline at end of file diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/types.rs b/JS/wasm/crates/arakoo-core/src/apis/http/types.rs deleted file mode 100644 index 121bb5f61..000000000 --- a/JS/wasm/crates/arakoo-core/src/apis/http/types.rs +++ /dev/null @@ -1,38 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; - -#[derive(Serialize, Deserialize, Debug)] -pub struct Request { - url: String, - method: String, - headers: HashMap, - body: String, - params: HashMap, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct Response { - pub headers: HashMap, - pub status: u16, - #[serde(rename = "statusText")] - pub status_text: String, - pub body: Option, -} - -impl Request { - pub fn new( - url: String, - method: String, - headers: HashMap, - body: String, - params: HashMap, - ) -> Self { - Self { - url, - method, - headers, - body, - params, - } - } -} diff --git a/JS/wasm/crates/arakoo-core/src/apis/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/mod.rs index e74bf75df..add1eebc9 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/mod.rs @@ -44,20 +44,25 @@ use anyhow::Result; use javy::Runtime; +use super::wit; pub use api_config::APIConfig; pub use console::LogStream; pub use runtime_ext::RuntimeExt; +pub mod http; +pub mod types; + mod api_config; mod console; mod random; mod runtime_ext; mod stream_io; mod text_encoding; - -pub mod http; mod pdfparse; +mod fetch; + + // mod jsonnet; @@ -96,5 +101,6 @@ pub fn add_to_runtime(runtime: &Runtime, config: APIConfig) -> Result<()> { http::Http.register(runtime, &config)?; // jsonnet::Jsonnet.register(runtime, &config)?; pdfparse::PDFPARSER.register(runtime, &config)?; + fetch::Fetch.register(runtime, &config)?; Ok(()) } diff --git a/JS/wasm/crates/arakoo-core/src/apis/types.rs b/JS/wasm/crates/arakoo-core/src/apis/types.rs new file mode 100644 index 000000000..3a9dc761e --- /dev/null +++ b/JS/wasm/crates/arakoo-core/src/apis/types.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; +use serde_bytes::ByteBuf; + +#[derive(Serialize, Deserialize, Debug)] +pub struct HttpRequest { + pub method: String, + pub uri: String, + #[serde(default)] + pub headers: HashMap, + pub params: HashMap, + pub body: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct HttpResponse { + pub status: u16, + #[serde(default)] + pub headers: HashMap, + pub body: Option, + pub status_text: String, + +} + +pub mod arakoo_http { + use anyhow::Result; + + /// The Arakoo HTTP request. + pub type Request = http::Request>; + + /// The Arakoo HTTP response. + pub type Response = http::Response>; + + /// Helper function to return a 404 Not Found response. + pub fn not_found() -> Result { + Ok(http::Response::builder() + .status(404) + .body(Some("Not Found".into()))?) + } + + /// Helper function to return a 500 Internal Server Error response. + pub fn internal_server_error() -> Result { + Ok(http::Response::builder() + .status(500) + .body(Some("Internal Server Error".into()))?) + } +} \ No newline at end of file diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index 7b7cd8432..8ab6c1962 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -2,7 +2,6 @@ use anyhow::anyhow; use anyhow::Result; use javy::quickjs::from_qjs_value; use javy::quickjs::to_qjs_value; -use javy::quickjs::Deserializer; use javy::quickjs::JSContextRef; use javy::quickjs::JSValue; use javy::quickjs::JSValueRef; @@ -18,25 +17,7 @@ use std::sync::Mutex; use serde_bytes::ByteBuf; -#[derive(Serialize, Deserialize, Debug)] -pub struct HttpRequest { - pub method: String, - pub uri: String, - #[serde(default)] - pub headers: HashMap, - pub params: HashMap, - pub body: Option, -} - -#[derive(Serialize, Deserialize, Debug)] -pub struct HttpResponse { - pub status: u16, - #[serde(default)] - pub headers: HashMap, - pub body: Option, - pub status_text: String, - -} +use crate::apis::types::HttpRequest; pub mod wit { use wit_bindgen::generate; @@ -48,6 +29,7 @@ pub mod wit { use super::Guest; export!(Guest); + pub use self::arakoo::edgechains::http_types::{Method, Request, Response}; pub use self::exports::arakoo::edgechains::inbound_http; pub use self::arakoo::edgechains; diff --git a/JS/wasm/crates/serve/Cargo.toml b/JS/wasm/crates/serve/Cargo.toml index f5cac2e22..de912720b 100644 --- a/JS/wasm/crates/serve/Cargo.toml +++ b/JS/wasm/crates/serve/Cargo.toml @@ -22,8 +22,8 @@ tracing-subscriber = { version = "^0.3.18", features = ["env-filter", "fmt"] } jrsonnet-evaluator = { version = "0.5.0-pre95" } jrsonnet-parser = { version = "0.5.0-pre95" } jrsonnet-stdlib = { version = "0.5.0-pre95" } -reqwest = { version = "0.11", features = [ - "blocking" -] } + arakoo-jsonnet = { path = "../../../jsonnet" } async-trait = "0.1.80" +http = {workspace = true} +reqwest = {workspace = true} diff --git a/JS/wasm/crates/serve/src/binding.rs b/JS/wasm/crates/serve/src/binding.rs index 5ed79f596..c9233e0bd 100644 --- a/JS/wasm/crates/serve/src/binding.rs +++ b/JS/wasm/crates/serve/src/binding.rs @@ -203,133 +203,83 @@ // Ok(()) // } -// pub fn add_fetch_to_linker(linker: &mut Linker) -> anyhow::Result<()> { -// let response: Arc> = Arc::new(Mutex::new(String::new())); -// let error: Arc> = Arc::new(Mutex::new(String::new())); +use super::http_types::{Headers, HttpError, Method, Response}; +use http::HeaderMap; -// let response_clone = response.clone(); -// let error_clone = error.clone(); -// linker.func_wrap( -// "arakoo", -// "fetch", -// move |mut caller: Caller<'_, WasiCtx>, request_ptr: i32, request_len: i32| { -// let response_arc = response_clone.clone(); -// let error = error_clone.clone(); -// let mem = match caller.get_export("memory") { -// Some(Extern::Memory(mem)) => mem, -// _ => { -// let mut error = error.lock().unwrap(); -// *error = "Memory not found".to_string(); -// return Err(Trap::NullReference.into()); -// } -// }; -// let request_offset = request_ptr as u32 as usize; -// let mut request_buffer = vec![0; request_len as usize]; -// let request = match mem.read(&caller, request_offset, &mut request_buffer) { -// Ok(_) => match std::str::from_utf8(&request_buffer) { -// Ok(s) => s.to_string(), // Clone the string here -// Err(_) => { -// let mut error = error.lock().unwrap(); -// *error = "Bad signature".to_string(); -// return Err(Trap::BadSignature.into()); -// } -// }, -// _ => { -// let mut error = error.lock().unwrap(); -// *error = "Memory out of bounds".to_string(); -// return Err(Trap::MemoryOutOfBounds.into()); -// } -// }; +pub fn log_reqwest_error(err: reqwest::Error) -> HttpError { + let error_desc = if err.is_timeout() { + "timeout error" + } else if err.is_connect() { + "connection error" + } else if err.is_body() || err.is_decode() { + "message body error" + } else if err.is_request() { + "request error" + } else { + "error" + }; + tracing::warn!( + "Outbound HTTP {}: URL {}, error detail {:?}", + error_desc, + err.url() + .map(|u| u.to_string()) + .unwrap_or_else(|| "".to_owned()), + err + ); + HttpError::RuntimeError +} -// let thread_result = std::thread::spawn(move || { -// Builder::new_current_thread() -// .enable_all() -// .build() -// .unwrap() -// .block_on(async { -// let request: WasmInput = match serde_json::from_str(&request) { -// Ok(r) => r, -// Err(e) => { -// return Err(anyhow::anyhow!("Error parsing request: {}", e)); -// } -// }; -// let method: reqwest::Method = match request.method().parse() { -// Ok(m) => m, -// Err(_) => { -// return Err(anyhow::anyhow!( -// "Invalid method: {}", -// request.method() -// )); -// } -// }; -// let client = reqwest::Client::new(); -// let mut builder = client.request(method, request.url()); -// let header = request.headers(); -// for (k, v) in header { -// builder = builder.header(k, v); -// } -// builder = builder.body(request.body().to_string()); -// match builder.send().await { -// Ok(r) => { -// let response = WasmOutput::from_reqwest_response(r).await?; -// Ok(response) -// } -// Err(e) => { -// error!("Error sending request: {}", e); -// return Err(anyhow::anyhow!("Error sending request: {}", e)); -// } -// } -// }) -// }) -// .join(); -// let response = match thread_result { -// Ok(Ok(r)) => r, -// Ok(Err(e)) => { -// let mut error = error.lock().unwrap(); -// *error = format!("Error sending request: {}", e); -// error!("Error sending request: {}", e); -// return Err(Trap::BadSignature.into()); -// } -// Err(_) => { -// let mut error = error.lock().unwrap(); -// *error = "Error sending request: thread join error".to_string(); -// error!("Error sending request: thread join error"); -// return Err(Trap::BadSignature.into()); -// } -// }; +pub fn method_from(m: Method) -> http::Method { + match m { + Method::Get => http::Method::GET, + Method::Post => http::Method::POST, + Method::Put => http::Method::PUT, + Method::Delete => http::Method::DELETE, + Method::Patch => http::Method::PATCH, + Method::Head => http::Method::HEAD, + Method::Options => http::Method::OPTIONS, + } +} -// let res = serde_json::to_string(&response).unwrap(); -// let mut response = response_arc.lock().unwrap(); -// *response = res; -// Ok(()) -// }, -// )?; -// // add the fetch_output_len and fetch_output functions here -// let response_clone = response.clone(); -// linker.func_wrap("arakoo", "get_response_len", move || -> i32 { -// let response_clone = response_clone.clone(); -// let response = response_clone.lock().unwrap().clone(); -// response.len() as i32 -// })?; +pub async fn response_from_reqwest(res: reqwest::Response) -> Result { + let status = res.status().as_u16(); + let headers = response_headers(res.headers()).map_err(|_| HttpError::RuntimeError)?; + let status_text = (&res.status().canonical_reason().unwrap_or("")).to_string(); + let body = Some( + res.bytes() + .await + .map_err(|_| HttpError::RuntimeError)? + .to_vec(), + ); -// // also add the fetch_error_len and fetch_error functions here -// linker.func_wrap( -// "arakoo", -// "get_response", -// move |mut _caller: Caller<'_, WasiCtx>, ptr: i32| { -// let response_clone = response.clone(); -// let mem = match _caller.get_export("memory") { -// Some(Extern::Memory(mem)) => mem, -// _ => return Err(Trap::NullReference.into()), -// }; -// let offset = ptr as u32 as usize; -// let response = response_clone.lock().unwrap().clone(); -// match mem.write(&mut _caller, offset, response.as_bytes()) { -// Ok(_) => {} -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// Ok(()) -// }, -// )?; -// Ok(()) -// } + Ok(Response { + status, + headers, + body, + status_text, + }) +} + +pub fn request_headers(h: Headers) -> anyhow::Result { + let mut res = HeaderMap::new(); + for (k, v) in h { + res.insert( + http::header::HeaderName::try_from(k)?, + http::header::HeaderValue::try_from(v)?, + ); + } + Ok(res) +} + +pub fn response_headers(h: &HeaderMap) -> anyhow::Result>> { + let mut res: Vec<(String, String)> = vec![]; + + for (k, v) in h { + res.push(( + k.to_string(), + std::str::from_utf8(v.as_bytes())?.to_string(), + )); + } + + Ok(Some(res)) +} diff --git a/JS/wasm/crates/serve/src/lib.rs b/JS/wasm/crates/serve/src/lib.rs index ee4b49640..e73bf3999 100644 --- a/JS/wasm/crates/serve/src/lib.rs +++ b/JS/wasm/crates/serve/src/lib.rs @@ -27,6 +27,7 @@ use hyper::{ Body, Request, Response, }; +use reqwest::Url; use tracing::{error, event, info, Level}; use tracing_subscriber::{filter::EnvFilter, FmtSubscriber}; // use wasi_common::WasiCtx; @@ -72,6 +73,7 @@ pub struct WorkerCtx { struct Host { table: ResourceTable, wasi: WasiCtx, + client: Option, } impl WasiView for Host { @@ -85,16 +87,47 @@ impl WasiView for Host { } use wit::arakoo::edgechains::http as outbound_http; +use wit::arakoo::edgechains::http_types::{HttpError}; + #[async_trait] impl outbound_http::Host for Host { async fn send_request( &mut self, - request: http_types::Request, + req: http_types::Request, ) -> wasmtime::Result> { - todo!() + // println!("Sending request: {:?}", request); + Ok(async { + tracing::log::trace!("Attempting to send outbound HTTP request to {}", req.uri); + + let method = binding::method_from(req.method); + let url = Url::parse(&req.uri).map_err(|_| HttpError::InvalidUrl)?; + let headers = binding::request_headers(req.headers).map_err(|_| HttpError::RuntimeError)?; + let body = req.body.unwrap_or_default().to_vec(); + + if !req.params.is_empty() { + tracing::log::warn!("HTTP params field is deprecated"); + } + + // Allow reuse of Client's internal connection pool for multiple requests + // in a single component execution + let client = self.client.get_or_insert_with(Default::default); + + let resp = client + .request(method, url) + .headers(headers) + .body(body) + .send() + .await + .map_err(binding::log_reqwest_error)?; + tracing::log::trace!("Returning response from outbound request to {}", req.uri); + binding::response_from_reqwest(resp).await + } + .await) } } +impl http_types::Host for Host {} + impl WorkerCtx { pub fn new(component_path: impl AsRef) -> anyhow::Result { tracing_subscriber(); @@ -165,7 +198,7 @@ impl WorkerCtx { } Err(e) => { - error!("Error: {}", e); + error!("Error: {:?}", e); let response = Response::builder() .status(500) .body(Body::from("Internal Server Error")) @@ -292,7 +325,7 @@ impl WorkerCtx { let table: ResourceTable = ResourceTable::new(); // Create a new store with the WASI context. - let mut store = Store::new(self.engine(), Host { table, wasi }); + let mut store = Store::new(self.engine(), Host { table, wasi, client: None}); // Instantiate the WebAssembly module with the linker and store. // linker.module(&mut store, "", self.module())?; @@ -323,6 +356,7 @@ impl WorkerCtx { params: wasm_input.params, body: wasm_input.body, }; + wit::Reactor::add_to_linker(&mut linker, |x| x)?; let (reactor, instance) = wit::Reactor::instantiate_async(&mut store, self.component(), &linker).await?; let guest = reactor.arakoo_edgechains_inbound_http(); diff --git a/JS/wasm/wit/inbound-http.wit b/JS/wasm/wit/inbound-http.wit new file mode 100644 index 000000000..31bb2b774 --- /dev/null +++ b/JS/wasm/wit/inbound-http.wit @@ -0,0 +1,5 @@ +interface inbound-http { + use http-types.{request, response, method}; + + handle-request: func(req: request) -> response; +} \ No newline at end of file From d91266aae686cce5e7f1ba0e2aa6b39a65def997 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Mon, 13 May 2024 19:45:24 +0530 Subject: [PATCH 09/13] Added jsonnet wit file --- JS/jsonnet/src/jsonnet.js | 4 +-- JS/jsonnet/src/lib.rs | 4 +-- JS/wasm/crates/serve/src/binding.rs | 47 ++++++++++++++++++++++------- JS/wasm/crates/serve/src/lib.rs | 15 +++++++-- JS/wasm/wit/arakoo.wit | 2 +- JS/wasm/wit/jsonnet.wit | 10 ++++++ 6 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 JS/wasm/wit/jsonnet.wit diff --git a/JS/jsonnet/src/jsonnet.js b/JS/jsonnet/src/jsonnet.js index 988c4a737..66b3d6ddc 100644 --- a/JS/jsonnet/src/jsonnet.js +++ b/JS/jsonnet/src/jsonnet.js @@ -8,7 +8,7 @@ if (!isArakoo) { jsonnet_evaluate_snippet, jsonnet_destroy, jsonnet_make, - ext_string, + jsonnet_ext_string, jsonnet_evaluate_file, get_func, set_func, @@ -24,7 +24,7 @@ if (!isArakoo) { } extString(key, value) { - ext_string(this.vm, key, value); + jsonnet_ext_string(this.vm, key, value); return this; } diff --git a/JS/jsonnet/src/lib.rs b/JS/jsonnet/src/lib.rs index 04cbbb96a..620e7076f 100755 --- a/JS/jsonnet/src/lib.rs +++ b/JS/jsonnet/src/lib.rs @@ -127,7 +127,7 @@ pub fn jsonnet_evaluate_file(vm: *mut VM, filename: &str) -> String { } #[wasm_bindgen] -pub fn ext_string(vm: *mut VM, key: &str, value: &str) { +pub fn jsonnet_ext_string(vm: *mut VM, key: &str, value: &str) { let vm = unsafe { &mut *vm }; // let context_initializer_ref = vm.state.context_initializer(); @@ -226,7 +226,7 @@ mod test { // .unwrap(); unsafe { - ext_string( + jsonnet_ext_string( &mut *vm, // name.as_ptr() as *const c_char, "name", // value.as_ptr() as *const c_char, "afshan", diff --git a/JS/wasm/crates/serve/src/binding.rs b/JS/wasm/crates/serve/src/binding.rs index c9233e0bd..6799e5353 100644 --- a/JS/wasm/crates/serve/src/binding.rs +++ b/JS/wasm/crates/serve/src/binding.rs @@ -1,3 +1,7 @@ +use super::http_types::{Headers, HttpError, Method, Response}; +use async_trait::async_trait; +use http::HeaderMap; + // use std::{ // env, // sync::{Arc, Mutex}, @@ -6,20 +10,16 @@ // use arakoo_jsonnet::{ // ext_string, jsonnet_destroy, jsonnet_evaluate_file, jsonnet_evaluate_snippet, jsonnet_make, // }; +use jrsonnet_evaluator::{function::TlaArg, gc::GcHashMap, manifest::ManifestFormat, trace::TraceFormat, State}; +use jrsonnet_parser::IStr; -// use std::{fs, io}; +use std::{fs, io}; // use tokio::runtime::Builder; -// use tracing::error; +use tracing::error; // use wasmtime::*; -// use crate::io::{WasmInput, WasmOutput}; +use crate::io::{WasmInput, WasmOutput}; -// // pub struct VM { -// // state: State, -// // manifest_format: Box, -// // trace_format: Box, -// // tla_args: GcHashMap, -// // } // /// Adds exported functions to the Wasm linker. // /// @@ -203,8 +203,33 @@ // Ok(()) // } -use super::http_types::{Headers, HttpError, Method, Response}; -use http::HeaderMap; +#[async_trait] +impl super::jsonnet::Host for super::Host{ + async fn jsonnet_make(&mut self,) -> wasmtime::Result { + let ptr = arakoo_jsonnet::jsonnet_make(); + Ok(ptr as u64) + } + + async fn jsonnet_evaluate_snippet(&mut self, vm: u64, code: String) -> wasmtime::Result { + let out = arakoo_jsonnet::jsonnet_evaluate_snippet(vm as *mut arakoo_jsonnet::VM, "snippet", &code); + Ok(out) + } + + async fn jsonnet_evaluate_file(&mut self, vm: u64, path: String) -> wasmtime::Result { + let out = arakoo_jsonnet::jsonnet_evaluate_file(vm as *mut arakoo_jsonnet::VM, &path); + Ok(out) + } + + async fn jsonnet_ext_string(&mut self, vm: u64, key: String, value: String) -> wasmtime::Result<()> { + arakoo_jsonnet::jsonnet_ext_string(vm as *mut arakoo_jsonnet::VM, &key, &value); + Ok(()) + } + + async fn jsonnet_destroy(&mut self, vm: u64) -> wasmtime::Result<()> { + arakoo_jsonnet::jsonnet_destroy(vm as *mut arakoo_jsonnet::VM); + Ok(()) + } +} pub fn log_reqwest_error(err: reqwest::Error) -> HttpError { let error_desc = if err.is_timeout() { diff --git a/JS/wasm/crates/serve/src/lib.rs b/JS/wasm/crates/serve/src/lib.rs index e73bf3999..6941334ec 100644 --- a/JS/wasm/crates/serve/src/lib.rs +++ b/JS/wasm/crates/serve/src/lib.rs @@ -87,7 +87,8 @@ impl WasiView for Host { } use wit::arakoo::edgechains::http as outbound_http; -use wit::arakoo::edgechains::http_types::{HttpError}; +use wit::arakoo::edgechains::http_types::HttpError; +use wit::arakoo::edgechains::jsonnet; #[async_trait] impl outbound_http::Host for Host { @@ -101,7 +102,8 @@ impl outbound_http::Host for Host { let method = binding::method_from(req.method); let url = Url::parse(&req.uri).map_err(|_| HttpError::InvalidUrl)?; - let headers = binding::request_headers(req.headers).map_err(|_| HttpError::RuntimeError)?; + let headers = + binding::request_headers(req.headers).map_err(|_| HttpError::RuntimeError)?; let body = req.body.unwrap_or_default().to_vec(); if !req.params.is_empty() { @@ -325,7 +327,14 @@ impl WorkerCtx { let table: ResourceTable = ResourceTable::new(); // Create a new store with the WASI context. - let mut store = Store::new(self.engine(), Host { table, wasi, client: None}); + let mut store = Store::new( + self.engine(), + Host { + table, + wasi, + client: None, + }, + ); // Instantiate the WebAssembly module with the linker and store. // linker.module(&mut store, "", self.module())?; diff --git a/JS/wasm/wit/arakoo.wit b/JS/wasm/wit/arakoo.wit index f44acdad8..a4433c5f2 100644 --- a/JS/wasm/wit/arakoo.wit +++ b/JS/wasm/wit/arakoo.wit @@ -1,7 +1,7 @@ package arakoo:edgechains; - world reactor { import http; + import jsonnet; export inbound-http; } \ No newline at end of file diff --git a/JS/wasm/wit/jsonnet.wit b/JS/wasm/wit/jsonnet.wit new file mode 100644 index 000000000..006303c26 --- /dev/null +++ b/JS/wasm/wit/jsonnet.wit @@ -0,0 +1,10 @@ +interface jsonnet { + record vars { + key:string + } + jsonnet-make: func() -> u64; + jsonnet-evaluate-snippet: func(vm: u64,code: string) -> string; + jsonnet-evaluate-file: func(vm: u64,path: string) -> string; + jsonnet-ext-string: func(vm: u64,key: string, value: string); + jsonnet-destroy: func(vm: u64); +} From 5169adb11b9db0ec9646fd70b4c0a43e84b0d0a5 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Sat, 18 May 2024 23:55:18 +0530 Subject: [PATCH 10/13] Added jsonnet binding in component model --- JS/jsonnet/src/jsonnet.js | 31 ++- JS/jsonnet/src/jsonnet_wasm.d.ts | 56 ++--- JS/jsonnet/src/jsonnet_wasm.js | 213 ++++++++-------- JS/jsonnet/src/jsonnet_wasm_bg.wasm | Bin 1787337 -> 1787345 bytes JS/jsonnet/src/jsonnet_wasm_bg.wasm.d.ts | 11 +- JS/jsonnet/test/dist/test.js | 48 ++-- .../arakoo-core/src/apis/http/shims/index.js | 5 +- .../arakoo-core/src/apis/jsonnet/mod.rs | 132 +++++----- JS/wasm/crates/arakoo-core/src/apis/mod.rs | 20 +- JS/wasm/crates/serve/src/binding.rs | 233 ++++-------------- JS/wasm/crates/serve/src/lib.rs | 48 +--- JS/wasm/examples/ec-wasmjs-hono/build.js | 2 +- JS/wasm/examples/ec-wasmjs-hono/src/index.js | 5 +- JS/wasm/wit/jsonnet.wit | 2 +- 14 files changed, 294 insertions(+), 512 deletions(-) diff --git a/JS/jsonnet/src/jsonnet.js b/JS/jsonnet/src/jsonnet.js index 66b3d6ddc..2cd69d04c 100644 --- a/JS/jsonnet/src/jsonnet.js +++ b/JS/jsonnet/src/jsonnet.js @@ -55,24 +55,35 @@ if (!isArakoo) { }; } else { Jsonnet = class Jsonnet { - constructor() { - this.vars = {}; + constructor() {} + + #getVm() { + if (!this.vm) { + this.vm = __jsonnet_make(); + } + return this.vm; + } + + evaluateSnippet(snippet) { + let vm = this.#getVm(); + return __jsonnet_evaluate_snippet(vm, snippet); } extString(key, value) { - this.vars[key] = value; + let vm = this.#getVm(); + __jsonnet_ext_string(vm, key, value); return this; } - evaluateSnippet(snippet) { - let vars = JSON.stringify(this.vars); - return __jsonnet_evaluate_snippet(vars, snippet); - } + evaluateFile(filename) { - let vars = JSON.stringify(this.vars); - return __jsonnet_evaluate_file(vars, filename); + let vm = this.#getVm(); + return __jsonnet_evaluate_file(vm, filename); } - destroy() {} + destroy() { + let vm = this.#getVm(); + __jsonnet_destroy(vm); + } }; } diff --git a/JS/jsonnet/src/jsonnet_wasm.d.ts b/JS/jsonnet/src/jsonnet_wasm.d.ts index b0876700d..160156e6c 100644 --- a/JS/jsonnet/src/jsonnet_wasm.d.ts +++ b/JS/jsonnet/src/jsonnet_wasm.d.ts @@ -1,45 +1,45 @@ /* tslint:disable */ /* eslint-disable */ /** - * @returns {number} - */ +* @returns {number} +*/ export function jsonnet_make(): number; /** - * @param {number} vm - */ +* @param {number} vm +*/ export function jsonnet_destroy(vm: number): void; /** - * @param {number} vm - * @param {string} filename - * @param {string} snippet - * @returns {string} - */ +* @param {number} vm +* @param {string} filename +* @param {string} snippet +* @returns {string} +*/ export function jsonnet_evaluate_snippet(vm: number, filename: string, snippet: string): string; /** - * @param {number} vm - * @param {string} filename - * @returns {string} - */ +* @param {number} vm +* @param {string} filename +* @returns {string} +*/ export function jsonnet_evaluate_file(vm: number, filename: string): string; /** - * @param {number} vm - * @param {string} key - * @param {string} value - */ -export function ext_string(vm: number, key: string, value: string): void; +* @param {number} vm +* @param {string} key +* @param {string} value +*/ +export function jsonnet_ext_string(vm: number, key: string, value: string): void; /** - * @param {string} name - * @returns {Function | undefined} - */ +* @param {string} name +* @returns {Function | undefined} +*/ export function get_func(name: string): Function | undefined; /** - * @param {string} name - * @param {Function} func - */ +* @param {string} name +* @param {Function} func +*/ export function set_func(name: string, func: Function): void; /** - * @param {number} vm - * @param {string} name - * @param {number} args_num - */ +* @param {number} vm +* @param {string} name +* @param {number} args_num +*/ export function register_native_callback(vm: number, name: string, args_num: number): void; diff --git a/JS/jsonnet/src/jsonnet_wasm.js b/JS/jsonnet/src/jsonnet_wasm.js index 32b2f627e..7f1d7c2cf 100644 --- a/JS/jsonnet/src/jsonnet_wasm.js +++ b/JS/jsonnet/src/jsonnet_wasm.js @@ -1,5 +1,5 @@ let imports = {}; -imports["__wbindgen_placeholder__"] = module.exports; +imports['__wbindgen_placeholder__'] = module.exports; let wasm; const { read_file } = require(String.raw`./snippets/arakoo-jsonnet-17c737407ebd2d3c/read-file.js`); const { TextEncoder, TextDecoder } = require(`util`); @@ -8,9 +8,7 @@ const heap = new Array(128).fill(undefined); heap.push(undefined, null, true, false); -function getObject(idx) { - return heap[idx]; -} +function getObject(idx) { return heap[idx]; } let WASM_VECTOR_LEN = 0; @@ -23,29 +21,27 @@ function getUint8Memory0() { return cachedUint8Memory0; } -let cachedTextEncoder = new TextEncoder("utf-8"); - -const encodeString = - typeof cachedTextEncoder.encodeInto === "function" - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); - } - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length, - }; - }; +let cachedTextEncoder = new TextEncoder('utf-8'); + +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); function passStringToWasm0(arg, malloc, realloc) { + if (realloc === undefined) { const buf = cachedTextEncoder.encode(arg); const ptr = malloc(buf.length, 1) >>> 0; - getUint8Memory0() - .subarray(ptr, ptr + buf.length) - .set(buf); + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); WASM_VECTOR_LEN = buf.length; return ptr; } @@ -59,7 +55,7 @@ function passStringToWasm0(arg, malloc, realloc) { for (; offset < len; offset++) { const code = arg.charCodeAt(offset); - if (code > 0x7f) break; + if (code > 0x7F) break; mem[ptr + offset] = code; } @@ -67,7 +63,7 @@ function passStringToWasm0(arg, malloc, realloc) { if (offset !== 0) { arg = arg.slice(offset); } - ptr = realloc(ptr, len, (len = offset + arg.length * 3), 1) >>> 0; + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; const view = getUint8Memory0().subarray(ptr + offset, ptr + len); const ret = encodeString(arg, view); @@ -106,7 +102,7 @@ function takeObject(idx) { return ret; } -let cachedTextDecoder = new TextDecoder("utf-8", { ignoreBOM: true, fatal: true }); +let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }); cachedTextDecoder.decode(); @@ -127,39 +123,39 @@ function addHeapObject(obj) { function debugString(val) { // primitive types const type = typeof val; - if (type == "number" || type == "boolean" || val == null) { - return `${val}`; + if (type == 'number' || type == 'boolean' || val == null) { + return `${val}`; } - if (type == "string") { + if (type == 'string') { return `"${val}"`; } - if (type == "symbol") { + if (type == 'symbol') { const description = val.description; if (description == null) { - return "Symbol"; + return 'Symbol'; } else { return `Symbol(${description})`; } } - if (type == "function") { + if (type == 'function') { const name = val.name; - if (typeof name == "string" && name.length > 0) { + if (typeof name == 'string' && name.length > 0) { return `Function(${name})`; } else { - return "Function"; + return 'Function'; } } // objects if (Array.isArray(val)) { const length = val.length; - let debug = "["; + let debug = '['; if (length > 0) { debug += debugString(val[0]); } - for (let i = 1; i < length; i++) { - debug += ", " + debugString(val[i]); + for(let i = 1; i < length; i++) { + debug += ', ' + debugString(val[i]); } - debug += "]"; + debug += ']'; return debug; } // Test for built-in @@ -171,14 +167,14 @@ function debugString(val) { // Failed to match the standard '[object ClassName]' return toString.call(val); } - if (className == "Object") { + if (className == 'Object') { // we're a user defined class or Object // JSON.stringify avoids problems with cycles, and is generally much // easier than looping through ownProperties of `val`. try { - return "Object(" + JSON.stringify(val) + ")"; + return 'Object(' + JSON.stringify(val) + ')'; } catch (_) { - return "Object"; + return 'Object'; } } // errors @@ -197,27 +193,27 @@ function handleError(f, args) { } } /** - * @returns {number} - */ -module.exports.jsonnet_make = function () { +* @returns {number} +*/ +module.exports.jsonnet_make = function() { const ret = wasm.jsonnet_make(); return ret >>> 0; }; /** - * @param {number} vm - */ -module.exports.jsonnet_destroy = function (vm) { +* @param {number} vm +*/ +module.exports.jsonnet_destroy = function(vm) { wasm.jsonnet_destroy(vm); }; /** - * @param {number} vm - * @param {string} filename - * @param {string} snippet - * @returns {string} - */ -module.exports.jsonnet_evaluate_snippet = function (vm, filename, snippet) { +* @param {number} vm +* @param {string} filename +* @param {string} snippet +* @returns {string} +*/ +module.exports.jsonnet_evaluate_snippet = function(vm, filename, snippet) { let deferred3_0; let deferred3_1; try { @@ -239,11 +235,11 @@ module.exports.jsonnet_evaluate_snippet = function (vm, filename, snippet) { }; /** - * @param {number} vm - * @param {string} filename - * @returns {string} - */ -module.exports.jsonnet_evaluate_file = function (vm, filename) { +* @param {number} vm +* @param {string} filename +* @returns {string} +*/ +module.exports.jsonnet_evaluate_file = function(vm, filename) { let deferred2_0; let deferred2_1; try { @@ -263,23 +259,23 @@ module.exports.jsonnet_evaluate_file = function (vm, filename) { }; /** - * @param {number} vm - * @param {string} key - * @param {string} value - */ -module.exports.ext_string = function (vm, key, value) { +* @param {number} vm +* @param {string} key +* @param {string} value +*/ +module.exports.jsonnet_ext_string = function(vm, key, value) { const ptr0 = passStringToWasm0(key, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; const ptr1 = passStringToWasm0(value, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len1 = WASM_VECTOR_LEN; - wasm.ext_string(vm, ptr0, len0, ptr1, len1); + wasm.jsonnet_ext_string(vm, ptr0, len0, ptr1, len1); }; /** - * @param {string} name - * @returns {Function | undefined} - */ -module.exports.get_func = function (name) { +* @param {string} name +* @returns {Function | undefined} +*/ +module.exports.get_func = function(name) { const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; const ret = wasm.get_func(ptr0, len0); @@ -287,81 +283,73 @@ module.exports.get_func = function (name) { }; /** - * @param {string} name - * @param {Function} func - */ -module.exports.set_func = function (name, func) { +* @param {string} name +* @param {Function} func +*/ +module.exports.set_func = function(name, func) { const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; wasm.set_func(ptr0, len0, addHeapObject(func)); }; /** - * @param {number} vm - * @param {string} name - * @param {number} args_num - */ -module.exports.register_native_callback = function (vm, name, args_num) { +* @param {number} vm +* @param {string} name +* @param {number} args_num +*/ +module.exports.register_native_callback = function(vm, name, args_num) { const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len0 = WASM_VECTOR_LEN; wasm.register_native_callback(vm, ptr0, len0, args_num); }; -module.exports.__wbindgen_string_get = function (arg0, arg1) { +module.exports.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); - const ret = typeof obj === "string" ? obj : undefined; - var ptr1 = isLikeNone(ret) - ? 0 - : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const ret = typeof(obj) === 'string' ? obj : undefined; + var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); var len1 = WASM_VECTOR_LEN; getInt32Memory0()[arg0 / 4 + 1] = len1; getInt32Memory0()[arg0 / 4 + 0] = ptr1; }; -module.exports.__wbindgen_object_drop_ref = function (arg0) { +module.exports.__wbindgen_object_drop_ref = function(arg0) { takeObject(arg0); }; -module.exports.__wbindgen_string_new = function (arg0, arg1) { +module.exports.__wbindgen_string_new = function(arg0, arg1) { const ret = getStringFromWasm0(arg0, arg1); return addHeapObject(ret); }; -module.exports.__wbg_readfile_5b48d0f7e3518df2 = function () { - return handleError(function (arg0, arg1, arg2) { - const ret = read_file(getStringFromWasm0(arg1, arg2)); - const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - const len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; - }, arguments); -}; +module.exports.__wbg_readfile_5b48d0f7e3518df2 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = read_file(getStringFromWasm0(arg1, arg2)); + const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); + const len1 = WASM_VECTOR_LEN; + getInt32Memory0()[arg0 / 4 + 1] = len1; + getInt32Memory0()[arg0 / 4 + 0] = ptr1; +}, arguments) }; -module.exports.__wbindgen_object_clone_ref = function (arg0) { +module.exports.__wbindgen_object_clone_ref = function(arg0) { const ret = getObject(arg0); return addHeapObject(ret); }; -module.exports.__wbg_call_27c0f87801dedf93 = function () { - return handleError(function (arg0, arg1) { - const ret = getObject(arg0).call(getObject(arg1)); - return addHeapObject(ret); - }, arguments); -}; +module.exports.__wbg_call_27c0f87801dedf93 = function() { return handleError(function (arg0, arg1) { + const ret = getObject(arg0).call(getObject(arg1)); + return addHeapObject(ret); +}, arguments) }; -module.exports.__wbg_call_b3ca7c6051f9bec1 = function () { - return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); - return addHeapObject(ret); - }, arguments); -}; +module.exports.__wbg_call_b3ca7c6051f9bec1 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).call(getObject(arg1), getObject(arg2)); + return addHeapObject(ret); +}, arguments) }; -module.exports.__wbg_new_abda76e883ba8a5f = function () { +module.exports.__wbg_new_abda76e883ba8a5f = function() { const ret = new Error(); return addHeapObject(ret); }; -module.exports.__wbg_stack_658279fe44541cf6 = function (arg0, arg1) { +module.exports.__wbg_stack_658279fe44541cf6 = function(arg0, arg1) { const ret = getObject(arg1).stack; const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len1 = WASM_VECTOR_LEN; @@ -369,7 +357,7 @@ module.exports.__wbg_stack_658279fe44541cf6 = function (arg0, arg1) { getInt32Memory0()[arg0 / 4 + 0] = ptr1; }; -module.exports.__wbg_error_f851667af71bcfc6 = function (arg0, arg1) { +module.exports.__wbg_error_f851667af71bcfc6 = function(arg0, arg1) { let deferred0_0; let deferred0_1; try { @@ -381,7 +369,7 @@ module.exports.__wbg_error_f851667af71bcfc6 = function (arg0, arg1) { } }; -module.exports.__wbindgen_debug_string = function (arg0, arg1) { +module.exports.__wbindgen_debug_string = function(arg0, arg1) { const ret = debugString(getObject(arg1)); const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); const len1 = WASM_VECTOR_LEN; @@ -389,14 +377,15 @@ module.exports.__wbindgen_debug_string = function (arg0, arg1) { getInt32Memory0()[arg0 / 4 + 0] = ptr1; }; -module.exports.__wbindgen_throw = function (arg0, arg1) { +module.exports.__wbindgen_throw = function(arg0, arg1) { throw new Error(getStringFromWasm0(arg0, arg1)); }; -const path = require("path").join(__dirname, "jsonnet_wasm_bg.wasm"); -const bytes = require("fs").readFileSync(path); +const path = require('path').join(__dirname, 'jsonnet_wasm_bg.wasm'); +const bytes = require('fs').readFileSync(path); const wasmModule = new WebAssembly.Module(bytes); const wasmInstance = new WebAssembly.Instance(wasmModule, imports); wasm = wasmInstance.exports; module.exports.__wasm = wasm; + diff --git a/JS/jsonnet/src/jsonnet_wasm_bg.wasm b/JS/jsonnet/src/jsonnet_wasm_bg.wasm index 0a1c7abb76cbb1398721d411ef0e723345727343..320978dac9fe35288b2c977be4f0afbeb2f37d64 100644 GIT binary patch delta 138 zcmWm6xe6yUxFL-n3Ys zcO*AeOn0*^q5G#{GTJphbrs!|7z4r#~W{CO`lH diff --git a/JS/jsonnet/src/jsonnet_wasm_bg.wasm.d.ts b/JS/jsonnet/src/jsonnet_wasm_bg.wasm.d.ts index b134fd153..82d72aa0d 100644 --- a/JS/jsonnet/src/jsonnet_wasm_bg.wasm.d.ts +++ b/JS/jsonnet/src/jsonnet_wasm_bg.wasm.d.ts @@ -3,16 +3,9 @@ export const memory: WebAssembly.Memory; export function jsonnet_make(): number; export function jsonnet_destroy(a: number): void; -export function jsonnet_evaluate_snippet( - a: number, - b: number, - c: number, - d: number, - e: number, - f: number -): void; +export function jsonnet_evaluate_snippet(a: number, b: number, c: number, d: number, e: number, f: number): void; export function jsonnet_evaluate_file(a: number, b: number, c: number, d: number): void; -export function ext_string(a: number, b: number, c: number, d: number, e: number): void; +export function jsonnet_ext_string(a: number, b: number, c: number, d: number, e: number): void; export function get_func(a: number, b: number): number; export function set_func(a: number, b: number, c: number): void; export function register_native_callback(a: number, b: number, c: number, d: number): void; diff --git a/JS/jsonnet/test/dist/test.js b/JS/jsonnet/test/dist/test.js index d9da59521..f30d37c0d 100644 --- a/JS/jsonnet/test/dist/test.js +++ b/JS/jsonnet/test/dist/test.js @@ -6,8 +6,7 @@ import { describe, it } from "mocha"; let jsonnet = new Jsonnet(); describe("Testing evaluateSnippet function of jsonnet library", () => { it("self reference", () => { - let result = JSON.parse( - jsonnet.evaluateSnippet(`{ + let result = JSON.parse(jsonnet.evaluateSnippet(`{ Martini: { local drink = self, ingredients: [ @@ -20,8 +19,7 @@ describe("Testing evaluateSnippet function of jsonnet library", () => { garnish: 'Olive', served: 'Straight Up', }, - }`) - ); + }`)); let expected = { Martini: { garnish: "Olive", @@ -42,15 +40,13 @@ describe("Testing evaluateSnippet function of jsonnet library", () => { expect(result).to.eql(expected); }); it("math operations", () => { - let result = JSON.parse( - jsonnet.evaluateSnippet(`{ + let result = JSON.parse(jsonnet.evaluateSnippet(`{ a: 1 + 2, b: 3 * 4, c: 5 / 6, d: 7 % 8, e: 9 - 10, - }`) - ); + }`)); let expected = { a: 3, b: 12, @@ -92,8 +88,7 @@ describe("Testing evaluateFile function of jsonnet library", () => { }); describe("Testing extString function of jsonnet library", () => { it("Test extString function", () => { - let result = JSON.parse( - jsonnet.extString("name", "Alice").evaluateSnippet(`local username = std.extVar('name'); + let result = JSON.parse(jsonnet.extString("name", "Alice").evaluateSnippet(`local username = std.extVar('name'); local Person(name='Alice') = { name: name, welcome: 'Hello ' + name + '!', @@ -101,8 +96,7 @@ describe("Testing extString function of jsonnet library", () => { { person1: Person(username), person2: Person('Bob'), - }`) - ); + }`)); let expected = { person1: { name: "Alice", @@ -135,13 +129,11 @@ describe("Testing regex function of jsonnet library", () => { }); describe("Testing join function of jsonnet library", () => { it("Test join function", () => { - let result = JSON.parse( - jsonnet.evaluateSnippet(`local a = "open"; + let result = JSON.parse(jsonnet.evaluateSnippet(`local a = "open"; local b = "source"; { "joined string":arakoo.join(a,b) - }`) - ); + }`)); let expected = { "joined string": "opensource", }; @@ -153,12 +145,10 @@ describe("Testing javascript native function of jsonnet library", () => { function add(a, b, c) { return a + b + c; } - let result = JSON.parse( - jsonnet.javascriptCallback("add", add).evaluateSnippet(`{ + let result = JSON.parse(jsonnet.javascriptCallback("add", add).evaluateSnippet(`{ "result": "Output "+arakoo.native("add")(1,2,3), "name":"Alice" - }`) - ); + }`)); expect(result).to.eql({ result: "Output 6", name: "Alice", @@ -173,12 +163,10 @@ describe("Testing javascript native function of jsonnet library", () => { } return sum; } - let result = JSON.parse( - jsonnet.javascriptCallback("arrsum", calcSum).evaluateSnippet(`{ + let result = JSON.parse(jsonnet.javascriptCallback("arrsum", calcSum).evaluateSnippet(`{ "result": "Output "+arakoo.native("arrsum")(${JSON.stringify(numArr)}), "name":"Alice" - }`) - ); + }`)); expect(result).to.eql({ result: "Output 15", name: "Alice", @@ -188,12 +176,10 @@ describe("Testing javascript native function of jsonnet library", () => { function concat(a, b) { return a + b; } - let result = JSON.parse( - jsonnet.javascriptCallback("concat", concat).evaluateSnippet(`{ + let result = JSON.parse(jsonnet.javascriptCallback("concat", concat).evaluateSnippet(`{ "result": "Output "+arakoo.native("concat")("Hello ","World"), "name":"Alice" - }`) - ); + }`)); expect(result).to.eql({ result: "Output Hello World", name: "Alice", @@ -202,11 +188,9 @@ describe("Testing javascript native function of jsonnet library", () => { }); describe("Testing includes function of jsonnet library", () => { it("Test includes function", () => { - let result = JSON.parse( - jsonnet.evaluateSnippet(`{ + let result = JSON.parse(jsonnet.evaluateSnippet(`{ "result":arakoo.includes("open source is awesome","source") - }`) - ); + }`)); let expected = { result: true, }; diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js index be0150745..c24cd95b8 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js +++ b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js @@ -173,7 +173,8 @@ class Request { this.headers = new Headers(input.headers || {}); let bodyArray = new Uint8Array(input.body); let bodyString = decoder.decode(bodyArray); - this.body = JSON.parse(bodyString); + if (bodyString != undefined && bodyString.length > 0) + this.body = JSON.parse(bodyString); this.params = input.params || {}; this.geo = input.geo || {}; } @@ -361,7 +362,7 @@ function fetch(uri, options) { }, arrayBuffer: () => Promise.resolve(body), ok: (status > 199 && status < 300), - statusText:httpStatus[status], + statusText: httpStatus[status], text: () => Promise.resolve(new TextDecoder().decode(body || new Uint8Array())), json: () => { let text = new TextDecoder().decode(body || new Uint8Array()) diff --git a/JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs index a6a7175d6..6b1474b34 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/jsonnet/mod.rs @@ -1,103 +1,99 @@ +use super::{wit::edgechains, APIConfig, JSApiSet}; use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; -use crate::{ - jsonnet_evaluate, jsonnet_evaluate_file, jsonnet_output, jsonnet_output_len, JSApiSet, -}; - pub(super) struct Jsonnet; impl JSApiSet for Jsonnet { - fn register(&self, runtime: &javy::Runtime, _config: &crate::APIConfig) -> anyhow::Result<()> { + fn register(&self, runtime: &javy::Runtime, _config: &APIConfig) -> anyhow::Result<()> { let context = runtime.context(); let global = context.global_object()?; + + global.set_property( + "__jsonnet_make", + context.wrap_callback(jsonnet_make_closure())?, + )?; + global.set_property( + "__jsonnet_ext_string", + context.wrap_callback(jsonnet_ext_string_closure())?, + )?; global.set_property( "__jsonnet_evaluate_snippet", - context.wrap_callback(jsonnet_evaluate_snippet_callback())?, + context.wrap_callback(jsonnet_evaluate_snippet_closure())?, )?; global.set_property( "__jsonnet_evaluate_file", - context.wrap_callback(jsonnet_evaluate_file_callback())?, + context.wrap_callback(jsonnet_evaluate_file_closure())?, + )?; + global.set_property( + "__jsonnet_destroy", + context.wrap_callback(jsonnet_destroy_closure())?, )?; - Ok(()) } } -fn jsonnet_evaluate_file_callback( +fn jsonnet_make_closure( +) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { + move |_ctx, _this, args| Ok(JSValue::Float(edgechains::jsonnet::jsonnet_make() as f64)) +} + +fn jsonnet_ext_string_closure( ) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { move |_ctx, _this, args| { // check the number of arguments - if args.len() != 2 { - return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len())); + if args.len() != 3 { + return Err(anyhow::anyhow!( + "Expected 2 arguments, got {}", + args.len() - 1 + )); } - let var = args.get(0).unwrap().to_string(); - let path = args.get(1).unwrap().to_string(); - let var_len = var.len() as i32; - let path_len = path.len() as i32; - let var_ptr = var.as_ptr(); - let path_ptr = path.as_ptr(); - - unsafe { jsonnet_evaluate_file(var_ptr, var_len, path_ptr, path_len) } - let out_len = unsafe { jsonnet_output_len() }; - - let mut out_buffer = Vec::with_capacity(out_len as usize); - let out_ptr = out_buffer.as_mut_ptr(); - let out_buffer = unsafe { - jsonnet_output(out_ptr); - Vec::from_raw_parts(out_ptr, out_len as usize, out_len as usize) - }; - // println!("out_buffer: {}", String::from_utf8(out_buffer.clone()).expect("unable to convert to string")); - - let jsonnet_output: serde_json::Value = match serde_json::from_slice(&out_buffer) { - Ok(output) => output, - Err(e) => { - eprintln!("Failed to parse jsonnet output: {}", e); - return Err(anyhow::anyhow!( - "Failed to parse jsonnet output: {}", - e.to_string() - )); - } - }; - let jsonnet_output = jsonnet_output.to_string(); - Ok(jsonnet_output.into()) + let vm = args.get(0).unwrap().as_f64().unwrap(); + let key = args.get(1).unwrap().to_string(); + let value = args.get(2).unwrap().to_string(); + edgechains::jsonnet::jsonnet_ext_string(vm as u64, &key, &value); + Ok(JSValue::Undefined) } } -fn jsonnet_evaluate_snippet_callback( +fn jsonnet_evaluate_snippet_closure( ) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { move |_ctx, _this, args| { // check the number of arguments if args.len() != 2 { return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len())); } - let var = args.get(0).unwrap().to_string(); + let vm = args.get(0).unwrap().as_f64().unwrap(); let code = args.get(1).unwrap().to_string(); - let var_len = var.len() as i32; - let path_len = code.len() as i32; - let var_ptr = var.as_ptr(); - let path_ptr = code.as_ptr(); - - unsafe { jsonnet_evaluate(var_ptr, var_len, path_ptr, path_len) } - let out_len = unsafe { jsonnet_output_len() }; + let code = code.as_str(); + let out = edgechains::jsonnet::jsonnet_evaluate_snippet(vm as u64, "snippet", code); + Ok(out.into()) + } +} - let mut out_buffer = Vec::with_capacity(out_len as usize); - let out_ptr = out_buffer.as_mut_ptr(); - let out_buffer = unsafe { - jsonnet_output(out_ptr); - Vec::from_raw_parts(out_ptr, out_len as usize, out_len as usize) - }; +fn jsonnet_evaluate_file_closure( +) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { + move |_ctx, _this, args| { + // check the number of arguments + if args.len() != 2 { + return Err(anyhow::anyhow!("Expected 2 arguments, got {}", args.len())); + } + let vm = args.get(0).unwrap().as_f64().unwrap(); + let path = args.get(1).unwrap().to_string(); + let path = path.as_str(); + let out = edgechains::jsonnet::jsonnet_evaluate_file(vm as u64, path); + Ok(out.into()) + } +} - let jsonnet_output: serde_json::Value = match serde_json::from_slice(&out_buffer) { - Ok(output) => output, - Err(e) => { - eprintln!("Failed to parse jsonnet output: {}", e); - return Err(anyhow::anyhow!( - "Failed to parse jsonnet output: {}", - e.to_string() - )); - } - }; - let jsonnet_output = jsonnet_output.to_string(); - Ok(jsonnet_output.into()) +fn jsonnet_destroy_closure( +) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { + move |_ctx, _this, args| { + // check the number of arguments + if args.len() != 1 { + return Err(anyhow::anyhow!("Expected 1 arguments, got {}", args.len())); + } + let vm = args.get(0).unwrap().as_f64().unwrap(); + edgechains::jsonnet::jsonnet_destroy(vm as u64); + Ok(JSValue::Undefined) } } diff --git a/JS/wasm/crates/arakoo-core/src/apis/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/mod.rs index add1eebc9..359481077 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/mod.rs @@ -61,23 +61,7 @@ mod stream_io; mod text_encoding; mod pdfparse; mod fetch; - - - -// mod jsonnet; - -// #[link(wasm_import_module = "arakoo")] -// extern "C" { -// fn jsonnet_evaluate(var_ptr: *const u8, var_len: i32, code_ptr: *const u8, code_len: i32); -// fn jsonnet_evaluate_file(var_ptr: *const u8, var_len: i32, path_ptr: *const u8, path_len: i32); -// fn jsonnet_output_len() -> i32; -// fn jsonnet_output(ptr: *mut u8); -// fn fetch(request_pointer: *const u8, request_len: i32); -// fn get_response_len() -> i32; -// fn get_response(ptr: *mut u8); -// } - - +mod jsonnet; pub(crate) trait JSApiSet { fn register(&self, runtime: &Runtime, config: &APIConfig) -> Result<()>; } @@ -99,7 +83,7 @@ pub fn add_to_runtime(runtime: &Runtime, config: APIConfig) -> Result<()> { stream_io::StreamIO.register(runtime, &config)?; text_encoding::TextEncoding.register(runtime, &config)?; http::Http.register(runtime, &config)?; - // jsonnet::Jsonnet.register(runtime, &config)?; + jsonnet::Jsonnet.register(runtime, &config)?; pdfparse::PDFPARSER.register(runtime, &config)?; fetch::Fetch.register(runtime, &config)?; Ok(()) diff --git a/JS/wasm/crates/serve/src/binding.rs b/JS/wasm/crates/serve/src/binding.rs index 6799e5353..3752d28cb 100644 --- a/JS/wasm/crates/serve/src/binding.rs +++ b/JS/wasm/crates/serve/src/binding.rs @@ -1,6 +1,7 @@ use super::http_types::{Headers, HttpError, Method, Response}; use async_trait::async_trait; use http::HeaderMap; +use reqwest::Url; // use std::{ // env, @@ -21,188 +22,6 @@ use tracing::error; use crate::io::{WasmInput, WasmOutput}; -// /// Adds exported functions to the Wasm linker. -// /// -// /// This function wraps the `jsonnet_evaluate`, `jsonnet_output_len`, and `jsonnet_output` -// /// functions to be called from WebAssembly. It sets up the necessary state and -// /// memory management for evaluating Jsonnet code and writing the output back to -// /// WebAssembly memory. -// pub fn add_jsonnet_to_linker(linker: &mut Linker) -> anyhow::Result<()> { -// // Create a shared output buffer that will be used to store the result of the Jsonnet evaluation. -// let output: Arc> = Arc::new(Mutex::new(String::new())); -// let mut output_clone = output.clone(); - -// // Wrap the `jsonnet_evaluate` function to be called from WebAssembly. -// linker.func_wrap( -// "arakoo", -// "jsonnet_evaluate", -// move |mut caller: Caller<'_, WasiCtx>, -// var_ptr: i32, -// var_len: i32, -// path_ptr: i32, -// code_len: i32| { -// // Clone the output buffer for use within the closure. -// let output = output_clone.clone(); -// // Get the WebAssembly memory instance. -// let mem = match caller.get_export("memory") { -// Some(Extern::Memory(mem)) => mem, -// _ => return Err(Trap::NullReference.into()), -// }; -// // Calculate the offsets for the variable and path buffers in WebAssembly memory. -// let var_offset = var_ptr as u32 as usize; -// let path_offset = path_ptr as u32 as usize; -// // Create buffers to read the variable and path data from WebAssembly memory. -// let mut var_buffer = vec![0; var_len as usize]; -// let mut path_buffer = vec![0; code_len as usize]; - -// // Read the path data from WebAssembly memory and convert it to a string. -// let path = match mem.read(&caller, path_offset, &mut path_buffer) { -// Ok(_) => match std::str::from_utf8(&path_buffer) { -// Ok(s) => s, -// Err(_) => return Err(Trap::BadSignature.into()), -// }, -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// // Read the variable data from WebAssembly memory and convert it to a string. -// let var = match mem.read(&caller, var_offset, &mut var_buffer) { -// Ok(_) => match std::str::from_utf8(&var_buffer) { -// Ok(s) => s, -// Err(_) => return Err(Trap::BadSignature.into()), -// }, -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// // Parse the variable data as JSON. -// let var_json: serde_json::Value = match serde_json::from_str(var) { -// Ok(v) => v, -// Err(e) => { -// error!("Error parsing var: {}", e); -// return Err(Trap::BadSignature.into()); -// } -// }; - -// // Initialize the Jsonnet VM state with default settings. -// let vm = jsonnet_make(); - -// // Evaluate the Jsonnet code snippet using the provided path and variables. -// let code = path; -// for (key, value) in var_json.as_object().unwrap() { -// // context.add_ext_var(key.into(), Val::Str(value.as_str().unwrap().into())); -// ext_string( -// vm, -// key, -// value.as_str().expect("ext_string value is not a string"), -// ); -// } -// let out = jsonnet_evaluate_snippet(vm, "deleteme", code); -// // Store the output of the Jsonnet evaluation in the shared output buffer. -// let mut output = output.lock().unwrap(); -// *output = out; -// jsonnet_destroy(vm); -// Ok(()) -// }, -// )?; - -// output_clone = output.clone(); -// // Wrap the `jsonnet_evaluate_file` function to be called from WebAssembly. -// linker.func_wrap( -// "arakoo", -// "jsonnet_evaluate_file", -// move |mut caller: Caller<'_, WasiCtx>, -// var_ptr: i32, -// var_len: i32, -// path_ptr: i32, -// code_len: i32| { -// // Clone the output buffer for use within the closure. -// let output = output_clone.clone(); -// // Get the WebAssembly memory instance. -// let mem = match caller.get_export("memory") { -// Some(Extern::Memory(mem)) => mem, -// _ => return Err(Trap::NullReference.into()), -// }; -// // Calculate the offsets for the variable and path buffers in WebAssembly memory. -// let var_offset = var_ptr as u32 as usize; -// let path_offset = path_ptr as u32 as usize; -// // Create buffers to read the variable and path data from WebAssembly memory. -// let mut var_buffer = vec![0; var_len as usize]; -// let mut path_buffer = vec![0; code_len as usize]; - -// // Read the path data from WebAssembly memory and convert it to a string. -// let path = match mem.read(&caller, path_offset, &mut path_buffer) { -// Ok(_) => match std::str::from_utf8(&path_buffer) { -// Ok(s) => s, -// Err(_) => return Err(Trap::BadSignature.into()), -// }, -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// // Read the variable data from WebAssembly memory and convert it to a string. -// let var = match mem.read(&caller, var_offset, &mut var_buffer) { -// Ok(_) => match std::str::from_utf8(&var_buffer) { -// Ok(s) => s, -// Err(_) => return Err(Trap::BadSignature.into()), -// }, -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// // Parse the variable data as JSON. -// let var_json: serde_json::Value = match serde_json::from_str(var) { -// Ok(v) => v, -// Err(e) => { -// error!("Error parsing var: {}", e); -// return Err(Trap::BadSignature.into()); -// } -// }; -// // println!("var_json: {:?}", var_json); - -// let vm = jsonnet_make(); - -// for (key, value) in var_json.as_object().unwrap() { -// ext_string( -// vm, -// key, -// value.as_str().expect("ext_string value is not a string"), -// ); -// } -// let code = fs::read_to_string(path).expect("File not found"); -// let out = jsonnet_evaluate_snippet(vm, "deleteme", &code); -// let mut output: std::sync::MutexGuard<'_, String> = output.lock().unwrap(); -// *output = out; - -// Ok(()) -// }, -// )?; - -// // Wrap the `jsonnet_output_len` function to be called from WebAssembly. -// // This function returns the length of the output string. -// let output_clone = output.clone(); -// linker.func_wrap("arakoo", "jsonnet_output_len", move || -> i32 { -// let output_clone = output_clone.clone(); -// let output = output_clone.lock().unwrap().clone(); -// output.len() as i32 -// })?; - -// // Wrap the `jsonnet_output` function to be called from WebAssembly. -// // This function writes the output string to the specified memory location. -// linker.func_wrap( -// "arakoo", -// "jsonnet_output", -// move |mut caller: Caller<'_, WasiCtx>, ptr: i32| { -// let output_clone = output.clone(); -// let mem = match caller.get_export("memory") { -// Some(Extern::Memory(mem)) => mem, -// _ => return Err(Trap::NullReference.into()), -// }; -// let offset = ptr as u32 as usize; -// let out = output_clone.lock().unwrap().clone(); -// match mem.write(&mut caller, offset, out.as_bytes()) { -// Ok(_) => {} -// _ => return Err(Trap::MemoryOutOfBounds.into()), -// }; -// Ok(()) -// }, -// )?; - -// Ok(()) -// } - #[async_trait] impl super::jsonnet::Host for super::Host{ async fn jsonnet_make(&mut self,) -> wasmtime::Result { @@ -210,13 +29,17 @@ impl super::jsonnet::Host for super::Host{ Ok(ptr as u64) } - async fn jsonnet_evaluate_snippet(&mut self, vm: u64, code: String) -> wasmtime::Result { - let out = arakoo_jsonnet::jsonnet_evaluate_snippet(vm as *mut arakoo_jsonnet::VM, "snippet", &code); + async fn jsonnet_evaluate_snippet(&mut self, vm: u64,file:String, code: String) -> wasmtime::Result { + let out = arakoo_jsonnet::jsonnet_evaluate_snippet(vm as *mut arakoo_jsonnet::VM, &file, &code); Ok(out) } async fn jsonnet_evaluate_file(&mut self, vm: u64, path: String) -> wasmtime::Result { - let out = arakoo_jsonnet::jsonnet_evaluate_file(vm as *mut arakoo_jsonnet::VM, &path); + let code = fs::read_to_string(&path).map_err(|e| { + error!("Failed to read file {}: {}", path, e); + io::Error::new(io::ErrorKind::Other, e) + })?; + let out = arakoo_jsonnet::jsonnet_evaluate_snippet(vm as *mut arakoo_jsonnet::VM, "snippet", &code); Ok(out) } @@ -231,6 +54,46 @@ impl super::jsonnet::Host for super::Host{ } } +// Bindings for jsonnet + +#[async_trait] +impl super::outbound_http::Host for super::Host { + async fn send_request( + &mut self, + req: super::http_types::Request, + ) -> wasmtime::Result> { + // println!("Sending request: {:?}", request); + Ok(async { + tracing::log::trace!("Attempting to send outbound HTTP request to {}", req.uri); + + let method = method_from(req.method); + let url = Url::parse(&req.uri).map_err(|_| HttpError::InvalidUrl)?; + let headers = + request_headers(req.headers).map_err(|_| HttpError::RuntimeError)?; + let body = req.body.unwrap_or_default().to_vec(); + + if !req.params.is_empty() { + tracing::log::warn!("HTTP params field is deprecated"); + } + + // Allow reuse of Client's internal connection pool for multiple requests + // in a single component execution + let client = self.client.get_or_insert_with(Default::default); + + let resp = client + .request(method, url) + .headers(headers) + .body(body) + .send() + .await + .map_err(log_reqwest_error)?; + tracing::log::trace!("Returning response from outbound request to {}", req.uri); + response_from_reqwest(resp).await + } + .await) + } +} + pub fn log_reqwest_error(err: reqwest::Error) -> HttpError { let error_desc = if err.is_timeout() { "timeout error" diff --git a/JS/wasm/crates/serve/src/lib.rs b/JS/wasm/crates/serve/src/lib.rs index 6941334ec..716cc3812 100644 --- a/JS/wasm/crates/serve/src/lib.rs +++ b/JS/wasm/crates/serve/src/lib.rs @@ -1,4 +1,8 @@ // mod binding; +use wit::arakoo::edgechains::http as outbound_http; +use wit::arakoo::edgechains::http_types::HttpError; +use wit::arakoo::edgechains::jsonnet; + mod binding; mod io; @@ -27,13 +31,11 @@ use hyper::{ Body, Request, Response, }; -use reqwest::Url; use tracing::{error, event, info, Level}; use tracing_subscriber::{filter::EnvFilter, FmtSubscriber}; // use wasi_common::WasiCtx; use wasmtime_wasi::{bindings, ResourceTable, WasiCtx, WasiCtxBuilder, WasiView}; -use async_trait::async_trait; use wasmtime::component::{Component, Linker}; use wasmtime::{Config, Engine, Store, WasmBacktraceDetails}; use wit::arakoo::edgechains::http_types; @@ -86,48 +88,6 @@ impl WasiView for Host { } } -use wit::arakoo::edgechains::http as outbound_http; -use wit::arakoo::edgechains::http_types::HttpError; -use wit::arakoo::edgechains::jsonnet; - -#[async_trait] -impl outbound_http::Host for Host { - async fn send_request( - &mut self, - req: http_types::Request, - ) -> wasmtime::Result> { - // println!("Sending request: {:?}", request); - Ok(async { - tracing::log::trace!("Attempting to send outbound HTTP request to {}", req.uri); - - let method = binding::method_from(req.method); - let url = Url::parse(&req.uri).map_err(|_| HttpError::InvalidUrl)?; - let headers = - binding::request_headers(req.headers).map_err(|_| HttpError::RuntimeError)?; - let body = req.body.unwrap_or_default().to_vec(); - - if !req.params.is_empty() { - tracing::log::warn!("HTTP params field is deprecated"); - } - - // Allow reuse of Client's internal connection pool for multiple requests - // in a single component execution - let client = self.client.get_or_insert_with(Default::default); - - let resp = client - .request(method, url) - .headers(headers) - .body(body) - .send() - .await - .map_err(binding::log_reqwest_error)?; - tracing::log::trace!("Returning response from outbound request to {}", req.uri); - binding::response_from_reqwest(resp).await - } - .await) - } -} - impl http_types::Host for Host {} impl WorkerCtx { diff --git a/JS/wasm/examples/ec-wasmjs-hono/build.js b/JS/wasm/examples/ec-wasmjs-hono/build.js index 3967761da..6c346df91 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/build.js +++ b/JS/wasm/examples/ec-wasmjs-hono/build.js @@ -21,7 +21,7 @@ build({ // entryPoints: ["src/index.js"], bundle: true, // minify: true, - // minifySyntax: true, + minifySyntax: true, // outfile: "bin/app.js", outdir: "bin", // inject:["shim.js"], diff --git a/JS/wasm/examples/ec-wasmjs-hono/src/index.js b/JS/wasm/examples/ec-wasmjs-hono/src/index.js index 76029fb89..d1af19b79 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/src/index.js +++ b/JS/wasm/examples/ec-wasmjs-hono/src/index.js @@ -24,7 +24,7 @@ app.get("/", (c) => { app.get("/file", (c) => { try { - let result = jsonnet.extString("extName", "Mohan").evaluateFile("example.jsonnet"); + let result = jsonnet.extString("extName", "Mohan").evaluateFile("/home/afshan/EdgeChains/JS/wasm/examples/ec-wasmjs-hono/src/example.jsonnet"); return c.json(JSON.parse(result)); } catch (error) { console.log("Error occured"); @@ -66,4 +66,5 @@ app.notFound((c) => { return c.text("404 not found", 404); }); -app.fire(); +// app.fire(); +_export = app; diff --git a/JS/wasm/wit/jsonnet.wit b/JS/wasm/wit/jsonnet.wit index 006303c26..dd9e200f8 100644 --- a/JS/wasm/wit/jsonnet.wit +++ b/JS/wasm/wit/jsonnet.wit @@ -3,7 +3,7 @@ interface jsonnet { key:string } jsonnet-make: func() -> u64; - jsonnet-evaluate-snippet: func(vm: u64,code: string) -> string; + jsonnet-evaluate-snippet: func(vm: u64,file: string,code: string) -> string; jsonnet-evaluate-file: func(vm: u64,path: string) -> string; jsonnet-ext-string: func(vm: u64,key: string, value: string); jsonnet-destroy: func(vm: u64); From 2e153792bde4b72ab87cdbd1ba3625380893cc80 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Sun, 26 May 2024 17:27:33 +0530 Subject: [PATCH 11/13] Added Event fetch event listener for Hono --- .../arakoo-core/src/apis/http/shims/index.js | 102 +++++------ JS/wasm/crates/arakoo-core/src/lib.rs | 164 +++++++++--------- JS/wasm/examples/ec-wasmjs-hono/src/index.js | 56 +++--- JS/wasm/examples/hono-fetch/build.js | 2 +- JS/wasm/examples/hono-fetch/src/index.js | 16 +- 5 files changed, 179 insertions(+), 161 deletions(-) diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js index c24cd95b8..2adecc1d4 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js +++ b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js @@ -271,45 +271,49 @@ class Response { return this.body; } } -// let handlerFunction; -// globalThis.addEventListener = (_eventName, handler) => { -// handlerFunction = handler; -// }; -// const requestToHandler = (input) => { -// const request = new Request(input); -// const event = { -// request, -// response: {}, -// respondWith(res) { -// this.response = res; -// }, -// }; +let handlerFunction; +globalThis.addEventListener = (_eventName, handler) => { + handlerFunction = handler; +}; -// try { -// handlerFunction(event); - -// Promise.resolve(event.response) -// .then((res) => { -// console.log("res: ", res); -// result = { -// body: res.body, -// headers: res.headers.headers, -// status: res.status, -// statusText: res.statusText, -// }; -// }) -// .catch((err) => { -// error = `err: \n${err}`; -// }); -// } catch (err) { -// error = `err: ${err}\n${err.stack}`; -// } -// }; +const requestToHandler = (input) => { + const request = new Request(input); + console.log("Request recieved ", JSON.stringify(request)); + const event = { + request, + response: {}, + respondWith(res) { + console.log(res.constructor.name) + console.log("Res ", JSON.stringify(res)) + this.response = res; + }, + }; -// globalThis.entrypoint = requestToHandler; -// globalThis.result = {}; -// globalThis.error = null; + try { + handlerFunction(event); + + Promise.resolve(event.response) + .then((res) => { + console.log("res: ", JSON.stringify(res)); + result = { + body: res.body, + headers: res.headers.headers, + status: res.status, + statusText: res.statusText, + }; + }) + .catch((err) => { + error = `err: \n${err}`; + }); + } catch (err) { + error = `err: ${err}\n${err.stack}`; + } +}; + +globalThis.entrypoint = requestToHandler; +globalThis.result = {}; +globalThis.error = null; // globalThis.fetch = async (resource, options = { method: "GET" }) => { // let response = await fetch_internal(resource, options); @@ -326,19 +330,19 @@ function encodeBody(body) { } } -globalThis.requestToEvent = (inputReq) => { - const request = new Request(inputReq); - const event = { - request, - response: {}, - respondWith(res) { - console.log("Response recieved ", res); - this.response = res; - }, - }; - console.log("event: ", JSON.stringify(event)) - return event; -} +// globalThis.requestToEvent = (inputReq) => { +// const request = new Request(inputReq); +// const event = { +// request, +// response: {}, +// respondWith(res) { +// console.log("Response recieved ", res); +// this.response = res; +// }, +// }; +// console.log("event: ", JSON.stringify(event)) +// return event; +// } function fetch(uri, options) { console.log("In fetch function", uri, options) diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index 8ab6c1962..b89fc4bf5 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -29,18 +29,17 @@ pub mod wit { use super::Guest; export!(Guest); - + + pub use self::arakoo::edgechains; pub use self::arakoo::edgechains::http_types::{Method, Request, Response}; pub use self::exports::arakoo::edgechains::inbound_http; - pub use self::arakoo::edgechains; } - struct Guest; // mod execution; -mod runtime; mod apis; +mod runtime; // const FUNCTION_MODULE_NAME: &str = "function.mjs"; @@ -50,40 +49,40 @@ static CONTEXT: OnceCell> = OnceCell::new(); static HANDLER: OnceCell> = OnceCell::new(); static GLOBAL: OnceCell> = OnceCell::new(); static mut RUNTIME_INSTANCE: Option = None; -static ON_RESOLVE: OnceCell> = OnceCell::new(); -static ON_REJECT: OnceCell> = OnceCell::new(); -static RESPONSE: Mutex> = Mutex::new(None); -static EXCEPTION: Mutex> = Mutex::new(None); +// static ON_RESOLVE: OnceCell> = OnceCell::new(); +// static ON_REJECT: OnceCell> = OnceCell::new(); +// static RESPONSE: Mutex> = Mutex::new(None); +// static EXCEPTION: Mutex> = Mutex::new(None); -fn on_resolve(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { - // (*args).clone_into(&mut cloned_args); - let mut qjs_value = Option::None; - if args.len() > 0 { - for arg in args { - qjs_value = Some(from_qjs_value(*arg).unwrap()); - // println!("Arg resolve: {:?}", qjs_value.as_ref().unwrap()); - } - RESPONSE.lock().unwrap().replace(qjs_value.unwrap()); - Ok(JSValue::Undefined) - } else { - Err(anyhow!("expected 1 argument, got {}", args.len())) - } -} +// fn on_resolve(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { +// // (*args).clone_into(&mut cloned_args); +// let mut qjs_value = Option::None; +// if args.len() > 0 { +// for arg in args { +// qjs_value = Some(from_qjs_value(*arg).unwrap()); +// // println!("Arg resolve: {:?}", qjs_value.as_ref().unwrap()); +// } +// RESPONSE.lock().unwrap().replace(qjs_value.unwrap()); +// Ok(JSValue::Undefined) +// } else { +// Err(anyhow!("expected 1 argument, got {}", args.len())) +// } +// } -fn on_reject(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { - // (*args).clone_into(&mut cloned_args); - let mut qjs_value = Option::None; - if (args.len() > 0) { - for arg in args { - qjs_value = Some(from_qjs_value(*arg).unwrap()); - println!("Arg reject : {:?}", qjs_value.as_ref().unwrap()); - } - EXCEPTION.lock().unwrap().replace(qjs_value.unwrap()); - Ok(JSValue::Undefined) - } else { - Err(anyhow!("expected 1 argument, got {}", args.len())) - } -} +// fn on_reject(context: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]) -> Result { +// // (*args).clone_into(&mut cloned_args); +// let mut qjs_value = Option::None; +// if (args.len() > 0) { +// for arg in args { +// qjs_value = Some(from_qjs_value(*arg).unwrap()); +// println!("Arg reject : {:?}", qjs_value.as_ref().unwrap()); +// } +// EXCEPTION.lock().unwrap().replace(qjs_value.unwrap()); +// Ok(JSValue::Undefined) +// } else { +// Err(anyhow!("expected 1 argument, got {}", args.len())) +// } +// } /// Used by Wizer to preinitialize the module #[export_name = "wizer.initialize"] @@ -116,14 +115,16 @@ pub extern "C" fn init() { .expect("Unable to get global object"); GLOBAL.set(SendWrapper::new(global)).unwrap(); - let hono = global.get_property("_export").unwrap(); - let handle_event = hono.get_property("fetch").expect("Hono app not exported"); - HANDLER.set(SendWrapper::new(handle_event)).unwrap(); + // let hono = global.get_property("_export").unwrap(); + let entrypoint = global + .get_property("entrypoint") + .expect("Entrypoint not found"); + HANDLER.set(SendWrapper::new(entrypoint)).unwrap(); - let on_resolve = context.wrap_callback(on_resolve).unwrap(); - ON_RESOLVE.set(SendWrapper::new(on_resolve)).unwrap(); - let on_reject = context.wrap_callback(on_reject).unwrap(); - ON_REJECT.set(SendWrapper::new(on_reject)).unwrap(); + // let on_resolve = context.wrap_callback(on_resolve).unwrap(); + // ON_RESOLVE.set(SendWrapper::new(on_resolve)).unwrap(); + // let on_reject = context.wrap_callback(on_reject).unwrap(); + // ON_REJECT.set(SendWrapper::new(on_reject)).unwrap(); } impl wit::inbound_http::Guest for Guest { @@ -132,7 +133,7 @@ impl wit::inbound_http::Guest for Guest { let context = **CONTEXT.get().unwrap(); let mut serializer = javy::quickjs::Serializer::from_context(context).expect("Unable to create serializer"); - let handler = **HANDLER.get().unwrap(); + // let handler = **HANDLER.get().unwrap(); let request = HttpRequest { method: match req.method { wit::Method::Get => "GET".to_string(), @@ -165,43 +166,50 @@ impl wit::inbound_http::Guest for Guest { .expect("unable to serialize httprequest"); let request_value = serializer.value; // println!("body of httpRequest : {:?}", from_qjs_value(request_value).unwrap()); - let global = GLOBAL.get().unwrap() ; - let request_to_event = global - .get_property("requestToEvent") - .expect("Unable to get requestToEvent"); - let event = request_to_event + let global = GLOBAL.get().unwrap(); + // let entrypoint = global + // .get_property("entrypoint") + // .expect("Unable to get entrypoint"); + let entrypoint = **HANDLER.get().unwrap(); + entrypoint .call(global, &[request_value]) .expect("Unable to call requestToEvent"); - let event_request = event - .get_property("request") - .expect("Unable to get request from event"); - let promise = handler - .call(global, &[event_request, event]) - .expect("Unable to call handler"); + // let event_request = event + // .get_property("request") + // .expect("Unable to get request from event"); + // let promise = handler + // .call(global, &[event_request, event]) + // .expect("Unable to call handler"); - let on_resolve = ON_RESOLVE.get().unwrap().clone() ; - let on_reject = ON_REJECT.get().unwrap().clone() ; - let then_func = promise.get_property("then").unwrap(); - if then_func.is_function() { - then_func - .call( - &promise, - &[on_resolve.deref().clone(), on_reject.deref().clone()], - ) - .unwrap(); - } else { - RESPONSE - .lock() - .unwrap() - .replace(from_qjs_value(promise).unwrap()); - } + // let on_resolve = ON_RESOLVE.get().unwrap().clone() ; + // let on_reject = ON_REJECT.get().unwrap().clone() ; + // let then_func = promise.get_property("then").unwrap(); + // if then_func.is_function() { + // then_func + // .call( + // &promise, + // &[on_resolve.deref().clone(), on_reject.deref().clone()], + // ) + // .unwrap(); + // } else { + // RESPONSE + // .lock() + // .unwrap() + // .replace(from_qjs_value(promise).unwrap()); + // } context .execute_pending() .expect("Unable to execute pending tasks"); + let result = global.get_property("result").unwrap(); + let error = global.get_property("error").unwrap(); + let response = from_qjs_value(result).unwrap(); + let error = from_qjs_value(error).unwrap(); + println!("Result : {:?}", response); + println!("Error : {:?}", error); // let response = to_qjs_value(context, &RESPONSE.lock().unwrap().take().unwrap()).unwrap(); - let response = RESPONSE.lock().unwrap().take().unwrap(); + // let response = RESPONSE.lock().unwrap().take().unwrap(); // let deserializer = &mut Deserializer::from(response); // let response = HttpResponse::deserialize(deserializer).unwrap(); @@ -212,13 +220,13 @@ impl wit::inbound_http::Guest for Guest { let status_text_ref = to_qjs_value(context, obj.get("statusText").unwrap()).unwrap(); let status_text = status_text_ref.as_str().unwrap(); let body_ref = to_qjs_value(context, obj.get("body").unwrap()).unwrap(); - let headers = obj.get("headers").unwrap(); + let headers_obj = obj.get("headers").unwrap(); let mut headers_vec = Vec::new(); - let headers_obj = match headers { - JSValue::Object(obj) => obj, - _ => panic!("Headers is not object {:?}", headers), - }; - if let JSValue::Object(headers_obj) = headers_obj.get("headers").unwrap() { + // let headers_obj = match headers { + // JSValue::Object(obj) => obj, + // _ => panic!("Headers is not object {:?}", headers), + // }; + if let JSValue::Object(headers_obj) = headers_obj { for (k, v) in headers_obj.iter() { let key = k.clone(); let value = (*v).to_string(); diff --git a/JS/wasm/examples/ec-wasmjs-hono/src/index.js b/JS/wasm/examples/ec-wasmjs-hono/src/index.js index d1af19b79..a4d85c3df 100644 --- a/JS/wasm/examples/ec-wasmjs-hono/src/index.js +++ b/JS/wasm/examples/ec-wasmjs-hono/src/index.js @@ -7,8 +7,13 @@ let jsonnet = new Jsonnet(); const app = new Hono(); const env = {}; + +app.get("/hello", (c) => { + return c.text("Hello World!"); +}); + app.get("/", (c) => { - const code = ` + const code = ` local username = std.extVar('name'); local Person(name='Alice') = { name: name, @@ -18,53 +23,52 @@ app.get("/", (c) => { person1: Person(username), person2: Person('Bob'), }`; - let result = jsonnet.extString("name", "ll").evaluateSnippet(code); - return c.json(JSON.parse(result)); + let result = jsonnet.extString("name", "ll").evaluateSnippet(code); + return c.json(JSON.parse(result)); }); app.get("/file", (c) => { - try { - let result = jsonnet.extString("extName", "Mohan").evaluateFile("/home/afshan/EdgeChains/JS/wasm/examples/ec-wasmjs-hono/src/example.jsonnet"); - return c.json(JSON.parse(result)); - } catch (error) { - console.log("Error occured"); - console.log(error); - return c.json("Unable to evaluate File"); - } + try { + let result = jsonnet.extString("extName", "Mohan").evaluateFile("/home/afshan/EdgeChains/JS/wasm/examples/ec-wasmjs-hono/src/example.jsonnet"); + return c.json(JSON.parse(result)); + } catch (error) { + console.log("Error occured"); + console.log(error); + return c.json("Unable to evaluate File"); + } }); app.get("/:username", (c) => { - const { username } = c.req.param(); - // redirect to /hello/:username - return c.redirect(`/hello/${username}`); + const { username } = c.req.param(); + // redirect to /hello/:username + return c.redirect(`/hello/${username}`); }); app.get("/hello/:name", async (c) => { - const name = c.req.param("name"); - return c.text(`Async Hello ${name}!`); + const name = c.req.param("name"); + return c.text(`Async Hello ${name}!`); }); app.get("/env/:key", async (c) => { - const key = c.req.param("key"); - return c.text(env[key]); + const key = c.req.param("key"); + return c.text(env[key]); }); const config = { - host: env["PLANETSCALE_HOST"], - username: env["PLANETSCALE_USERNAME"], - password: env["PLANETSCALE_PASSWORD"], + host: env["PLANETSCALE_HOST"], + username: env["PLANETSCALE_USERNAME"], + password: env["PLANETSCALE_PASSWORD"], }; const conn = connect(config); app.get("/db", async (c) => { - const result = await conn.execute("SHOW TABLES"); + const result = await conn.execute("SHOW TABLES"); - return c.json(result); + return c.json(result); }); app.notFound((c) => { - return c.text("404 not found", 404); + return c.text("404 not found", 404); }); -// app.fire(); -_export = app; +app.fire(); diff --git a/JS/wasm/examples/hono-fetch/build.js b/JS/wasm/examples/hono-fetch/build.js index a29865111..993562656 100644 --- a/JS/wasm/examples/hono-fetch/build.js +++ b/JS/wasm/examples/hono-fetch/build.js @@ -6,7 +6,7 @@ build({ entryPoints: ["src/index.js"], bundle: true, minify: false, - outfile: "bin/app.js", + outfile: "bin/index.js", format: "esm", target: "esnext", platform: "node", diff --git a/JS/wasm/examples/hono-fetch/src/index.js b/JS/wasm/examples/hono-fetch/src/index.js index 3411548e2..75dc2d952 100644 --- a/JS/wasm/examples/hono-fetch/src/index.js +++ b/JS/wasm/examples/hono-fetch/src/index.js @@ -15,18 +15,20 @@ app.get("/hello", (c) => { app.get("/post", async (c) => { try { - let response = await fetch("https://jsonplaceholder.typicode.com/posts", { + let response = await fetch("https://dummy.restapiexample.com/api/v1/create", { method: "POST", body: JSON.stringify({ - title: "foo", - body: "bar", - userId: 1, + name: "test", + salary: "123", + age: "23" }), headers: { "Content-type": "application/json", }, }); - return c.json(response); + let data = await response.json(); + console.log("data",data) + return c.json(data); } catch (error) { console.log("error occured"); console.log(error); @@ -36,13 +38,13 @@ app.get("/post", async (c) => { app.get("/get", async (c) => { try { - let response = await fetch("https://jsonplaceholder.typicode.com/todos/1"); + let response = await fetch("https://fakerestapi.azurewebsites.net/api/v1/Activities"); let body = await response.json(); return c.json(body); } catch (error) { console.log("error occured"); console.log(error); - return c.json(output); + return c.json(error); } }); From 7ad5a92db80e0a44e64397da57d1d4b53c228263 Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Sun, 26 May 2024 18:30:14 +0530 Subject: [PATCH 12/13] programmatically adapt module to component in compiler --- JS/wasm/crates/cli/Cargo.toml | 3 ++- JS/wasm/crates/cli/build.rs | 5 +++++ JS/wasm/crates/cli/src/main.rs | 9 +++++++++ .../crates/cli}/wasi_snapshot_preview1.reactor.wasm | Bin 4 files changed, 16 insertions(+), 1 deletion(-) rename JS/{ => wasm/crates/cli}/wasi_snapshot_preview1.reactor.wasm (100%) diff --git a/JS/wasm/crates/cli/Cargo.toml b/JS/wasm/crates/cli/Cargo.toml index de325a491..28e1403e7 100644 --- a/JS/wasm/crates/cli/Cargo.toml +++ b/JS/wasm/crates/cli/Cargo.toml @@ -13,8 +13,9 @@ path = "src/main.rs" [dependencies] wizer = { workspace = true } anyhow = { workspace = true } -clap = { version = "4.1.4", features = [ "derive" ] } +clap = { version = "4.1.4", features = ["derive"] } binaryen = { git = "https://github.com/pepyakin/binaryen-rs" } +wit-component = { version = "0.208.1" } [build-dependencies] anyhow = "1.0.79" diff --git a/JS/wasm/crates/cli/build.rs b/JS/wasm/crates/cli/build.rs index 9d2752189..7f21a279d 100644 --- a/JS/wasm/crates/cli/build.rs +++ b/JS/wasm/crates/cli/build.rs @@ -7,6 +7,11 @@ use std::path::{Path, PathBuf}; use anyhow::Result; fn main() -> Result<()> { + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + // copy wasi_snapshot_preview1.reactor.wasm file from current directory to OUT_DIR + fs::copy("wasi_snapshot_preview1.reactor.wasm", out_dir.join("adapter.wasm"))?; + println!("cargo:rerun-if-changed=wasi_snapshot_preview1.reactor.wasm"); + println!("cargo:warning=copied wasi_snapshot_preview1.reactor.wasm to adapter.wasm in OUT_DIR"); if let Ok("cargo-clippy") = env::var("CARGO_CFG_FEATURE").as_ref().map(String::as_str) { stub_javy_core_for_clippy() } else { diff --git a/JS/wasm/crates/cli/src/main.rs b/JS/wasm/crates/cli/src/main.rs index 37816c94c..83972ca8d 100644 --- a/JS/wasm/crates/cli/src/main.rs +++ b/JS/wasm/crates/cli/src/main.rs @@ -9,6 +9,7 @@ use std::io::Read; use std::process::Command; use std::{env, fs::File, path::PathBuf}; use wizer::Wizer; +use wit_component::ComponentEncoder; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -63,6 +64,14 @@ fn main() -> Result<()> { bail!("Unable to read wasm binary for wasm-opt optimizations"); } + println!("Adapting module for component model"); + let adapter_path = concat!(env!("OUT_DIR"), "/adapter.wasm"); + wasm = ComponentEncoder::default() + .validate(true) + .module(&wasm)? + .adapter("wasi_snapshot_preview1", &fs::read(adapter_path).expect("Unable to read adapter"))? + .encode()?; + fs::write(&opts.output, wasm)?; return Ok(()); } diff --git a/JS/wasi_snapshot_preview1.reactor.wasm b/JS/wasm/crates/cli/wasi_snapshot_preview1.reactor.wasm similarity index 100% rename from JS/wasi_snapshot_preview1.reactor.wasm rename to JS/wasm/crates/cli/wasi_snapshot_preview1.reactor.wasm From 8f85a2154c60557513b9daafb8e4cb5fdbf3bdcf Mon Sep 17 00:00:00 2001 From: afshan ahmed khan Date: Mon, 27 May 2024 02:16:37 +0530 Subject: [PATCH 13/13] Log messages on the basis of env variables --- JS/wasm/crates/arakoo-core/Cargo.toml | 2 ++ .../arakoo-core/src/apis/console/index.js | 31 ++++++++++++++++ .../arakoo-core/src/apis/console/mod.rs | 23 +++++++++--- .../crates/arakoo-core/src/apis/fetch/mod.rs | 7 ++-- .../arakoo-core/src/apis/http/shims/index.js | 11 +++--- JS/wasm/crates/arakoo-core/src/lib.rs | 14 ++++---- JS/wasm/crates/cli/src/main.rs | 36 ++++++++++++------- 7 files changed, 90 insertions(+), 34 deletions(-) create mode 100644 JS/wasm/crates/arakoo-core/src/apis/console/index.js diff --git a/JS/wasm/crates/arakoo-core/Cargo.toml b/JS/wasm/crates/arakoo-core/Cargo.toml index af6c0ba2c..ea970497e 100644 --- a/JS/wasm/crates/arakoo-core/Cargo.toml +++ b/JS/wasm/crates/arakoo-core/Cargo.toml @@ -22,3 +22,5 @@ http = { workspace = true } quickjs-wasm-rs = "3.0.0" bytes = { version = "1.6.0", features = ["serde"] } fastrand = "2.1.0" +log = {version = "*"} +env_logger = {version = "*"} \ No newline at end of file diff --git a/JS/wasm/crates/arakoo-core/src/apis/console/index.js b/JS/wasm/crates/arakoo-core/src/apis/console/index.js new file mode 100644 index 000000000..44d4875a1 --- /dev/null +++ b/JS/wasm/crates/arakoo-core/src/apis/console/index.js @@ -0,0 +1,31 @@ +globalThis.process = { + env: {} +} + +console.debug = function (...msg) { + let str = "QUICKJS ENGINE DEBUG - "; + if (process.env.JS_LOG=="debug") { + for(let i=0;i Result<()> { - register_console( + let result = register_console( runtime.context(), config.console.log_stream.to_stream(), config.console.error_stream.to_stream(), - ) + ); + let context = runtime.context(); + context.eval_global("console.js", include_str!("index.js"))?; + let global = context.global_object()?; + let env = global.get_property("process")?.get_property("env")?; + debug!("env: {:?}", env::vars().collect::>()); + for (key, value) in env::vars() { + env.set_property(key, to_qjs_value(context, &JSValue::String(value)).unwrap())?; + } + result } } @@ -66,7 +76,10 @@ where if !arg.is_undefined() { let proto = arg.get_property("__proto__").unwrap().to_string(); if proto.contains("rror") { - log_line.push_str(&format!("__proto__ is {} Error in js evaluation : {:?}", proto,val)); + log_line.push_str(&format!( + "__proto__ is {} Error in js evaluation : {:?}", + proto, val + )); } else { let line: String = log_js_value(&val); log_line.push_str(&line); diff --git a/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs b/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs index bfd2df2f8..473f5d897 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs +++ b/JS/wasm/crates/arakoo-core/src/apis/fetch/mod.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, Result}; use http::{request, HeaderName, HeaderValue}; +use log::debug; use serde_bytes::ByteBuf; // use crate::{types::{HttpRequest, HttpResponse}, JSApiSet}; use javy::quickjs::{JSContextRef, JSValue, JSValueRef}; @@ -36,7 +37,7 @@ impl JSApiSet for Fetch { fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValueRef]) -> Result { match args { [request] => { - println!("Request recieved in send_http_request: {:?}", from_qjs_value(*request).unwrap()); + debug!("Request recieved in send_http_request: {:?}", from_qjs_value(*request).unwrap()); let deserializer = &mut Deserializer::from(request.clone()); let request = HttpRequest::deserialize(deserializer).expect("Unable to deserialize request"); @@ -54,11 +55,11 @@ fn send_http_request(context: &JSContextRef, _this: &JSValueRef, args: &[JSValue } let outbound_request = builder.body(request.body.map(|buffer| buffer.into_vec().into())).expect("Unable to build request body"); - println!("outbound_request in wrap_callback: {:?}", outbound_request); + debug!("outbound_request in wrap_callback: {:?}", outbound_request); let response = outbound_http::send_request( outbound_request )?; - println!("outbound_response in wrap_callback: {:?}", response); + debug!("outbound_response in wrap_callback: {:?}", response); let response = HttpResponse { status: response.status().as_u16(), diff --git a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js index 2adecc1d4..a81d55597 100644 --- a/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js +++ b/JS/wasm/crates/arakoo-core/src/apis/http/shims/index.js @@ -279,13 +279,12 @@ globalThis.addEventListener = (_eventName, handler) => { const requestToHandler = (input) => { const request = new Request(input); - console.log("Request recieved ", JSON.stringify(request)); + console.debug("Request recieved ", JSON.stringify(request)); const event = { request, response: {}, respondWith(res) { - console.log(res.constructor.name) - console.log("Res ", JSON.stringify(res)) + console.debug(res.constructor.name) this.response = res; }, }; @@ -295,7 +294,7 @@ const requestToHandler = (input) => { Promise.resolve(event.response) .then((res) => { - console.log("res: ", JSON.stringify(res)); + console.log("Successfully responded to request"); result = { body: res.body, headers: res.headers.headers, @@ -345,7 +344,7 @@ function encodeBody(body) { // } function fetch(uri, options) { - console.log("In fetch function", uri, options) + console.debug("In fetch function", uri, options) let encodedBodyData = (options && options.body) ? encodeBody(options.body) : new Uint8Array().buffer const { status, headers, body } = __internal_http_send({ method: (options && options.method) || "GET", @@ -354,7 +353,7 @@ function fetch(uri, options) { body: encodedBodyData, params: (options && options.params) || {}, }) - console.log("Response from fetch", status, headers, body) + console.debug("Response from fetch", status, headers, body) let obj; try { obj = { diff --git a/JS/wasm/crates/arakoo-core/src/lib.rs b/JS/wasm/crates/arakoo-core/src/lib.rs index b89fc4bf5..53f36b425 100644 --- a/JS/wasm/crates/arakoo-core/src/lib.rs +++ b/JS/wasm/crates/arakoo-core/src/lib.rs @@ -6,15 +6,14 @@ use javy::quickjs::JSContextRef; use javy::quickjs::JSValue; use javy::quickjs::JSValueRef; use javy::Runtime; +use log::debug; +use log::info; use once_cell::sync::OnceCell; use send_wrapper::SendWrapper; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::io; use std::io::Read; -use std::ops::Deref; -use std::sync::Mutex; - use serde_bytes::ByteBuf; use crate::apis::types::HttpRequest; @@ -89,6 +88,7 @@ static mut RUNTIME_INSTANCE: Option = None; pub extern "C" fn init() { let mut contents = String::new(); let res = io::stdin().read_to_string(&mut contents); + env_logger::init(); match res { Ok(len) => println!("Read {} bytes", len), Err(err) => println!( @@ -107,7 +107,7 @@ pub extern "C" fn init() { CONTEXT.set(SendWrapper::new(context)).unwrap(); match context.eval_global("javascriptCode.js", &contents) { Ok(_) => (), - Err(err) => println!("Error in evaluating script function.js : {:?}", err), + Err(err) => panic!("Error in evaluating script function.js : {:?}", err), }; let global = context @@ -129,7 +129,7 @@ pub extern "C" fn init() { impl wit::inbound_http::Guest for Guest { fn handle_request(req: wit::Request) -> wit::Response { - println!("{:?}", req); + debug!("{:?}", req); let context = **CONTEXT.get().unwrap(); let mut serializer = javy::quickjs::Serializer::from_context(context).expect("Unable to create serializer"); @@ -206,8 +206,8 @@ impl wit::inbound_http::Guest for Guest { let error = global.get_property("error").unwrap(); let response = from_qjs_value(result).unwrap(); let error = from_qjs_value(error).unwrap(); - println!("Result : {:?}", response); - println!("Error : {:?}", error); + debug!("Result : {:?}", response); + debug!("Error : {:?}", error); // let response = to_qjs_value(context, &RESPONSE.lock().unwrap().take().unwrap()).unwrap(); // let response = RESPONSE.lock().unwrap().take().unwrap(); diff --git a/JS/wasm/crates/cli/src/main.rs b/JS/wasm/crates/cli/src/main.rs index 83972ca8d..787d79052 100644 --- a/JS/wasm/crates/cli/src/main.rs +++ b/JS/wasm/crates/cli/src/main.rs @@ -8,8 +8,8 @@ use std::fs; use std::io::Read; use std::process::Command; use std::{env, fs::File, path::PathBuf}; -use wizer::Wizer; use wit_component::ComponentEncoder; +use wizer::Wizer; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -26,24 +26,31 @@ pub struct Options { fn main() -> Result<()> { let opts = Options::parse(); - if env::var("EDECHAINS_JS_WIZEN").eq(&Ok("1".into())) { env::remove_var("EDECHAINS_JS_WIZEN"); + // remove unnecessary env var except RUST_LOG + for (key, _) in env::vars() { + if key.eq("RUST_LOG") || key.eq("JS_LOG") { + } else { + env::remove_var(&key); + } + } println!("\nStarting to build arakoo compatible module"); - let wasm: Vec = if let Ok(wasm_bytes) = std::fs::read(concat!(env!("OUT_DIR"), "/engine.wasm")) { - wasm_bytes - } else { - // Provide a fallback wasm binary if the file is not found - panic!("Engine wasm not found"); - }; + let wasm: Vec = + if let Ok(wasm_bytes) = std::fs::read(concat!(env!("OUT_DIR"), "/engine.wasm")) { + wasm_bytes + } else { + // Provide a fallback wasm binary if the file is not found + panic!("Engine wasm not found"); + }; println!("Preinitializing using Wizer"); - let mut wasm = Wizer::new() .allow_wasi(true)? .inherit_stdio(true) .wasm_bulk_memory(true) + .inherit_env(true) .run(wasm.as_slice())?; let codegen_config = CodegenConfig { @@ -67,10 +74,13 @@ fn main() -> Result<()> { println!("Adapting module for component model"); let adapter_path = concat!(env!("OUT_DIR"), "/adapter.wasm"); wasm = ComponentEncoder::default() - .validate(true) - .module(&wasm)? - .adapter("wasi_snapshot_preview1", &fs::read(adapter_path).expect("Unable to read adapter"))? - .encode()?; + .validate(true) + .module(&wasm)? + .adapter( + "wasi_snapshot_preview1", + &fs::read(adapter_path).expect("Unable to read adapter"), + )? + .encode()?; fs::write(&opts.output, wasm)?; return Ok(());