diff --git a/cli/cli.js b/cli/cli.js index 3ff16b9a..c75daa43 100755 --- a/cli/cli.js +++ b/cli/cli.js @@ -110,6 +110,7 @@ function withCommonRunOptions (cmd) { .env("W4_NO_QR") .default(false) ) + .option("--metering", "Inject gas metering into WASM", false) .option("--hot", "Enable hot swapping. When the cart is reloaded, the console memory will be preserved, allowing code changes to the cart without resetting.", false); } diff --git a/cli/lib/server.js b/cli/lib/server.js index 35751e94..7c1838de 100644 --- a/cli/lib/server.js +++ b/cli/lib/server.js @@ -7,6 +7,7 @@ const { Server: WebSocketServer } = require("ws"); const open = require("open"); const process = require("process"); const { buffer } = require('node:stream/consumers'); +const metering = require("wasm-metering"); async function start (cartFile, opts) { @@ -16,23 +17,27 @@ async function start (cartFile, opts) { if (cartFile === "-") { // Filename "-" means read from standard in. // This can only be read once, so must be cached. - let cart_data = await buffer(process.stdin); + const cartRawWasm = await buffer(process.stdin); + const cartWasm = opts.metering ? metering.meterWASM(cartRawWasm) : cartRawWasm; app.get("/cart.wasm", (req, res) => { - res.send(cart_data); + res.send(cartWasm); }); } else if (!(await fs.stat(cartFile)).isFile()) { // If the file is not a regular file, such as a fifo, input stream etc. // we must also cache the data. - let cart_data = await fs.readFile(cartFile); + const cartRawWasm = await fs.readFile(cartFile); + const cartWasm = opts.metering ? metering.meterWASM(cartRawWasm) : cartRawWasm; app.get("/cart.wasm", (req, res) => { - res.send(cart_data); + res.send(cartWasm); }); } else { // otherwise it's a regular file, and can be read from disk every time. - app.get("/cart.wasm", (req, res) => { - res.sendFile(path.resolve(cartFile)); + app.get("/cart.wasm", async (req, res) => { + const cartRawWasm = await fs.readFile(cartFile); + const cartWasm = opts.metering ? metering.meterWASM(cartRawWasm) : cartRawWasm; + res.send(cartWasm); }); app.get("/cart.wasm.map", (req, res) => { res.sendFile(path.resolve(cartFile+".map")); diff --git a/cli/package-lock.json b/cli/package-lock.json index 1d3589eb..fea72f67 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -17,6 +17,7 @@ "pngjs": "^6.0.0", "qrcode": "^1.4.4", "recursive-copy": "^2.0.13", + "wasm-metering": "^0.2.1", "ws": "^7.5.3" }, "bin": { @@ -110,6 +111,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -139,6 +145,14 @@ "concat-map": "0.0.1" } }, + "node_modules/buffer-pipe": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/buffer-pipe/-/buffer-pipe-0.0.0.tgz", + "integrity": "sha512-PvKbsvQOH4dcUyUEvQQSs3CIkkuPcOHt3gKnXwf4HsPKFDxSN7bkmICVIWgOmW/jx/fAEGGn4mIayIJPLs7G8g==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -589,6 +603,15 @@ "node": ">=0.10.0" } }, + "node_modules/leb128": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/leb128/-/leb128-0.0.4.tgz", + "integrity": "sha512-2zejk0fCIgY8RVcc/KzvyfpDio5Oo8HgPZmkrOmdwmbk0KpKpgD+JKwikxKk8cZYkANIhwHK50SNukkCm3XkCQ==", + "dependencies": { + "bn.js": "^4.11.6", + "buffer-pipe": "0.0.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -1132,6 +1155,41 @@ "node": ">= 0.8" } }, + "node_modules/wasm-json-toolkit": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/wasm-json-toolkit/-/wasm-json-toolkit-0.2.3.tgz", + "integrity": "sha512-W0pESOST9hHFEmHq9kzMxAEhcPYuASdYCDw4FavKSyQKh3uOmH2slRXR/MhTKJY+gp1AauUDNd9DeE0cS4bV4A==", + "dependencies": { + "bn.js": "^4.11.8", + "buffer-pipe": "0.0.2", + "leb128": "0.0.4", + "safe-buffer": "^5.1.1" + }, + "bin": { + "json2wasm": "bin/json2wasm", + "wasm2json": "bin/wasm2json" + } + }, + "node_modules/wasm-json-toolkit/node_modules/buffer-pipe": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/buffer-pipe/-/buffer-pipe-0.0.2.tgz", + "integrity": "sha512-YlqzbWVqMv+xEeRyg0OXAJym3zAFTAIuku9l7okwxOXNDxbmSlL5o3QaF5k6IQ2iHO9o1OCo6tT4UkrQkI5VbQ==", + "dependencies": { + "safe-buffer": "^5.1.1" + } + }, + "node_modules/wasm-metering": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/wasm-metering/-/wasm-metering-0.2.1.tgz", + "integrity": "sha512-YDlTPY4jspknNyDaVBQhLTuTYBh+39qI0P9F0grmR88NR4oh7qfgpTwZ2ly4oX2hHCj9KlIwhy2Yyez+3/wY2Q==", + "dependencies": { + "leb128": "^0.0.4", + "wasm-json-toolkit": "0.2.3" + }, + "bin": { + "wasm-meter": "bin/wasm-meter" + } + }, "node_modules/which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", @@ -1275,6 +1333,11 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "body-parser": { "version": "1.19.0", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", @@ -1301,6 +1364,14 @@ "concat-map": "0.0.1" } }, + "buffer-pipe": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/buffer-pipe/-/buffer-pipe-0.0.0.tgz", + "integrity": "sha512-PvKbsvQOH4dcUyUEvQQSs3CIkkuPcOHt3gKnXwf4HsPKFDxSN7bkmICVIWgOmW/jx/fAEGGn4mIayIJPLs7G8g==", + "requires": { + "safe-buffer": "^5.1.1" + } + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -1646,6 +1717,15 @@ "resolved": "https://registry.npmjs.org/junk/-/junk-1.0.3.tgz", "integrity": "sha1-h75jSIZJy9ym9Tqzm+yczSNH9ZI=" }, + "leb128": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/leb128/-/leb128-0.0.4.tgz", + "integrity": "sha512-2zejk0fCIgY8RVcc/KzvyfpDio5Oo8HgPZmkrOmdwmbk0KpKpgD+JKwikxKk8cZYkANIhwHK50SNukkCm3XkCQ==", + "requires": { + "bn.js": "^4.11.6", + "buffer-pipe": "0.0.0" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2049,6 +2129,36 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "wasm-json-toolkit": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/wasm-json-toolkit/-/wasm-json-toolkit-0.2.3.tgz", + "integrity": "sha512-W0pESOST9hHFEmHq9kzMxAEhcPYuASdYCDw4FavKSyQKh3uOmH2slRXR/MhTKJY+gp1AauUDNd9DeE0cS4bV4A==", + "requires": { + "bn.js": "^4.11.8", + "buffer-pipe": "0.0.2", + "leb128": "0.0.4", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "buffer-pipe": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/buffer-pipe/-/buffer-pipe-0.0.2.tgz", + "integrity": "sha512-YlqzbWVqMv+xEeRyg0OXAJym3zAFTAIuku9l7okwxOXNDxbmSlL5o3QaF5k6IQ2iHO9o1OCo6tT4UkrQkI5VbQ==", + "requires": { + "safe-buffer": "^5.1.1" + } + } + } + }, + "wasm-metering": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/wasm-metering/-/wasm-metering-0.2.1.tgz", + "integrity": "sha512-YDlTPY4jspknNyDaVBQhLTuTYBh+39qI0P9F0grmR88NR4oh7qfgpTwZ2ly4oX2hHCj9KlIwhy2Yyez+3/wY2Q==", + "requires": { + "leb128": "^0.0.4", + "wasm-json-toolkit": "0.2.3" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", diff --git a/cli/package.json b/cli/package.json index d0e3ef20..037ac946 100644 --- a/cli/package.json +++ b/cli/package.json @@ -42,6 +42,7 @@ "pngjs": "^6.0.0", "qrcode": "^1.4.4", "recursive-copy": "^2.0.13", + "wasm-metering": "^0.2.1", "ws": "^7.5.3" }, "engines": { diff --git a/devtools/web/src/components/devtools/devtools.ts b/devtools/web/src/components/devtools/devtools.ts index 6fa11b50..fe104b85 100644 --- a/devtools/web/src/components/devtools/devtools.ts +++ b/devtools/web/src/components/devtools/devtools.ts @@ -70,6 +70,7 @@ export class Wasm4Devtools extends LitElement { private _renderGeneralView = ({ memoryView, fps, + gasUsed, wasmBufferByteLen, }: UpdateControllerState) => { const drawColors = memoryView.drawColors ?? 0; @@ -110,6 +111,10 @@ export class Wasm4Devtools extends LitElement {