From 4a025f26050d441776d12ab8fd06d65f777adf50 Mon Sep 17 00:00:00 2001
From: Alexey Andreev <konsoletyper@gmail.com>
Date: Fri, 11 Nov 2022 14:04:56 +0100
Subject: [PATCH] Wasm: trying to revive GC debugger in wasm2c generator and
 implement emulation for WASI

---
 .idea/checkstyle-idea.xml                     | 34 ++++----
 .../org/teavm/backend/wasm/WasmTarget.java    |  8 +-
 .../wasm/intrinsics/ConsoleIntrinsic.java     |  4 +-
 .../backend/wasm/intrinsics/GCIntrinsic.java  |  2 +-
 .../wasm/intrinsics/WasmHeapIntrinsic.java    | 12 +++
 .../backend/wasm/render/WasmCRenderer.java    | 11 ++-
 .../backend/wasm/runtime/WasiSupport.java     |  5 +-
 .../WasiSupportClassTransformer.java          |  2 +
 .../org/teavm/backend/wasm/wasm-runtime.c     | 78 ++++++++++++++++++-
 9 files changed, 129 insertions(+), 27 deletions(-)

diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml
index 2c339c219..6f3de1980 100644
--- a/.idea/checkstyle-idea.xml
+++ b/.idea/checkstyle-idea.xml
@@ -1,19 +1,25 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
-  <component name="CheckStyle-IDEA">
-    <option name="configuration">
-      <map>
-        <entry key="active-configuration" value="LOCAL_FILE:$PROJECT_DIR$/checkstyle.xml:TeaVM" />
-        <entry key="checkstyle-version" value="8.41" />
-        <entry key="copy-libs" value="false" />
-        <entry key="location-0" value="BUNDLED:(bundled):Sun Checks" />
-        <entry key="location-1" value="BUNDLED:(bundled):Google Checks" />
-        <entry key="location-2" value="LOCAL_FILE:$PROJECT_DIR$/checkstyle.xml:TeaVM" />
-        <entry key="property-2.config_loc" value="." />
-        <entry key="scan-before-checkin" value="false" />
-        <entry key="scanscope" value="JavaOnlyWithTests" />
-        <entry key="suppress-errors" value="false" />
-      </map>
+  <component name="CheckStyle-IDEA" serialisationVersion="2">
+    <checkstyleVersion>8.41.1</checkstyleVersion>
+    <scanScope>JavaOnlyWithTests</scanScope>
+    <option name="thirdPartyClasspath" />
+    <option name="activeLocationIds">
+      <option value="c65b2ab0-cb40-4423-befb-a37d514deee5" />
+    </option>
+    <option name="locations">
+      <list>
+        <ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
+        <ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
+        <ConfigurationLocation id="c65b2ab0-cb40-4423-befb-a37d514deee5" type="LOCAL_FILE" scope="All" description="TeaVM">
+          $PROJECT_DIR$/checkstyle.xml
+          <option name="properties">
+            <map>
+              <entry key="config_loc" value="." />
+            </map>
+          </option>
+        </ConfigurationLocation>
+      </list>
     </option>
   </component>
 </project>
\ No newline at end of file
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 09cbfb417..d6e35a07b 100644
--- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java
+++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java
@@ -348,8 +348,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
                 String.class, void.class)).use();
         dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt",
                 int.class, void.class)).use();
-        dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory",
-                void.class)).use();
+        dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printOutOfMemory", void.class)).use();
 
         dependencyAnalyzer.linkMethod(INIT_HEAP_REF).use();
         dependencyAnalyzer.linkMethod(RESIZE_HEAP_REF).use();
@@ -471,10 +470,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
         context.addIntrinsic(new ObjectIntrinsic());
         context.addIntrinsic(new ConsoleIntrinsic());
         context.addGenerator(new ArrayGenerator());
