Skip to content

Commit 0d71609

Browse files
committed
fix all slice ABI issues stemming from WASI ABI change
See: WebAssembly/tool-conventions#88
1 parent 0edb270 commit 0d71609

File tree

6 files changed

+95
-33
lines changed

6 files changed

+95
-33
lines changed

Makefile

+9-8
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ endif
3030
WASMTIME_TARGET_DIR := ${PWD}/target
3131
NATIVE_TARGET_DIR := ${PWD}/target/native/${PLATFORM}/${ARCH}
3232
WASM_TESTS := $(wildcard tests/*/Cargo.toml)
33+
RUSTV := "+stable"
3334

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

4546
.PHONY: clean
@@ -50,14 +51,14 @@ clean:
5051

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

5758
.PHONY: build
5859
build:
5960
@echo "====> Building"
60-
cd wasmtime-jni && cargo build
61+
cd wasmtime-jni && cargo ${RUSTV} build
6162
$(MAKE) ${WASM_TESTS}
6263

6364
rm -rf ${PWD}/target/native
@@ -66,12 +67,12 @@ build:
6667
.PHONY: ${WASM_TESTS}
6768
${WASM_TESTS}:
6869
@echo "====> Building $(dir $@)"
69-
cd $(dir $@) && cargo build --target ${WASM_TARGET}
70+
cd $(dir $@) && cargo ${RUSTV} build --target ${WASM_TARGET}
7071

7172
.PHONY: test
7273
test: build
7374
@echo "====> Testing"
74-
cd wasmtime-jni && cargo test
75+
cd wasmtime-jni && cargo ${RUSTV} test
7576
$(MAKE) mvn-test
7677

7778
.PHONY: mvn-test
@@ -88,6 +89,6 @@ package: build
8889

8990
.PHONY: cleanliness
9091
cleanliness:
91-
cargo clean -p wasmtime-jni -p wasmtime-jni-exports -p math -p slices -p strings
92-
cargo clippy -- -D warnings
93-
cargo fmt -- --check
92+
cargo ${RUSTV} clean -p wasmtime-jni -p wasmtime-jni-exports -p math -p slices -p strings
93+
cargo ${RUSTV} clippy -- -D warnings
94+
cargo ${RUSTV} fmt -- --check

tests/slices/src/lib.rs

+24-18
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,27 @@ pub use wasmtime_jni_exports;
66
// These functions are declared in Java and use the Linker to associate them to the Module Instance.
77
#[link(wasm_import_module = "test")]
88
extern "C" {
9-
fn hello_to_java(data: WasmSlice);
10-
fn reverse_bytes_java(data: WasmSlice, result: &mut WasmSlice);
9+
fn hello_to_java(data_ptr: i32, data_len: i32);
10+
fn reverse_bytes_java(data_ptr: i32, data_len: i32, result: &mut WasmSlice);
1111
}
1212

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

1717
let bytes = hello.as_bytes();
18-
unsafe {
19-
hello_to_java(WasmSlice {
20-
ptr: bytes.as_ptr() as i32,
21-
len: bytes.len() as i32,
22-
})
23-
}
18+
unsafe { hello_to_java(bytes.as_ptr() as i32, bytes.len() as i32) }
2419
}
2520

2621
/// # Safety
2722
///
2823
/// This relies on an external method having properly allocated the WasmSlice before calling this method.
2924
#[no_mangle]
30-
pub unsafe extern "C" fn print_bytes(slice: WasmSlice) {
25+
pub unsafe extern "C" fn print_bytes(slice_ptr: i32, slice_len: i32) {
26+
let slice = WasmSlice {
27+
ptr: slice_ptr,
28+
len: slice_len,
29+
};
3130
println!(
3231
"slices::print_bytes: ptr: {:x?} len: {}",
3332
slice.ptr, slice.len
@@ -41,7 +40,11 @@ pub unsafe extern "C" fn print_bytes(slice: WasmSlice) {
4140
///
4241
/// This relies on an external method having properly allocated the WasmSlice before calling this method.
4342
#[no_mangle]
44-
pub unsafe extern "C" fn reverse_bytes(slice: WasmSlice, slice_ref: &mut WasmSlice) {
43+
pub unsafe extern "C" fn reverse_bytes(slice_ptr: i32, slice_len: i32, slice_ref: &mut WasmSlice) {
44+
let slice = WasmSlice {
45+
ptr: slice_ptr,
46+
len: slice_len,
47+
};
4548
println!(
4649
"slices::reverse_bytes: ptr: {:x?} len: {}",
4750
slice.ptr, slice.len
@@ -65,9 +68,17 @@ pub unsafe extern "C" fn reverse_bytes(slice: WasmSlice, slice_ref: &mut WasmSli
6568
/// # Safety
6669
/// Assumes that the data input is properly allocated slice, and the result has an allocated WasmSlice object at the pointer.
6770
#[no_mangle]
68-
pub unsafe extern "C" fn reverse_bytes_in_java(data: WasmSlice, result: &mut WasmSlice) {
71+
pub unsafe extern "C" fn reverse_bytes_in_java(
72+
data_ptr: i32,
73+
data_len: i32,
74+
result: &mut WasmSlice,
75+
) {
76+
let data = WasmSlice {
77+
ptr: data_ptr,
78+
len: data_len,
79+
};
6980
println!("slices::reverse_bytes_in_java: {:?}", data);
70-
reverse_bytes_java(data, result);
81+
reverse_bytes_java(data.ptr, data.len, result);
7182
}
7283

7384
#[cfg(test)]
@@ -77,11 +88,6 @@ mod tests {
7788
#[test]
7889
fn test_print_bytes() {
7990
let bytes = &[0u8, 1, 2] as &[u8];
80-
unsafe {
81-
print_bytes(WasmSlice {
82-
ptr: bytes.as_ptr() as i32,
83-
len: bytes.len() as i32,
84-
})
85-
};
91+
unsafe { print_bytes(bytes.as_ptr() as i32, bytes.len() as i32) };
8692
}
8793
}

tests/strings/src/lib.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@ use wasmtime_jni_exports::WasmSlice;
33
/// test imports from Java
44
#[link(wasm_import_module = "test")]
55
extern "C" {
6-
fn say_hello_to_java(data: WasmSlice, response: &mut WasmSlice);
6+
fn say_hello_to_java(data_ptr: i32, data_len: i32, response: &mut WasmSlice);
77
}
88

99
/// Greetings
1010
///
1111
/// # Safety
1212
/// Passed in WasmSlice is owned by caller
1313
#[no_mangle]
14-
pub unsafe extern "C" fn greet(name: WasmSlice) {
14+
pub unsafe extern "C" fn greet(name_ptr: i32, name_len: i32) {
15+
let name = WasmSlice {
16+
ptr: name_ptr,
17+
len: name_len,
18+
};
1519
let name = name.as_bytes();
1620
let name = String::from_utf8_lossy(name);
1721

@@ -21,7 +25,11 @@ pub unsafe extern "C" fn greet(name: WasmSlice) {
2125
/// # Safety
2226
/// Passed in WasmSlice is owned by caller
2327
#[no_mangle]
24-
pub unsafe extern "C" fn say_hello_to(name: WasmSlice, response: &mut WasmSlice) {
28+
pub unsafe extern "C" fn say_hello_to(name_ptr: i32, name_len: i32, response: &mut WasmSlice) {
29+
let name = WasmSlice {
30+
ptr: name_ptr,
31+
len: name_len,
32+
};
2533
let name = name.as_bytes();
2634
let name = String::from_utf8_lossy(name);
2735

@@ -46,6 +54,6 @@ pub unsafe extern "C" fn say_hello_to(name: WasmSlice, response: &mut WasmSlice)
4654
/// # Safety
4755
/// Passed in WasmSlice is owned by caller
4856
#[no_mangle]
49-
pub unsafe extern "C" fn say_hello_in_java(name: WasmSlice, response: &mut WasmSlice) {
50-
say_hello_to_java(name, response)
57+
pub unsafe extern "C" fn say_hello_in_java(data_ptr: i32, data_len: i32, response: &mut WasmSlice) {
58+
say_hello_to_java(data_ptr, data_len, response)
5159
}

wasmtime-jni-exports/src/lib.rs

+25
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl From<Vec<u8>> for WasmSlice {
6666
}
6767
}
6868

69+
// FIXME: this either needs a Drop impl or should take a slice of bytes and have an associated lifetime instead...
6970
impl From<Box<[u8]>> for WasmSlice {
7071
#[inline]
7172
fn from(bytes: Box<[u8]>) -> Self {
@@ -78,3 +79,27 @@ impl From<Box<[u8]>> for WasmSlice {
7879
Self { ptr, len }
7980
}
8081
}
82+
83+
impl WasmString {
84+
/// # Safety
85+
/// This relies on the ptr and len being accurate for the current memory environment. Inside a WASM runtime for example.
86+
#[inline]
87+
pub unsafe fn as_bytes(&self) -> &[u8] {
88+
self.0.as_bytes()
89+
}
90+
}
91+
92+
/// A WasmSlice is an offset into the local `memory` of the WASM module instance.
93+
///
94+
/// It is only valid in the context of a `memory` contiguous region and a module's associated `Store`
95+
#[repr(transparent)]
96+
#[derive(Clone, Copy, Debug)]
97+
pub struct WasmString(WasmSlice);
98+
99+
impl From<String> for WasmString {
100+
#[inline]
101+
fn from(s: String) -> Self {
102+
let bytes = s.into_bytes();
103+
WasmString(WasmSlice::from(bytes))
104+
}
105+
}

wasmtime-jni/src/wasm_function.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::convert::TryFrom;
33
use std::slice;
44

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

43+
let method_name = get_method_name(env, &method)?;
44+
debug!("building WASM function from method: \"{}\"", method_name);
45+
4346
// collect all the arguments from
4447
let param_list = env.get_list(param_tys)?;
4548
let mut wasm_args: Vec<ValType> = Vec::with_capacity(param_list.size()? as usize);
@@ -303,7 +306,13 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmFunction_createFunc<'j>(
303306
Ok(())
304307
};
305308

306-
let func = Func::new(&store, FuncType::new(wasm_args, wasm_ret), func);
309+
let func_type = FuncType::new(wasm_args, wasm_ret);
310+
debug!(
311+
"method \"{}\" as function in WASM: {:?}",
312+
method_name, func_type
313+
);
314+
315+
let func = Func::new(&store, func_type, func);
307316

308317
Ok(OpaquePtr::from(func).make_opaque())
309318
})
@@ -422,3 +431,14 @@ pub extern "system" fn Java_net_bluejekyll_wasmtime_WasmFunction_callNtv<'j>(
422431
},
423432
)
424433
}
434+
435+
fn get_method_name<'j, O>(env: &JNIEnv<'j>, object: O) -> Result<String, Error>
436+
where
437+
O: Into<JObject<'j>>,
438+
{
439+
let name = env.call_method(object, "getName", "()Ljava/lang/String;", &[])?;
440+
let name = name.l()?;
441+
let name = JString::from(name);
442+
let name = env.get_string(name)?;
443+
Ok(Cow::from(&name).to_string())
444+
}

wasmtime-jni/src/wasm_instance.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::borrow::Cow;
33
use jni::objects::{JClass, JString};
44
use jni::sys::jlong;
55
use jni::JNIEnv;
6+
use log::debug;
67
use wasmtime::Instance;
78

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

4748
let func_ptr = if let Some(func) = func {
49+
debug!("found function in WASM: {}:{:?}", name, func.ty());
4850
let func = OpaquePtr::from(func);
4951
func.make_opaque()
5052
} else {

0 commit comments

Comments
 (0)