Skip to content

Commit

Permalink
fix all slice ABI issues stemming from WASI ABI change
Browse files Browse the repository at this point in the history
  • Loading branch information
bluejekyll committed May 6, 2021
1 parent 0edb270 commit 0d71609
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 33 deletions.
17 changes: 9 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ endif
WASMTIME_TARGET_DIR := ${PWD}/target
NATIVE_TARGET_DIR := ${PWD}/target/native/${PLATFORM}/${ARCH}
WASM_TESTS := $(wildcard tests/*/Cargo.toml)
RUSTV := "+stable"

## This can be changed to the different wasm targets
# WASM_TARGET := wasm32-unknown-unknown
Expand All @@ -39,7 +40,7 @@ WASM_TARGET := wasm32-wasi
init:
@echo "====> Testing for all tools"
@mvn -version || (echo maven is required, e.g. 'brew install maven' && mvn -version)
@cargo --version || (echo rust is required, e.g. 'curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh' && cargo --version)
@cargo ${RUSTV} --version || (echo rust is required, e.g. 'curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh' && cargo --version)
rustup target add ${WASM_TARGET}

.PHONY: clean
Expand All @@ -50,14 +51,14 @@ clean:

target/native:
@echo "====> Building wasmtime-jni for ${PLATFORM} arch ${ARCH}"
cd wasmtime-jni && cargo build ${RELEASE} --lib
cd wasmtime-jni && cargo ${RUSTV} build ${RELEASE} --lib
@mkdir -p ${NATIVE_TARGET_DIR}
@cp -rpf ${WASMTIME_TARGET_DIR}/debug/*.${DYLIB_EXT} ${NATIVE_TARGET_DIR}/

.PHONY: build
build:
@echo "====> Building"
cd wasmtime-jni && cargo build
cd wasmtime-jni && cargo ${RUSTV} build
$(MAKE) ${WASM_TESTS}

rm -rf ${PWD}/target/native
Expand All @@ -66,12 +67,12 @@ build:
.PHONY: ${WASM_TESTS}
${WASM_TESTS}:
@echo "====> Building $(dir $@)"
cd $(dir $@) && cargo build --target ${WASM_TARGET}
cd $(dir $@) && cargo ${RUSTV} build --target ${WASM_TARGET}

.PHONY: test
test: build
@echo "====> Testing"
cd wasmtime-jni && cargo test
cd wasmtime-jni && cargo ${RUSTV} test
$(MAKE) mvn-test

.PHONY: mvn-test
Expand All @@ -88,6 +89,6 @@ package: build

.PHONY: cleanliness
cleanliness:
cargo clean -p wasmtime-jni -p wasmtime-jni-exports -p math -p slices -p strings
cargo clippy -- -D warnings
cargo fmt -- --check
cargo ${RUSTV} clean -p wasmtime-jni -p wasmtime-jni-exports -p math -p slices -p strings
cargo ${RUSTV} clippy -- -D warnings
cargo ${RUSTV} fmt -- --check
42 changes: 24 additions & 18 deletions tests/slices/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,27 @@ pub use wasmtime_jni_exports;
// These functions are declared in Java and use the Linker to associate them to the Module Instance.
#[link(wasm_import_module = "test")]
extern "C" {
fn hello_to_java(data: WasmSlice);
fn reverse_bytes_java(data: WasmSlice, result: &mut WasmSlice);
fn hello_to_java(data_ptr: i32, data_len: i32);
fn reverse_bytes_java(data_ptr: i32, data_len: i32, result: &mut WasmSlice);
}

#[no_mangle]
pub extern "C" fn say_hello_to_java() {
let hello = "Hello Java!";

let bytes = hello.as_bytes();
unsafe {
hello_to_java(WasmSlice {
ptr: bytes.as_ptr() as i32,
len: bytes.len() as i32,
})
}
unsafe { hello_to_java(bytes.as_ptr() as i32, bytes.len() as i32) }
}

/// # Safety
///
/// This relies on an external method having properly allocated the WasmSlice before calling this method.
#[no_mangle]
pub unsafe extern "C" fn print_bytes(slice: WasmSlice) {
pub unsafe extern "C" fn print_bytes(slice_ptr: i32, slice_len: i32) {
let slice = WasmSlice {
ptr: slice_ptr,
len: slice_len,
};
println!(
"slices::print_bytes: ptr: {:x?} len: {}",
slice.ptr, slice.len
Expand All @@ -41,7 +40,11 @@ pub unsafe extern "C" fn print_bytes(slice: WasmSlice) {
///
/// This relies on an external method having properly allocated the WasmSlice before calling this method.
#[no_mangle]
pub unsafe extern "C" fn reverse_bytes(slice: WasmSlice, slice_ref: &mut WasmSlice) {
pub unsafe extern "C" fn reverse_bytes(slice_ptr: i32, slice_len: i32, slice_ref: &mut WasmSlice) {
let slice = WasmSlice {
ptr: slice_ptr,
len: slice_len,
};
println!(
"slices::reverse_bytes: ptr: {:x?} len: {}",
slice.ptr, slice.len
Expand All @@ -65,9 +68,17 @@ pub unsafe extern "C" fn reverse_bytes(slice: WasmSlice, slice_ref: &mut WasmSli
/// # Safety
/// Assumes that the data input is properly allocated slice, and the result has an allocated WasmSlice object at the pointer.
#[no_mangle]
pub unsafe extern "C" fn reverse_bytes_in_java(data: WasmSlice, result: &mut WasmSlice) {
pub unsafe extern "C" fn reverse_bytes_in_java(
data_ptr: i32,
data_len: i32,
result: &mut WasmSlice,
) {
let data = WasmSlice {
ptr: data_ptr,
len: data_len,
};
println!("slices::reverse_bytes_in_java: {:?}", data);
reverse_bytes_java(data, result);
reverse_bytes_java(data.ptr, data.len, result);
}

#[cfg(test)]
Expand All @@ -77,11 +88,6 @@ mod tests {
#[test]
fn test_print_bytes() {
let bytes = &[0u8, 1, 2] as &[u8];
unsafe {
print_bytes(WasmSlice {
ptr: bytes.as_ptr() as i32,
len: bytes.len() as i32,
})
};
unsafe { print_bytes(bytes.as_ptr() as i32, bytes.len() as i32) };
}
}
18 changes: 13 additions & 5 deletions tests/strings/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ use wasmtime_jni_exports::WasmSlice;
/// test imports from Java
#[link(wasm_import_module = "test")]
extern "C" {
fn say_hello_to_java(data: WasmSlice, response: &mut WasmSlice);
fn say_hello_to_java(data_ptr: i32, data_len: i32, response: &mut WasmSlice);
}

/// Greetings
///
/// # Safety
/// Passed in WasmSlice is owned by caller
#[no_mangle]
pub unsafe extern "C" fn greet(name: WasmSlice) {
pub unsafe extern "C" fn greet(name_ptr: i32, name_len: i32) {
let name = WasmSlice {
ptr: name_ptr,
len: name_len,
};
let name = name.as_bytes();
let name = String::from_utf8_lossy(name);

Expand All @@ -21,7 +25,11 @@ pub unsafe extern "C" fn greet(name: WasmSlice) {
/// # Safety
/// Passed in WasmSlice is owned by caller
#[no_mangle]
pub unsafe extern "C" fn say_hello_to(name: WasmSlice, response: &mut WasmSlice) {
pub unsafe extern "C" fn say_hello_to(name_ptr: i32, name_len: i32, response: &mut WasmSlice) {
let name = WasmSlice {
ptr: name_ptr,
len: name_len,
};
let name = name.as_bytes();
let name = String::from_utf8_lossy(name);

Expand All @@ -46,6 +54,6 @@ pub unsafe extern "C" fn say_hello_to(name: WasmSlice, response: &mut WasmSlice)
/// # Safety
/// Passed in WasmSlice is owned by caller
#[no_mangle]
pub unsafe extern "C" fn say_hello_in_java(name: WasmSlice, response: &mut WasmSlice) {
say_hello_to_java(name, response)
pub unsafe extern "C" fn say_hello_in_java(data_ptr: i32, data_len: i32, response: &mut WasmSlice) {
say_hello_to_java(data_ptr, data_len, response)
}
25 changes: 25 additions & 0 deletions wasmtime-jni-exports/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ impl From<Vec<u8>> for WasmSlice {
}
}

// FIXME: this either needs a Drop impl or should take a slice of bytes and have an associated lifetime instead...
impl From<Box<[u8]>> for WasmSlice {
#[inline]
fn from(bytes: Box<[u8]>) -> Self {
Expand All @@ -78,3 +79,27 @@ impl From<Box<[u8]>> for WasmSlice {
Self { ptr, len }
}
}

impl WasmString {
/// # Safety
/// This relies on the ptr and len being accurate for the current memory environment. Inside a WASM runtime for example.
#[inline]
pub unsafe fn as_bytes(&self) -> &[u8] {
self.0.as_bytes()
}
}

/// A WasmSlice is an offset into the local `memory` of the WASM module instance.
///
/// It is only valid in the context of a `memory` contiguous region and a module's associated `Store`
#[repr(transparent)]
#[derive(Clone, Copy, Debug)]
pub struct WasmString(WasmSlice);

impl From<String> for WasmString {
#[inline]
fn from(s: String) -> Self {
let bytes = s.into_bytes();
WasmString(WasmSlice::from(bytes))
}
}
24 changes: 22 additions & 2 deletions wasmtime-jni/src/wasm_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::convert::TryFrom;
use std::slice;

use anyhow::{anyhow, Context, Error};
use jni::objects::{JClass, JMethodID, JObject, JValue, ReleaseMode};
use jni::objects::{JClass, JMethodID, JObject, JString, JValue, ReleaseMode};
use jni::signature::JavaType;
use jni::sys::{jlong, jobject, jobjectArray};
use jni::JNIEnv;
Expand Down Expand Up @@ -40,6 +40,9 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmFunction_createFunc<'j>(
let obj = env.new_global_ref(obj)?;
let jvm = env.get_java_vm()?;

let method_name = get_method_name(env, &method)?;
debug!("building WASM function from method: \"{}\"", method_name);

// collect all the arguments from
let param_list = env.get_list(param_tys)?;
let mut wasm_args: Vec<ValType> = Vec::with_capacity(param_list.size()? as usize);
Expand Down Expand Up @@ -303,7 +306,13 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmFunction_createFunc<'j>(
Ok(())
};

let func = Func::new(&store, FuncType::new(wasm_args, wasm_ret), func);
let func_type = FuncType::new(wasm_args, wasm_ret);
debug!(
"method \"{}\" as function in WASM: {:?}",
method_name, func_type
);

let func = Func::new(&store, func_type, func);

Ok(OpaquePtr::from(func).make_opaque())
})
Expand Down Expand Up @@ -422,3 +431,14 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmFunction_callNtv<'j>(
},
)
}

fn get_method_name<'j, O>(env: &JNIEnv<'j>, object: O) -> Result<String, Error>
where
O: Into<JObject<'j>>,
{
let name = env.call_method(object, "getName", "()Ljava/lang/String;", &[])?;
let name = name.l()?;
let name = JString::from(name);
let name = env.get_string(name)?;
Ok(Cow::from(&name).to_string())
}
2 changes: 2 additions & 0 deletions wasmtime-jni/src/wasm_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::borrow::Cow;
use jni::objects::{JClass, JString};
use jni::sys::jlong;
use jni::JNIEnv;
use log::debug;
use wasmtime::Instance;

use crate::opaque_ptr::OpaquePtr;
Expand Down Expand Up @@ -45,6 +46,7 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmInstance_getFunctionNtv<
let func = instance.get_func(&name);

let func_ptr = if let Some(func) = func {
debug!("found function in WASM: {}:{:?}", name, func.ty());
let func = OpaquePtr::from(func);
func.make_opaque()
} else {
Expand Down

0 comments on commit 0d71609

Please sign in to comment.