A few more tests for emscripten support for #7

This commit is contained in:
Chad Retz
2017-04-26 21:45:20 -05:00
parent b4140c8189
commit e9cdfc3b0f
8 changed files with 252 additions and 57 deletions

View File

@ -0,0 +1,50 @@
package run.jvm.emscripten;
import asmble.annotation.WasmName;
public class Common {
private final Env env;
public Common(Env env) {
this.env = env;
}
public void abort() {
throw new UnsupportedOperationException();
}
@WasmName("__assert_fail")
public void assertFail(int conditionPtr, int filenamePtr, int line, int funcPtr) {
throw new AssertionError("Assertion failed: " + env.mem.getCString(conditionPtr) + ", at " +
env.mem.getCString(filenamePtr) + ":" + line + ", func " + env.mem.getCString(funcPtr));
}
public int atexit(int funcPtr) {
env.addCallback(funcPtr, null);
return 0;
}
public int atexit(int funcPtr, int arg) {
env.addCallback(funcPtr, arg);
return 0;
}
@WasmName("__cxa_call_unexpected")
public void callUnexpected(int ex) {
throw new EmscriptenException("Unexpected: " + ex);
}
@WasmName("__lock")
public void lock(int arg) {
throw new UnsupportedOperationException();
}
public int sbrk(int increment) {
throw new UnsupportedOperationException();
}
@WasmName("__unlock")
public void unlock(int arg) {
throw new UnsupportedOperationException();
}
}

View File