-        if (!Boolean.parseBoolean(System.getProperty("teavm.wasm.vmAssertions", "false"))) {
+        boolean vmAssertions = Boolean.parseBoolean(System.getProperty("teavm.wasm.vmAssertions", "false"));
+        if (!vmAssertions) {
             context.addIntrinsic(new MemoryTraceIntrinsic());
         }
-        context.addIntrinsic(new WasmHeapIntrinsic());
+        context.addIntrinsic(new WasmHeapIntrinsic(vmAssertions));
         context.addIntrinsic(new FiberIntrinsic());
 
         IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext();
diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java
index bb591c578..7c184ef41 100644
--- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java
+++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/ConsoleIntrinsic.java
@@ -49,13 +49,13 @@ public class ConsoleIntrinsic implements WasmIntrinsic {
         switch (invocation.getMethod().getName()) {
             case "printString": {
                 String name = manager.getNames().forMethod(PRINT_STRING);
-                WasmCall call = new WasmCall(name, true);
+                WasmCall call = new WasmCall(name);
                 call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
                 return call;
             }
             case "printInt": {
                 String name = manager.getNames().forMethod(PRINT_INT);
-                WasmCall call = new WasmCall(name, true);
+                WasmCall call = new WasmCall(name);
                 call.getArguments().add(manager.generate(invocation.getArguments().get(0)));
                 return call;
             }
diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java
index 0b774d93c..43c3e6d65 100644
--- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java
+++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/GCIntrinsic.java
@@ -111,7 +111,7 @@ public class GCIntrinsic implements WasmIntrinsic {
                 return intToLong(getStaticField(manager, "heapSize"));
             case "outOfMemory": {
                 WasmBlock block = new WasmBlock(false);
-                WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY), true);
+                WasmCall call = new WasmCall(manager.getNames().forMethod(PRINT_OUT_OF_MEMORY));
                 block.getBody().add(call);
                 block.getBody().add(new WasmUnreachable());
                 return block;
diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java
index dcbd32659..94e577065 100644
--- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java
+++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmHeapIntrinsic.java
@@ -17,17 +17,27 @@ package org.teavm.backend.wasm.intrinsics;
 
 import org.teavm.ast.InvocationExpr;
 import org.teavm.backend.wasm.WasmHeap;
+import org.teavm.backend.wasm.model.expression.WasmDrop;
 import org.teavm.backend.wasm.model.expression.WasmExpression;
+import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
 import org.teavm.backend.wasm.model.expression.WasmMemoryGrow;
 import org.teavm.model.MethodReference;
 
 public class WasmHeapIntrinsic implements WasmIntrinsic {
+    private boolean trace;
+
+    public WasmHeapIntrinsic(boolean trace) {
+        this.trace = trace;
+    }
+
     @Override
     public boolean isApplicable(MethodReference methodReference) {
         if (!methodReference.getClassName().equals(WasmHeap.class.getName())) {
             return false;
         }
         switch (methodReference.getName()) {
+            case "initHeapTrace":
+                return !trace;
             case "growMemory":
                 return true;
             default:
@@ -38,6 +48,8 @@ public class WasmHeapIntrinsic implements WasmIntrinsic {
     @Override
     public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
         switch (invocation.getMethod().getName()) {
+            case "initHeapTrace":
+                return new WasmDrop(new WasmInt32Constant(0));
             case "growMemory":
                 return new WasmMemoryGrow(manager.generate(invocation.getArguments().get(0)));
             default:
diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java
index 4da803fa8..7db62ca66 100644
--- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java
+++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderer.java
@@ -20,6 +20,7 @@ import java.io.IOException;
 import java.io.InputStreamReader;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import org.teavm.backend.wasm.model.WasmFunction;
 import org.teavm.backend.wasm.model.WasmLocal;
 import org.teavm.backend.wasm.model.WasmMemorySegment;
@@ -80,9 +81,11 @@ public class WasmCRenderer {
             }
         }
 
-        line("void main() {");
+        line("void main(int argc, char** argv) {");
         indent();
 
+        line("wasm_args = argc;");
+        line("wasm_argv = argv;");
         renderHeap(module);
         if (module.getStartFunction() != null) {
             line(module.getStartFunction().getName() + "();");
@@ -94,6 +97,12 @@ public class WasmCRenderer {
             }
         }
 
+        for (WasmFunction function : module.getFunctions().values()) {
+            if (Objects.equals(function.getExportName(), "_start")) {
+                line(function.getName() + "();");
+            }
+        }
+
         outdent();
         line("}");
     }
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 5bc705ef6..7bead97ab 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
@@ -102,10 +102,6 @@ public class WasiSupport {
         printString("Out of memory");
     }
 
-    @Unmanaged
-    public static void initHeapTrace(int maxHeap) {
-    }
-
     public static String[] getArgs() {
         Address buffer = WasiBuffer.getBuffer();
         IntResult sizesReceiver = buffer.toStructure();
@@ -143,6 +139,7 @@ public class WasiSupport {
 
     private static int nextRandom(int bits) {
         if (!randomInitialized) {
+            randomInitialized = true;
             short errno = Wasi.randomGet(WasiBuffer.getBuffer(), 8);
 
             if (errno != ERRNO_SUCCESS) {
diff --git a/core/src/main/java/org/teavm/backend/wasm/transformation/WasiSupportClassTransformer.java b/core/src/main/java/org/teavm/backend/wasm/transformation/WasiSupportClassTransformer.java
index 578716b23..047896690 100644
--- a/core/src/main/java/org/teavm/backend/wasm/transformation/WasiSupportClassTransformer.java
+++ b/core/src/main/java/org/teavm/backend/wasm/transformation/WasiSupportClassTransformer.java
@@ -17,6 +17,7 @@ package org.teavm.backend.wasm.transformation;
 
 import org.teavm.backend.wasm.runtime.WasiSupport;
 import org.teavm.backend.wasm.runtime.WasmSupport;
+import org.teavm.interop.Import;
 import org.teavm.model.ClassHierarchy;
 import org.teavm.model.ClassHolder;
 import org.teavm.model.ClassHolderTransformer;
@@ -45,6 +46,7 @@ public class WasiSupportClassTransformer implements ClassHolderTransformer {
                 if (method.hasModifier(ElementModifier.NATIVE)) {
                     method.getModifiers().remove(ElementModifier.NATIVE);
                 }
+                method.getAnnotations().remove(Import.class.getName());
                 ProgramEmitter pe = ProgramEmitter.create(method, classHierarchy);
                 ValueEmitter[] args = new ValueEmitter[method.parameterCount()];
                 for (int i = 0; i < args.length; ++i) {
diff --git a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c
index 3b7da14a9..ef3fdbdb0 100644
--- a/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c
+++ b/core/src/main/resources/org/teavm/backend/wasm/wasm-runtime.c
@@ -8,9 +8,12 @@
 #include <wctype.h>
 #include <time.h>
 #include <uchar.h>
+#include <unistd.h>
 
 static int8_t *wasm_heap;
 static int32_t wasm_heap_size;
+static int wasm_args;
+static char** wasm_argv;
 
 float teavm_teavm_getNaN() {
     return NAN;
@@ -22,7 +25,7 @@ float teavm_teavm_getNaN() {
 #define teavmMath_ceil ceil
 #define teavmMath_floor floor
 
-double teavm_currentTimeMillis() {
+int64_t teavm_currentTimeMillis() {
     struct timespec time;
     clock_gettime(CLOCK_REALTIME, &time);
 
@@ -74,4 +77,77 @@ void teavm_logString(int32_t string) {
 
 void teavm_logInt(int32_t v) {
     wprintf(L"%" PRId32, v);
+}
+
+int32_t wasi_snapshot_preview1_clock_time_get(int32_t clock_id, int64_t precision, int32_t result_ptr) {
+    int64_t* resultAddr = (int64_t*) (wasm_heap + result_ptr);
+    *resultAddr = teavm_currentTimeMillis();
+    return 0;
+}
+
+int32_t wasi_snapshot_preview1_args_sizes_get(int32_t argv_size, int32_t argv_buffer_size) {
+    int32_t* argvSizePtr = (int32_t*) (wasm_heap + argv_size);
+    int32_t* argvBufferSizePtr = (int32_t*) (wasm_heap + argv_buffer_size);
+    *argvSizePtr = (int32_t) wasm_args;
+
+    int32_t bufferSize = 0;
+    for (int i = 0; i < wasm_args; ++i) {
+        bufferSize += (int32_t) strlen(wasm_argv[i]);
+    }
+    *argvBufferSizePtr = bufferSize;
+    return 0;
+}
+
+int32_t wasi_snapshot_preview1_args_get(int32_t sizes_ptr, int32_t args_ptr) {
+    int32_t* sizesPtr = (int32_t*) (wasm_heap + sizes_ptr);
+    char* argsPtr = (char*) (wasm_heap + args_ptr);
+    int offset = 0;
+    for (int i = 0; i < wasm_args; ++i) {
+        sizesPtr[i] = (int32_t) offset;
+        int len = strlen(wasm_argv[i]);
+        memcpy(argsPtr + offset, wasm_argv[i], len);
+        offset += len;
+    }
+    return 0;
+}
+
+typedef struct {
+    int32_t tag;
+    union {
+        struct {
+            int32_t name_length;
+        } dir;
+    } data;
+} WasiPrestat;
+
+int32_t wasi_snapshot_preview1_fd_prestat_get(int32_t fd, int32_t prestat_ptr) {
+    if (fd != 3) {
+        return 8;
+    }
+    WasiPrestat* prestat = (WasiPrestat*) (wasm_heap + prestat_ptr);
+    prestat->tag = 0;
+    prestat->data.dir.name_length = 1;
+    return 0;
+}
+
+int32_t wasi_snapshot_preview1_fd_prestat_dir_name(int32_t fd, int32_t path, int32_t path_length) {
+    char* pathPtr = (char*) (wasm_heap + path);
+    *pathPtr = '/';
+    return 0;
+}
+
+typedef struct {
+    int32_t buf;
+    int32_t buf_len;
+} WasiIOVec;
+
+int32_t wasi_snapshot_preview1_fd_write(int32_t fd, int32_t iovs, int32_t count, int32_t result) {
+    WasiIOVec* vec = (WasiIOVec*) (wasm_heap + iovs);
+    int32_t written = 0;
+    for (int32_t i = 0; i < count; ++i) {
+        written += write((int) fd, (char*) (wasm_heap + vec->buf), vec->buf_len);
+    }
+    int32_t* resultPtr = (int32_t*) (wasm_heap + result);
+    *resultPtr = written;
+    return 0;
 }
\ No newline at end of file