Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to rquickjs #618

Merged
merged 23 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7b08602
chore: Initial migration to rquickjs
saulecabrera Mar 6, 2024
300c3ca
More progress on migration:
saulecabrera Mar 19, 2024
878ddd5
Migrate all of the APIs to use rquickjs
saulecabrera Apr 5, 2024
2c4b52b
Finalize migration of the toolchain as a whole
saulecabrera Apr 11, 2024
b0d8285
Rebase
saulecabrera Apr 11, 2024
a126ede
Uncomment integration tests
saulecabrera Apr 11, 2024
85abf57
Throw `TypeError` when `fatal` decoding is specified.
saulecabrera Apr 11, 2024
2bea077
Fix clippy errors
saulecabrera Apr 11, 2024
d599ed4
Update documentation where applicable and remove reference to older c…
saulecabrera Apr 11, 2024
b255b55
Handle converting from JS strings to Rust strings for text encoding
saulecabrera Apr 12, 2024
c039625
Migrate serializers and deserializers to use rquickjs and move them u…
saulecabrera Apr 16, 2024
7e4ec6f
Move quickcheck to the top level Cargo.toml
saulecabrera Apr 16, 2024
9af48b0
Update versions and CHANGELOGs
saulecabrera Apr 16, 2024
cdcac4d
Audit dependencies
saulecabrera Apr 16, 2024
9eec07b
Improve safety comment
saulecabrera Apr 16, 2024
055de24
Port previous implementation of read sync
saulecabrera Apr 16, 2024
2aa4f6f
Review comments
saulecabrera Apr 16, 2024
29361f3
Improve comment for why `ManuallyDrop`
saulecabrera Apr 16, 2024
5fbcf8a
Use `ManuallyDrop` for Context
saulecabrera Apr 23, 2024
6d7fec5
Format comments where applicable
saulecabrera Apr 23, 2024
5b2f0ad
Use rquickjs 0.6.0 through a fork
saulecabrera May 1, 2024
3b0d041
Cargo vet config
saulecabrera May 1, 2024
d51ae19
Remove comment, cargo vet doesn't like them
saulecabrera May 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,298 changes: 653 additions & 645 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ wasmtime-wasi = "16"
wasi-common = "16"
anyhow = "1.0"
once_cell = "1.19"
javy = { path = "crates/javy", version = "2.2.1-alpha.1" }
javy = { path = "crates/javy", version = "3.0.0-alpha.1" }

[profile.release]
lto = true
Expand Down
5 changes: 1 addition & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ docs:
cargo doc --package=javy-cli --open
cargo doc --package=javy-core --open --target=wasm32-wasi

test-quickjs-wasm-rs:
cargo wasi test --package=quickjs-wasm-rs -- --nocapture

test-javy:
cargo wasi test --package=javy --features json,messagepack -- --nocapture

Expand All @@ -47,7 +44,7 @@ test-wpt:
npm install --prefix wpt
npm test --prefix wpt

tests: test-quickjs-wasm-rs test-javy test-apis test-core test-cli test-wpt
tests: test-javy test-apis test-core test-cli test-wpt

fmt: fmt-quickjs-wasm-sys fmt-quickjs-wasm-rs fmt-javy fmt-apis fmt-core fmt-cli

Expand Down
5 changes: 5 additions & 0 deletions crates/apis/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Rewrite the APIs on top of Javy v3.0.0, which drops support for
`quickjs-wasm-rs` in favor of `rquickjs`

## [2.2.0] - 2024-01-31

### Changed
Expand Down
2 changes: 1 addition & 1 deletion crates/apis/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "javy-apis"
version = "2.2.1-alpha.1"
version = "3.0.0-alpha.1"
authors.workspace = true
edition.workspace = true
license.workspace = true
Expand Down
15 changes: 8 additions & 7 deletions crates/apis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ APIs are registered by enabling crate features.
## Example usage

