From 3134e33919cfbe099020ee5f4c4de4e438b8f463 Mon Sep 17 00:00:00 2001 From: darckfast <21337578+Darckfast@users.noreply.github.com> Date: Fri, 26 Jun 2026 21:14:07 -0300 Subject: [PATCH 1/3] wasm: patch wasm_exec.js and wasm_exec_node.js from Go 1.18+ Port this commit https://github.com/golang/go/commit/680caf15355057ca84857a2a291b6f5c44e73329 removing polyfills from `wasm_exec.js`, now the environment is expected to provide all the required polyfills. `wasm_exec.js` now only provides stub fallbacks for globalThis.fs and globalThis.process. All NodeJS specific code is now in a separate file `wasm_exec_node.js` with its required polyfills. --- targets/wasm.json | 2 +- targets/wasm_exec.js | 113 +++++++++----------------------------- targets/wasm_exec_node.js | 42 ++++++++++++++ testdata/wasmexit.js | 10 ++++ testdata/wasmexport.js | 10 ++++ testdata/wasmfunc.js | 10 ++++ 6 files changed, 100 insertions(+), 87 deletions(-) create mode 100644 targets/wasm_exec_node.js diff --git a/targets/wasm.json b/targets/wasm.json index ddce374649..dbae9165e5 100644 --- a/targets/wasm.json +++ b/targets/wasm.json @@ -27,5 +27,5 @@ "src/runtime/asm_tinygowasm.S", "src/runtime/gc_boehm.c" ], - "emulator": "node {root}/targets/wasm_exec.js {}" + "emulator": "node {root}/targets/wasm_exec_node.js {}" } diff --git a/targets/wasm_exec.js b/targets/wasm_exec.js index 2045a05de5..1cab8e5487 100644 --- a/targets/wasm_exec.js +++ b/targets/wasm_exec.js @@ -3,51 +3,25 @@ // license that can be found in the LICENSE file. // // This file has been modified for use by the TinyGo compiler. +"use strict"; (() => { - // Map multiple JavaScript environments to a single common API, - // preferring web standards over Node.js API. - // - // Environments considered: - // - Browsers - // - Node.js - // - Electron - // - Parcel - - if (typeof global !== "undefined") { - // global already exists - } else if (typeof window !== "undefined") { - window.global = window; - } else if (typeof self !== "undefined") { - self.global = self; - } else { - throw new Error("cannot export Go (neither global, window nor self is defined)"); - } - - if (!global.require && typeof require !== "undefined") { - global.require = require; - } - - if (!global.fs && global.require) { - global.fs = require("node:fs"); - } - const enosys = () => { const err = new Error("not implemented"); err.code = "ENOSYS"; return err; }; - if (!global.fs) { + if (!globalThis.fs) { let outputBuf = ""; - global.fs = { - constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1 }, // unused + globalThis.fs = { + constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_DIRECTORY: -1 }, // unused writeSync(fd, buf) { outputBuf += decoder.decode(buf); const nl = outputBuf.lastIndexOf("\n"); if (nl != -1) { - console.log(outputBuf.substr(0, nl)); - outputBuf = outputBuf.substr(nl + 1); + console.log(outputBuf.substring(0, nl)); + outputBuf = outputBuf.substring(nl + 1); } return buf.length; }, @@ -85,8 +59,8 @@ }; } - if (!global.process) { - global.process = { + if (!globalThis.process) { + globalThis.process = { getuid() { return -1; }, getgid() { return -1; }, geteuid() { return -1; }, @@ -100,41 +74,29 @@ } } - if (!global.crypto) { - const nodeCrypto = require("node:crypto"); - global.crypto = { - getRandomValues(b) { - nodeCrypto.randomFillSync(b); - }, - }; + if (!globalThis.crypto) { + throw new Error("globalThis.crypto is not available, polyfill required (crypto.getRandomValues only)"); } - if (!global.performance) { - global.performance = { - now() { - const [sec, nsec] = process.hrtime(); - return sec * 1000 + nsec / 1000000; - }, - }; + if (!globalThis.performance) { + throw new Error("globalThis.performance is not available, polyfill required (performance.now only)"); } - if (!global.TextEncoder) { - global.TextEncoder = require("node:util").TextEncoder; + if (!globalThis.TextEncoder) { + throw new Error("globalThis.TextEncoder is not available, polyfill required"); } - if (!global.TextDecoder) { - global.TextDecoder = require("node:util").TextDecoder; + if (!globalThis.TextDecoder) { + throw new Error("globalThis.TextDecoder is not available, polyfill required"); } - // End of polyfills for common API. - const encoder = new TextEncoder("utf-8"); const decoder = new TextDecoder("utf-8"); let reinterpretBuf = new DataView(new ArrayBuffer(8)); var logLine = []; const wasmExit = {}; // thrown to exit via proc_exit (not an error) - global.Go = class { + globalThis.Go = class { constructor() { this._callbackTimeouts = new Map(); this._nextCallbackTimeoutID = 1; @@ -248,13 +210,13 @@ nwritten_ptr >>>= 0; let nwritten = 0; if (fd == 1) { - for (let iovs_i=0; iovs_i { - return unboxValue(v_ref) instanceof unboxValue(t_ref); + return unboxValue(v_ref) instanceof unboxValue(t_ref); }, // func copyBytesToGo(dst []byte, src ref) (int, bool) @@ -520,7 +482,7 @@ null, true, false, - global, + globalThis, this, ]; this._goRefCounts = []; // number of references that Go has to a JS value, indexed by reference id @@ -565,7 +527,7 @@ _makeFuncWrapper(id) { const go = this; - return function () { + return function() { const event = { id: id, this: this, args: arguments }; go._pendingEvent = event; go._resume(); @@ -573,26 +535,5 @@ }; } } - - if ( - global.require && - global.require.main === module && - global.process && - global.process.versions && - !global.process.versions.electron - ) { - if (process.argv.length != 3) { - console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); - process.exit(1); - } - - const go = new Go(); - WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => { - let exitCode = await go.run(result.instance); - process.exit(exitCode); - }).catch((err) => { - console.error(err); - process.exit(1); - }); - } })(); + diff --git a/targets/wasm_exec_node.js b/targets/wasm_exec_node.js new file mode 100644 index 0000000000..4b333c581d --- /dev/null +++ b/targets/wasm_exec_node.js @@ -0,0 +1,42 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +// +// This file has been modified for use by the TinyGo compiler. +// Polyfill required to maintain compatibility with NodeJS 18 +"use strict"; + +if (process.argv.length < 3) { + console.error("usage: go_js_wasm_exec [wasm binary] [arguments]"); + process.exit(1); +} + +globalThis.require = require; +globalThis.fs = require("fs"); +globalThis.TextEncoder = require("util").TextEncoder; +globalThis.TextDecoder = require("util").TextDecoder; + +globalThis.performance = { + now() { + const [sec, nsec] = process.hrtime(); + return sec * 1000 + nsec / 1000000; + }, +}; + +const crypto = require("crypto"); +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + +require("./wasm_exec"); + +const go = new Go(); +WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then(async (result) => { + let exitCode = await go.run(result.instance); + process.exit(exitCode); +}).catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/testdata/wasmexit.js b/testdata/wasmexit.js index b41991e3a7..068e4e7f05 100644 --- a/testdata/wasmexit.js +++ b/testdata/wasmexit.js @@ -1,3 +1,13 @@ +const crypto = require('crypto'); +const fs = require('fs') + +// Polyfill required to maintain compatibility with NodeJS 18 +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + require('../targets/wasm_exec.js'); function runTests() { diff --git a/testdata/wasmexport.js b/testdata/wasmexport.js index 73a6d1e6b5..50142c4f9f 100644 --- a/testdata/wasmexport.js +++ b/testdata/wasmexport.js @@ -1,3 +1,13 @@ +const crypto = require('crypto'); +const fs = require('fs') + +// Polyfill required to maintain compatibility with NodeJS 18 +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + require('../targets/wasm_exec.js'); function runTests() { diff --git a/testdata/wasmfunc.js b/testdata/wasmfunc.js index 3b1831ee4c..a946d5be3e 100644 --- a/testdata/wasmfunc.js +++ b/testdata/wasmfunc.js @@ -1,3 +1,13 @@ +const crypto = require('crypto'); +const fs = require('fs') + +// Polyfill required to maintain compatibility with NodeJS 18 +globalThis.crypto = { + getRandomValues(b) { + crypto.randomFillSync(b); + }, +}; + require('../targets/wasm_exec.js'); var callback; From ff7e5ec17f9cd76843b2d7c970d6a95df139f9b5 Mon Sep 17 00:00:00 2001 From: darckfast <21337578+Darckfast@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:37:42 -0300 Subject: [PATCH 2/3] feat: bump minimum node version to 22 Drops official support for NodeJS 18 for WASM by removing the provided polyfills in the `wasm_exec.js`. Now the environment is expected to provide the polyfill if it is running on older NodeJS versions The new minimum required version for WASM is NodeJS 22+. --- .github/workflows/linux.yml | 2 +- GNUmakefile | 2 +- targets/wasm_exec_node.js | 9 --------- testdata/wasmexit.js | 8 -------- testdata/wasmexport.js | 8 -------- testdata/wasmfunc.js | 8 -------- 6 files changed, 2 insertions(+), 35 deletions(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index c6daaba052..fc519c07dd 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -209,7 +209,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v6 with: - node-version: '18' + node-version: '22' - name: Install wasmtime uses: bytecodealliance/actions/wasmtime/setup@v1 with: diff --git a/GNUmakefile b/GNUmakefile index ecba864f88..474db27188 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -304,7 +304,7 @@ wasi-cm: rsync -rv --delete --exclude go.mod --exclude '*_test.go' --exclude '*_json.go' --exclude '*.md' --exclude LICENSE $(shell go list -m -f {{.Dir}} $(WASM_TOOLS_MODULE)/cm)/ ./src/internal/cm # Check for Node.js used during WASM tests. -MIN_NODEJS_VERSION=18 +MIN_NODEJS_VERSION=22 .PHONY: check-nodejs-version check-nodejs-version: diff --git a/targets/wasm_exec_node.js b/targets/wasm_exec_node.js index 4b333c581d..fb45140fc1 100644 --- a/targets/wasm_exec_node.js +++ b/targets/wasm_exec_node.js @@ -13,8 +13,6 @@ if (process.argv.length < 3) { globalThis.require = require; globalThis.fs = require("fs"); -globalThis.TextEncoder = require("util").TextEncoder; -globalThis.TextDecoder = require("util").TextDecoder; globalThis.performance = { now() { @@ -23,13 +21,6 @@ globalThis.performance = { }, }; -const crypto = require("crypto"); -globalThis.crypto = { - getRandomValues(b) { - crypto.randomFillSync(b); - }, -}; - require("./wasm_exec"); const go = new Go(); diff --git a/testdata/wasmexit.js b/testdata/wasmexit.js index 068e4e7f05..83fda32131 100644 --- a/testdata/wasmexit.js +++ b/testdata/wasmexit.js @@ -1,13 +1,5 @@ -const crypto = require('crypto'); const fs = require('fs') -// Polyfill required to maintain compatibility with NodeJS 18 -globalThis.crypto = { - getRandomValues(b) { - crypto.randomFillSync(b); - }, -}; - require('../targets/wasm_exec.js'); function runTests() { diff --git a/testdata/wasmexport.js b/testdata/wasmexport.js index 50142c4f9f..0c26a0e6b1 100644 --- a/testdata/wasmexport.js +++ b/testdata/wasmexport.js @@ -1,13 +1,5 @@ -const crypto = require('crypto'); const fs = require('fs') -// Polyfill required to maintain compatibility with NodeJS 18 -globalThis.crypto = { - getRandomValues(b) { - crypto.randomFillSync(b); - }, -}; - require('../targets/wasm_exec.js'); function runTests() { diff --git a/testdata/wasmfunc.js b/testdata/wasmfunc.js index a946d5be3e..2b9321d20f 100644 --- a/testdata/wasmfunc.js +++ b/testdata/wasmfunc.js @@ -1,13 +1,5 @@ -const crypto = require('crypto'); const fs = require('fs') -// Polyfill required to maintain compatibility with NodeJS 18 -globalThis.crypto = { - getRandomValues(b) { - crypto.randomFillSync(b); - }, -}; - require('../targets/wasm_exec.js'); var callback; From 69827e34bd094d7f8532ca8491a1be917266fa04 Mon Sep 17 00:00:00 2001 From: darckfast <21337578+Darckfast@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:46:26 -0300 Subject: [PATCH 3/3] chore: removed wrong comment `wasm_exec_node.js` no longer contains the polyfill for NodeJS 18 --- targets/wasm_exec_node.js | 1 - 1 file changed, 1 deletion(-) diff --git a/targets/wasm_exec_node.js b/targets/wasm_exec_node.js index fb45140fc1..3f476ca572 100644 --- a/targets/wasm_exec_node.js +++ b/targets/wasm_exec_node.js @@ -3,7 +3,6 @@ // license that can be found in the LICENSE file. // // This file has been modified for use by the TinyGo compiler. -// Polyfill required to maintain compatibility with NodeJS 18 "use strict"; if (process.argv.length < 3) {