wasm: patch wasm_exec.js and wasm_exec_node.js from Go 1.18+ (#5483)

* 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.

* 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+.

* chore: removed wrong comment

`wasm_exec_node.js` no longer contains the polyfill for NodeJS 18
This commit is contained in:
Victor Costa
2026-07-08 07:32:00 -03:00
committed by GitHub
parent 2dbfdbae10
commit bd61913973
8 changed files with 68 additions and 89 deletions
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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 {}"
}
+27 -86
View File
@@ -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<iovs_len;iovs_i++) {
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
for (let iovs_i = 0; iovs_i < iovs_len; iovs_i++) {
let iov_ptr = iovs_ptr + iovs_i * 8; // assuming wasm32
let ptr = mem().getUint32(iov_ptr + 0, true);
let len = mem().getUint32(iov_ptr + 4, true);
nwritten += len;
for (let i=0; i<len; i++) {
let c = mem().getUint8(ptr+i);
for (let i = 0; i < len; i++) {
let c = mem().getUint8(ptr + i);
if (c == 13) { // CR
// ignore
} else if (c == 10) { // LF
@@ -316,7 +278,7 @@
} catch (e) {
if (e !== wasmExit) throw e;
}
}, Number(timeout)/1e6);
}, Number(timeout) / 1e6);
},
// func finalizeRef(v ref)
@@ -432,7 +394,7 @@
mem().setUint8(ret_addr + 8, 1);
} catch (err) {
storeValue(ret_addr, err);
mem().setUint8(ret_addr+ 8, 0);
mem().setUint8(ret_addr + 8, 0);
}
},
@@ -460,7 +422,7 @@
// func valueInstanceOf(v ref, t ref) bool
"syscall/js.valueInstanceOf": (v_ref, t_ref) => {
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);
});
}
})();
+32
View File
@@ -0,0 +1,32 @@
// 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.
"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.performance = {
now() {
const [sec, nsec] = process.hrtime();
return sec * 1000 + nsec / 1000000;
},
};
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);
});
+2
View File
@@ -1,3 +1,5 @@
const fs = require('fs')
require('../targets/wasm_exec.js');
function runTests() {
+2
View File
@@ -1,3 +1,5 @@
const fs = require('fs')
require('../targets/wasm_exec.js');
function runTests() {
+2
View File
@@ -1,3 +1,5 @@
const fs = require('fs')
require('../targets/wasm_exec.js');
var callback;