1
0
mirror of https://github.com/golang/go synced 2024-11-25 09:37:56 -07:00

cmd/link: support wasmexport on js/wasm

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>
This commit is contained in:
Cherry Mui 2024-08-19 15:17:04 -04:00
parent 24fd1a043d
commit f38d42f2c4
3 changed files with 58 additions and 7 deletions

View File

@ -216,10 +216,15 @@
return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len)); 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(); const timeOrigin = Date.now() - performance.now();
this.importObject = { this.importObject = {
_gotest: { _gotest: {
add: (a, b) => a + b, add: (a, b) => a + b,
callExport: testCallExport,
}, },
gojs: { gojs: {
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) // Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters)

View File

@ -446,7 +446,7 @@ func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
ctxt.Out.WriteByte(0x02) // mem export ctxt.Out.WriteByte(0x02) // mem export
writeUleb128(ctxt.Out, 0) // memidx writeUleb128(ctxt.Out, 0) // memidx
case "js": 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"} { for _, name := range []string{"run", "resume", "getsp"} {
s := ldr.Lookup("wasm_export_"+name, 0) s := ldr.Lookup("wasm_export_"+name, 0)
if s == 0 { if s == 0 {
@ -457,6 +457,12 @@ func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
ctxt.Out.WriteByte(0x00) // func export ctxt.Out.WriteByte(0x00) // func export
writeUleb128(ctxt.Out, uint64(idx)) // funcidx 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 writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
ctxt.Out.WriteByte(0x02) // mem export ctxt.Out.WriteByte(0x02) // mem export
writeUleb128(ctxt.Out, 0) // memidx writeUleb128(ctxt.Out, 0) // memidx

View 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) { func TestBool(t *testing.T) {
want := true want := true
o := dummys.Get("someBool") o := dummys.Get("someBool")
@ -613,7 +653,7 @@ func TestCallAllocations(t *testing.T) {
tmpArray := js.Global().Get("Array").New(0) tmpArray := js.Global().Get("Array").New(0)
numAllocs := testing.AllocsPerRun(100, func() { numAllocs := testing.AllocsPerRun(100, func() {
tmpArray.Call("concat", args...) tmpArray.Call("concat", args...)
}); })
if numAllocs != float64(test.expected) { if numAllocs != float64(test.expected) {
t.Errorf("got numAllocs %#v, want %#v", numAllocs, 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) concatFunc := tmpArray.Get("concat").Call("bind", tmpArray)
numAllocs := testing.AllocsPerRun(100, func() { numAllocs := testing.AllocsPerRun(100, func() {
concatFunc.Invoke(args...) concatFunc.Invoke(args...)
}); })
if numAllocs != float64(test.expected) { if numAllocs != float64(test.expected) {
t.Errorf("got numAllocs %#v, want %#v", numAllocs, 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() { numAllocs := testing.AllocsPerRun(100, func() {
arrayConstructor.New(args...) arrayConstructor.New(args...)
}); })
if numAllocs != float64(test.expected) { if numAllocs != float64(test.expected) {
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected) t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)