mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Wasm: initial WASI support
This commit is contained in:
parent
d5cdd740f5
commit
1ca2c75e1c
|
@ -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")
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
core/src/main/java/org/teavm/backend/wasm/wasi/IOVec.java
Normal file
24
core/src/main/java/org/teavm/backend/wasm/wasi/IOVec.java
Normal 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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
31
core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java
Normal file
31
core/src/main/java/org/teavm/backend/wasm/wasi/Wasi.java
Normal 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);
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -18,5 +18,6 @@ package org.teavm.tooling;
|
||||||
public enum TeaVMTargetType {
|
public enum TeaVMTargetType {
|
||||||
JAVASCRIPT,
|
JAVASCRIPT,
|
||||||
WEBASSEMBLY,
|
WEBASSEMBLY,
|
||||||
|
WEBASSEMBLY_WASI,
|
||||||
C
|
C
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user