From 34eb06f5f057bd451f12c6015d7ba4dde4180d16 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 6 Nov 2022 21:21:49 +0100 Subject: [PATCH] Wasm: support command line args in WASI --- .../org/teavm/backend/wasm/WasmTarget.java | 10 +++-- .../backend/wasm/runtime/WasiSupport.java | 40 ++++++++++++++++++- .../backend/wasm/runtime/WasmSupport.java | 4 ++ .../org/teavm/backend/wasm/wasi/Wasi.java | 8 ++++ 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 62d4f6b45..1eed35199 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -104,6 +104,7 @@ import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmCRenderer; import org.teavm.backend.wasm.render.WasmRenderer; +import org.teavm.backend.wasm.runtime.WasmSupport; import org.teavm.backend.wasm.transformation.IndirectCallTraceTransformation; import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation; import org.teavm.backend.wasm.transformation.WasiSupportClassTransformer; @@ -388,6 +389,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { dependencyAnalyzer.linkMethod(new MethodReference(EventQueue.class, "isStopped", boolean.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(WasmSupport.class, "getArgs", String[].class)).use(); ClassReader fiberClass = dependencyAnalyzer.getClassSource().get(Fiber.class.getName()); for (MethodReader method : fiberClass.getMethods()) { @@ -516,7 +518,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { module.add(initFunction); module.setStartFunction(initFunction); module.add(createStartFunction(names)); - module.add(createStartCallerFunction()); + module.add(createStartCallerFunction(names)); for (String functionName : classGenerator.getFunctionTable()) { WasmFunction function = module.getFunctions().get(functionName); @@ -568,12 +570,14 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { return function; } - private WasmFunction createStartCallerFunction() { + private WasmFunction createStartCallerFunction(NameProvider names) { WasmFunction function = new WasmFunction("teavm_call_start"); function.setExportName("_start"); + WasmCall argsCall = new WasmCall(names.forMethod(new MethodReference(WasmSupport.class, + "getArgs", String[].class))); WasmCall call = new WasmCall("teavm_start"); - call.getArguments().add(new WasmInt32Constant(0)); + call.getArguments().add(argsCall); function.getBody().add(call); return function; diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasiSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasiSupport.java index a4b4bc6c1..290380794 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasiSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasiSupport.java @@ -15,6 +15,7 @@ */ package org.teavm.backend.wasm.runtime; +import java.nio.charset.StandardCharsets; import org.teavm.backend.wasm.wasi.IOVec; import org.teavm.backend.wasm.wasi.LongResult; import org.teavm.backend.wasm.wasi.SizeResult; @@ -65,7 +66,7 @@ public class WasiSupport { int end = Math.min(s.length(), i + charsInChunk); int count = end - i; for (int j = 0; j < count; ++j) { - buffer.add(offsetInBuffer + j).putByte((byte) s.charAt(j)); + buffer.add(j).putByte((byte) s.charAt(i + j)); } putCharsStderr(buffer, count); } @@ -74,6 +75,8 @@ public class WasiSupport { @Unmanaged public static void printInt(int i) { int count = 0; + boolean negative = i < 0; + i = Math.abs(i); Address buffer = WasiBuffer.getBuffer().add(WasiBuffer.getBufferSize()); do { ++count; @@ -81,6 +84,11 @@ public class WasiSupport { buffer.putByte((byte) ((i % 10) + (int) '0')); i /= 10; } while (i > 0); + if (negative) { + ++count; + buffer = buffer.add(-1); + buffer.putByte((byte) '-'); + } putCharsStderr(buffer, count); } @@ -92,4 +100,34 @@ public class WasiSupport { @Unmanaged public static void initHeapTrace(int maxHeap) { } + + public static String[] getArgs() { + Address buffer = WasiBuffer.getBuffer(); + SizeResult sizesReceiver = buffer.toStructure(); + SizeResult bufferSizeReceiver = buffer.add(Structure.sizeOf(SizeResult.class)).toStructure(); + short errno = Wasi.argsSizesGet(sizesReceiver, bufferSizeReceiver); + + if (errno != Wasi.ERRNO_SUCCESS) { + throw new RuntimeException("Could not get command line arguments"); + } + int argvSize = sizesReceiver.value; + int argvBufSize = bufferSizeReceiver.value; + + int[] argvOffsets = new int[argvSize]; + byte[] argvBuffer = new byte[argvBufSize]; + errno = Wasi.argsGet(Address.ofData(argvOffsets), Address.ofData(argvBuffer)); + + if (errno != Wasi.ERRNO_SUCCESS) { + throw new RuntimeException("Could not get command line arguments"); + } + + String[] args = new String[argvSize - 1]; + for (int i = 1; i < argvSize; ++i) { + int offset = argvOffsets[i] - Address.ofData(argvBuffer).toInt(); + int length = (i == argvSize - 1 ? argvBufSize - offset : argvOffsets[i + 1] - argvOffsets[i]) - 1; + args[i - 1] = new String(argvBuffer, offset, length, StandardCharsets.UTF_8); + } + + return args; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmSupport.java b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmSupport.java index 2cded5b7f..0fbaaebd3 100644 --- a/core/src/main/java/org/teavm/backend/wasm/runtime/WasmSupport.java +++ b/core/src/main/java/org/teavm/backend/wasm/runtime/WasmSupport.java @@ -49,4 +49,8 @@ public class WasmSupport { @Import(name = "init", module = "teavmHeapTrace") public static native void initHeapTrace(int maxHeap); + + public static String[] getArgs() { + return new String[0]; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java b/core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java index 5515d34cf..1eb5c8ef1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java +++ b/core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java @@ -15,10 +15,12 @@ */ package org.teavm.backend.wasm.wasi; +import org.teavm.interop.Address; import org.teavm.interop.Import; public final class Wasi { public static final int CLOCKID_REALTIME = 0; + public static final short ERRNO_SUCCESS = 0; private Wasi() { } @@ -28,4 +30,10 @@ public final class Wasi { @Import(name = "clock_time_get", module = "wasi_snapshot_preview1") public static native short clockTimeGet(int clockId, long precision, LongResult result); + + @Import(name = "args_sizes_get", module = "wasi_snapshot_preview1") + public static native short argsSizesGet(SizeResult argvSize, SizeResult argvBufSize); + + @Import(name = "args_get", module = "wasi_snapshot_preview1") + public static native short argsGet(Address argv, Address argvBuf); }