```rust
use javy::{quickjs::JSValue, Runtime};
// with `console` feature enabled

// With the `console` feature enabled.
use javy::{Runtime, from_js_error};
use javy_apis::RuntimeExt;
use anyhow::Result;

fn main() -> Result<()> {
let runtime = Runtime::new_with_defaults()?;
let context = runtime.context();
context.eval_global("hello.js", "console.log('hello!');")?;
context.with(|cx| {
cx.eval_with_options(Default::default(), "console.log('hello!');")
.map_err(|e| to_js_error(cx.clone(), e))?
});
Ok(())
}
```
Expand All @@ -30,7 +35,3 @@ If you want to customize the runtime or the APIs, you can use the `Runtime::new_
## Publishing to crates.io

To publish this crate to crates.io, run `./publish.sh`.

## Using a custom WASI SDK

This crate can be compiled using a custom [WASI SDK](https://github.com/WebAssembly/wasi-sdk). When building this crate, set the `QUICKJS_WASM_SYS_WASI_SDK_PATH` environment variable to the absolute path where you installed the SDK.
150 changes: 86 additions & 64 deletions crates/apis/src/console/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::io::Write;

use anyhow::Result;
use anyhow::{Error, Result};
use javy::{
quickjs::{JSContextRef, JSValue, JSValueRef},
Runtime,
hold, hold_and_release, print,
quickjs::{prelude::MutFn, Context, Function, Object, Value},
to_js_error, Args, Runtime,
};

use crate::{APIConfig, JSApiSet};
Expand Down Expand Up @@ -31,50 +32,65 @@ impl JSApiSet for Console {
}
}

fn register_console<T, U>(context: &JSContextRef, log_stream: T, error_stream: U) -> Result<()>
fn register_console<T, U>(context: &Context, mut log_stream: T, mut error_stream: U) -> Result<()>
where
T: Write + 'static,
U: Write + 'static,
{
let console_log_callback = context.wrap_callback(console_log_to(log_stream))?;
let console_error_callback = context.wrap_callback(console_log_to(error_stream))?;
let console_object = context.object_value()?;
console_object.set_property("log", console_log_callback)?;
console_object.set_property("error", console_error_callback)?;
context
.global_object()?
.set_property("console", console_object)?;
context.with(|this| {
let globals = this.globals();
let console = Object::new(this.clone())?;

console.set(
"log",
Function::new(
this.clone(),
MutFn::new(move |cx, args| {
let (cx, args) = hold_and_release!(cx, args);
log(hold!(cx.clone(), args), &mut log_stream).map_err(|e| to_js_error(cx, e))
}),
)?,
)?;

console.set(
"error",
Function::new(
this.clone(),
MutFn::new(move |cx, args| {
let (cx, args) = hold_and_release!(cx, args);
log(hold!(cx.clone(), args), &mut error_stream).map_err(|e| to_js_error(cx, e))
}),
)?,
)?;

globals.set("console", console)?;
Ok::<_, Error>(())
})?;
Ok(())
}

fn console_log_to<T>(
mut stream: T,
) -> impl FnMut(&JSContextRef, JSValueRef, &[JSValueRef]) -> Result<JSValue>
where
T: Write + 'static,
{
move |_ctx: &JSContextRef, _this: JSValueRef, args: &[JSValueRef]| {
// Write full string to in-memory destination before writing to stream since each write call to the stream
// will invoke a hostcall.
let mut log_line = String::new();
for (i, arg) in args.iter().enumerate() {
if i != 0 {
log_line.push(' ');
}
let line = arg.to_string();
log_line.push_str(&line);
fn log<'js, T: Write>(args: Args<'js>, stream: &mut T) -> Result<Value<'js>> {
let (ctx, args) = args.release();
let mut buf = String::new();
for (i, arg) in args.iter().enumerate() {
if i != 0 {
buf.push(' ');
}
print(arg, &mut buf)?;
}

writeln!(stream, "{log_line}")?;
writeln!(stream, "{buf}")?;

Ok(JSValue::Undefined)
}
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};
Expand All @@ -88,9 +104,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(())
}

Expand All @@ -102,24 +122,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(())
}

Expand All @@ -131,24 +152,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(())
}

Expand Down
49 changes: 34 additions & 15 deletions crates/apis/src/random/mod.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
use anyhow::Result;
use javy::{quickjs::JSValue, Runtime};
use anyhow::{Error, Result};
use javy::{
quickjs::{prelude::Func, Object},
Runtime,
};

use crate::{APIConfig, JSApiSet};

pub struct Random;

impl JSApiSet for Random {
fn register(&self, runtime: &Runtime, _config: &APIConfig) -> Result<()> {
let ctx = runtime.context();
ctx.global_object()?.get_property("Math")?.set_property(
"random",
// TODO figure out if we can lazily initialize the PRNG
ctx.wrap_callback(|_ctx, _this, _args| Ok(JSValue::Float(fastrand::f64())))?,
)?;
runtime.context().with(|cx| {
let globals = cx.globals();
let math: Object<'_> = globals.get("Math").expect("Math global to be defined");
math.set("random", Func::from(fastrand::f64))?;

Ok::<_, Error>(())
})?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::{random::Random, APIConfig, JSApiSet};
use anyhow::Result;
use javy::Runtime;
use anyhow::{Error, Result};
use javy::{
quickjs::{context::EvalOptions, 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| {
let eval_opts = EvalOptions {
strict: false,
..Default::default()
};
this.eval_with_options("result = Math.random()", eval_opts)?;
let result: f64 = this
.globals()
.get::<&str, Value<'_>>("result")?
.as_float()
.unwrap();
assert!(result >= 0.0);
assert!(result < 1.0);
Ok::<_, Error>(())
})?;

Ok(())
}
}
1 change: 1 addition & 0 deletions crates/apis/src/runtime_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl RuntimeExt for Runtime {
fn new_with_apis(config: Config, api_config: APIConfig) -> Result<Runtime> {
let runtime = Runtime::new(config)?;
crate::add_to_runtime(&runtime, api_config)?;

Ok(runtime)
}

Expand Down
4 changes: 2 additions & 2 deletions crates/apis/src/stream_io/io.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
}
return __javy_io_readSync(
fd,
data.buffer,
data,
data.byteOffset,
data.byteLength
);
Expand All @@ -19,7 +19,7 @@
}
return __javy_io_writeSync(
fd,
data.buffer,
data,
saulecabrera marked this conversation as resolved.
Show resolved Hide resolved
data.byteOffset,
data.byteLength
);
Expand Down
Loading
Loading