diff --git a/src/main/java/io/github/kawamuray/wasmtime/Engine.java b/src/main/java/io/github/kawamuray/wasmtime/Engine.java index 2a61dd3..063d368 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/Engine.java +++ b/src/main/java/io/github/kawamuray/wasmtime/Engine.java @@ -6,6 +6,14 @@ import lombok.Getter; import lombok.experimental.Accessors; +/** + * An Engine which is a global context for compilation and management of wasm modules. + *

+ * Engines store global configuration preferences such as compilation settings, enabled features, etc. + * You'll likely only need at most one of these for a program. + * + * @see Rust Documentation + */ @Accessors(fluent = true) @EqualsAndHashCode @AllArgsConstructor(access = AccessLevel.PACKAGE) diff --git a/src/main/java/io/github/kawamuray/wasmtime/ExportType.java b/src/main/java/io/github/kawamuray/wasmtime/ExportType.java new file mode 100644 index 0000000..4cf4866 --- /dev/null +++ b/src/main/java/io/github/kawamuray/wasmtime/ExportType.java @@ -0,0 +1,46 @@ +package io.github.kawamuray.wasmtime; + +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; + +@Accessors(fluent = true) +@AllArgsConstructor(access = AccessLevel.PACKAGE) +public class ExportType { + @Getter + private final ExternType type; + + @Getter(AccessLevel.PACKAGE) + private final Object typeObj; + + @Getter + private final String name; + + private void ensureType(ExternType expected) { + if (type != expected) { + throw new RuntimeException( + String.format("ImportType expected to have type %s but is actually %s", expected, type)); + } + } + + public FuncType func() { + ensureType(ExternType.FUNC); + return (FuncType) typeObj; + } + + public GlobalType global() { + ensureType(ExternType.GLOBAL); + return (GlobalType) typeObj; + } + + public MemoryType memory() { + ensureType(ExternType.MEMORY); + return (MemoryType) typeObj; + } + + public TableType table() { + ensureType(ExternType.TABLE); + return (TableType) typeObj; + } +} diff --git a/src/main/java/io/github/kawamuray/wasmtime/ExternType.java b/src/main/java/io/github/kawamuray/wasmtime/ExternType.java new file mode 100644 index 0000000..bb4d097 --- /dev/null +++ b/src/main/java/io/github/kawamuray/wasmtime/ExternType.java @@ -0,0 +1,11 @@ +package io.github.kawamuray.wasmtime; + +public enum ExternType { + FUNC, + GLOBAL, + TABLE, + MEMORY, + // TODO: Currently Unsupported + INSTANCE, + MODULE +} diff --git a/src/main/java/io/github/kawamuray/wasmtime/Func.java b/src/main/java/io/github/kawamuray/wasmtime/Func.java index c1aa1ff..feedcad 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/Func.java +++ b/src/main/java/io/github/kawamuray/wasmtime/Func.java @@ -1,16 +1,26 @@ package io.github.kawamuray.wasmtime; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Optional; - import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +/** + * A WebAssembly function which can be called. + * + * This type is either provided by a WebAssembly Module or implemented in Java as a Host-function. + * + * A Func "belongs" to the store that it was originally created within. Operations on a Func only work with the store + * it belongs to. Otherwise an exception will be thrown. + * + * @see Rust Documentation + */ @Slf4j @Accessors(fluent = true) @AllArgsConstructor(access = AccessLevel.PACKAGE) diff --git a/src/main/java/io/github/kawamuray/wasmtime/ImportType.java b/src/main/java/io/github/kawamuray/wasmtime/ImportType.java index baea6d7..d3b6c3a 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/ImportType.java +++ b/src/main/java/io/github/kawamuray/wasmtime/ImportType.java @@ -8,18 +8,8 @@ @Accessors(fluent = true) @AllArgsConstructor(access = AccessLevel.PACKAGE) public class ImportType { - public enum Type { - FUNC, - GLOBAL, - TABLE, - MEMORY, - // TODO: Currently Unsupported - INSTANCE, - MODULE - } - @Getter - private final Type type; + private final ExternType type; @Getter(AccessLevel.PACKAGE) private final Object typeObj; @@ -30,7 +20,7 @@ public enum Type { @Getter private final String name; - private void ensureType(ImportType.Type expected) { + private void ensureType(ExternType expected) { if (type != expected) { throw new RuntimeException( String.format("ImportType expected to have type %s but is actually %s", expected, type)); @@ -38,22 +28,22 @@ private void ensureType(ImportType.Type expected) { } public FuncType func() { - ensureType(ImportType.Type.FUNC); + ensureType(ExternType.FUNC); return (FuncType) typeObj; } public GlobalType global() { - ensureType(ImportType.Type.GLOBAL); + ensureType(ExternType.GLOBAL); return (GlobalType) typeObj; } public MemoryType memory() { - ensureType(ImportType.Type.MEMORY); + ensureType(ExternType.MEMORY); return (MemoryType) typeObj; } public TableType table() { - ensureType(ImportType.Type.TABLE); + ensureType(ExternType.TABLE); return (TableType) typeObj; } } diff --git a/src/main/java/io/github/kawamuray/wasmtime/Module.java b/src/main/java/io/github/kawamuray/wasmtime/Module.java index 4e4cbdc..72634c8 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/Module.java +++ b/src/main/java/io/github/kawamuray/wasmtime/Module.java @@ -25,6 +25,8 @@ public static Module fromBinary(Engine engine, byte[] bytes) { public native ImportType[] imports(); + public native ExportType[] exports(); + @Override public native void dispose(); diff --git a/src/main/java/io/github/kawamuray/wasmtime/Store.java b/src/main/java/io/github/kawamuray/wasmtime/Store.java index f4a34fc..f091eec 100644 --- a/src/main/java/io/github/kawamuray/wasmtime/Store.java +++ b/src/main/java/io/github/kawamuray/wasmtime/Store.java @@ -6,6 +6,47 @@ import lombok.Getter; import lombok.experimental.Accessors; +/** + * A Store is a collection of WebAssembly instances and host-defined state. + *

+ * All WebAssembly instances and items will be attached to and refer to a Store. For example instances, functions, + * globals, and tables are all attached to a Store. Instances are created by instantiating a Module within a Store. + *

+ * A Store is intended to be a short-lived object in a program. No form of GC is implemented at this time so once an + * instance is created within a Store it will not be deallocated until the Store itself is dropped. This makes Store + * unsuitable for creating an unbounded number of instances in it because Store will never release this memory. It’s + * recommended to have a Store correspond roughly to the lifetime of a "main instance" that an embedding is interested + * in executing. + * + *

Type parameter T

+ *

+ * Each Store has a type parameter T associated with it. This T represents state defined by the host. + * This state will be accessible through the Caller type that host-defined functions get access to. + * This T is suitable for storing Store-specific information which imported functions may want access to. + *

+ * The data T can be accessed through methods like Store::data and Store::data_mut. + * Stores, contexts, oh my + *

+ * Most methods in Wasmtime take something of the form AsContext or AsContextMut as the first argument. + * These two traits allow ergonomically passing in the context you currently have to any method. + * The primary two sources of contexts are: + *

+ * Store<T> + * Caller<'_, T> + *

+ * corresponding to what you create and what you have access to in a host function. You can also explicitly acquire + * a StoreContext or StoreContextMut and pass that around as well. + *

+ * Note that all methods on Store are mirrored onto StoreContext, StoreContextMut, and Caller. This way no matter what + * form of context you have you can call various methods, create objects, etc. + * + *

Stores and Default

+ *

+ * You can create a store with default configuration settings using Store::default(). This will create a + * brand new Engine with default configuration (see Config for more information). + * + * @param State defined by the host + */ @Accessors(fluent = true) @AllArgsConstructor(access = AccessLevel.PACKAGE) public class Store implements Disposable { diff --git a/src/test/java/io/github/kawamuray/wasmtime/ModuleTest.java b/src/test/java/io/github/kawamuray/wasmtime/ModuleTest.java index 25dee18..4dbc716 100644 --- a/src/test/java/io/github/kawamuray/wasmtime/ModuleTest.java +++ b/src/test/java/io/github/kawamuray/wasmtime/ModuleTest.java @@ -33,6 +33,15 @@ public class ModuleTest { " (func (export \"run\") (call $hello))\n" + ")").getBytes(); + private static final byte[] EXPORT_WAT_BINARY = ("(module" + + " (memory (export \"memmy\") 20 22)\n" + + " (table (export \"tabby\") 0 1 funcref)\n" + + " (export \"globby\" (global 0)) (global i32 (i32.const 0))\n" + + " (func (export \"life\") (param $p1 i32) (param $p2 i32) (result i32)" + + " (i32.const 42)\n" + + " )" + + ")").getBytes(); + @Test public void testCreateDispose() { try (Engine engine = new Engine()) { @@ -75,6 +84,43 @@ public void testAccessImports() { } } + @Test + public void testAccessExport() { + try ( + Engine engine = new Engine(); + Module module = new Module(engine, EXPORT_WAT_BINARY) + ) { + runExportTest(module, new TestExportData[]{ + TestExportData.memory("memmy", 20, 22), + TestExportData.table("tabby", Val.Type.FUNC_REF, 0, 1), + TestExportData.global("globby", Val.Type.I32, Mutability.CONST), + TestExportData.func("life", new Val.Type[]{Val.Type.I32, Val.Type.I32}, new Val.Type[]{Val.Type.I32}), + }); + } + } + + private void runExportTest(Module module, TestExportData[] testData) { + int i = 0; + for (ExportType export : module.exports()) { + Assert.assertTrue("Test Data not big enough", testData.length > i); + TestExportData data = testData[i]; + Assert.assertEquals(data.getName(), export.name()); + Assert.assertEquals(data.getType(), export.type()); + checkExportType(data, export); + i += 1; + } + Assert.assertEquals("Not Every Test Case was returned", testData.length, i); + } + + private void checkExportType(TestExportData data, ExportType type) { + Class clazz = data.getClazz(); + Object typeObj = type.typeObj(); + Assert.assertNotNull("Type Object is null", typeObj); + Class typeObjClass = typeObj.getClass(); + Assert.assertTrue(String.format("Expected Type is different. Expected %s but was %s", clazz, typeObjClass), clazz.isAssignableFrom(typeObjClass)); + data.verify(type, typeObj); + } + private void runImportTest(Module module, TestImportData[] testData) { int i = 0; for (ImportType imp : module.imports()) { @@ -98,18 +144,101 @@ private void checkImportType(TestImportData data, ImportType type) { data.verify(type, typeObj); } + @Data + private static class TestExportData { + private final String name; + private final ExternType type; + private final Class clazz; + private final Consumer verifyExport; + private final Consumer consumer; + + public static TestExportData func(String name, Val.Type[] params, Val.Type[] results) { + return new TestExportData<>( + name, ExternType.FUNC, FuncType.class, + mod -> { + Assert.assertEquals(mod.typeObj(), mod.func()); + Assert.assertThrows(RuntimeException.class, mod::global); + Assert.assertThrows(RuntimeException.class, mod::memory); + Assert.assertThrows(RuntimeException.class, mod::table); + }, + func -> { + Assert.assertArrayEquals(params, func.getParams()); + Assert.assertArrayEquals(results, func.getResults()); + } + ); + } + + public static TestExportData memory(String name, int min, int max) { + return new TestExportData<>( + name, ExternType.MEMORY, MemoryType.class, + mod -> { + Assert.assertThrows(RuntimeException.class, mod::func); + Assert.assertThrows(RuntimeException.class, mod::global); + Assert.assertEquals(mod.typeObj(), mod.memory()); + Assert.assertThrows(RuntimeException.class, mod::table); + }, + mem -> { + MemoryType.Limit limit = mem.limit(); + Assert.assertEquals(min, limit.min()); + Assert.assertEquals(max, limit.max()); + } + ); + } + + public static TestExportData table(String name, Val.Type content, int min, int max) { + return new TestExportData<>( + name, ExternType.TABLE, TableType.class, + mod -> { + Assert.assertThrows(RuntimeException.class, mod::func); + Assert.assertThrows(RuntimeException.class, mod::global); + Assert.assertThrows(RuntimeException.class, mod::memory); + Assert.assertEquals(mod.typeObj(), mod.table()); + }, + table -> { + Assert.assertEquals(content, table.element()); + + MemoryType.Limit limit = table.limit(); + Assert.assertEquals(min, limit.min()); + Assert.assertEquals(max, limit.max()); + } + ); + } + + public static TestExportData global(String name, Val.Type content, Mutability mutability) { + return new TestExportData<>( + name, ExternType.GLOBAL, GlobalType.class, + mod -> { + Assert.assertThrows(RuntimeException.class, mod::func); + Assert.assertEquals(mod.typeObj(), mod.global()); + Assert.assertThrows(RuntimeException.class, mod::memory); + Assert.assertThrows(RuntimeException.class, mod::table); + }, + global -> { + Assert.assertEquals(content, global.getContent()); + Assert.assertEquals(mutability, global.getMutability()); + } + ); + } + + @SuppressWarnings("unchecked") + public void verify(final ExportType imp, final Object typeObj) { + this.verifyExport.accept(imp); + this.consumer.accept((T) typeObj); + } + } + @Data private static class TestImportData { private final String module; private final String name; - private final ImportType.Type type; + private final ExternType type; private final Class clazz; private final Consumer verifyImport; private final Consumer consumer; static TestImportData memory(String module, String name, int min, int max) { return new TestImportData<>( - module, name, ImportType.Type.MEMORY, MemoryType.class, + module, name, ExternType.MEMORY, MemoryType.class, mod -> { Assert.assertThrows(RuntimeException.class, mod::func); Assert.assertThrows(RuntimeException.class, mod::global); @@ -126,7 +255,7 @@ static TestImportData memory(String module, String name, int min, in static TestImportData global(String module, String name, Val.Type content, Mutability mutability) { return new TestImportData<>( - module, name, ImportType.Type.GLOBAL, GlobalType.class, + module, name, ExternType.GLOBAL, GlobalType.class, mod -> { Assert.assertThrows(RuntimeException.class, mod::func); Assert.assertEquals(mod.typeObj(), mod.global()); @@ -142,7 +271,7 @@ static TestImportData global(String module, String name, Val.Type co static TestImportData func(String module, String name, Val.Type[] params, Val.Type[] results) { return new TestImportData<>( - module, name, ImportType.Type.FUNC, FuncType.class, + module, name, ExternType.FUNC, FuncType.class, mod -> { Assert.assertEquals(mod.typeObj(), mod.func()); Assert.assertThrows(RuntimeException.class, mod::global); @@ -158,7 +287,7 @@ static TestImportData func(String module, String name, Val.Type[] para public static TestImportData table(String module, String name, Val.Type content, int min, int max) { return new TestImportData<>( - module, name, ImportType.Type.TABLE, TableType.class, + module, name, ExternType.TABLE, TableType.class, mod -> { Assert.assertThrows(RuntimeException.class, mod::func); Assert.assertThrows(RuntimeException.class, mod::global); diff --git a/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/imp.rs b/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/imp.rs index 74b47e5..59fdc36 100644 --- a/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/imp.rs +++ b/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/imp.rs @@ -1,17 +1,16 @@ use super::JniModule; use crate::errors::{self, Result}; -use crate::wval::types_into_java_array; -use crate::{interop, utils, wmut, wval}; +use crate::{interop, utils}; use jni::objects::{JClass, JObject, JString}; -use jni::sys::{jbyteArray, jint, jlong, jobjectArray}; +use jni::sys::{jbyteArray, jlong, jobjectArray}; use jni::JNIEnv; -use wasmtime::{Engine, ExternType, Limits, Module}; +use wasmtime::{Engine, Module}; +use crate::wextern::{EXTERN_TYPE_CLASS, type_into_java}; pub(super) struct JniModuleImpl; const OBJECT_CLASS: &'static str = "java/lang/Object"; -const LIMIT_TYPE: &str = "io/github/kawamuray/wasmtime/MemoryType$Limit"; -pub const IMPORT_TYPE_CLASS: &'static str = "io/github/kawamuray/wasmtime/ImportType$Type"; +pub const STRING_CLASS: &str = "java/lang/String"; impl<'a> JniModule<'a> for JniModuleImpl { type Error = errors::Error; @@ -21,8 +20,32 @@ impl<'a> JniModule<'a> for JniModuleImpl { Ok(()) } + fn exports(env: &JNIEnv, this: JObject) -> std::result::Result { + const EXPORT_TYPE: &str = "io/github/kawamuray/wasmtime/ExportType"; + let module = interop::get_inner::(env, this)?; + let it = module.exports(); + let mut exports = Vec::with_capacity(it.len()); + for obj in it { + let name = env.new_string(obj.name()); + let (ty, ty_obj) = type_into_java(env, obj.ty())?; + let export = env.new_object( + EXPORT_TYPE, + format!( + "(L{};L{};L{};)V", + EXTERN_TYPE_CLASS, OBJECT_CLASS, STRING_CLASS + ), + &[ + ty.into_inner().into(), + ty_obj.into_inner().into(), + name?.into(), + ], + )?; + exports.push(export); + } + Ok(utils::into_java_array(env, EXPORT_TYPE, exports)?) + } + fn imports(env: &JNIEnv, this: JObject) -> std::result::Result { - const STRING_CLASS: &str = "java/lang/String"; const IMPORT_TYPE: &str = "io/github/kawamuray/wasmtime/ImportType"; let module = interop::get_inner::(env, this)?; @@ -30,78 +53,18 @@ impl<'a> JniModule<'a> for JniModuleImpl { let mut imports = Vec::with_capacity(it.len()); for obj in it { let module = obj.module(); - let (ty, ty_obj) = match obj.ty() { - ExternType::Func(func) => { - let results = types_into_java_array(env, func.results()); - let params = types_into_java_array(env, func.params()); - - ( - into_java_import_type(env, "FUNC"), - env.new_object( - "io/github/kawamuray/wasmtime/FuncType", - format!("([L{};[L{};)V", wval::VAL_TYPE, wval::VAL_TYPE), - &[params?.into(), results?.into()], - ), - ) - } - ExternType::Global(global) => ( - into_java_import_type(env, "GLOBAL"), - env.new_object( - "io/github/kawamuray/wasmtime/GlobalType", - format!("(L{};L{};)V", wval::VAL_TYPE, wmut::MUT_TYPE), - &[ - wval::type_into_java(env, global.content().to_owned())? - .into_inner() - .into(), - wmut::mutability_into_java(env, global.mutability())? - .into_inner() - .into(), - ], - ), - ), - ExternType::Table(tab) => { - const TABLE_TYPE: &str = "io/github/kawamuray/wasmtime/TableType"; - let limit = limit_into_java(env, tab.limits()); - let val = wval::type_into_java(env, tab.element().to_owned()); - let table = env.new_object( - TABLE_TYPE, - format!("(L{};L{};)V", wval::VAL_TYPE, LIMIT_TYPE), - &[val?.into_inner().into(), limit?.into_inner().into()], - ); - - (into_java_import_type(env, "TABLE"), table) - } - ExternType::Memory(mem) => { - const MEMORY_TYPE: &str = "io/github/kawamuray/wasmtime/MemoryType"; - let limit = limit_into_java(env, mem.limits()); - let mem = env.new_object( - MEMORY_TYPE, - format!("(L{};)V", LIMIT_TYPE), - &[limit?.into_inner().into()], - ); - - (into_java_import_type(env, "MEMORY"), mem) - } - // WebAssembly module-linking proposal - ExternType::Instance(_) => { - (into_java_import_type(env, "INSTANCE"), Ok(JObject::null())) - } - // WebAssembly module-linking proposal - ExternType::Module(_) => { - (into_java_import_type(env, "MODULE"), Ok(JObject::null())) - } - }; + let (ty, ty_obj) = type_into_java(env, obj.ty())?; let name = obj.name().unwrap_or_else(|| ""); let import = env.new_object( IMPORT_TYPE, format!( "(L{};L{};L{};L{};)V", - IMPORT_TYPE_CLASS, OBJECT_CLASS, STRING_CLASS, STRING_CLASS + EXTERN_TYPE_CLASS, OBJECT_CLASS, STRING_CLASS, STRING_CLASS ), &[ - ty?.into_inner().into(), - ty_obj?.into_inner().into(), + ty.into_inner().into(), + ty_obj.into_inner().into(), env.new_string(module)?.into(), env.new_string(name)?.into(), ], @@ -146,17 +109,3 @@ impl<'a> JniModule<'a> for JniModuleImpl { Ok(interop::into_raw::(module)) } } - -fn limit_into_java<'a>(env: &'a JNIEnv, limits: &Limits) -> jni::errors::Result> { - let min = limits.min() as jint; - let max = match limits.max() { - None => -1, - Some(max) => max as jint, - }; - env.new_object(LIMIT_TYPE, "(II)V", &[min.into(), max.into()]) -} - -pub fn into_java_import_type<'a>(env: &'a JNIEnv, ty: &'a str) -> jni::errors::Result> { - env.get_static_field(IMPORT_TYPE_CLASS, ty, format!("L{};", IMPORT_TYPE_CLASS))? - .l() -} diff --git a/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/mod.rs b/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/mod.rs index c893163..27f9551 100644 --- a/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/mod.rs +++ b/wasmtime-jni/src/io_github_kawamuray_wasmtime_Module/mod.rs @@ -22,6 +22,7 @@ macro_rules! wrap_error { trait JniModule<'a> { type Error: Desc<'a, JThrowable<'a>>; fn dispose(env: &JNIEnv, this: JObject) -> Result<(), Self::Error>; + fn exports(env: &JNIEnv, this: JObject) -> Result; fn imports(env: &JNIEnv, this: JObject) -> Result; fn new_from_binary( env: &JNIEnv, @@ -48,6 +49,18 @@ extern "system" fn Java_io_github_kawamuray_wasmtime_Module_dispose(env: JNIEnv, wrap_error!(env, JniModuleImpl::dispose(&env, this), Default::default()) } +#[no_mangle] +extern "system" fn Java_io_github_kawamuray_wasmtime_Module_exports( + env: JNIEnv, + this: JObject, +) -> jobjectArray { + wrap_error!( + env, + JniModuleImpl::exports(&env, this), + JObject::null().into_inner() + ) +} + #[no_mangle] extern "system" fn Java_io_github_kawamuray_wasmtime_Module_imports( env: JNIEnv, diff --git a/wasmtime-jni/src/wextern.rs b/wasmtime-jni/src/wextern.rs index c250dad..27913b0 100644 --- a/wasmtime-jni/src/wextern.rs +++ b/wasmtime-jni/src/wextern.rs @@ -1,8 +1,14 @@ use crate::errors::{Error, Result}; -use crate::{interop, utils}; +use crate::{interop, utils, wval}; use jni::objects::JObject; use jni::JNIEnv; -use wasmtime::{Extern, Func, Global, Memory, Table}; +use jni::sys::jint; +use wasmtime::{Extern, ExternType, Func, Global, Limits, Memory, Table}; +use crate::wmut::{MUT_TYPE, mutability_into_java}; +use crate::wval::VAL_TYPE; + +pub const EXTERN_TYPE_CLASS: &'static str = "io/github/kawamuray/wasmtime/ExternType"; +const LIMIT_TYPE: &str = "io/github/kawamuray/wasmtime/MemoryType$Limit"; pub fn from_java(env: &JNIEnv, obj: JObject) -> Result { let ty = env @@ -96,6 +102,68 @@ pub fn into_java<'a>(env: &'a JNIEnv, ext: Extern) -> Result> { }) } +pub fn type_into_java<'a>(env: &'a JNIEnv, ty: ExternType) -> Result<(JObject<'a>, JObject<'a>)> { + let (ty, ty_obj) = match ty { + ExternType::Func(func) => { + let results = wval::types_into_java_array(env, func.results()); + let params = wval::types_into_java_array(env, func.params()); + + ( + into_java_extern_type(env, "FUNC"), + env.new_object( + "io/github/kawamuray/wasmtime/FuncType", + format!("([L{};[L{};)V", VAL_TYPE, VAL_TYPE), + &[params?.into(), results?.into()], + ), + ) + } + ExternType::Global(global) => ( + into_java_extern_type(env, "GLOBAL"), + env.new_object( + "io/github/kawamuray/wasmtime/GlobalType", + format!("(L{};L{};)V", VAL_TYPE, MUT_TYPE), + &[ + wval::type_into_java(env, global.content().to_owned())? + .into_inner() + .into(), + mutability_into_java(env, global.mutability())? + .into_inner() + .into(), + ], + ), + ), + ExternType::Table(tab) => { + const TABLE_TYPE: &str = "io/github/kawamuray/wasmtime/TableType"; + let limit = limit_into_java(env, tab.limits()); + let val = wval::type_into_java(env, tab.element().to_owned()); + let table = env.new_object( + TABLE_TYPE, + format!("(L{};L{};)V", VAL_TYPE, LIMIT_TYPE), + &[val?.into_inner().into(), limit?.into_inner().into()], + ); + + (into_java_extern_type(env, "TABLE"), table) + } + ExternType::Memory(mem) => { + const MEMORY_TYPE: &str = "io/github/kawamuray/wasmtime/MemoryType"; + let limit = limit_into_java(env, mem.limits()); + let mem = env.new_object( + MEMORY_TYPE, + format!("(L{};)V", LIMIT_TYPE), + &[limit?.into_inner().into()], + ); + + (into_java_extern_type(env, "MEMORY"), mem) + } + // WebAssembly module-linking proposal + ExternType::Instance(_) => (into_java_extern_type(env, "INSTANCE"), Ok(JObject::null())), + // WebAssembly module-linking proposal + ExternType::Module(_) => (into_java_extern_type(env, "MODULE"), Ok(JObject::null())), + }; + + Ok((ty?, ty_obj?)) +} + pub fn unknown<'a>(env: &'a JNIEnv) -> Result> { Ok(env .get_static_field( @@ -105,3 +173,17 @@ pub fn unknown<'a>(env: &'a JNIEnv) -> Result> { )? .l()?) } + +fn limit_into_java<'a>(env: &'a JNIEnv, limits: &Limits) -> jni::errors::Result> { + let min = limits.min() as jint; + let max = match limits.max() { + None => -1, + Some(max) => max as jint, + }; + env.new_object(LIMIT_TYPE, "(II)V", &[min.into(), max.into()]) +} + +fn into_java_extern_type<'a>(env: &'a JNIEnv, ty: &'a str) -> jni::errors::Result> { + env.get_static_field(EXTERN_TYPE_CLASS, ty, format!("L{};", EXTERN_TYPE_CLASS))? + .l() +}