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

Create plugin-api crate and move most of core's logic into it #795

Merged
merged 3 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
11 changes: 9 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ members = [
"crates/javy",
"crates/core",
"crates/cli",
"crates/plugin-api",
"crates/test-macros",
"crates/runner",
"fuzz",
Expand All @@ -21,8 +22,8 @@ wasmtime = "19"
wasmtime-wasi = "19"
wasi-common = "19"
anyhow = "1.0"
once_cell = "1.20"
javy = { path = "crates/javy", version = "3.0.2-alpha.1" }
javy-plugin-api = { path = "crates/plugin-api", version = "1.0.0-alpha.1" }
tempfile = "3.13.0"
uuid = { version = "1.10", features = ["v4"] }
serde = { version = "1.0", default-features = false }
Expand Down
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ docs:
test-javy:
CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime --dir=." cargo hack test --package=javy --target=wasm32-wasip1 --each-feature -- --nocapture

test-plugin-api:
CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime --dir=." cargo hack test --package=javy-plugin-api --target=wasm32-wasip1 --each-feature -- --nocapture

test-core:
CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime" cargo test --package=javy-core --target=wasm32-wasip1 -- --nocapture

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

tests: test-javy test-core test-runner test-cli test-wpt
tests: test-javy test-plugin-api test-core test-runner test-cli test-wpt

fmt: fmt-javy fmt-core fmt-cli
fmt: fmt-javy fmt-plugin-api fmt-core fmt-cli

fmt-javy:
cargo fmt --package=javy -- --check
cargo clippy --package=javy --target=wasm32-wasip1 --all-targets -- -D warnings

fmt-plugin-api:
cargo fmt --package=javy-plugin-api -- --check
cargo clippy --package=javy-plugin-api --target=wasm32-wasip1 --all-targets -- -D warnings

fmt-core:
cargo fmt --package=javy-core -- --check
cargo clippy --package=javy-core --target=wasm32-wasip1 --all-targets -- -D warnings
Expand Down
5 changes: 2 additions & 3 deletions crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ crate-type = ["cdylib"]

[dependencies]
anyhow = { workspace = true }
javy = { workspace = true, features = ["export_alloc_fns", "json"] }
once_cell = { workspace = true }
javy-plugin-api = { workspace = true, features = ["json"] }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it intended to have this dependency at the workspace level? I think it's not shared across any other crates in the workspace, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move it out of the workspace into javy-core. For some reason I thought the javy crate was only used by javy-core but I can see after looking that the fuzzer also uses it.

serde = { workspace = true }
serde_json = { workspace = true }

[features]
experimental_event_loop = []
experimental_event_loop = ["javy-plugin-api/experimental_event_loop"]
82 changes: 0 additions & 82 deletions crates/core/src/execution.rs

This file was deleted.

105 changes: 6 additions & 99 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,12 @@
use anyhow::anyhow;
use javy::Config;
use javy::Runtime;
use namespace::import_namespace;
use once_cell::sync::OnceCell;
use javy_plugin_api::import_namespace;
use javy_plugin_api::javy::Config;
use shared_config::SharedConfig;
use std::io;
use std::io::Read;
use std::slice;
use std::str;

mod execution;
mod namespace;
mod shared_config;

const FUNCTION_MODULE_NAME: &str = "function.mjs";

static mut COMPILE_SRC_RET_AREA: [u32; 2] = [0; 2];

static mut RUNTIME: OnceCell<Runtime> = OnceCell::new();

import_namespace!("javy_quickjs_provider_v3");

/// Used by Wizer to preinitialize the module.
Expand Down Expand Up @@ -48,99 +36,18 @@ pub extern "C" fn initialize_runtime() {
shared_config.apply_to_config(&mut config);
}

let runtime = Runtime::new(config).unwrap();
unsafe {
RUNTIME.take(); // Allow re-initializing.
RUNTIME
.set(runtime)
// `unwrap` requires error `T` to implement `Debug` but `set`
// returns the `javy::Runtime` on error and `javy::Runtime` does not
// implement `Debug`.
.map_err(|_| anyhow!("Could not pre-initialize javy::Runtime"))
.unwrap();
};
}

