From 878ddd5f11b3a247f58410b403e6f5babab10cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Cabrera?= Date: Fri, 5 Apr 2024 15:11:47 -0400 Subject: [PATCH] Migrate all of the APIs to use rquickjs This commit migrates the `console`, `stream_io`, `random` and `text_encoding` APIs to use rquickjs. One notable change in this commit is the introduction of the `Args` struct to tie the lifetime of `Ctx<'js>` and `Rest>` arguments, given that explicit lifetime binding is not possible in Rust closures at the moment. --- crates/apis/src/console/mod.rs | 121 +++++++++-------- crates/apis/src/lib.rs | 22 +++- crates/apis/src/random/mod.rs | 21 ++- crates/apis/src/stream_io/mod.rs | 187 ++++++++++++++++----------- crates/apis/src/text_encoding/mod.rs | 170 ++++++++++++++---------- crates/core/src/execution.rs | 51 ++++---- crates/javy/Cargo.toml | 2 +- 7 files changed, 347 insertions(+), 227 deletions(-) diff --git a/crates/apis/src/console/mod.rs b/crates/apis/src/console/mod.rs index 68422519..6abdffb1 100644 --- a/crates/apis/src/console/mod.rs +++ b/crates/apis/src/console/mod.rs @@ -2,20 +2,18 @@ use std::io::Write; use anyhow::{Error, Result}; use javy::{ - quickjs::{ - prelude::{MutFn, Rest}, - Context, Ctx, Function, Object, Value, - }, + quickjs::{prelude::MutFn, Context, Function, Object, Value}, Runtime, }; -use crate::{print, APIConfig, JSApiSet}; +use crate::{print, APIConfig, Args, JSApiSet}; pub(super) use config::ConsoleConfig; pub use config::LogStream; mod config; +// TODO: #[derive(Default)] pub(super) struct Console {} impl Console { @@ -34,20 +32,27 @@ impl JSApiSet for Console { } } -fn register_console<'js, T, U>(context: &Context, log_stream: T, error_stream: U) -> Result<()> +fn register_console<'js, T, U>( + context: &Context, + mut log_stream: T, + mut error_stream: U, +) -> Result<()> where - T: Write, - U: Write, + T: Write + 'static, + U: Write + 'static, { - context.with(|cx| { - let globals = cx.globals(); - let console = Object::new(cx)?; + // TODO: Revisit the callback signatures, there's a possibility that we can + // actually convert from anyhow::Error to quickjs::Error. + context.with(|this| { + let globals = this.globals(); + let console = Object::new(this.clone())?; + console.set( "log", Function::new( - cx, - MutFn::new(move |cx: Ctx<'js>, args: Rest>| { - log(cx, &args, &mut log_stream).unwrap() + this.clone(), + MutFn::new(move |cx, args| { + log(Args::hold(cx, args), &mut log_stream).expect("console.log to succeed") }), )?, )?; @@ -55,20 +60,21 @@ where console.set( "error", Function::new( - cx, - MutFn::new(move |cx: Ctx<'js>, args: Rest>| { - log(cx, &args, &mut error_stream).unwrap() + this, + MutFn::new(move |cx, args| { + log(Args::hold(cx, args), &mut error_stream).expect("console.error to succeed") }), )?, )?; globals.set("console", console)?; Ok::<_, Error>(()) - }); + })?; Ok(()) } -fn log<'js, T: Write>(ctx: Ctx<'js>, args: &[Value<'js>], mut stream: T) -> Result> { +fn log<'js, T: Write>(args: Args<'js>, stream: &mut T) -> Result> { + let (ctx, args) = args.release(); let mut buf = String::new(); for (i, arg) in args.iter().enumerate() { if i != 0 { @@ -79,13 +85,16 @@ fn log<'js, T: Write>(ctx: Ctx<'js>, args: &[Value<'js>], mut stream: T) -> Resu writeln!(stream, "{buf}")?; - Ok(Value::new_undefined(ctx)) + Ok(Value::new_undefined(ctx.clone())) } #[cfg(test)] mod tests { - use anyhow::Result; - use javy::Runtime; + use anyhow::{Error, Result}; + use javy::{ + quickjs::{Object, Value}, + Runtime, + }; use std::cell::RefCell; use std::rc::Rc; use std::{cmp, io}; @@ -99,9 +108,13 @@ mod tests { fn test_register() -> Result<()> { let runtime = Runtime::default(); Console::new().register(&runtime, &APIConfig::default())?; - let console = runtime.context().global_object()?.get_property("console")?; - assert!(console.get_property("log").is_ok()); - assert!(console.get_property("error").is_ok()); + runtime.context().with(|cx| { + let console: Object<'_> = cx.globals().get("console")?; + assert!(console.get::<&str, Value<'_>>("log").is_ok()); + assert!(console.get::<&str, Value<'_>>("error").is_ok()); + + Ok::<_, Error>(()) + })?; Ok(()) } @@ -113,24 +126,25 @@ mod tests { let ctx = runtime.context(); register_console(ctx, stream.clone(), stream.clone())?; - ctx.eval_global("main", "console.log(\"hello world\");")?; - assert_eq!(b"hello world\n", stream.buffer.borrow().as_slice()); + ctx.with(|this| { + this.eval("console.log(\"hello world\");")?; + assert_eq!(b"hello world\n", stream.buffer.borrow().as_slice()); + stream.clear(); - stream.clear(); + this.eval("console.log(\"bonjour\", \"le\", \"monde\")")?; + assert_eq!(b"bonjour le monde\n", stream.buffer.borrow().as_slice()); - ctx.eval_global("main", "console.log(\"bonjour\", \"le\", \"monde\")")?; - assert_eq!(b"bonjour le monde\n", stream.buffer.borrow().as_slice()); + stream.clear(); - stream.clear(); + this.eval("console.log(2.3, true, { foo: 'bar' }, null, undefined)")?; + assert_eq!( + b"2.3 true [object Object] null undefined\n", + stream.buffer.borrow().as_slice() + ); + + Ok::<_, Error>(()) + })?; - ctx.eval_global( - "main", - "console.log(2.3, true, { foo: 'bar' }, null, undefined)", - )?; - assert_eq!( - b"2.3 true [object Object] null undefined\n", - stream.buffer.borrow().as_slice() - ); Ok(()) } @@ -142,24 +156,25 @@ mod tests { let ctx = runtime.context(); register_console(ctx, stream.clone(), stream.clone())?; - ctx.eval_global("main", "console.error(\"hello world\");")?; - assert_eq!(b"hello world\n", stream.buffer.borrow().as_slice()); + ctx.with(|this| { + this.eval("console.error(\"hello world\");")?; + assert_eq!(b"hello world\n", stream.buffer.borrow().as_slice()); - stream.clear(); + stream.clear(); - ctx.eval_global("main", "console.error(\"bonjour\", \"le\", \"monde\")")?; - assert_eq!(b"bonjour le monde\n", stream.buffer.borrow().as_slice()); + this.eval("console.error(\"bonjour\", \"le\", \"monde\")")?; + assert_eq!(b"bonjour le monde\n", stream.buffer.borrow().as_slice()); - stream.clear(); + stream.clear(); + + this.eval("console.error(2.3, true, { foo: 'bar' }, null, undefined)")?; + assert_eq!( + b"2.3 true [object Object] null undefined\n", + stream.buffer.borrow().as_slice() + ); + Ok::<_, Error>(()) + })?; - ctx.eval_global( - "main", - "console.error(2.3, true, { foo: 'bar' }, null, undefined)", - )?; - assert_eq!( - b"2.3 true [object Object] null undefined\n", - stream.buffer.borrow().as_slice() - ); Ok(()) } diff --git a/crates/apis/src/lib.rs b/crates/apis/src/lib.rs index 851a9a68..123e321f 100644 --- a/crates/apis/src/lib.rs +++ b/crates/apis/src/lib.rs @@ -45,7 +45,7 @@ use anyhow::Result; use javy::Runtime; -use javy::quickjs::{Type, Value}; +use javy::quickjs::{prelude::Rest, Ctx, Type, Value}; use std::fmt::Write; pub use api_config::APIConfig; @@ -126,3 +126,23 @@ pub(crate) fn print<'js>(val: &Value<'js>, sink: &mut String) -> Result<()> { _ => unimplemented!(), } } + +/// A struct to hold the current [Ctx] and [Value]s passed as arguments to Rust +/// functions. +/// A struct here is used to explicitly tie these values with a particular +/// lifetime. +// +// See: https://github.com/rust-lang/rfcs/pull/3216 +pub(crate) struct Args<'js>(Ctx<'js>, Rest>); + +impl<'js> Args<'js> { + /// Tie the [Ctx] and [Rest]. + fn hold(cx: Ctx<'js>, args: Rest>) -> Self { + Self(cx, args) + } + + /// Get the [Ctx] and [Rest]. + fn release(self) -> (Ctx<'js>, Rest>) { + (self.0, self.1) + } +} diff --git a/crates/apis/src/random/mod.rs b/crates/apis/src/random/mod.rs index fb5fc31a..7522ab05 100644 --- a/crates/apis/src/random/mod.rs +++ b/crates/apis/src/random/mod.rs @@ -25,18 +25,25 @@ impl JSApiSet for Random { #[cfg(test)] mod tests { use crate::{random::Random, APIConfig, JSApiSet}; - use anyhow::Result; - use javy::Runtime; + use anyhow::{Error, Result}; + use javy::{quickjs::Value, Runtime}; #[test] fn test_random() -> Result<()> { let runtime = Runtime::default(); Random.register(&runtime, &APIConfig::default())?; - let ctx = runtime.context(); - ctx.eval_global("test.js", "result = Math.random()")?; - let result = ctx.global_object()?.get_property("result")?.as_f64()?; - assert!(result >= 0.0); - assert!(result < 1.0); + runtime.context().with(|this| { + this.eval("result = Math.random()")?; + let result: f64 = this + .globals() + .get::<&str, Value<'_>>("result")? + .as_float() + .unwrap(); + assert!(result >= 0.0); + assert!(result < 1.0); + Ok::<_, Error>(()) + }); + Ok(()) } } diff --git a/crates/apis/src/stream_io/mod.rs b/crates/apis/src/stream_io/mod.rs index 13d980cc..77094e11 100644 --- a/crates/apis/src/stream_io/mod.rs +++ b/crates/apis/src/stream_io/mod.rs @@ -1,102 +1,145 @@ -use anyhow::{bail, Error, Result}; +use anyhow::{anyhow, bail, Error, Result}; use std::io::{Read, Write}; use javy::{ - quickjs::{prelude::MutFn, Ctx, Function, Object, Value}, + quickjs::{Function, Object, Value}, Runtime, }; -use crate::{APIConfig, JSApiSet}; +use crate::{APIConfig, Args, JSApiSet}; pub(super) struct StreamIO; -fn write<'js>(cx: Ctx<'js>, args: &[Value<'js>]) -> Result { +fn ensure_io_args<'a, 'js: 'a>( + args: &'a [Value<'js>], + for_func: &str, +) -> Result<( + &'a Value<'js>, + &'a Value<'js>, + &'a Value<'js>, + &'a Value<'js>, +)> { let [fd, data, offset, length, ..] = args else { - bail!("Expected 4 parameters to write to stdout") + bail!( + r#" + {} expects 4 parameters: the file descriptor, the + TypedArray buffer, the TypedArray byteOffset and the TypedArray + byteLength. + + Got: {} parameters. + "#, + for_func, + args.len() + ); }; - let mut fd: Box = match fd.try_into()? { + Ok((fd, data, offset, length)) +} + +fn write<'js>(args: Args<'js>) -> Result> { + let (cx, args) = args.release(); + let (fd, data, offset, length) = ensure_io_args(&args, "Javy.IO.writeSync")?; + let mut fd: Box = match fd + .as_int() + .ok_or_else(|| anyhow!("File descriptor must be a number"))? + { + // TODO: Drop the `Box` to avoid a heap allocation? 1 => Box::new(std::io::stdout()), 2 => Box::new(std::io::stderr()), - _ => anyhow::bail!("Only stdout and stderr are supported"), + x => anyhow::bail!( + "Wrong file descriptor: {}. Only stdin(1) and stderr(2) are supported", + x + ), }; - let data: Vec = data.try_into()?; - let offset: usize = offset.try_into()?; - let length: usize = length.try_into()?; + let data = data + .as_array() + .ok_or_else(|| anyhow!("Data must be an Array object"))? + .as_typed_array::() + .ok_or_else(|| anyhow!("Data must be a UInt8Array"))? + .as_bytes() + .ok_or_else(|| anyhow!("Could not represent data as &[u8]"))?; + + // TODO: Revisit the f64 to usize conversions. + let offset = offset + .as_number() + .ok_or_else(|| anyhow!("offset must be a number"))? as usize; + let length = length + .as_number() + .ok_or_else(|| anyhow!("offset must be a number"))? as usize; let data = &data[offset..(offset + length)]; let n = fd.write(data)?; fd.flush()?; - Ok(n.into()) + + Ok(Value::new_number(cx, n as f64)) +} + +fn read<'js>(args: Args<'js>) -> Result> { + let (cx, args) = args.release(); + let (fd, data, offset, length) = ensure_io_args(&args, "Javy.IO.readSync")?; + + let mut fd: Box = match fd + .as_int() + .ok_or_else(|| anyhow!("File descriptor must be a number"))? + { + // TODO: Drop the `Box` to avoid a heap allocation? + 0 => Box::new(std::io::stdin()), + x => anyhow::bail!("Wrong file descriptor: {}. Only stdin(1) is supported", x), + }; + + let offset = offset + .as_number() + .ok_or_else(|| anyhow!("offset must be a number"))? as usize; + let length = length + .as_number() + .ok_or_else(|| anyhow!("length must be a number"))? as usize; + + let data = data + .as_array() + .ok_or_else(|| anyhow!("Data must be an Array object"))? + .as_typed_array::() + .ok_or_else(|| anyhow!("Data must be a UInt8Array"))? + .as_bytes() + .ok_or_else(|| anyhow!("Could not represent data as &[u8]"))?; + + let len = data.len(); + let mut_ptr = data.as_ptr() as *mut u8; + // TODO: Can we avoid doing this? Is there a way to expose a safe way to mutate + // the underlying array buffer with rquickjs? + let mut_data = unsafe { &mut *std::ptr::slice_from_raw_parts_mut(mut_ptr, len) }; + + let data = &mut mut_data[offset..(offset + length)]; + let n = fd.read(data)?; + + Ok(Value::new_number(cx, n as f64)) } impl JSApiSet for StreamIO { - fn register(&self, runtime: &Runtime, _config: &APIConfig) -> Result<()> { - runtime.context().with(|cx| { - let globals = cx.globals(); + fn register<'js>(&self, runtime: &Runtime, _config: &APIConfig) -> Result<()> { + runtime.context().with(|this| { + let globals = this.globals(); + // TODO: Do we need this? if globals.get::<_, Object>("Javy").is_err() { - globals.set("Javy", Object::new(cx)?)? + globals.set("Javy", Object::new(this.clone())?)? } - globals.set("__javy_io_writeSync", Function::new(cx, MutFn::new()))?; + globals.set( + "__javy_io_writeSync", + Function::new(this.clone(), |cx, args| { + write(Args::hold(cx, args)).expect("write to succeed") + }), + )?; + + globals.set( + "__javy_io_readSync", + Function::new(this.clone(), |cx, args| { + read(Args::hold(cx, args)).expect("read to succeed") + }), + )?; + this.eval(include_str!("io.js"))?; Ok::<_, Error>(()) - }); + })?; Ok(()) - // let context = runtime.context(); - // let global = context.global_object()?; - - // let mut javy_object = global.get_property("Javy")?; - // if javy_object.is_undefined() { - // javy_object = context.object_value()?; - // global.set_property("Javy", javy_object)?; - // } - - // global.set_property( - // "__javy_io_writeSync", - // context.wrap_callback(|_, _this_arg, args| { - // let [fd, data, offset, length, ..] = args else { - // anyhow::bail!("Invalid number of parameters"); - // }; - - // let mut fd: Box = match fd.try_into()? { - // 1 => Box::new(std::io::stdout()), - // 2 => Box::new(std::io::stderr()), - // _ => anyhow::bail!("Only stdout and stderr are supported"), - // }; - // let data: Vec = data.try_into()?; - // let offset: usize = offset.try_into()?; - // let length: usize = length.try_into()?; - // let data = &data[offset..(offset + length)]; - // let n = fd.write(data)?; - // fd.flush()?; - // Ok(n.into()) - // })?, - // )?; - - // global.set_property( - // "__javy_io_readSync", - // context.wrap_callback(|_, _this_arg, args| { - // let [fd, data, offset, length, ..] = args else { - // anyhow::bail!("Invalid number of parameters"); - // }; - // let mut fd: Box = match fd.try_into()? { - // 0 => Box::new(std::io::stdin()), - // _ => anyhow::bail!("Only stdin is supported"), - // }; - // let offset: usize = offset.try_into()?; - // let length: usize = length.try_into()?; - // if !data.is_array_buffer() { - // anyhow::bail!("Data needs to be an ArrayBuffer"); - // } - // let data = data.as_bytes_mut()?; - // let data = &mut data[offset..(offset + length)]; - // let n = fd.read(data)?; - // Ok(n.into()) - // })?, - // )?; - - // context.eval_global("io.js", include_str!("io.js"))?; - // Ok(()) } } diff --git a/crates/apis/src/text_encoding/mod.rs b/crates/apis/src/text_encoding/mod.rs index 7d800764..df608d10 100644 --- a/crates/apis/src/text_encoding/mod.rs +++ b/crates/apis/src/text_encoding/mod.rs @@ -1,102 +1,130 @@ -use std::{borrow::Cow, str}; +use std::str; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, bail, Error, Result}; use javy::{ - quickjs::{JSContextRef, JSError, JSValue, JSValueRef}, + quickjs::{Error as JSError, Function, String as JSString, TypedArray, Value}, Runtime, }; -use crate::{APIConfig, JSApiSet}; +use crate::{APIConfig, Args, JSApiSet}; pub(super) struct TextEncoding; impl JSApiSet for TextEncoding { - fn register(&self, runtime: &Runtime, _config: &APIConfig) -> Result<()> { - let context = runtime.context(); - let global = context.global_object()?; - global.set_property( - "__javy_decodeUtf8BufferToString", - context.wrap_callback(decode_utf8_buffer_to_js_string())?, - )?; - global.set_property( - "__javy_encodeStringToUtf8Buffer", - context.wrap_callback(encode_js_string_to_utf8_buffer())?, - )?; - - context.eval_global("text-encoding.js", include_str!("./text-encoding.js"))?; + fn register<'js>(&self, runtime: &Runtime, _: &APIConfig) -> Result<()> { + runtime.context().with(|this| { + let globals = this.globals(); + globals.set( + "__javy_decodeUtf8BufferToString", + Function::new(this.clone(), |cx, args| { + decode(Args::hold(cx, args)).expect("decode to succeed") + }), + )?; + globals.set( + "__javy_encodeStringToUtf8Buffer", + Function::new(this.clone(), |cx, args| { + encode(Args::hold(cx, args)).expect("encode to succeed") + }), + )?; + this.eval(include_str!("./text-encoding.js"))?; + Ok::<_, Error>(()) + })?; + Ok(()) } } -fn decode_utf8_buffer_to_js_string( -) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { - move |_ctx: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]| { - if args.len() != 5 { - return Err(anyhow!("Expecting 5 arguments, received {}", args.len())); - } - - let buffer: Vec = args[0].try_into()?; - let byte_offset: usize = args[1].try_into()?; - let byte_length: usize = args[2].try_into()?; - let fatal: bool = args[3].try_into()?; - let ignore_bom: bool = args[4].try_into()?; - - let mut view = buffer - .get(byte_offset..(byte_offset + byte_length)) - .ok_or_else(|| { - anyhow!("Provided offset and length is not valid for provided buffer") - })?; - - if !ignore_bom { - view = match view { - // [0xEF, 0xBB, 0xBF] is the UTF-8 BOM which we want to strip - [0xEF, 0xBB, 0xBF, rest @ ..] => rest, - _ => view, - }; - } - - let str = - if fatal { - Cow::from(str::from_utf8(view).map_err(|_| { - JSError::Type("The encoded data was not valid utf-8".to_string()) - })?) - } else { - String::from_utf8_lossy(view) - }; - Ok(str.to_string().into()) +/// Decode a UTF-8 byte buffer as a JavaScript String. +fn decode<'js>(args: Args<'js>) -> Result> { + let (cx, args) = args.release(); + if args.len() != 5 { + bail!( + "Wrong number of arguments. Expected 5 arguments. Got: {}", + args.len() + ); } -} -fn encode_js_string_to_utf8_buffer( -) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> anyhow::Result { - move |_ctx: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]| { - if args.len() != 1 { - return Err(anyhow!("Expecting 1 argument, got {}", args.len())); - } + let buffer = args[0] + .as_array() + .ok_or_else(|| anyhow!("buffer must be an array"))? + .as_typed_array::() + .ok_or_else(|| anyhow!("buffer must be a UInt8Array"))? + .as_bytes() + .ok_or_else(|| anyhow!("Couldn't retrive &[u8] from buffer"))?; + + let byte_offset = args[1] + .as_number() + .ok_or_else(|| anyhow!("offset must be a number"))? as usize; + let byte_length = args[2] + .as_number() + .ok_or_else(|| anyhow!("byte_length must be a number"))? as usize; + let fatal = args[3] + .as_bool() + .ok_or_else(|| anyhow!("fatal must be a boolean"))?; + let ignore_bom = args[4] + .as_bool() + .ok_or_else(|| anyhow!("ignore_bom must be a boolean"))?; + + let mut view = buffer + .get(byte_offset..(byte_offset + byte_length)) + .ok_or_else(|| anyhow!("Provided offset and length is not valid for provided buffer"))?; - let js_string: String = args[0].try_into()?; - Ok(js_string.into_bytes().into()) + if !ignore_bom { + view = match view { + // [0xEF, 0xBB, 0xBF] is the UTF-8 BOM which we want to strip + [0xEF, 0xBB, 0xBF, rest @ ..] => rest, + _ => view, + }; } + + let js_string = if fatal { + JSString::from_str(cx, str::from_utf8(view).map_err(JSError::Utf8)?) + } else { + let str = String::from_utf8_lossy(view); + JSString::from_str(cx, &str) + }; + + Ok(Value::from_string(js_string?)) +} + +/// Encode a JavaScript String into a JavaScript UInt8Array. +fn encode<'js>(args: Args<'js>) -> Result> { + let (cx, args) = args.release(); + if args.len() != 1 { + bail!("Wrong number of arguments. Expected 1. Got {}", args.len()); + } + + let js_string = args[0] + .as_string() + .ok_or_else(|| anyhow!("Argument must be a String"))? + .to_string()?; + + Ok(TypedArray::new(cx, js_string.into_bytes())? + .as_value() + .to_owned()) } #[cfg(test)] mod tests { use crate::{APIConfig, JSApiSet}; - use anyhow::Result; - use javy::Runtime; + use anyhow::{Error, Result}; + use javy::{quickjs::Value, Runtime}; use super::TextEncoding; #[test] fn test_text_encoder_decoder() -> Result<()> { let runtime = Runtime::default(); - let context = runtime.context(); - TextEncoding.register(&runtime, &APIConfig::default())?; - let result = context.eval_global( - "main", - "let encoder = new TextEncoder(); let buffer = encoder.encode('hello'); let decoder = new TextDecoder(); decoder.decode(buffer) == 'hello';" - )?; - assert!(result.as_bool()?); + + runtime.context().with(|this| { + + TextEncoding.register(&runtime, &APIConfig::default())?; + let result: Value<'_> = this.eval( + "let encoder = new TextEncoder(); let buffer = encoder.encode('hello'); let decoder = new TextDecoder(); decoder.decode(buffer) == 'hello';" + )?; + assert!(result.as_bool().unwrap()); + Ok::<_, Error>(()) + })?; Ok(()) } } diff --git a/crates/core/src/execution.rs b/crates/core/src/execution.rs index 992aacd9..941ef300 100644 --- a/crates/core/src/execution.rs +++ b/crates/core/src/execution.rs @@ -1,35 +1,42 @@ use std::process; use anyhow::{bail, Error, Result}; -use javy::{quickjs::JSContextRef, Runtime}; +use javy::{ + quickjs::{Ctx, Module}, + Runtime, +}; 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); + runtime.context().with(|this| { + // Module::instantiate_read_object(this.clone(), bytecode) + // .and_then(|_| process_event_loop(this)) + // .unwrap_or_else(handle_error); + }); + // context + // .eval_binary(bytecode) + // .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); + // 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"); - } +fn process_event_loop<'js>(cx: Ctx<'js>) -> 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(()) } diff --git a/crates/javy/Cargo.toml b/crates/javy/Cargo.toml index f8cba6f1..3cbbc913 100644 --- a/crates/javy/Cargo.toml +++ b/crates/javy/Cargo.toml @@ -12,7 +12,7 @@ categories = ["wasm"] [dependencies] anyhow = { workspace = true } quickjs-wasm-rs = { version = "3.0.1-alpha.1", path = "../quickjs-wasm-rs" } -rquickjs = "0.5.1" +rquickjs = {version = "0.5.1", features = ["array-buffer"] } serde_json = { version = "1.0", optional = true } serde-transcode = { version = "1.1", optional = true } rmp-serde = { version = "^1.1", optional = true }