Wasm: initial WASI support

This commit is contained in:
Alexey Andreev 2022-11-06 11:53:52 +01:00
parent d5cdd740f5
commit 1ca2c75e1c
22 changed files with 503 additions and 45 deletions

View File

@ -16,7 +16,9 @@
package org.teavm.classlib.impl.console; package org.teavm.classlib.impl.console;
import org.teavm.backend.c.intrinsic.RuntimeInclude; import org.teavm.backend.c.intrinsic.RuntimeInclude;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.PlatformDetector;
import org.teavm.interop.Address;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
import org.teavm.jso.JSBody; import org.teavm.jso.JSBody;
@ -25,29 +27,35 @@ public final class Console {
private Console() { private Console() {
} }
public static void writeStderr(int b) { public static void writeStderr(byte[] data, int off, int len) {
if (PlatformDetector.isC()) { if (PlatformDetector.isC()) {
writeC(b); for (int i = 0; i < len; ++i) {
byte b = data[i + off];
writeC(b & 0xFF);
}
} else if (PlatformDetector.isWebAssembly()) { } else if (PlatformDetector.isWebAssembly()) {
writeWasm(b); WasmSupport.putCharsStderr(Address.ofData(data).add(off), len);
} else { } else {
writeJs(b); for (int i = 0; i < len; ++i) {
byte b = data[i + off];
writeJs(b & 0xFF);
}
} }
} }
public static void writeStdout(int b) { public static void writeStdout(byte[] data, int off, int len) {
if (PlatformDetector.isC()) { if (PlatformDetector.isC()) {
writeC(b); for (int i = 0; i < len; ++i) {
byte b = data[i + off];
writeC(b & 0xFF);
}
} else if (PlatformDetector.isWebAssembly()) { } else if (PlatformDetector.isWebAssembly()) {
writeWasm(b); WasmSupport.putCharsStdout(Address.ofData(data).add(off), len);
} else { } else {
writeJsStdout(b); for (int i = 0; i < len; ++i) {
} byte b = data[i + off];
} writeJs(b & 0xFF);
}
public static void writeStdout(String s) {
for (int i = 0; i < s.length(); ++i) {
writeStderr(s.charAt(i));
} }
} }
@ -57,9 +65,6 @@ public final class Console {
@JSBody(params = "b", script = "$rt_putStdout(b);") @JSBody(params = "b", script = "$rt_putStdout(b);")
private static native void writeJsStdout(int b); private static native void writeJsStdout(int b);
@Import(name = "putwchar", module = "teavm")
private static native void writeWasm(int b);
@Unmanaged @Unmanaged
@Import(name = "teavm_logchar") @Import(name = "teavm_logchar")
@RuntimeInclude("log.h") @RuntimeInclude("log.h")

View File

@ -0,0 +1,32 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.impl.console;
import java.io.IOException;
import java.io.OutputStream;
public abstract class ConsoleOutputStream extends OutputStream {
private byte[] buffer = new byte[1];
@Override
public void write(int b) throws IOException {
buffer[0] = (byte) b;
write(buffer);
}
@Override
public abstract void write(byte[] b, int off, int len) throws IOException;
}

View File

@ -16,16 +16,15 @@
package org.teavm.classlib.impl.console; package org.teavm.classlib.impl.console;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
public class StderrOutputStream extends OutputStream { public class StderrOutputStream extends ConsoleOutputStream {
public static final StderrOutputStream INSTANCE = new StderrOutputStream(); public static final StderrOutputStream INSTANCE = new StderrOutputStream();
private StderrOutputStream() { private StderrOutputStream() {
} }
@Override @Override
public void write(int b) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
Console.writeStderr(b); Console.writeStderr(b, off, len);
} }
} }

View File

@ -16,16 +16,15 @@
package org.teavm.classlib.impl.console; package org.teavm.classlib.impl.console;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
public class StdoutOutputStream extends OutputStream { public class StdoutOutputStream extends ConsoleOutputStream {
public static final StdoutOutputStream INSTANCE = new StdoutOutputStream(); public static final StdoutOutputStream INSTANCE = new StdoutOutputStream();
private StdoutOutputStream() { private StdoutOutputStream() {
} }
@Override @Override
public void write(int b) throws IOException { public void write(byte[] b, int off, int len) throws IOException {
Console.writeStdout(b); Console.writeStdout(b, off, len);
} }
} }

