hamzaremmal/amy

View on GitHub
compiler/src/main/scala/amyc/backend/wasm/gen/HTMLWrapper.scala

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
package amyc.backend.wasm.gen

import amyc.backend.wasm.Modules.*

object HTMLWrapper :

  def apply(moduleFile: String, module: Module): String =
    s"""|<!doctype html>
        |
        |<html>
        |  <head>
        |    <meta charset="utf-8">
        |    <title>${module.name}</title>
        |  </head>
        |
        |  <body>
        |    <p id="htmlText"></p>
        |    <script>
        |
        |      // This library function fetches the wasm module at 'url', instantiates it with
        |      // the given 'importObject', and returns the instantiated object instance
        |      // Taken from https://github.com/WebAssembly/spec
        |      function fetchAndInstantiate(url, importObject) {
        |        return fetch(url).then(response =>
        |          response.arrayBuffer()
        |        ).then(bytes =>
        |          WebAssembly.instantiate(bytes, importObject)
        |        ).then(results =>
        |          results.instance
        |        );
        |      }
        |
        |      const log = (line) => document.getElementById("htmlText").innerHTML += line + "<br>";
        |      const waitInput = () => window.prompt("Input to WASM program:");
        |      const exit = () => log('<span style="color: red">Exited</span>');
        |
        |      const memory = new WebAssembly.Memory({initial:100});
        |
        |      $importObject
        |
        |      fetchAndInstantiate('$moduleFile', importObject).then(function(instance) {
        |""".stripMargin ++
      module.functions.filter(_.result.isEmpty).map { f =>
        s"        instance.exports.${f.name}();\n"
      }.mkString ++
      """|      });
         |    </script>
         |  </body>
         |
         |</html>
         |
          """.stripMargin

  /** JavaScript object containing the values to be imported into the WASM instance.
    * Requires a waitInput(), log(line) and exit() function to be in scope.
    */
  lazy val importObject: String =
    s"""const importObject = {
       |  system: {
       |    mem: memory,
       |
       |    printInt: function(arg) {
       |      log(arg);
       |      0;
       |    },
       |
       |    printString: function(arg) {
       |      var bufView = new Uint8Array(memory.buffer);
       |      var i = arg;
       |      var result = "";
       |      while(bufView[i] != 0) {
       |       result += String.fromCharCode(bufView[i]);
       |       i = i + 1;
       |      }
       |      log(result);
       |      0;
       |    },
       |
       |    readInt: function() {
       |      var res = parseInt(waitInput());
       |      if (isNaN(res)) {
       |        log("Error: Could not parse int");
       |        exit();
       |      } else {
       |        return res;
       |      }
       |    },
       |
       |    // This function has a weird signature due to restrictions of the current WASM format:
       |    // It takes as argument the position that it needs to store the string to,
       |    // and returns the first position after the new string.
       |    readString0: function(memB) {
       |      var s = waitInput();
       |      var size = s.length;
       |      var padding = 4 - size % 4;
       |      var fullString = s + "\u0000".repeat(padding);
       |      var newMemB = memB + size + padding;
       |      var bufView8 = new Uint8Array(memory.buffer);
       |      for (var i = 0; i < fullString.length; i++) {
       |        bufView8[memB + i] = fullString.charCodeAt(i);
       |      }
       |      return newMemB;
       |    }
       |  }
       |};
       |""".stripMargin