aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--main_test.go40
-rw-r--r--src/runtime/runtime_wasm_js.go21
-rw-r--r--src/runtime/runtime_wasmentry.go2
-rw-r--r--targets/wasm_exec.js19
-rw-r--r--testdata/wasmexport.js40
5 files changed, 94 insertions, 28 deletions
diff --git a/main_test.go b/main_test.go
index c471f7620..95fad9442 100644
--- a/main_test.go
+++ b/main_test.go
@@ -690,6 +690,46 @@ func TestWasmExport(t *testing.T) {
}
}
+// Test //go:wasmexport in JavaScript (using NodeJS).
+func TestWasmExportJS(t *testing.T) {
+ type testCase struct {
+ name string
+ buildMode string
+ }
+
+ tests := []testCase{
+ {name: "default"},
+ {name: "c-shared", buildMode: "c-shared"},
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ // Build the wasm binary.
+ tmpdir := t.TempDir()
+ options := optionsFromTarget("wasm", sema)
+ options.BuildMode = tc.buildMode
+ buildConfig, err := builder.NewConfig(&options)
+ if err != nil {
+ t.Fatal(err)
+ }
+ result, err := builder.Build("testdata/wasmexport-noscheduler.go", ".wasm", tmpdir, buildConfig)
+ if err != nil {
+ t.Fatal("failed to build binary:", err)
+ }
+
+ // Test the resulting binary using NodeJS.
+ output := &bytes.Buffer{}
+ cmd := exec.Command("node", "testdata/wasmexport.js", result.Binary, buildConfig.BuildMode())
+ cmd.Stdout = output
+ cmd.Stderr = output
+ err = cmd.Run()
+ if err != nil {
+ t.Error("failed to run node:", err)
+ }
+ checkOutput(t, "testdata/wasmexport.txt", output.Bytes())
+ })
+ }
+}
+
// Check whether the output of a test equals the expected output.
func checkOutput(t *testing.T, filename string, actual []byte) {
expectedOutput, err := os.ReadFile(filename)
diff --git a/src/runtime/runtime_wasm_js.go b/src/runtime/runtime_wasm_js.go
index 0b1aa5bc4..89898b554 100644
--- a/src/runtime/runtime_wasm_js.go
+++ b/src/runtime/runtime_wasm_js.go
@@ -2,26 +2,15 @@
package runtime
-import "unsafe"
-
type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
// wasmNested is used to detect scheduler nesting (WASM calls into JS calls back into WASM).
// When this happens, we need to use a reduced version of the scheduler.
+//
+// TODO: this variable can probably be removed once //go:wasmexport is the only
+// allowed way to export a wasm function (currently, //export also works).
var wasmNested bool
-//export _start
-func _start() {
- // These need to be initialized early so that the heap can be initialized.
- heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
- heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
-
- wasmNested = true
- run()
- __stdio_exit()
- wasmNested = false
-}
-
var handleEvent func()
//go:linkname setEventHandler syscall/js.setEventHandler
@@ -50,3 +39,7 @@ func sleepTicks(d timeUnit)
//go:wasmimport gojs runtime.ticks
func ticks() timeUnit
+
+func beforeExit() {
+ __stdio_exit()
+}
diff --git a/src/runtime/runtime_wasmentry.go b/src/runtime/runtime_wasmentry.go
index 7bb1e1b44..ff7b0c119 100644
--- a/src/runtime/runtime_wasmentry.go
+++ b/src/runtime/runtime_wasmentry.go
@@ -1,4 +1,4 @@
-//go:build tinygo.wasm && !js
+//go:build tinygo.wasm
package runtime
diff --git a/targets/wasm_exec.js b/targets/wasm_exec.js
index 690245440..c430cc2b2 100644
--- a/targets/wasm_exec.js
+++ b/targets/wasm_exec.js
@@ -466,20 +466,13 @@
this._idPool = []; // unused ids that have been garbage collected
this.exited = false; // whether the Go program has exited
- while (true) {
- const callbackPromise = new Promise((resolve) => {
- this._resolveCallbackPromise = () => {
- if (this.exited) {
- throw new Error("bad callback: Go program has already exited");
- }
- setTimeout(resolve, 0); // make sure it is asynchronous
- };
- });
+ if (this._inst.exports._start) {
this._inst.exports._start();
- if (this.exited) {
- break;
- }
- await callbackPromise;
+
+ // TODO: wait until the program exists.
+ await new Promise(() => {});
+ } else {
+ this._inst.exports._initialize();
}
}
diff --git a/testdata/wasmexport.js b/testdata/wasmexport.js
new file mode 100644
index 000000000..c4a065125
--- /dev/null
+++ b/testdata/wasmexport.js
@@ -0,0 +1,40 @@
+require('../targets/wasm_exec.js');
+
+function runTests() {
+ let testCall = (name, params, expected) => {
+ let result = go._inst.exports[name].apply(null, params);
+ if (result !== expected) {
+ console.error(`${name}(...${params}): expected result ${expected}, got ${result}`);
+ }
+ }
+
+ // These are the same tests as in TestWasmExport.
+ testCall('hello', [], undefined);
+ testCall('add', [3, 5], 8);
+ testCall('add', [7, 9], 16);
+ testCall('add', [6, 1], 7);
+ testCall('reentrantCall', [2, 3], 5);
+ testCall('reentrantCall', [1, 8], 9);
+}
+
+let go = new Go();
+go.importObject.tester = {
+ callOutside: (a, b) => {
+ return go._inst.exports.add(a, b);
+ },
+ callTestMain: () => {
+ runTests();
+ },
+};
+WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
+ let buildMode = process.argv[3];
+ if (buildMode === 'default') {
+ go.run(result.instance);
+ } else if (buildMode === 'c-shared') {
+ go.run(result.instance);
+ runTests();
+ }
+}).catch((err) => {
+ console.error(err);
+ process.exit(1);
+});