View File

@ -19,6 +19,7 @@ import java.util.Enumeration;
import java.util.Properties; import java.util.Properties;
import org.teavm.backend.c.intrinsic.RuntimeInclude; import org.teavm.backend.c.intrinsic.RuntimeInclude;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.fs.VirtualFileSystemProvider; import org.teavm.classlib.fs.VirtualFileSystemProvider;
import org.teavm.classlib.fs.c.CFileSystem; import org.teavm.classlib.fs.c.CFileSystem;
@ -144,15 +145,12 @@ public final class TSystem extends TObject {
private static long currentTimeMillisLowLevel() { private static long currentTimeMillisLowLevel() {
if (PlatformDetector.isWebAssembly()) { if (PlatformDetector.isWebAssembly()) {
return (long) currentTimeMillisWasm(); return WasmSupport.currentTimeMillis();
} else { } else {
return (long) currentTimeMillisC(); return currentTimeMillisC();
} }
} }
@Import(name = "currentTimeMillis", module = "teavm")
private static native double currentTimeMillisWasm();
@Import(name = "teavm_currentTimeMillis") @Import(name = "teavm_currentTimeMillis")
@RuntimeInclude("time.h") @RuntimeInclude("time.h")
private static native long currentTimeMillisC(); private static native long currentTimeMillisC();

View File

@ -20,4 +20,6 @@ import org.teavm.vm.spi.TeaVMHostExtension;
public interface TeaVMWasmHost extends TeaVMHostExtension { public interface TeaVMWasmHost extends TeaVMHostExtension {
void add(WasmIntrinsicFactory intrinsicFactory); void add(WasmIntrinsicFactory intrinsicFactory);
WasmRuntimeType getRuntimeType();
} }

View File

@ -15,8 +15,8 @@
*/ */
package org.teavm.backend.wasm; package org.teavm.backend.wasm;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
@ -60,8 +60,9 @@ public final class WasmHeap {
public static native void growMemory(int amount); public static native void growMemory(int amount);
@Import(name = "init", module = "teavmHeapTrace") private static void initHeapTrace(int maxHeap) {
private static native void initHeapTrace(int maxHeap); WasmSupport.initHeapTrace(maxHeap);
}
public static void initHeap(Address start, int minHeap, int maxHeap, int stackSize) { public static void initHeap(Address start, int minHeap, int maxHeap, int stackSize) {
initHeapTrace(maxHeap); initHeapTrace(maxHeap);

View File

@ -15,8 +15,8 @@
*/ */
package org.teavm.backend.wasm; package org.teavm.backend.wasm;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.interop.Address; import org.teavm.interop.Address;
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit; import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged; import org.teavm.interop.Unmanaged;
import org.teavm.runtime.RuntimeObject; import org.teavm.runtime.RuntimeObject;
@ -84,17 +84,21 @@ public final class WasmRuntime {
return value; return value;
} }
@Import(name = "print", module = "spectest") public static void print(int a) {
public static native void print(int a); WasmSupport.print(a);
}
@Import(name = "logString", module = "teavm") public static void printString(String s) {
public static native void printString(String s); WasmSupport.printString(s);
}
@Import(name = "logInt", module = "teavm") public static void printInt(int i) {
public static native void printInt(int i); WasmSupport.printInt(i);
}
@Import(name = "logOutOfMemory", module = "teavm") public static void printOutOfMemory() {
public static native void printOutOfMemory(); WasmSupport.printOutOfMemory();
}
public static void fillZero(Address address, int count) { public static void fillZero(Address address, int count) {
fill(address, (byte) 0, count); fill(address, (byte) 0, count);

View File

@ -0,0 +1,21 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm;
public enum WasmRuntimeType {
TEAVM,
WASI
}

View File

@ -106,6 +106,7 @@ 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.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.common.ServiceRepository; import org.teavm.common.ServiceRepository;
import org.teavm.dependency.ClassDependency; import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyAnalyzer;
@ -199,6 +200,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
private boolean obfuscated; private boolean obfuscated;
private Set<MethodReference> asyncMethods; private Set<MethodReference> asyncMethods;
private boolean hasThreads; private boolean hasThreads;
private WasmRuntimeType runtimeType = WasmRuntimeType.TEAVM;
@Override @Override
public void setController(TeaVMTargetController controller) { public void setController(TeaVMTargetController controller) {
@ -233,6 +235,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
List<ClassHolderTransformer> transformers = new ArrayList<>(); List<ClassHolderTransformer> transformers = new ArrayList<>();
transformers.add(new ClassPatch()); transformers.add(new ClassPatch());
transformers.add(new WasmDependencyListener()); transformers.add(new WasmDependencyListener());
if (runtimeType == WasmRuntimeType.WASI) {
transformers.add(new WasiSupportClassTransformer());
}
return transformers; return transformers;
} }
@ -291,6 +296,15 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
this.obfuscated = obfuscated; this.obfuscated = obfuscated;
} }
public void setRuntimeType(WasmRuntimeType runtimeType) {
this.runtimeType = runtimeType;
}
@Override
public WasmRuntimeType getRuntimeType() {
return runtimeType;
}
@Override @Override
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) { public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) { for (Class<?> type : Arrays.asList(int.class, long.class, float.class, double.class)) {
@ -502,6 +516,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());
for (String functionName : classGenerator.getFunctionTable()) { for (String functionName : classGenerator.getFunctionTable()) {
WasmFunction function = module.getFunctions().get(functionName); WasmFunction function = module.getFunctions().get(functionName);
@ -553,6 +568,17 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
return function; return function;
} }
private WasmFunction createStartCallerFunction() {
WasmFunction function = new WasmFunction("teavm_call_start");
function.setExportName("_start");
WasmCall call = new WasmCall("teavm_start");
call.getArguments().add(new WasmInt32Constant(0));
function.getBody().add(call);
return function;
}
private class IntrinsicFactoryContext implements WasmIntrinsicFactoryContext { private class IntrinsicFactoryContext implements WasmIntrinsicFactoryContext {
@Override @Override
public ClassReaderSource getClassSource() { public ClassReaderSource getClassSource() {

View File

@ -0,0 +1,33 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime;
import org.teavm.interop.Address;
public final class WasiBuffer {
private static byte[] argBuffer = new byte[256];
private WasiBuffer() {
}
public static int getBufferSize() {
return argBuffer.length;
}
public static Address getBuffer() {
return Address.ofData(argBuffer);
}
}

View File

@ -0,0 +1,95 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime;
import org.teavm.backend.wasm.wasi.IOVec;
import org.teavm.backend.wasm.wasi.LongResult;
import org.teavm.backend.wasm.wasi.SizeResult;
import org.teavm.backend.wasm.wasi.Wasi;
import org.teavm.interop.Address;
import org.teavm.interop.Structure;
import org.teavm.interop.Unmanaged;
@Unmanaged
public class WasiSupport {
private WasiSupport() {
}
@Unmanaged
public static void putCharsStdout(Address address, int count) {
putChars(1, address, count);
}
@Unmanaged
public static void putCharsStderr(Address address, int count) {
putChars(2, address, count);
}
@Unmanaged
public static void putChars(int fd, Address address, int count) {
Address argsAddress = WasiBuffer.getBuffer();
IOVec ioVec = argsAddress.toStructure();
SizeResult result = argsAddress.add(Structure.sizeOf(IOVec.class)).toStructure();
ioVec.buffer = address;
ioVec.bufferLength = count;
Wasi.fdWrite(fd, ioVec, 1, result);
}
@Unmanaged
public static long currentTimeMillis() {
Address argsAddress = WasiBuffer.getBuffer();
LongResult result = argsAddress.toStructure();
Wasi.clockTimeGet(Wasi.CLOCKID_REALTIME, 10, result);
return result.value;
}
@Unmanaged
public static void printString(String s) {
int charsInChunk = 128;
int offsetInBuffer = 128;
Address buffer = WasiBuffer.getBuffer().add(offsetInBuffer);
for (int i = 0; i < s.length(); i += charsInChunk) {
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));
}
putCharsStderr(buffer, count);
}
}
@Unmanaged
public static void printInt(int i) {
int count = 0;
Address buffer = WasiBuffer.getBuffer().add(WasiBuffer.getBufferSize());
do {
++count;
buffer = buffer.add(-1);
buffer.putByte((byte) ((i % 10) + (int) '0'));
i /= 10;
} while (i > 0);
putCharsStderr(buffer, count);
}
@Unmanaged
public static void printOutOfMemory() {
printString("Out of memory");
}
@Unmanaged
public static void initHeapTrace(int maxHeap) {
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.runtime;
import org.teavm.interop.Address;
import org.teavm.interop.Import;
public class WasmSupport {
private WasmSupport() {
}
@Import(name = "putwcharsOut", module = "teavm")
public static native void putCharsStderr(Address address, int count);
@Import(name = "putwcharsErr", module = "teavm")
public static native void putCharsStdout(Address address, int count);
public static long currentTimeMillis() {
return (long) currentTimeMillisImpl();
}
@Import(name = "currentTimeMillis", module = "teavm")
private static native double currentTimeMillisImpl();
@Import(name = "print", module = "spectest")
public static native void print(int a);
@Import(name = "logString", module = "teavm")
public static native void printString(String s);
@Import(name = "logInt", module = "teavm")
public static native void printInt(int i);
@Import(name = "logOutOfMemory", module = "teavm")
public static native void printOutOfMemory();
@Import(name = "init", module = "teavmHeapTrace")
public static native void initHeapTrace(int maxHeap);
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.transformation;
import org.teavm.backend.wasm.runtime.WasiSupport;
import org.teavm.backend.wasm.runtime.WasmSupport;
import org.teavm.model.ClassHierarchy;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.ValueType;
import org.teavm.model.emit.ProgramEmitter;
import org.teavm.model.emit.ValueEmitter;
public class WasiSupportClassTransformer implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (cls.getName().equals(WasmSupport.class.getName())) {
transformWasm(cls, context.getHierarchy());
}
}
private void transformWasm(ClassHolder cls, ClassHierarchy classHierarchy) {
ClassReader sourceCls = classHierarchy.getClassSource().get(WasiSupport.class.getName());
for (MethodHolder method : cls.getMethods()) {
MethodReader sourceMethod = sourceCls.getMethod(method.getDescriptor());
if (sourceMethod != null) {
if (method.hasModifier(ElementModifier.NATIVE)) {
method.getModifiers().remove(ElementModifier.NATIVE);
}
ProgramEmitter pe = ProgramEmitter.create(method, classHierarchy);
ValueEmitter[] args = new ValueEmitter[method.parameterCount()];
for (int i = 0; i < args.length; ++i) {
args[i] = pe.var(i + 1, method.parameterType(i));
}
ValueEmitter result = pe.invoke(sourceMethod.getReference(), args);
if (method.getResultType() != ValueType.VOID) {
result.returnValue();
} else {
pe.exit();
}
}
}
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Address;
import org.teavm.interop.Structure;
public class IOVec extends Structure {
public Address buffer;
public int bufferLength;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class LongResult extends Structure {
public long value;
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Structure;
public class SizeResult extends Structure {
public int value;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2022 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.backend.wasm.wasi;
import org.teavm.interop.Import;
public final class Wasi {
public static final int CLOCKID_REALTIME = 0;
private Wasi() {
}
@Import(name = "fd_write", module = "wasi_snapshot_preview1")
public static native short fdWrite(int fd, IOVec vectors, int vectorsCont, SizeResult result);
@Import(name = "clock_time_get", module = "wasi_snapshot_preview1")
public static native short clockTimeGet(int clockId, long precision, LongResult result);
}

View File

@ -31,6 +31,14 @@ TeaVM.wasm = function() {
lineBuffer += String.fromCharCode(charCode); lineBuffer += String.fromCharCode(charCode);
} }
} }
function putwchars(buffer, count) {
let instance = controller.instance;
let memory = instance.exports.memory.buffer;
for (let i = 0; i < count; ++i) {
// TODO: support UTF-8
putwchar(memory[buffer++]);
}
}
function towlower(code) { function towlower(code) {
return String.fromCharCode(code).toLowerCase().charCodeAt(0); return String.fromCharCode(code).toLowerCase().charCodeAt(0);
} }
@ -90,7 +98,8 @@ TeaVM.wasm = function() {
teavm_getNaN: function() { return NaN; }, teavm_getNaN: function() { return NaN; },
isinf: function(n) { return !isFinite(n) }, isinf: function(n) { return !isFinite(n) },
isfinite: isFinite, isfinite: isFinite,
putwchar: putwchar, putwcharsOut: (chars, count) => putwchars(controller, chars, count),
putwcharsErr: (chars, count) => putwchars(controller, chars, count),
towlower: towlower, towlower: towlower,
towupper: towupper, towupper: towupper,
getNativeOffset: getNativeOffset, getNativeOffset: getNativeOffset,

View File

@ -58,7 +58,7 @@ public final class TeaVMRunner {
options.addOption(Option.builder("t") options.addOption(Option.builder("t")
.argName("target") .argName("target")
.hasArg() .hasArg()
.desc("target type (javascript/js, webassembly/wasm, C)") .desc("target type (javascript/js, webassembly/wasm, webassembly-wasi/wasm-wasi/wasi, C)")
.build()); .build());
options.addOption(Option.builder("d") options.addOption(Option.builder("d")
.argName("directory") .argName("directory")
@ -220,6 +220,11 @@ public final class TeaVMRunner {
case "wasm": case "wasm":
tool.setTargetType(TeaVMTargetType.WEBASSEMBLY); tool.setTargetType(TeaVMTargetType.WEBASSEMBLY);
break; break;
case "webassembly-wasi":
case "wasm-wasi":
case "wasi":
tool.setTargetType(TeaVMTargetType.WEBASSEMBLY);
break;
case "c": case "c":
tool.setTargetType(TeaVMTargetType.C); tool.setTargetType(TeaVMTargetType.C);
break; break;

View File

@ -18,5 +18,6 @@ package org.teavm.tooling;
public enum TeaVMTargetType { public enum TeaVMTargetType {
JAVASCRIPT, JAVASCRIPT,
WEBASSEMBLY, WEBASSEMBLY,
WEBASSEMBLY_WASI,
C C
} }

View File

@ -37,6 +37,7 @@ import org.teavm.backend.c.generate.CNameProvider;
import org.teavm.backend.c.generate.ShorteningFileNameProvider; import org.teavm.backend.c.generate.ShorteningFileNameProvider;
import org.teavm.backend.c.generate.SimpleFileNameProvider; import org.teavm.backend.c.generate.SimpleFileNameProvider;
import org.teavm.backend.javascript.JavaScriptTarget; import org.teavm.backend.javascript.JavaScriptTarget;
import org.teavm.backend.wasm.WasmRuntimeType;
import org.teavm.backend.wasm.WasmTarget; import org.teavm.backend.wasm.WasmTarget;
import org.teavm.backend.wasm.render.WasmBinaryVersion; import org.teavm.backend.wasm.render.WasmBinaryVersion;
import org.teavm.cache.AlwaysStaleCacheStatus; import org.teavm.cache.AlwaysStaleCacheStatus;
@ -315,7 +316,9 @@ public class TeaVMTool {
case JAVASCRIPT: case JAVASCRIPT:
return prepareJavaScriptTarget(); return prepareJavaScriptTarget();
case WEBASSEMBLY: case WEBASSEMBLY:
return prepareWebAssemblyTarget(); return prepareWebAssemblyDefaultTarget();
case WEBASSEMBLY_WASI:
return prepareWebAssemblyWasiTarget();
case C: case C:
return prepareCTarget(); return prepareCTarget();
} }
@ -347,6 +350,18 @@ public class TeaVMTool {
return webAssemblyTarget; return webAssemblyTarget;
} }
private WasmTarget prepareWebAssemblyDefaultTarget() {
WasmTarget target = prepareWebAssemblyTarget();
target.setRuntimeType(WasmRuntimeType.TEAVM);
return target;
}
private WasmTarget prepareWebAssemblyWasiTarget() {
WasmTarget target = prepareWebAssemblyTarget();
target.setRuntimeType(WasmRuntimeType.WASI);
return target;
}
private CTarget prepareCTarget() { private CTarget prepareCTarget() {
cTarget = new CTarget(new CNameProvider()); cTarget = new CTarget(new CNameProvider());
cTarget.setMinHeapSize(minHeapSize); cTarget.setMinHeapSize(minHeapSize);