Wasm: support command line args in WASI

This commit is contained in:
Alexey Andreev 2022-11-06 21:21:49 +01:00
parent 1ca2c75e1c
commit 34eb06f5f0
4 changed files with 58 additions and 4 deletions

View File

@ -104,6 +104,7 @@ import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.backend.wasm.render.WasmBinaryWriter; import org.teavm.backend.wasm.render.WasmBinaryWriter;
import org.teavm.backend.wasm.render.WasmCRenderer; import org.teavm.backend.wasm.render.WasmCRenderer;
import org.teavm.backend.wasm.render.WasmRenderer; 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.IndirectCallTraceTransformation;
import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation; import org.teavm.backend.wasm.transformation.MemoryAccessTraceTransformation;
import org.teavm.backend.wasm.transformation.WasiSupportClassTransformer; 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(EventQueue.class, "isStopped", boolean.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class, dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "setCurrentThread", Thread.class,
void.class)).use(); void.class)).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmSupport.class, "getArgs", String[].class)).use();
ClassReader fiberClass = dependencyAnalyzer.getClassSource().get(Fiber.class.getName()); ClassReader fiberClass = dependencyAnalyzer.getClassSource().get(Fiber.class.getName());
for (MethodReader method : fiberClass.getMethods()) { for (MethodReader method : fiberClass.getMethods()) {
@ -516,7 +518,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
module.add(initFunction); module.add(initFunction);
module.setStartFunction(initFunction); module.setStartFunction(initFunction);
module.add(createStartFunction(names)); module.add(createStartFunction(names));
module.add(createStartCallerFunction()); module.add(createStartCallerFunction(names));
for (String functionName : classGenerator.getFunctionTable()) { for (String functionName : classGenerator.getFunctionTable()) {
WasmFunction function = module.getFunctions().get(functionName); WasmFunction function = module.getFunctions().get(functionName);
@ -568,12 +570,14 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
return function; return function;
} }
private WasmFunction createStartCallerFunction() { private WasmFunction createStartCallerFunction(NameProvider names) {
WasmFunction function = new WasmFunction("teavm_call_start"); WasmFunction function = new WasmFunction("teavm_call_start");
function.setExportName("_start"); function.setExportName("_start");
WasmCall argsCall = new WasmCall(names.forMethod(new MethodReference(WasmSupport.class,
"getArgs", String[].class)));
WasmCall call = new WasmCall("teavm_start"); WasmCall call = new WasmCall("teavm_start");
call.getArguments().add(new WasmInt32Constant(0)); call.getArguments().add(argsCall);
function.getBody().add(call); function.getBody().add(call);
return function; return function;

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.runtime; package org.teavm.backend.wasm.runtime;
import java.nio.charset.StandardCharsets;
import org.teavm.backend.wasm.wasi.IOVec; import org.teavm.backend.wasm.wasi.IOVec;
import org.teavm.backend.wasm.wasi.LongResult; import org.teavm.backend.wasm.wasi.LongResult;
import org.teavm.backend.wasm.wasi.SizeResult; import org.teavm.backend.wasm.wasi.SizeResult;
@ -65,7 +66,7 @@ public class WasiSupport {
int end = Math.min(s.length(), i + charsInChunk); int end = Math.min(s.length(), i + charsInChunk);
int count = end - i; int count = end - i;
for (int j = 0; j < count; ++j) { 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); putCharsStderr(buffer, count);
} }
@ -74,6 +75,8 @@ public class WasiSupport {
@Unmanaged @Unmanaged
public static void printInt(int i) { public static void printInt(int i) {
int count = 0; int count = 0;
boolean negative = i < 0;
i = Math.abs(i);
Address buffer = WasiBuffer.getBuffer().add(WasiBuffer.getBufferSize()); Address buffer = WasiBuffer.getBuffer().add(WasiBuffer.getBufferSize());
do { do {
++count; ++count;
@ -81,6 +84,11 @@ public class WasiSupport {
buffer.putByte((byte) ((i % 10) + (int) '0')); buffer.putByte((byte) ((i % 10) + (int) '0'));
i /= 10; i /= 10;
} while (i > 0); } while (i > 0);
if (negative) {
++count;
buffer = buffer.add(-1);
buffer.putByte((byte) '-');
}
putCharsStderr(buffer, count); putCharsStderr(buffer, count);
} }
@ -92,4 +100,34 @@ public class WasiSupport {
@Unmanaged @Unmanaged
public static void initHeapTrace(int maxHeap) { 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;
}
} }

View File

@ -49,4 +49,8 @@ public class WasmSupport {
@Import(name = "init", module = "teavmHeapTrace") @Import(name = "init", module = "teavmHeapTrace")
public static native void initHeapTrace(int maxHeap); public static native void initHeapTrace(int maxHeap);
public static String[] getArgs() {
return new String[0];
}
} }

View File

@ -15,10 +15,12 @@
*/ */
package org.teavm.backend.wasm.wasi; package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Address;
import org.teavm.interop.Import; import org.teavm.interop.Import;
public final class Wasi { public final class Wasi {
public static final int CLOCKID_REALTIME = 0; public static final int CLOCKID_REALTIME = 0;
public static final short ERRNO_SUCCESS = 0;
private Wasi() { private Wasi() {
} }
@ -28,4 +30,10 @@ public final class Wasi {
@Import(name = "clock_time_get", module = "wasi_snapshot_preview1") @Import(name = "clock_time_get", module = "wasi_snapshot_preview1")
public static native short clockTimeGet(int clockId, long precision, LongResult result); 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);
} }