/// Compiles JS source code to QuickJS bytecode.
///
/// Returns a pointer to a buffer containing a 32-bit pointer to the bytecode byte array and the
/// u32 length of the bytecode byte array.
///
/// # Arguments
///
/// * `js_src_ptr` - A pointer to the start of a byte array containing UTF-8 JS source code
/// * `js_src_len` - The length of the byte array containing JS source code
///
/// # 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 initialized runtime when compiling because certain runtime
// configurations can cause different bytecode to be emitted.
//
// For example, given the following JS:
// ```
// function foo() {
// "use math"
// 1234 % 32
// }
// ```
//
// Setting `config.bignum_extension` to `true` will produce different
// bytecode than if it were set to `false`.
let runtime = unsafe { RUNTIME.get().unwrap() };
let js_src = str::from_utf8(slice::from_raw_parts(js_src_ptr, js_src_len)).unwrap();

let bytecode = runtime
.compile_to_bytecode(FUNCTION_MODULE_NAME, js_src)
.unwrap();

// We need the bytecode buffer to live longer than this function so it can be read from memory
let len = bytecode.len();
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] = len.try_into().unwrap();
COMPILE_SRC_RET_AREA.as_ptr()
javy_plugin_api::initialize_runtime(config, |runtime| runtime).unwrap();
}

/// Evaluates QuickJS bytecode
///
/// # Safety
///
/// * `bytecode_ptr` must reference a valid array of unsigned bytes of `bytecode_len` length
// This will be removed as soon as we stop emitting calls to it in dynamically
// linked modules.
#[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, None);
}

/// Evaluates QuickJS bytecode and optionally invokes exported JS function with
/// name.
///
/// # Safety
///
/// * `bytecode_ptr` must reference a valid array of bytes of `bytecode_len`
/// length.
/// * If `fn_name_ptr` is not 0, it must reference a UTF-8 string with
/// `fn_name_len` byte length.
#[export_name = "invoke"]
pub unsafe extern "C" fn invoke(
bytecode_ptr: *const u8,
bytecode_len: usize,
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 = if !fn_name_ptr.is_null() && fn_name_len != 0 {
Some(str::from_utf8_unchecked(slice::from_raw_parts(
fn_name_ptr,
fn_name_len,
)))
} else {
None
};
execution::run_bytecode(runtime, bytecode, fn_name);
javy_plugin_api::run_bytecode(bytecode, None);
}
11 changes: 11 additions & 0 deletions crates/plugin-api/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic
Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

Initial release
18 changes: 18 additions & 0 deletions crates/plugin-api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "javy-plugin-api"
version = "1.0.0-alpha.1"
authors.workspace = true
edition.workspace = true
license.workspace = true
description = "APIs for Javy plugins"
homepage = "https://github.com/bytecodealliance/javy/tree/main/crates/javy-plugin-api"
repository = "https://github.com/bytecodealliance/javy/tree/main/crates/javy-plugin-api"
categories = ["wasm"]

[dependencies]
anyhow = { workspace = true }
javy = { workspace = true, features = ["export_alloc_fns"] }

[features]
experimental_event_loop = []
json = ["javy/json"]
37 changes: 37 additions & 0 deletions crates/plugin-api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div align="center">
<h1><code>javy-plugin-api</code></h1>
<p>
<strong>A crate for creating Javy plugins</strong>
</p>
<p>
<a href="https://docs.rs/javy-plugin-api"><img src="https://docs.rs/javy-plugin-api/badge.svg" alt="Documentation Status" /></a>
<a href="https://crates.io/crates/javy-plugin-api"><img src="https://img.shields.io/crates/v/javy-plugin-api.svg" alt="crates.io status" /></a>
</p>
</div>

Refer to the [crate level documentation](https://docs.rs/javy-plugin-api) to learn more.

Example usage:

```rust
use javy_plugin_api::import_namespace;
use javy_plugin_api::javy::Config;

// Dynamically linked modules will use `my_javy_plugin_v1` as the import
// namespace.
import_namespace!("my_javy_plugin_v1");

#[export_name = "initialize_runtime"]
pub extern "C" fn initialize_runtime() {
let mut config = Config::default();
config
.text_encoding(true)
.javy_stream_io(true);

javy_plugin_api::initialize_runtime(config, |runtime| runtime).unwrap();
}
```

## Publishing to crates.io

To publish this crate to crates.io, run `./publish.sh`.
5 changes: 5 additions & 0 deletions crates/plugin-api/publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash

set -e

cargo publish --target=wasm32-wasip1
Loading
Loading