diff --git a/modules.json b/modules.json index f39fae66a0..7e3cc69422 100644 --- a/modules.json +++ b/modules.json @@ -86,6 +86,9 @@ "remote_execution": { "tabs": [] }, + "wasm": { + "tabs": [] + }, "arcade_2d": { "tabs": [ "ArcadeTwod" diff --git a/package.json b/package.json index dff04b8ae8..74c32f2e41 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,8 @@ "react-ace": "^10.1.0", "react-dom": "^17.0.2", "regl": "^2.1.0", + "source-academy-utils": "^1.0.0", + "source-academy-wabt": "^1.0.4", "tslib": "^2.3.1" }, "jest": { diff --git a/src/bundles/wasm/index.ts b/src/bundles/wasm/index.ts new file mode 100644 index 0000000000..89cbef8e9c --- /dev/null +++ b/src/bundles/wasm/index.ts @@ -0,0 +1,87 @@ +/** + * WebAssembly Module for Source Academy, for developing with WebAssembly Text and Binaries. + * + * Examples: + * Simple 'add' library: + * ```wat + * import {wcompile, wrun} from "wasm"; + * + * const program = ` + * (module + * (func (param f64) (param f64) (result f64) + * local.get 0 + * local.get 1 + * f64.add) + * (export "add" (func 0)) + * ) + * `; + * + * + * const binary = wcompile(program); + * const exports = wrun(binary); + * + * display(binary); + * display_list(exports); + * + * const add_fn = head(tail(exports)); + * + * display(add_fn(10, 35)); + * ``` + * + * 'Calculator Language': + * ```wat + * // Type your program in here! + * import {wcompile, wrun} from "wasm"; + * + * const program = ` + * (module + * (func (param f64) (param f64) (result f64) + * local.get 0 + * local.get 1 + * f64.add) + * (func (param f64) (param f64) (result f64) + * local.get 0 + * local.get 1 + * f64.sub) + * (func (param f64) (param f64) (result f64) + * local.get 0 + * local.get 1 + * f64.mul) + * (func (param f64) (param f64) (result f64) + * local.get 0 + * local.get 1 + * f64.div) + * (export "add" (func 0)) + * (export "sub" (func 1)) + * (export "mul" (func 2)) + * (export "div" (func 3)) + * )`; + * + * + * const encoding = wcompile(program); + * let exports = wrun(encoding); + * + * display_list(exports); + * + * const div = head(tail(exports)); + * exports = tail(tail(exports)); + * const mul = head(tail(exports)); + * exports = tail(tail(exports)); + * const sub = head(tail(exports)); + * exports = tail(tail(exports)); + * const add = head(tail(exports)); + * + * display(div(10, -35)); + * display(mul(10, 12347)); + * display(sub(12347, 12347)); + * display(add(10, 0)); + * ``` + * + * + * @module wasm + * @author Kim Yongbeom + */ +export { + wcompile, + wrun, +} from './wabt'; diff --git a/src/bundles/wasm/wabt.ts b/src/bundles/wasm/wabt.ts new file mode 100644 index 0000000000..bdf5d5a0f8 --- /dev/null +++ b/src/bundles/wasm/wabt.ts @@ -0,0 +1,23 @@ +import { compile } from 'source-academy-wabt'; +import { objectToLinkedList } from 'source-academy-utils'; + +/** + * Compile a (hopefully valid) WebAssembly Text module to binary. + * @param program program to compile + * @returns an array of 8-bit unsigned integers. + */ +export const wcompile = (program: string) => Array.from(compile(program)); + +/** + * Run a compiled WebAssembly Binary Buffer. + * @param buffer an array of 8-bit unsigned integers to run + * @returns a linked list of exports that the relevant WebAssembly Module exports + */ +export const wrun = (buffer: number[] | Uint8Array) => { + if (buffer instanceof Array) { + buffer = new Uint8Array(buffer); + } + + const exps = new WebAssembly.Instance(new WebAssembly.Module(buffer)).exports; + return objectToLinkedList(exps); +}; diff --git a/yarn.lock b/yarn.lock index d724019355..170e2ed6ed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1706,6 +1706,11 @@ cjs-module-lexer@^1.0.0: resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== +class-transformer@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" + integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== + classnames@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" @@ -4938,6 +4943,11 @@ readable-stream@~2.3.6: string_decoder "~1.1.1" util-deprecate "~1.0.1" +reflect-metadata@^0.1.13: + version "0.1.13" + resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" + integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== + regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" @@ -5206,6 +5216,21 @@ socks@^2.6.2: ip "^2.0.0" smart-buffer "^4.2.0" +source-academy-utils@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-academy-utils/-/source-academy-utils-1.0.2.tgz#fc35a4e21e6e6a14743ed560978a9701af1a8bdc" + integrity sha512-cSx/Rxr0CEOr+KJKILKicOVSVknG82fMEozaituD5mjh92przLW8C4kafzXrfGMjPVb6p7lxFMk5S6QyiYI2/g== + +source-academy-wabt@^1.0.4: + version "1.0.10" + resolved "https://registry.yarnpkg.com/source-academy-wabt/-/source-academy-wabt-1.0.10.tgz#4187804a10b8233dc0f3498b49d07523940b4789" + integrity sha512-eRm9Q+wm9rNKpaX3X+ykKjcLyrV2O6elAIG3qmkuOeOLk3f26QEFfroBvNxLtvVokkItWRHek9T/d5Gqrqo5tQ== + dependencies: + class-transformer "^0.5.1" + lodash "^4.17.21" + reflect-metadata "^0.1.13" + typescript "4" + source-map-support@0.5.13: version "0.5.13" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" @@ -5584,6 +5609,11 @@ typedoc@^0.23.23: minimatch "^6.1.6" shiki "^0.14.1" +typescript@4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + typescript@4.8: version "4.8.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"