Skip to content

Commit 12400d6

Browse files
Implement accessing Module Imports (#31)
* Allows accessing ImportTypes from compiled Module * Implement Unit Test * Do not bump Version in PR * General Refactoring * Redesign imports method Currently only accessing imports is supported * Restrict direct access to value object and make it typesafe * Code Cleanup and fix Unit Tests * Implement Global Import Inspection * Implement Accessing Import Memory * Implement Accessing Import Tables
1 parent 8dea4e0 commit 12400d6

File tree

15 files changed

+449
-40
lines changed

15 files changed

+449
-40
lines changed

Diff for: build.gradle

+3-1
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ group = 'io.github.kawamuray.wasmtime'
1212
version = '0.9.0'
1313

1414
repositories {
15-
jcenter()
15+
mavenCentral()
1616
}
1717

1818
dependencies {
1919
compileOnly 'org.projectlombok:lombok:1.18.12'
2020
annotationProcessor 'org.projectlombok:lombok:1.18.12'
21+
testCompileOnly 'org.projectlombok:lombok:1.18.12'
22+
testAnnotationProcessor 'org.projectlombok:lombok:1.18.12'
2123
implementation 'org.slf4j:slf4j-api:1.7.25'
2224
testImplementation 'junit:junit:4.13'
2325
testRuntimeOnly 'ch.qos.logback:logback-classic:1.2.3'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.github.kawamuray.wasmtime;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Value;
5+
6+
@Value
7+
@AllArgsConstructor
8+
public class GlobalType {
9+
Val.Type content;
10+
Mutability mutability;
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package io.github.kawamuray.wasmtime;
2+
3+
import lombok.AccessLevel;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
import lombok.experimental.Accessors;
7+
8+
@Accessors(fluent = true)
9+
@AllArgsConstructor(access = AccessLevel.PACKAGE)
10+
public class ImportType {
11+
public enum Type {
12+
FUNC,
13+
GLOBAL,
14+
TABLE,
15+
MEMORY,
16+
// TODO: Currently Unsupported
17+
INSTANCE,
18+
MODULE
19+
}
20+
21+
@Getter
22+
private final Type type;
23+
24+
@Getter(AccessLevel.PACKAGE)
25+
private final Object typeObj;
26+
27+
@Getter
28+
private final String module;
29+
30+
@Getter
31+
private final String name;
32+
33+
private void ensureType(ImportType.Type expected) {
34+
if (type != expected) {
35+
throw new RuntimeException(
36+
String.format("ImportType expected to have type %s but is actually %s", expected, type));
37+
}
38+
}
39+
40+
public FuncType func() {
41+
ensureType(ImportType.Type.FUNC);
42+
return (FuncType) typeObj;
43+
}
44+
45+
public GlobalType global() {
46+
ensureType(ImportType.Type.GLOBAL);
47+
return (GlobalType) typeObj;
48+
}
49+
50+
public MemoryType memory() {
51+
ensureType(ImportType.Type.MEMORY);
52+
return (MemoryType) typeObj;
53+
}
54+
55+
public TableType table() {
56+
ensureType(ImportType.Type.TABLE);
57+
return (TableType) typeObj;
58+
}
59+
}

Diff for: src/main/java/io/github/kawamuray/wasmtime/Module.java

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public static Module fromBinary(Engine engine, byte[] bytes) {
2323
return new Module(newFromBinary(engine.innerPtr(), bytes));
2424
}
2525

26+
public native ImportType[] imports();
27+
2628
@Override
2729
public native void dispose();
2830

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package io.github.kawamuray.wasmtime;
2+
3+
public enum Mutability {
4+
CONST,
5+
VAR,
6+
}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.github.kawamuray.wasmtime;
2+
3+
import lombok.Value;
4+
import lombok.experimental.Accessors;
5+
6+
@Value
7+
@Accessors(fluent = true)
8+
public class TableType {
9+
Val.Type element;
10+
MemoryType.Limit limit;
11+
}
+170-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,184 @@
11
package io.github.kawamuray.wasmtime;
22

3+
import lombok.Data;
4+
import org.junit.Assert;
35
import org.junit.Test;
46

7+
import java.util.function.Consumer;
8+
59
public class ModuleTest {
610
private static final byte[] WAT_BINARY = ("(module"
7-
+ " (func (export \"add\") (param $p1 i32) (param $p2 i32) (result i32)"
8-
+ " local.get $p1\n"
9-
+ " local.get $p2\n"
10-
+ " i32.add)"
11-
+ ")").getBytes();
11+
+ " (func (export \"add\") (param $p1 i32) (param $p2 i32) (result i32)"
12+
+ " local.get $p1\n"
13+
+ " local.get $p2\n"
14+
+ " i32.add)"
15+
+ ")").getBytes();
16+
17+
// Required: Only one memory may be defined
18+
private static final byte[] MEM2 = ("(module" +
19+
" (import \"mem\" \"two\" (memory $mem2 13 37))" +
20+
")").getBytes();
21+
22+
private static final byte[] IMPORT_WAT_BINARY = ("(module" +
23+
" (global $m1 (import \"globals\" \"mutable\") (mut i32))\n" +
24+
" (global $c2 (import \"globalz\" \"const\") i64)\n" +
25+
" (func $hello (import \"first\" \"package\"))\n" +
26+
" (import \"tbl\" \"small\" (table 0 4 anyfunc))\n" +
27+
" (import \"tbl\" \"big\" (table 12 1995 anyfunc))\n" +
28+
" (import \"lua\" \"integration\" (table 1 anyfunc))\n" +
29+
" (import \"env\" \"memory\" (memory $mem 1))\n" +
30+
" (import \"\" \"package\" (func $world (param $p1 i32)))\n" +
31+
" (import \"xyz\" \"return\" (func (result i32)))\n" +
32+
" (import \"xyz\" \"return\" (func (param i32 i32 i32 i32 i32)))\n" +
33+
" (func (export \"run\") (call $hello))\n" +
34+
")").getBytes();
35+
1236
@Test
1337
public void testCreateDispose() {
1438
try (Engine engine = new Engine()) {
1539
Module module = new Module(engine, WAT_BINARY);
1640
module.close();
1741
}
1842
}
43+
44+
@Test
45+
public void testMem2() {
46+
try (
47+
Engine engine = new Engine();
48+
Module module = new Module(engine, MEM2)
49+
) {
50+
runImportTest(module, new TestImportData[]{
51+
TestImportData.memory("mem", "two", 13, 37)
52+
});
53+
}
54+
}
55+
56+
@Test
57+
public void testAccessImports() {
58+
try (
59+
Engine engine = new Engine();
60+
Module module = new Module(engine, IMPORT_WAT_BINARY)
61+
) {
62+
// TODO: Test other import typese
63+
runImportTest(module, new TestImportData[]{
64+
TestImportData.global("globals", "mutable", Val.Type.I32, Mutability.VAR),
65+
TestImportData.global("globalz", "const", Val.Type.I64, Mutability.CONST),
66+
TestImportData.func("first", "package", new Val.Type[]{}, new Val.Type[]{}),
67+
TestImportData.table("tbl", "small", Val.Type.FUNC_REF, 0, 4),
68+
TestImportData.table("tbl", "big", Val.Type.FUNC_REF, 12, 1995),
69+
TestImportData.table("lua", "integration", Val.Type.FUNC_REF, 1, -1),
70+
TestImportData.memory("env", "memory", 1, -1),
71+
TestImportData.func("", "package", new Val.Type[]{Val.Type.I32}, new Val.Type[]{}),
72+
TestImportData.func("xyz", "return", new Val.Type[]{}, new Val.Type[]{Val.Type.I32}),
73+
TestImportData.func("xyz", "return", new Val.Type[]{Val.Type.I32, Val.Type.I32, Val.Type.I32, Val.Type.I32, Val.Type.I32}, new Val.Type[]{})
74+
});
75+
}
76+
}
77+
78+
private void runImportTest(Module module, TestImportData<?>[] testData) {
79+
int i = 0;
80+
for (ImportType imp : module.imports()) {
81+
Assert.assertTrue("Test Data not big enough", testData.length > i);
82+
TestImportData<?> data = testData[i];
83+
Assert.assertEquals(data.getModule(), imp.module());
84+
Assert.assertEquals(data.getName(), imp.name());
85+
Assert.assertEquals(data.getType(), imp.type());
86+
checkImportType(data, imp);
87+
i += 1;
88+
}
89+
Assert.assertEquals("Not Every Test Case was returned", testData.length, i);
90+
}
91+
92+
private <T> void checkImportType(TestImportData<T> data, ImportType type) {
93+
Class<T> clazz = data.getClazz();
94+
Object typeObj = type.typeObj();
95+
Assert.assertNotNull("Type Object is null", typeObj);
96+
Class<?> typeObjClass = typeObj.getClass();
97+
Assert.assertTrue(String.format("Expected Type is different. Expected %s but was %s", clazz, typeObjClass), clazz.isAssignableFrom(typeObjClass));
98+
data.verify(type, typeObj);
99+
}
100+
101+
@Data
102+
private static class TestImportData<T> {
103+
private final String module;
104+
private final String name;
105+
private final ImportType.Type type;
106+
private final Class<T> clazz;
107+
private final Consumer<ImportType> verifyImport;
108+
private final Consumer<T> consumer;
109+
110+
static TestImportData<MemoryType> memory(String module, String name, int min, int max) {
111+
return new TestImportData<>(
112+
module, name, ImportType.Type.MEMORY, MemoryType.class,
113+
mod -> {
114+
Assert.assertThrows(RuntimeException.class, mod::func);
115+
Assert.assertThrows(RuntimeException.class, mod::global);
116+
Assert.assertEquals(mod.typeObj(), mod.memory());
117+
Assert.assertThrows(RuntimeException.class, mod::table);
118+
},
119+
mem -> {
120+
MemoryType.Limit limit = mem.limit();
121+
Assert.assertEquals(min, limit.min());
122+
Assert.assertEquals(max, limit.max());
123+
}
124+
);
125+
}
126+
127+
static TestImportData<GlobalType> global(String module, String name, Val.Type content, Mutability mutability) {
128+
return new TestImportData<>(
129+
module, name, ImportType.Type.GLOBAL, GlobalType.class,
130+
mod -> {
131+
Assert.assertThrows(RuntimeException.class, mod::func);
132+
Assert.assertEquals(mod.typeObj(), mod.global());
133+
Assert.assertThrows(RuntimeException.class, mod::memory);
134+
Assert.assertThrows(RuntimeException.class, mod::table);
135+
},
136+
global -> {
137+
Assert.assertEquals(content, global.getContent());
138+
Assert.assertEquals(mutability, global.getMutability());
139+
}
140+
);
141+
}
142+
143+
static TestImportData<FuncType> func(String module, String name, Val.Type[] params, Val.Type[] results) {
144+
return new TestImportData<>(
145+
module, name, ImportType.Type.FUNC, FuncType.class,
146+
mod -> {
147+
Assert.assertEquals(mod.typeObj(), mod.func());
148+
Assert.assertThrows(RuntimeException.class, mod::global);
149+
Assert.assertThrows(RuntimeException.class, mod::memory);
150+
Assert.assertThrows(RuntimeException.class, mod::table);
151+
},
152+
func -> {
153+
Assert.assertArrayEquals(params, func.getParams());
154+
Assert.assertArrayEquals(results, func.getResults());
155+
}
156+
);
157+
}
158+
159+
public static TestImportData<TableType> table(String module, String name, Val.Type content, int min, int max) {
160+
return new TestImportData<>(
161+
module, name, ImportType.Type.TABLE, TableType.class,
162+
mod -> {
163+
Assert.assertThrows(RuntimeException.class, mod::func);
164+
Assert.assertThrows(RuntimeException.class, mod::global);
165+
Assert.assertThrows(RuntimeException.class, mod::memory);
166+
Assert.assertEquals(mod.typeObj(), mod.table());
167+
},
168+
table -> {
169+
Assert.assertEquals(content, table.element());
170+
171+
MemoryType.Limit limit = table.limit();
172+
Assert.assertEquals(min, limit.min());
173+
Assert.assertEquals(max, limit.max());
174+
}
175+
);
176+
}
177+
178+
@SuppressWarnings("unchecked")
179+
public void verify(final ImportType imp, final Object typeObj) {
180+
this.verifyImport.accept(imp);
181+
this.consumer.accept((T) typeObj);
182+
}
183+
}
19184
}

Diff for: wasmtime-jni/src/io_github_kawamuray_wasmtime_Global/imp.rs

+4-14
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,17 @@ use super::JniGlobal;
22
use crate::errors::{self, Result};
33
use crate::store::StoreData;
44
use crate::{interop, wval};
5-
use jni::objects::{JObject};
5+
use jni::objects::JObject;
66
use jni::sys::{jlong, jobject};
7-
use jni::{JNIEnv};
7+
use jni::JNIEnv;
88
use wasmtime::{Global, Store};
99

1010
pub(super) struct JniGlobalImpl;
1111

1212
impl<'a> JniGlobal<'a> for JniGlobalImpl {
1313
type Error = errors::Error;
1414

15-
fn native_get(
16-
env: &JNIEnv,
17-
this: JObject,
18-
store_ptr: jlong,
19-
) -> Result<jobject, Self::Error> {
15+
fn native_get(env: &JNIEnv, this: JObject, store_ptr: jlong) -> Result<jobject, Self::Error> {
2016
let mut store = interop::ref_from_raw::<Store<StoreData>>(store_ptr)?;
2117
let global = interop::get_inner::<Global>(&env, this)?;
2218

@@ -35,14 +31,9 @@ impl<'a> JniGlobal<'a> for JniGlobalImpl {
3531
let global = interop::get_inner::<Global>(&env, this)?;
3632
global.set(&mut *store, wval::from_java(env, val)?)?;
3733
Ok(())
38-
3934
}
4035

41-
fn native_mutable(
42-
env: &JNIEnv,
43-
this: JObject,
44-
store_ptr: jlong,
45-
) -> Result<u8, Self::Error> {
36+
fn native_mutable(env: &JNIEnv, this: JObject, store_ptr: jlong) -> Result<u8, Self::Error> {
4637
let mut store = interop::ref_from_raw::<Store<StoreData>>(store_ptr)?;
4738
let global = interop::get_inner::<Global>(&env, this)?;
4839
let ty = global.ty(&mut *store);
@@ -56,5 +47,4 @@ impl<'a> JniGlobal<'a> for JniGlobalImpl {
5647
interop::dispose_inner::<Global>(&env, this)?;
5748
Ok(())
5849
}
59-
6050
}

Diff for: wasmtime-jni/src/io_github_kawamuray_wasmtime_Global/mod.rs

+3-15
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,14 @@ macro_rules! wrap_error {
2222
trait JniGlobal<'a> {
2323
type Error: Desc<'a, JThrowable<'a>>;
2424
fn dispose(env: &JNIEnv, this: JObject) -> Result<(), Self::Error>;
25-
fn native_get(
26-
env: &JNIEnv,
27-
this: JObject,
28-
store_ptr: jlong,
29-
) -> Result<jobject, Self::Error>;
25+
fn native_get(env: &JNIEnv, this: JObject, store_ptr: jlong) -> Result<jobject, Self::Error>;
3026
fn native_set(
3127
env: &JNIEnv,
3228
this: JObject,
3329
store_ptr: jlong,
3430
val: JObject,
3531
) -> Result<(), Self::Error>;
36-
fn native_mutable(
37-
env: &JNIEnv,
38-
this: JObject,
39-
store_ptr: jlong,
40-
) -> Result<u8, Self::Error>;
32+
fn native_mutable(env: &JNIEnv, this: JObject, store_ptr: jlong) -> Result<u8, Self::Error>;
4133
}
4234

4335
#[no_mangle]
@@ -78,9 +70,5 @@ extern "system" fn Java_io_github_kawamuray_wasmtime_Global_nativeMutable(
7870
this: JObject,
7971
store_ptr: jlong,
8072
) -> jboolean {
81-
wrap_error!(
82-
env,
83-
JniGlobalImpl::native_mutable(&env, this, store_ptr),
84-
0
85-
)
73+
wrap_error!(env, JniGlobalImpl::native_mutable(&env, this, store_ptr), 0)
8674
}

0 commit comments

Comments
 (0)