]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: support wasmexport on js/wasm
authorCherry Mui <cherryyz@google.com>
Mon, 19 Aug 2024 19:17:04 +0000 (15:17 -0400)
committerCherry Mui <cherryyz@google.com>
Wed, 21 Aug 2024 01:11:27 +0000 (01:11 +0000)
Add export functions to the wasm module on GOOS=js. (Other parts
work the same way as wasip1.)

Add a test.

Fixes #65199.

Change-Id: Ia22580859fe40631d487f70ee293c12867e0c988
Reviewed-on: https://go-review.googlesource.com/c/go/+/606855
Reviewed-by: Zxilly Chou <zxilly@outlook.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
Reviewed-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
misc/wasm/wasm_exec.js
src/cmd/link/internal/wasm/asm.go
src/syscall/js/js_test.go

index 0f635d6d540717a4ddeb12e55533cb7c3084812d..af7e28f5f48e07f58ab71ba5fbe4cf8575e89dfd 100644 (file)
                                return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
                        }
 
+                       const testCallExport = (a, b) => {
+                               return this._inst.exports.testExport(a, b);
+                       }
+
                        const timeOrigin = Date.now() - performance.now();
                        this.importObject = {
                                _gotest: {
                                        add: (a, b) => a + b,
+                                       callExport: testCallExport,
                                },
                                gojs: {
                                        // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)
index 87a67754ccb3a6d2e72afacdffa754b620ee72fd..2a4c1ee7ea78724f58daf2f969f7e63ff2369383 100644 (file)
@@ -446,7 +446,7 @@ func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
                ctxt.Out.WriteByte(0x02)      // mem export
                writeUleb128(ctxt.Out, 0)     // memidx
        case "js":
-               writeUleb128(ctxt.Out, 4) // number of exports
+               writeUleb128(ctxt.Out, uint64(4+len(ldr.WasmExports))) // number of exports
                for _, name := range []string{"run", "resume", "getsp"} {
                        s := ldr.Lookup("wasm_export_"+name, 0)
                        if s == 0 {
@@ -457,6 +457,12 @@ func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
                        ctxt.Out.WriteByte(0x00)            // func export
                        writeUleb128(ctxt.Out, uint64(idx)) // funcidx
                }
+               for _, s := range ldr.WasmExports {
+                       idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
+                       writeName(ctxt.Out, ldr.SymName(s))
+                       ctxt.Out.WriteByte(0x00)            // func export
+                       writeUleb128(ctxt.Out, uint64(idx)) // funcidx
+               }
                writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
                ctxt.Out.WriteByte(0x02)   // mem export
                writeUleb128(ctxt.Out, 0)  // memidx
index cec5f28a08242a22555c4b6603d24a1801da29df..d6bcc6370d4e7b0f98b4cafe6dd3cbd2cb1ef671 100644 (file)
@@ -56,6 +56,46 @@ func TestWasmImport(t *testing.T) {
        }
 }
 
+// testCallExport is imported from host (wasm_exec.js), which calls testExport.
+//
+//go:wasmimport _gotest callExport
+func testCallExport(a int32, b int64) int64
+
+//go:wasmexport testExport
+func testExport(a int32, b int64) int64 {
+       testExportCalled = true
+       // test stack growth
+       growStack(1000)
+       // force a goroutine switch
+       ch := make(chan int64)
+       go func() {
+               ch <- int64(a)
+               ch <- b
+       }()
+       return <-ch + <-ch
+}
+
+var testExportCalled bool
+
+func growStack(n int64) {
+       if n > 0 {
+               growStack(n - 1)
+       }
+}
+
+func TestWasmExport(t *testing.T) {
+       testExportCalled = false
+       a := int32(123)
+       b := int64(456)
+       want := int64(a) + b
+       if got := testCallExport(a, b); got != want {
+               t.Errorf("got %v, want %v", got, want)
+       }
+       if !testExportCalled {
+               t.Error("testExport not called")
+       }
+}
+
 func TestBool(t *testing.T) {
        want := true
        o := dummys.Get("someBool")
@@ -587,13 +627,13 @@ func TestGarbageCollection(t *testing.T) {
 // Note: All JavaScript functions return a JavaScript array, which will cause
 // one allocation to be created to track the Value.gcPtr for the Value finalizer.
 var allocTests = []struct {
-       argLen  int // The number of arguments to use for the syscall
+       argLen   int // The number of arguments to use for the syscall
        expected int // The expected number of allocations
 }{
        // For less than or equal to 16 arguments, we expect 1 allocation:
        // - makeValue new(ref)
-       {0,  1},
-       {2,  1},
+       {0, 1},
+       {2, 1},
        {15, 1},
        {16, 1},
        // For greater than 16 arguments, we expect 3 allocation:
@@ -613,7 +653,7 @@ func TestCallAllocations(t *testing.T) {
                tmpArray := js.Global().Get("Array").New(0)
                numAllocs := testing.AllocsPerRun(100, func() {
                        tmpArray.Call("concat", args...)
-               });
+               })
 
                if numAllocs != float64(test.expected) {
                        t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
@@ -630,7 +670,7 @@ func TestInvokeAllocations(t *testing.T) {
                concatFunc := tmpArray.Get("concat").Call("bind", tmpArray)
                numAllocs := testing.AllocsPerRun(100, func() {
                        concatFunc.Invoke(args...)
-               });
+               })
 
                if numAllocs != float64(test.expected) {
                        t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
@@ -647,7 +687,7 @@ func TestNewAllocations(t *testing.T) {
 
                numAllocs := testing.AllocsPerRun(100, func() {
                        arrayConstructor.New(args...)
-               });
+               })
 
                if numAllocs != float64(test.expected) {
                        t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)