@ -1,72 +1,94 @@
package run.jvm.emscripten;
import asmble.annotation.WasmName;
import java.io.OutputStream;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Env {
public static final int TOTAL_STACK = 5242880;
public static final int TOTAL_MEMORY = 16777216;
public static final List<Function<Env, Object>> subModules = Arrays.asList(
Common::new,
Syscall::new
);
private static int alignTo16(int num) {
return ((int) Math.ceil(num / 16.0)) * 16;
}
private final ByteBuffer memory;
private final int staticBump;
final Mem mem;
final OutputStream out;
final int argc;
final int argv;
private final List<AtExitCallback> atExitCallbacks = new ArrayList<>();
public Env(int staticBump, OutputStream out) {
this(ByteBuffer.allocateDirect(TOTAL_MEMORY), staticBump, out);
public Env(int staticBump, OutputStream out, String programName, String... args) {
this(new Mem(staticBump), out, programName, args);
}
public Env(ByteBuffer memory, int staticBump, OutputStream out) {
this.memory = memory.order(ByteOrder.LITTLE_ENDIAN);
this.staticBump = staticBump;
public Env(Mem mem, OutputStream out, String programName, String... args) {
this.mem = mem;
this.out = out;
// Emscripten sets where "stack top" can start in mem at position 1024.
// See https://github.com/WebAssembly/binaryen/issues/979
int stackBase = alignTo16(staticBump + 1024 + 16);
int stackTop = stackBase + TOTAL_STACK;
memory.putInt(1024, stackTop);
// We need to add the args which is an int array
argc = args.length + 1;
int[] argv = new int[argc];
argv[0] = mem.putCString(programName);
for (int i = 0; i < args.length; i++) {
argv[i + 1] = mem.putCString(args[i]);
}
this.argv = mem.putIntArray(argv);
}
public ByteBuffer getMemory() {
return memory;
return mem.buf;
}
public byte[] getMemoryBulk(int index, int len) {
byte[] ret = new byte[len];
ByteBuffer dup = memory.duplicate();
dup.position(index);
dup.get(ret);
return ret;
public int getArgc() {
return argc;
}
public void abort() {
throw new UnsupportedOperationException();
public int getArgv() {
return argv;
}
@WasmName("__lock")
public void lock(int arg) {
throw new UnsupportedOperationException();
public void addCallback(int funcPtr, Integer arg) {
atExitCallbacks.add(new AtExitCallback(funcPtr, arg));
}
public int sbrk(int increment) {
throw new UnsupportedOperationException();
public void runAtExitCallbacks(Object moduleInst) throws Throwable {
MethodHandle noArg = null;
MethodHandle withArg = null;
for (int i = atExitCallbacks.size() - 1; i >= 0; i--) {
AtExitCallback cb = atExitCallbacks.get(i);
if (cb.arg == null) {
Field table = moduleInst.getClass().getDeclaredField("table");
table.setAccessible(true);
MethodHandle[] h = (MethodHandle[]) MethodHandles.lookup().unreflectGetter(table).invoke(moduleInst);
h[cb.funcPtr].invoke();
if (noArg == null) {
noArg = MethodHandles.lookup().bind(moduleInst,
"dynCall_v", MethodType.methodType(Void.TYPE, Integer.TYPE));
}
noArg.invokeExact(cb.funcPtr);
} else {
if (withArg == null) {
withArg = MethodHandles.lookup().bind(moduleInst,
"dynCall_vi", MethodType.methodType(Void.TYPE, Integer.TYPE, Integer.TYPE));
}
withArg.invokeExact(cb.funcPtr, cb.arg.intValue());
}
}
}
@WasmName("__unlock")
public void unlock(int arg) {
throw new UnsupportedOperationException();
public static class AtExitCallback {
public final int funcPtr;
public final Integer arg;
public AtExitCallback(int funcPtr, Integer arg) {
this.funcPtr = funcPtr;
this.arg = arg;
}
}
}

View File

@ -0,0 +1,91 @@
package run.jvm.emscripten;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
public class Mem {
public static final int TOTAL_STACK = 5242880;
public static final int TOTAL_MEMORY = 16777216;
private static int alignTo16(int num) {
return ((int) Math.ceil(num / 16.0)) * 16;
}
final ByteBuffer buf;
private int counter = 0;
public Mem(int staticBump) {
this(ByteBuffer.allocateDirect(TOTAL_MEMORY), staticBump);
}
public Mem(ByteBuffer buf, int staticBump) {
this.buf = buf.order(ByteOrder.LITTLE_ENDIAN);
// Emscripten sets where "stack top" can start in mem at position 1024.
// See https://github.com/WebAssembly/binaryen/issues/979
int staticTop = 1024 + staticBump + 16;
int stackBase = alignTo16(staticTop);
int stackTop = stackBase + TOTAL_STACK;
buf.putInt(1024, stackTop);
}
public byte[] getBulk(int index, int len) {
byte[] ret = new byte[len];
if (len > 0) {
ByteBuffer dup = buf.duplicate();
dup.position(index);
dup.get(ret);
}
return ret;
}
public byte[] getCStringBytes(int index) {
if (index == 0) return new byte[0];
// Not really sure the highest performing approach. What we're going to do though is just
// find the first index of 0, then get the array.
int len = 0;
while (buf.get(index + len) != 0) len++;
return getBulk(index, len);
}
public String getCString(int index) {
byte[] bytes = getCStringBytes(index);
if (bytes.length == 0) return "";
return new String(getCStringBytes(index), StandardCharsets.ISO_8859_1);
}
public synchronized int putCString(String str) {
// For now we'll just trust it doesn't already contain a 0
int ret = allocate(str.getBytes(StandardCharsets.ISO_8859_1));
allocate((byte) 0);
return ret;
}
public int putIntArray(int[] arr) {
int ret = counter;
for (int i = 0; i < arr.length; i++) {
buf.putInt(counter, arr[i]);
counter += 4;
}
return ret;
}
public int allocate(byte b) {
int ret = counter;
buf.put(counter, b);
counter++;
return ret;
}
public int allocate(byte[] bytes) {
int ret = counter;
if (bytes.length > 0) {
ByteBuffer dup = buf.duplicate();
dup.position(counter);
dup.put(bytes);
counter += bytes.length;
}
return ret;
}
}

View File

@ -2,6 +2,8 @@ package run.jvm.emscripten;
import asmble.annotation.WasmName;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@ -54,17 +56,17 @@ public class Syscall {
public int llseek(int arg0, int arg1) {
throw new UnsupportedOperationException();
}
//
@WasmName("__syscall146")
public int writev(int which, int varargs) {
FStream fd = fd(env.getMemory().getInt(varargs));
int iov = env.getMemory().getInt(varargs + 4);
int iovcnt = env.getMemory().getInt(varargs + 8);
return IntStream.range(0, iovcnt).reduce(0, (total, i) -> {
int ptr = env.getMemory().getInt(iov + (i * 8));
int len = env.getMemory().getInt(iov + (i * 8) + 4);
if (len > 0) fd.write(env.getMemoryBulk(ptr, len));
return total + len;
int ptr = env.getMemory().getInt(iov + (i * 8));
int len = env.getMemory().getInt(iov + (i * 8) + 4);
if (len > 0) fd.write(env.mem.getBulk(ptr, len));
return total + len;
});
}