mirror of
https://github.com/fluencelabs/asmble
synced 2025-06-13 23:01:23 +00:00
A few more tests for emscripten support for #7
This commit is contained in:
@ -35,14 +35,6 @@ data class ScriptContext(
|
|||||||
fun withHarnessRegistered(out: PrintWriter = PrintWriter(System.out, true)) =
|
fun withHarnessRegistered(out: PrintWriter = PrintWriter(System.out, true)) =
|
||||||
withModuleRegistered("spectest", Module.Native(TestHarness(out)))
|
withModuleRegistered("spectest", Module.Native(TestHarness(out)))
|
||||||
|
|
||||||
fun withEmscriptenRegistered(metadata: Emscripten.Metadata, out: OutputStream) =
|
|
||||||
Env(metadata.staticBump, out).let { env ->
|
|
||||||
val mods = Env.subModules.fold(listOf(Module.Native(env))) { mods, subMod ->
|
|
||||||
mods + Module.Native(subMod.apply(env))
|
|
||||||
}
|
|
||||||
withModuleRegistered("env", Module.Composite(mods))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun withModuleRegistered(name: String, mod: Module) = copy(registrations = registrations + (name to mod))
|
fun withModuleRegistered(name: String, mod: Module) = copy(registrations = registrations + (name to mod))
|
||||||
|
|
||||||
fun runCommand(cmd: Script.Cmd) = when (cmd) {
|
fun runCommand(cmd: Script.Cmd) = when (cmd) {
|
||||||
|
@ -6,6 +6,7 @@ import asmble.io.AstToSExpr
|
|||||||
import asmble.io.SExprToStr
|
import asmble.io.SExprToStr
|
||||||
import org.junit.Assume
|
import org.junit.Assume
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import run.jvm.emscripten.Env
|
||||||
import java.io.ByteArrayOutputStream
|
import java.io.ByteArrayOutputStream
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
import java.io.PrintWriter
|
import java.io.PrintWriter
|
||||||
@ -39,7 +40,14 @@ abstract class TestRunner<out T : BaseTestUnit>(val unit: T) : TestBase() {
|
|||||||
).withHarnessRegistered(PrintWriter(OutputStreamWriter(out, Charsets.UTF_8), true))
|
).withHarnessRegistered(PrintWriter(OutputStreamWriter(out, Charsets.UTF_8), true))
|
||||||
|
|
||||||
// If there's a staticBump, we are an emscripten mod and we need to include the env
|
// If there's a staticBump, we are an emscripten mod and we need to include the env
|
||||||
unit.emscriptenMetadata?.also { scriptContext = scriptContext.withEmscriptenRegistered(it, out) }
|
var emEnv: Env? = null
|
||||||
|
unit.emscriptenMetadata?.also {
|
||||||
|
emEnv = Env(it.staticBump, out, "unknown-program")
|
||||||
|
val mods = Env.subModules.fold(listOf(Module.Native(emEnv!!))) { mods, subMod ->
|
||||||
|
mods + Module.Native(subMod.apply(emEnv))
|
||||||
|
}
|
||||||
|
scriptContext = scriptContext.withModuleRegistered("env", Module.Composite(mods))
|
||||||
|
}
|
||||||
|
|
||||||
// This will fail assertions as necessary
|
// This will fail assertions as necessary
|
||||||
scriptContext = unit.script.commands.fold(scriptContext) { scriptContext, cmd ->
|
scriptContext = unit.script.commands.fold(scriptContext) { scriptContext, cmd ->
|
||||||
@ -58,12 +66,16 @@ abstract class TestRunner<out T : BaseTestUnit>(val unit: T) : TestBase() {
|
|||||||
if (mainMethod.parameterTypes.isEmpty())
|
if (mainMethod.parameterTypes.isEmpty())
|
||||||
mainMethod.invoke(lastMod.instance(scriptContext))
|
mainMethod.invoke(lastMod.instance(scriptContext))
|
||||||
else if (mainMethod.parameterTypes.asList() == listOf(Int::class.java, Int::class.java))
|
else if (mainMethod.parameterTypes.asList() == listOf(Int::class.java, Int::class.java))
|
||||||
mainMethod.invoke(lastMod.instance(scriptContext), 0, 0)
|
mainMethod.invoke(lastMod.instance(scriptContext), emEnv?.argc ?: 0, emEnv?.argv ?: 0)
|
||||||
else
|
else
|
||||||
error("Unrecognized main method params for $mainMethod")
|
error("Unrecognized main method params for $mainMethod")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there was an emscripten env, we have to run the exit callbacks
|
||||||
|
emEnv?.also { it.runAtExitCallbacks(scriptContext.modules.last().instance(scriptContext)) }
|
||||||
|
|
||||||
|
// Check the output
|
||||||
unit.expectedOutput?.let {
|
unit.expectedOutput?.let {
|
||||||
// Sadly, sometimes the expected output is trimmed in Emscripten tests
|
// Sadly, sometimes the expected output is trimmed in Emscripten tests
|
||||||
assertEquals(it.trimEnd(), out.toByteArray().toString(Charsets.UTF_8).trimEnd())
|
assertEquals(it.trimEnd(), out.toByteArray().toString(Charsets.UTF_8).trimEnd())
|
||||||
|
@ -7,7 +7,6 @@ import org.junit.runners.Parameterized
|
|||||||
|
|
||||||
@RunWith(Parameterized::class)
|
@RunWith(Parameterized::class)
|
||||||
class EmscriptenTest(unit: EmscriptenTestUnit) : TestRunner<EmscriptenTestUnit>(unit) {
|
class EmscriptenTest(unit: EmscriptenTestUnit) : TestRunner<EmscriptenTestUnit>(unit) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var failureReason: Throwable? = null
|
var failureReason: Throwable? = null
|
||||||
|
|
||||||
|
@ -18,7 +18,28 @@ class EmscriptenTestUnit(
|
|||||||
) : BaseTestUnit(name, wast, expectedOutput) {
|
) : BaseTestUnit(name, wast, expectedOutput) {
|
||||||
companion object {
|
companion object {
|
||||||
val knownGoodNames = listOf(
|
val knownGoodNames = listOf(
|
||||||
"core/test_addr_of_stacked"
|
"core/test_addr_of_stacked",
|
||||||
|
// TODO: this fails for me even in the browser on windows
|
||||||
|
// "core/test_alloca",
|
||||||
|
"core/test_alloca_stack",
|
||||||
|
"core/test_array2",
|
||||||
|
"core/test_array2b",
|
||||||
|
// TODO: use of undeclared identifier 'true'
|
||||||
|
// "core/test_assert",
|
||||||
|
// TODO: I'm running the callbacks, but nothing happening
|
||||||
|
// "core/test_atexit",
|
||||||
|
"core/test_atomic",
|
||||||
|
// TODO: lots of special calls, wait for emscripten-wasm to finish out
|
||||||
|
// "core/test_atomic_cxx",
|
||||||
|
"core/test_atoX",
|
||||||
|
// TODO: must use 'struct' tag to refer to type 'Struct'
|
||||||
|
// "core/test_bigarray",
|
||||||
|
// TODO: must use 'struct' tag to refer to type 'bitty'
|
||||||
|
// "core/test_bitfields"
|
||||||
|
"core/test_bsearch"
|
||||||
|
// TODO: unknown type name '_LIBCPP_BEGIN_NAMESPACE_STD'
|
||||||
|
// "core/test_bswap64"
|
||||||
|
// TODO: the rest...maybe if emscripten is cleaned up
|
||||||
)
|
)
|
||||||
|
|
||||||
val allUnits by lazy { loadUnits() }
|
val allUnits by lazy { loadUnits() }
|
||||||
@ -45,7 +66,8 @@ class EmscriptenTestUnit(
|
|||||||
try {
|
try {
|
||||||
// Run emcc on the cFile
|
// Run emcc on the cFile
|
||||||
val nameSansExt = cFile.fileName.toString().substringBeforeLast(".c")
|
val nameSansExt = cFile.fileName.toString().substringBeforeLast(".c")
|
||||||
val cmdArgs = emccCommand + cFile.toString() + "-s" + "WASM=1" + "-o" + "$nameSansExt.html"
|
val cmdArgs = emccCommand + cFile.toString() +
|
||||||
|
arrayOf("-s", "WASM=1", "-o", "$nameSansExt.html")
|
||||||
TestBase.logger.debug { "Running ${cmdArgs.joinToString(" ")}" }
|
TestBase.logger.debug { "Running ${cmdArgs.joinToString(" ")}" }
|
||||||
val proc = ProcessBuilder(*cmdArgs).
|
val proc = ProcessBuilder(*cmdArgs).
|
||||||
directory(tempDir).
|
directory(tempDir).
|
||||||
@ -55,10 +77,15 @@ class EmscriptenTestUnit(
|
|||||||
proc.inputStream.bufferedReader().forEachLine { TestBase.logger.debug { "[OUT] $it" } }
|
proc.inputStream.bufferedReader().forEachLine { TestBase.logger.debug { "[OUT] $it" } }
|
||||||
Assert.assertTrue("Timeout", proc.waitFor(10, TimeUnit.SECONDS))
|
Assert.assertTrue("Timeout", proc.waitFor(10, TimeUnit.SECONDS))
|
||||||
Assert.assertEquals(0, proc.exitValue())
|
Assert.assertEquals(0, proc.exitValue())
|
||||||
|
var outFile = cFile.resolveSibling("$nameSansExt.out")
|
||||||
|
if (Files.notExists(outFile)) {
|
||||||
|
outFile = cFile.resolveSibling("$nameSansExt.txt")
|
||||||
|
require(Files.exists(outFile)) { "Cannot find out file for $cFile" }
|
||||||
|
}
|
||||||
EmscriptenTestUnit(
|
EmscriptenTestUnit(
|
||||||
name = cFile.unitNameFromCFile(),
|
name = cFile.unitNameFromCFile(),
|
||||||
wast = File(tempDir, "$nameSansExt.wast").readText(),
|
wast = File(tempDir, "$nameSansExt.wast").readText(),
|
||||||
expectedOutput = cFile.resolveSibling("$nameSansExt.out").toFile().readText()
|
expectedOutput = outFile.toFile().readText()
|
||||||
)
|
)
|
||||||
} catch (e: Exception) { throw Exception("Unable to compile $cFile", e) }
|
} catch (e: Exception) { throw Exception("Unable to compile $cFile", e) }
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -1,72 +1,94 @@
|
|||||||
package run.jvm.emscripten;
|
package run.jvm.emscripten;
|
||||||
|
|
||||||
import asmble.annotation.WasmName;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
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.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class Env {
|
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(
|
public static final List<Function<Env, Object>> subModules = Arrays.asList(
|
||||||
|
Common::new,
|
||||||
Syscall::new
|
Syscall::new
|
||||||
);
|
);
|
||||||
|
|
||||||
private static int alignTo16(int num) {
|
final Mem mem;
|
||||||
return ((int) Math.ceil(num / 16.0)) * 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final ByteBuffer memory;
|
|
||||||
private final int staticBump;
|
|
||||||
final OutputStream out;
|
final OutputStream out;
|
||||||
|
final int argc;
|
||||||
|
final int argv;
|
||||||
|
private final List<AtExitCallback> atExitCallbacks = new ArrayList<>();
|
||||||
|
|
||||||
public Env(int staticBump, OutputStream out) {
|
public Env(int staticBump, OutputStream out, String programName, String... args) {
|
||||||
this(ByteBuffer.allocateDirect(TOTAL_MEMORY), staticBump, out);
|
this(new Mem(staticBump), out, programName, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Env(ByteBuffer memory, int staticBump, OutputStream out) {
|
public Env(Mem mem, OutputStream out, String programName, String... args) {
|
||||||
this.memory = memory.order(ByteOrder.LITTLE_ENDIAN);
|
this.mem = mem;
|
||||||
this.staticBump = staticBump;
|
|
||||||
this.out = out;
|
this.out = out;
|
||||||
// Emscripten sets where "stack top" can start in mem at position 1024.
|
// We need to add the args which is an int array
|
||||||
// See https://github.com/WebAssembly/binaryen/issues/979
|
argc = args.length + 1;
|
||||||
int stackBase = alignTo16(staticBump + 1024 + 16);
|
int[] argv = new int[argc];
|
||||||
int stackTop = stackBase + TOTAL_STACK;
|
argv[0] = mem.putCString(programName);
|
||||||
memory.putInt(1024, stackTop);
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
argv[i + 1] = mem.putCString(args[i]);
|
||||||
|
}
|
||||||
|
this.argv = mem.putIntArray(argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ByteBuffer getMemory() {
|
public ByteBuffer getMemory() {
|
||||||
return memory;
|
return mem.buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getMemoryBulk(int index, int len) {
|
public int getArgc() {
|
||||||
byte[] ret = new byte[len];
|
return argc;
|
||||||
ByteBuffer dup = memory.duplicate();
|
|
||||||
dup.position(index);
|
|
||||||
dup.get(ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void abort() {
|
public int getArgv() {
|
||||||
throw new UnsupportedOperationException();
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
@WasmName("__lock")
|
public void addCallback(int funcPtr, Integer arg) {
|
||||||
public void lock(int arg) {
|
atExitCallbacks.add(new AtExitCallback(funcPtr, arg));
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int sbrk(int increment) {
|
public void runAtExitCallbacks(Object moduleInst) throws Throwable {
|
||||||
throw new UnsupportedOperationException();
|
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 static class AtExitCallback {
|
||||||
public void unlock(int arg) {
|
public final int funcPtr;
|
||||||
throw new UnsupportedOperationException();
|
public final Integer arg;
|
||||||
|
|
||||||
|
public AtExitCallback(int funcPtr, Integer arg) {
|
||||||
|
this.funcPtr = funcPtr;
|
||||||
|
this.arg = arg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
91
emscripten-runtime/src/main/java/run/jvm/emscripten/Mem.java
Normal file
91
emscripten-runtime/src/main/java/run/jvm/emscripten/Mem.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package run.jvm.emscripten;
|
|||||||
|
|
||||||
import asmble.annotation.WasmName;
|
import asmble.annotation.WasmName;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@ -54,17 +56,17 @@ public class Syscall {
|
|||||||
public int llseek(int arg0, int arg1) {
|
public int llseek(int arg0, int arg1) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
//
|
|
||||||
@WasmName("__syscall146")
|
@WasmName("__syscall146")
|
||||||
public int writev(int which, int varargs) {
|
public int writev(int which, int varargs) {
|
||||||
FStream fd = fd(env.getMemory().getInt(varargs));
|
FStream fd = fd(env.getMemory().getInt(varargs));
|
||||||
int iov = env.getMemory().getInt(varargs + 4);
|
int iov = env.getMemory().getInt(varargs + 4);
|
||||||
int iovcnt = env.getMemory().getInt(varargs + 8);
|
int iovcnt = env.getMemory().getInt(varargs + 8);
|
||||||
return IntStream.range(0, iovcnt).reduce(0, (total, i) -> {
|
return IntStream.range(0, iovcnt).reduce(0, (total, i) -> {
|
||||||
int ptr = env.getMemory().getInt(iov + (i * 8));
|
int ptr = env.getMemory().getInt(iov + (i * 8));
|
||||||
int len = env.getMemory().getInt(iov + (i * 8) + 4);
|
int len = env.getMemory().getInt(iov + (i * 8) + 4);
|
||||||
if (len > 0) fd.write(env.getMemoryBulk(ptr, len));
|
if (len > 0) fd.write(env.mem.getBulk(ptr, len));
|
||||||
return total + len;
|
return total + len;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user