Created
July 6, 2021 05:00
-
-
Save evanphx/da7193a9301e2b16e32cb484ee5243e4 to your computer and use it in GitHub Desktop.
Patch to rebase wasm import support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js | |
index 231185a123..13030eea00 100644 | |
--- a/misc/wasm/wasm_exec.js | |
+++ b/misc/wasm/wasm_exec.js | |
@@ -256,6 +256,9 @@ | |
const timeOrigin = Date.now() - performance.now(); | |
this.importObject = { | |
+ _gotest: { | |
+ add: (a, b) => a + b, | |
+ }, | |
go: { | |
// Go's SP does not change as long as no Go code is running. Some operations (e.g. calls, getters and setters) | |
// may synchronously trigger a Go event handler. This makes Go code get executed in the middle of the imported | |
diff --git a/src/cmd/compile/internal/gc/compile.go b/src/cmd/compile/internal/gc/compile.go | |
index 00504451a8..210b84db5d 100644 | |
--- a/src/cmd/compile/internal/gc/compile.go | |
+++ b/src/cmd/compile/internal/gc/compile.go | |
@@ -42,6 +42,10 @@ func enqueueFunc(fn *ir.Func) { | |
return // we'll get this as part of its enclosing function | |
} | |
+ if ssagen.CreateWasmImportWrapper(fn) { | |
+ return | |
+ } | |
+ | |
if len(fn.Body) == 0 { | |
// Initialize ABI wrappers if necessary. | |
ssagen.InitLSym(fn, false) | |
diff --git a/src/cmd/compile/internal/ir/func.go b/src/cmd/compile/internal/ir/func.go | |
index 20fe965711..7ba44bd5c2 100644 | |
--- a/src/cmd/compile/internal/ir/func.go | |
+++ b/src/cmd/compile/internal/ir/func.go | |
@@ -6,11 +6,20 @@ package ir | |
import ( | |
"cmd/compile/internal/base" | |
+ "cmd/compile/internal/syntax" | |
"cmd/compile/internal/types" | |
"cmd/internal/obj" | |
"cmd/internal/src" | |
) | |
+type WasmInfo struct { | |
+ // Used by the //go:wasmimport directive to store info | |
+ // about a WebAssembly import. These are eventually assembled | |
+ // into a single WasmImport struct in $GOROOT/src/cmd/compile/ssagen/abi.go | |
+ Wasmimport *syntax.Wasmimport | |
+ Wasmfields *obj.FuncWasmfields | |
+} | |
+ | |
// A Func corresponds to a single function in a Go program | |
// (and vice versa: each function is denoted by exactly one *Func). | |
// | |
@@ -132,6 +141,8 @@ type Func struct { | |
// function for go:nowritebarrierrec analysis. Only filled in | |
// if nowritebarrierrecCheck != nil. | |
NWBRCalls *[]SymAndPos | |
+ | |
+ WasmInfo *WasmInfo | |
} | |
func NewFunc(pos src.XPos) *Func { | |
diff --git a/src/cmd/compile/internal/ir/sizeof_test.go b/src/cmd/compile/internal/ir/sizeof_test.go | |
index a4421fcf53..72b6320261 100644 | |
--- a/src/cmd/compile/internal/ir/sizeof_test.go | |
+++ b/src/cmd/compile/internal/ir/sizeof_test.go | |
@@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) { | |
_32bit uintptr // size on 32bit platforms | |
_64bit uintptr // size on 64bit platforms | |
}{ | |
- {Func{}, 192, 328}, | |
+ {Func{}, 196, 336}, | |
{Name{}, 112, 200}, | |
} | |
diff --git a/src/cmd/compile/internal/noder/noder.go b/src/cmd/compile/internal/noder/noder.go | |
index 5fcad096c2..df2a52314e 100644 | |
--- a/src/cmd/compile/internal/noder/noder.go | |
+++ b/src/cmd/compile/internal/noder/noder.go | |
@@ -571,12 +571,28 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { | |
f.Nname.Defn = f | |
f.Nname.Ntype = t | |
+ isWasmImport := false | |
if pragma, ok := fun.Pragma.(*pragmas); ok { | |
f.Pragma = pragma.Flag & funcPragmas | |
if pragma.Flag&ir.Systemstack != 0 && pragma.Flag&ir.Nosplit != 0 { | |
base.ErrorfAt(f.Pos(), "go:nosplit and go:systemstack cannot be combined") | |
} | |
pragma.Flag &^= funcPragmas | |
+ if pragma.Wasmimport != nil { | |
+ isWasmImport = true | |
+ // While functions annotated with //go:wasmimport are bodyless, | |
+ // the compiler generates a WebAssembly body for them. However, | |
+ // the body will never grow the Go stack. | |
+ f.Pragma |= ir.Nosplit | |
+ | |
+ wi := f.WasmInfo | |
+ if wi == nil { | |
+ wi = &ir.WasmInfo{} | |
+ f.WasmInfo = wi | |
+ } | |
+ | |
+ wi.Wasmimport = pragma.Wasmimport | |
+ } | |
p.checkUnused(pragma) | |
} | |
@@ -590,6 +606,9 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { | |
if f.Pragma&ir.Noescape != 0 { | |
base.ErrorfAt(f.Pos(), "can only use //go:noescape with external func implementations") | |
} | |
+ if isWasmImport { | |
+ base.ErrorfAt(f.Pos(), "cannot have function body with //go:wasmimport") | |
+ } | |
} else { | |
if base.Flag.Complete || strings.HasPrefix(ir.FuncName(f), "init.") { | |
// Linknamed functions are allowed to have no body. Hopefully | |
@@ -601,7 +620,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node { | |
break | |
} | |
} | |
- if !isLinknamed { | |
+ if !isLinknamed && !isWasmImport { | |
base.ErrorfAt(f.Pos(), "missing function body") | |
} | |
} | |
@@ -1540,9 +1559,10 @@ var allowedStdPragmas = map[string]bool{ | |
// *pragmas is the value stored in a syntax.pragmas during parsing. | |
type pragmas struct { | |
- Flag ir.PragmaFlag // collected bits | |
- Pos []pragmaPos // position of each individual flag | |
- Embeds []pragmaEmbed | |
+ Flag ir.PragmaFlag // collected bits | |
+ Pos []pragmaPos // position of each individual flag | |
+ Embeds []pragmaEmbed | |
+ Wasmimport *syntax.Wasmimport | |
} | |
type pragmaPos struct { | |
@@ -1606,6 +1626,20 @@ func (p *noder) pragma(pos syntax.Pos, blankLine bool, text string, old syntax.P | |
} | |
switch { | |
+ case strings.HasPrefix(text, "go:wasmimport "): | |
+ f := strings.Fields(text) | |
+ if len(f) != 3 { | |
+ p.error(syntax.Error{Pos: pos, Msg: "usage: //go:wasmimport module_name import_name"}) | |
+ } | |
+ if !base.Flag.CompilingRuntime && base.Ctxt.Pkgpath != "syscall/js" && base.Ctxt.Pkgpath != "syscall/js_test" { | |
+ p.error(syntax.Error{Pos: pos, Msg: "//go:wasmimport directive cannot be used outside of runtime or syscall/js"}) | |
+ } | |
+ module := f[1] | |
+ name := f[2] | |
+ pragma.Wasmimport = &syntax.Wasmimport{ | |
+ Module: module, | |
+ Name: name, | |
+ } | |
case strings.HasPrefix(text, "go:linkname "): | |
f := strings.Fields(text) | |
if !(2 <= len(f) && len(f) <= 3) { | |
diff --git a/src/cmd/compile/internal/ssagen/abi.go b/src/cmd/compile/internal/ssagen/abi.go | |
index e460adaf95..1235c948db 100644 | |
--- a/src/cmd/compile/internal/ssagen/abi.go | |
+++ b/src/cmd/compile/internal/ssagen/abi.go | |
@@ -14,6 +14,7 @@ import ( | |
"cmd/compile/internal/base" | |
"cmd/compile/internal/ir" | |
+ "cmd/compile/internal/objw" | |
"cmd/compile/internal/staticdata" | |
"cmd/compile/internal/typecheck" | |
"cmd/compile/internal/types" | |
@@ -224,6 +225,77 @@ func (s *SymABIs) GenABIWrappers() { | |
} | |
} | |
+// CreateWasmImportWrapper is called when considering each function during | |
+// compilation. It detects if the function is a declaration point for a | |
+// function that will be imported from the wasm runtime. | |
+func CreateWasmImportWrapper(fn *ir.Func) bool { | |
+ info := fn.WasmInfo | |
+ if info == nil { | |
+ return false | |
+ } | |
+ | |
+ wi := info.Wasmimport | |
+ if wi == nil || buildcfg.GOARCH != "wasm" { | |
+ return false | |
+ } | |
+ | |
+ // Taken from `enqueueFunc` in "gc" package | |
+ // Specifically, the `len(fn.Body) == 0` if statement | |
+ types.CalcSize(fn.Type()) | |
+ a := AbiForBodylessFuncStackMap(fn) | |
+ a.ABIAnalyze(fn.Type(), false) | |
+ | |
+ if wi.Module != "go" { | |
+ info := fn.WasmInfo | |
+ if info == nil { | |
+ info = &ir.WasmInfo{} | |
+ fn.WasmInfo = info | |
+ } | |
+ | |
+ // See `setupTextLSym` | |
+ info.Wasmfields = &obj.FuncWasmfields{ | |
+ Params: toWasmFields(fn.Type().Params().FieldSlice()), | |
+ Results: toWasmFields(fn.Type().Results().FieldSlice()), | |
+ } | |
+ } | |
+ InitLSym(fn, true) | |
+ // Go programs cannot call imported WASM functions directly. | |
+ // For each WASM import, the compiler generates a wrapper function, which | |
+ // follows the Go calling convention. All calls to the import go through the wrapper. | |
+ // Create an empty function, the body is generated in $GOROOT/src/cmd/internal/obj/wasm/wasmobj.go | |
+ pp := objw.NewProgs(fn, 0) | |
+ defer pp.Free() | |
+ pp.Text.To.Type = obj.TYPE_TEXTSIZE | |
+ pp.Text.To.Val = int32(types.Rnd(fn.Type().ArgWidth(), int64(types.RegSize))) | |
+ // Wrapper functions never need their own stack frame | |
+ pp.Text.To.Offset = 0 | |
+ pp.Flush() | |
+ return true | |
+} | |
+ | |
+func toWasmFields(fields []*types.Field) []obj.WasmField { | |
+ wfs := make([]obj.WasmField, len(fields)) | |
+ for i, f := range fields { | |
+ t := f.Type | |
+ switch { | |
+ case t.IsInteger() && t.Width == 4: | |
+ wfs[i].Type = obj.WasmI32 | |
+ case t.IsInteger() && t.Width == 8: | |
+ wfs[i].Type = obj.WasmI64 | |
+ case t.IsFloat() && t.Width == 4: | |
+ wfs[i].Type = obj.WasmF32 | |
+ case t.IsFloat() && t.Width == 8: | |
+ wfs[i].Type = obj.WasmF64 | |
+ case t.IsPtr(): | |
+ wfs[i].Type = obj.WasmPtr | |
+ default: | |
+ base.Fatalf("wasm import has bad function signature") | |
+ } | |
+ wfs[i].Offset = f.Offset | |
+ } | |
+ return wfs | |
+} | |
+ | |
// InitLSym defines f's obj.LSym and initializes it based on the | |
// properties of f. This includes setting the symbol flags and ABI and | |
// creating and initializing related DWARF symbols. | |
@@ -453,4 +525,53 @@ func setupTextLSym(f *ir.Func, flag int) { | |
} | |
base.Ctxt.InitTextSym(f.LSym, flag) | |
+ | |
+ // Handle any wasm specific bits last. This info is populated on | |
+ // funcs regardless of the target ARCH, so we just bail out early | |
+ // when the target arch isn't wasm. | |
+ if buildcfg.GOARCH != "wasm" { | |
+ return | |
+ } | |
+ | |
+ info := f.WasmInfo | |
+ if info == nil { | |
+ return | |
+ } | |
+ | |
+ wi := info.Wasmimport | |
+ if wi != nil { | |
+ return | |
+ } | |
+ | |
+ if wi.Module == "go" { | |
+ // Functions that are imported from the "go" module use a special ABI | |
+ // that just accepts the stack ptr. | |
+ // Example: | |
+ // ``` | |
+ // go:wasmimport go add | |
+ // func importedAdd(a, b uint) uint | |
+ // ``` | |
+ // will roughly become | |
+ // (import "go" "add" (func (param i32))) | |
+ f.LSym.Func().WasmImport = &obj.WasmImport{ | |
+ Module: wi.Module, | |
+ Name: wi.Name, | |
+ Params: []obj.WasmField{{Type: obj.WasmI32}}, | |
+ } | |
+ } else { | |
+ // All other imported functions use the normal WASM ABI. | |
+ // Example: | |
+ // ``` | |
+ // //go:wasmimport a_module add | |
+ // func importedAdd(a, b uint) uint | |
+ // ``` | |
+ // will roughly become | |
+ // (import "a_module" "add" (func (param i32 i32) (result i32))) | |
+ f.LSym.Func().WasmImport = &obj.WasmImport{ | |
+ Module: wi.Module, | |
+ Name: wi.Name, | |
+ Params: info.Wasmfields.Params, | |
+ Results: info.Wasmfields.Results, | |
+ } | |
+ } | |
} | |
diff --git a/src/cmd/compile/internal/syntax/syntax.go b/src/cmd/compile/internal/syntax/syntax.go | |
index f3d4c09ed5..1ee53dc98a 100644 | |
--- a/src/cmd/compile/internal/syntax/syntax.go | |
+++ b/src/cmd/compile/internal/syntax/syntax.go | |
@@ -53,6 +53,12 @@ type Pragma interface{} | |
// Blank specifies whether the line is blank before the pragma. | |
type PragmaHandler func(pos Pos, blank bool, text string, current Pragma) Pragma | |
+// Wasmimport stores metadata associated with the //go:wasmimport pragma | |
+type Wasmimport struct { | |
+ Module string | |
+ Name string | |
+} | |
+ | |
// Parse parses a single Go source file from src and returns the corresponding | |
// syntax tree. If there are errors, Parse will return the first error found, | |
// and a possibly partially constructed syntax tree, or nil. | |
diff --git a/src/cmd/internal/goobj/objfile.go b/src/cmd/internal/goobj/objfile.go | |
index e2858bd57d..9a0fabfe15 100644 | |
--- a/src/cmd/internal/goobj/objfile.go | |
+++ b/src/cmd/internal/goobj/objfile.go | |
@@ -436,6 +436,7 @@ const ( | |
AuxPcline | |
AuxPcinline | |
AuxPcdata | |
+ AuxWasmImport | |
) | |
func (a *Aux) Type() uint8 { return a[0] } | |
diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go | |
index 28626e6e03..53aaf484d2 100644 | |
--- a/src/cmd/internal/obj/link.go | |
+++ b/src/cmd/internal/obj/link.go | |
@@ -37,6 +37,7 @@ import ( | |
"cmd/internal/objabi" | |
"cmd/internal/src" | |
"cmd/internal/sys" | |
+ "encoding/binary" | |
"fmt" | |
"sync" | |
"sync/atomic" | |
@@ -487,7 +488,9 @@ type FuncInfo struct { | |
OpenCodedDeferInfo *LSym | |
ArgInfo *LSym // argument info for traceback | |
- FuncInfoSym *LSym | |
+ FuncInfoSym *LSym | |
+ WasmImportSym *LSym | |
+ WasmImport *WasmImport | |
} | |
// NewFuncInfo allocates and returns a FuncInfo for LSym. | |
@@ -537,6 +540,64 @@ func (s *LSym) File() *FileInfo { | |
return f | |
} | |
+type WasmImport struct { | |
+ Module string | |
+ Name string | |
+ Params []WasmField | |
+ Results []WasmField | |
+} | |
+ | |
+func (wi *WasmImport) WriteToSymContent(ctxt *Link, sym *LSym) { | |
+ var b [8]byte | |
+ writeByte := func(x byte) { | |
+ sym.WriteBytes(ctxt, sym.Size, []byte{x}) | |
+ } | |
+ writeUint32 := func(x uint32) { | |
+ binary.LittleEndian.PutUint32(b[:], x) | |
+ sym.WriteBytes(ctxt, sym.Size, b[:4]) | |
+ } | |
+ writeInt64 := func(x int64) { | |
+ binary.LittleEndian.PutUint64(b[:], uint64(x)) | |
+ sym.WriteBytes(ctxt, sym.Size, b[:]) | |
+ } | |
+ writeString := func(s string) { | |
+ writeUint32(uint32(len(s))) | |
+ sym.WriteString(ctxt, sym.Size, len(s), s) | |
+ } | |
+ writeString(wi.Module) | |
+ writeString(wi.Name) | |
+ writeUint32(uint32(len(wi.Params))) | |
+ for _, f := range wi.Params { | |
+ writeByte(byte(f.Type)) | |
+ writeInt64(f.Offset) | |
+ } | |
+ writeUint32(uint32(len(wi.Results))) | |
+ for _, f := range wi.Results { | |
+ writeByte(byte(f.Type)) | |
+ writeInt64(f.Offset) | |
+ } | |
+} | |
+ | |
+type FuncWasmfields struct { | |
+ Results []WasmField | |
+ Params []WasmField | |
+} | |
+ | |
+type WasmField struct { | |
+ Type WasmFieldType | |
+ Offset int64 | |
+} | |
+ | |
+type WasmFieldType byte | |
+ | |
+const ( | |
+ WasmI32 WasmFieldType = iota | |
+ WasmI64 | |
+ WasmF32 | |
+ WasmF64 | |
+ WasmPtr | |
+) | |
+ | |
type InlMark struct { | |
// When unwinding from an instruction in an inlined body, mark | |
// where we should unwind to. | |
diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go | |
index 24fb5a19de..3bdb4de592 100644 | |
--- a/src/cmd/internal/obj/objfile.go | |
+++ b/src/cmd/internal/obj/objfile.go | |
@@ -549,7 +549,12 @@ func (w *writer) Aux(s *LSym) { | |
for _, pcSym := range fn.Pcln.Pcdata { | |
w.aux1(goobj.AuxPcdata, pcSym) | |
} | |
- | |
+ if fn.WasmImportSym != nil { | |
+ if fn.WasmImportSym.Size == 0 { | |
+ panic("wasmimport aux sym must have non-zero size") | |
+ } | |
+ w.aux1(goobj.AuxWasmImport, fn.WasmImportSym) | |
+ } | |
} | |
} | |
@@ -644,6 +649,12 @@ func nAuxSym(s *LSym) int { | |
n++ | |
} | |
n += len(fn.Pcln.Pcdata) | |
+ if fn.WasmImport != nil { | |
+ if fn.WasmImportSym == nil || fn.WasmImportSym.Size == 0 { | |
+ panic("wasmimport aux sym must exist and have non-zero size") | |
+ } | |
+ n++ | |
+ } | |
} | |
return n | |
} | |
@@ -720,7 +731,7 @@ func genFuncInfoSyms(ctxt *Link) { | |
fn.FuncInfoSym = isym | |
b.Reset() | |
- dwsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym} | |
+ dwsyms := []*LSym{fn.dwarfRangesSym, fn.dwarfLocSym, fn.dwarfDebugLinesSym, fn.dwarfInfoSym, fn.WasmImportSym} | |
for _, s := range dwsyms { | |
if s == nil || s.Size == 0 { | |
continue | |
diff --git a/src/cmd/internal/obj/plist.go b/src/cmd/internal/obj/plist.go | |
index 6beb4dd94c..d788c68751 100644 | |
--- a/src/cmd/internal/obj/plist.go | |
+++ b/src/cmd/internal/obj/plist.go | |
@@ -78,7 +78,7 @@ func Flushplist(ctxt *Link, plist *Plist, newprog ProgAlloc, myimportpath string | |
// Add reference to Go arguments for assembly functions without them. | |
if ctxt.IsAsm { | |
for _, s := range text { | |
- if !strings.HasPrefix(s.Name, "\"\".") { | |
+ if !strings.HasPrefix(s.Name, "\"\".") || s.Func().WasmImport != nil { | |
continue | |
} | |
// The current args_stackmap generation in the compiler assumes | |
diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go | |
index 9e8b4dd790..78b06800be 100644 | |
--- a/src/cmd/internal/obj/sym.go | |
+++ b/src/cmd/internal/obj/sym.go | |
@@ -392,7 +392,7 @@ func (ctxt *Link) traverseFuncAux(flag traverseFlag, fsym *LSym, fn func(parent | |
} | |
} | |
- dwsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym} | |
+ dwsyms := []*LSym{fninfo.dwarfRangesSym, fninfo.dwarfLocSym, fninfo.dwarfDebugLinesSym, fninfo.dwarfInfoSym, fninfo.WasmImportSym} | |
for _, dws := range dwsyms { | |
if dws == nil || dws.Size == 0 { | |
continue | |
diff --git a/src/cmd/internal/obj/wasm/a.out.go b/src/cmd/internal/obj/wasm/a.out.go | |
index 72ecaa9286..5840ee84bd 100644 | |
--- a/src/cmd/internal/obj/wasm/a.out.go | |
+++ b/src/cmd/internal/obj/wasm/a.out.go | |
@@ -18,8 +18,7 @@ const ( | |
* wasm | |
*/ | |
const ( | |
- ACallImport = obj.ABaseWasm + obj.A_ARCHSPECIFIC + iota | |
- AGet | |
+ AGet = obj.ABaseWasm + obj.A_ARCHSPECIFIC + iota | |
ASet | |
ATee | |
ANot // alias for I32Eqz | |
diff --git a/src/cmd/internal/obj/wasm/anames.go b/src/cmd/internal/obj/wasm/anames.go | |
index 94123849ee..676552285e 100644 | |
--- a/src/cmd/internal/obj/wasm/anames.go | |
+++ b/src/cmd/internal/obj/wasm/anames.go | |
@@ -5,8 +5,7 @@ package wasm | |
import "cmd/internal/obj" | |
var Anames = []string{ | |
- obj.A_ARCHSPECIFIC: "CallImport", | |
- "Get", | |
+ obj.A_ARCHSPECIFIC: "Get", | |
"Set", | |
"Tee", | |
"Not", | |
diff --git a/src/cmd/internal/obj/wasm/wasmobj.go b/src/cmd/internal/obj/wasm/wasmobj.go | |
index ceeae7a257..d79a72c6db 100644 | |
--- a/src/cmd/internal/obj/wasm/wasmobj.go | |
+++ b/src/cmd/internal/obj/wasm/wasmobj.go | |
@@ -100,7 +100,6 @@ var unaryDst = map[obj.As]bool{ | |
ATee: true, | |
ACall: true, | |
ACallIndirect: true, | |
- ACallImport: true, | |
ABr: true, | |
ABrIf: true, | |
ABrTable: true, | |
@@ -135,7 +134,7 @@ var ( | |
const ( | |
/* mark flags */ | |
- WasmImport = 1 << 0 | |
+ IsWasmImport = 1 << 0 | |
) | |
func instinit(ctxt *obj.Link) { | |
@@ -149,6 +148,168 @@ func instinit(ctxt *obj.Link) { | |
jmpdefer = ctxt.LookupABI(`"".jmpdefer`, obj.ABI0) | |
} | |
+func setupImport(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc, wi *obj.WasmImport) { | |
+ appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog { | |
+ if p.As != obj.ANOP { | |
+ p2 := obj.Appendp(p, newprog) | |
+ p2.Pc = p.Pc | |
+ p = p2 | |
+ } | |
+ p.As = as | |
+ switch len(args) { | |
+ case 0: | |
+ p.From = obj.Addr{} | |
+ p.To = obj.Addr{} | |
+ case 1: | |
+ if unaryDst[as] { | |
+ p.From = obj.Addr{} | |
+ p.To = args[0] | |
+ } else { | |
+ p.From = args[0] | |
+ p.To = obj.Addr{} | |
+ } | |
+ case 2: | |
+ p.From = args[0] | |
+ p.To = args[1] | |
+ default: | |
+ panic("bad args") | |
+ } | |
+ return p | |
+ } | |
+ | |
+ auxSym := &obj.LSym{} | |
+ wi.WriteToSymContent(ctxt, auxSym) | |
+ s.Func().WasmImportSym = auxSym | |
+ p := s.Func().Text | |
+ if p.Link != nil { | |
+ panic("wrapper functions for WASM imports should not have a body") | |
+ } | |
+ to := obj.Addr{ | |
+ Type: obj.TYPE_MEM, | |
+ Name: obj.NAME_EXTERN, | |
+ Sym: s, | |
+ } | |
+ if wi.Module == "go" { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) | |
+ p = appendp(p, ACall, to) | |
+ p.Mark = IsWasmImport | |
+ } else { | |
+ if len(wi.Results) > 1 { | |
+ panic("invalid results type") // impossible until multi-value proposal has landed | |
+ } | |
+ if len(wi.Results) == 1 { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) // address has to be before the value | |
+ } | |
+ for _, f := range wi.Params { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) | |
+ f.Offset += 8 | |
+ switch f.Type { | |
+ case obj.WasmI32: | |
+ p = appendp(p, AI32Load, constAddr(f.Offset)) | |
+ case obj.WasmI64: | |
+ p = appendp(p, AI64Load, constAddr(f.Offset)) | |
+ case obj.WasmF32: | |
+ p = appendp(p, AF32Load, constAddr(f.Offset)) | |
+ case obj.WasmF64: | |
+ p = appendp(p, AF64Load, constAddr(f.Offset)) | |
+ case obj.WasmPtr: | |
+ p = appendp(p, AI64Load, constAddr(f.Offset)) | |
+ p = appendp(p, AI32WrapI64) | |
+ default: | |
+ panic("bad param type") | |
+ } | |
+ } | |
+ p = appendp(p, ACall, to) | |
+ p.Mark = IsWasmImport | |
+ if len(wi.Results) == 1 { | |
+ f := wi.Results[0] | |
+ f.Offset += 8 | |
+ switch f.Type { | |
+ case obj.WasmI32: | |
+ p = appendp(p, AI32Store, constAddr(f.Offset)) | |
+ case obj.WasmI64: | |
+ p = appendp(p, AI64Store, constAddr(f.Offset)) | |
+ case obj.WasmF32: | |
+ p = appendp(p, AF32Store, constAddr(f.Offset)) | |
+ case obj.WasmF64: | |
+ p = appendp(p, AF64Store, constAddr(f.Offset)) | |
+ case obj.WasmPtr: | |
+ p = appendp(p, AI64ExtendI32U) | |
+ p = appendp(p, AI64Store, constAddr(f.Offset)) | |
+ default: | |
+ panic("bad result type") | |
+ } | |
+ } | |
+ } | |
+ p = appendp(p, obj.ARET) | |
+ auxSym = &obj.LSym{} | |
+ wi.WriteToSymContent(ctxt, auxSym) | |
+ s.Func().WasmImportSym = auxSym | |
+ p = s.Func().Text | |
+ if p.Link != nil { | |
+ panic("wrapper functions for WASM imports should not have a body") | |
+ } | |
+ to = obj.Addr{ | |
+ Type: obj.TYPE_MEM, | |
+ Name: obj.NAME_EXTERN, | |
+ Sym: s, | |
+ } | |
+ if wi.Module == "go" { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) | |
+ p = appendp(p, ACall, to) | |
+ p.Mark = IsWasmImport | |
+ } else { | |
+ if len(wi.Results) > 1 { | |
+ panic("invalid results type") // impossible until multi-value proposal has landed | |
+ } | |
+ if len(wi.Results) == 1 { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) // address has to be before the value | |
+ } | |
+ for _, f := range wi.Params { | |
+ p = appendp(p, AGet, regAddr(REG_SP)) | |
+ f.Offset += 8 | |
+ switch f.Type { | |
+ case obj.WasmI32: | |
+ p = appendp(p, AI32Load, constAddr(f.Offset)) | |
+ case obj.WasmI64: | |
+ p = appendp(p, AI64Load, constAddr(f.Offset)) | |
+ case obj.WasmF32: | |
+ p = appendp(p, AF32Load, constAddr(f.Offset)) | |
+ case obj.WasmF64: | |
+ p = appendp(p, AF64Load, constAddr(f.Offset)) | |
+ case obj.WasmPtr: | |
+ p = appendp(p, AI64Load, constAddr(f.Offset)) | |
+ p = appendp(p, AI32WrapI64) | |
+ default: | |
+ panic("bad param type") | |
+ } | |
+ } | |
+ p = appendp(p, ACall, to) | |
+ p.Mark = IsWasmImport | |
+ if len(wi.Results) == 1 { | |
+ f := wi.Results[0] | |
+ f.Offset += 8 | |
+ switch f.Type { | |
+ case obj.WasmI32: | |
+ p = appendp(p, AI32Store, constAddr(f.Offset)) | |
+ case obj.WasmI64: | |
+ p = appendp(p, AI64Store, constAddr(f.Offset)) | |
+ case obj.WasmF32: | |
+ p = appendp(p, AF32Store, constAddr(f.Offset)) | |
+ case obj.WasmF64: | |
+ p = appendp(p, AF64Store, constAddr(f.Offset)) | |
+ case obj.WasmPtr: | |
+ p = appendp(p, AI64ExtendI32U) | |
+ p = appendp(p, AI64Store, constAddr(f.Offset)) | |
+ default: | |
+ panic("bad result type") | |
+ } | |
+ } | |
+ } | |
+ p = appendp(p, obj.ARET) | |
+ | |
+} | |
+ | |
func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { | |
appendp := func(p *obj.Prog, as obj.As, args ...obj.Addr) *obj.Prog { | |
if p.As != obj.ANOP { | |
@@ -185,6 +346,11 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { | |
s.Func().Args = s.Func().Text.To.Val.(int32) | |
s.Func().Locals = int32(framesize) | |
+ wi := s.Func().WasmImport | |
+ if wi != nil { | |
+ setupImport(ctxt, s, newprog, wi) | |
+ } | |
+ | |
if s.Func().Text.From.Sym.Wrapper() { | |
// if g._panic != nil && g._panic.argp == FP { | |
// g._panic.argp = bottom-of-frame | |
@@ -698,12 +864,6 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { | |
default: | |
panic("bad MOV type") | |
} | |
- | |
- case ACallImport: | |
- p.As = obj.ANOP | |
- p = appendp(p, AGet, regAddr(REG_SP)) | |
- p = appendp(p, ACall, obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: s}) | |
- p.Mark = WasmImport | |
} | |
} | |
@@ -1006,7 +1166,7 @@ func assemble(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) { | |
r.Siz = 1 // actually variable sized | |
r.Off = int32(w.Len()) | |
r.Type = objabi.R_CALL | |
- if p.Mark&WasmImport != 0 { | |
+ if p.Mark&IsWasmImport != 0 { | |
r.Type = objabi.R_WASMIMPORT | |
} | |
r.Sym = p.To.Sym | |
diff --git a/src/cmd/link/internal/loader/loader.go b/src/cmd/link/internal/loader/loader.go | |
index efca824d98..ae8482ad47 100644 | |
--- a/src/cmd/link/internal/loader/loader.go | |
+++ b/src/cmd/link/internal/loader/loader.go | |
@@ -1640,6 +1640,27 @@ func (l *Loader) Aux(i Sym, j int) Aux { | |
return Aux{r.Aux(li, j), r, l} | |
} | |
+// GetFuncWasmImportSym collects and returns the auxilary WebAssembly import | |
+// symbol associated with a given function symbol. The aux sym only exists for | |
+// Go function stubs that have been annotated with the //go:wasmimport directive. | |
+// The aux sym contains the information necessary for the linker to add a WebAssembly | |
+// import statement. (https://webassembly.github.io/spec/core/syntax/modules.html#imports) | |
+func (l *Loader) GetFuncWasmImportSym(fnSymIdx Sym) Sym { | |
+ if l.SymType(fnSymIdx) != sym.STEXT { | |
+ log.Fatalf("error: non-function sym %d/%s t=%s passed to GetFuncWasmImportSym", fnSymIdx, l.SymName(fnSymIdx), l.SymType(fnSymIdx).String()) | |
+ } | |
+ r, li := l.toLocal(fnSymIdx) | |
+ auxs := r.Auxs(li) | |
+ for i := range auxs { | |
+ a := &auxs[i] | |
+ switch a.Type() { | |
+ case goobj.AuxWasmImport: | |
+ return l.resolve(r, a.Sym()) | |
+ } | |
+ } | |
+ panic("GetFuncWasmImportSym called for func without aux Wasm import sym") | |
+} | |
+ | |
// GetFuncDwarfAuxSyms collects and returns the auxiliary DWARF | |
// symbols associated with a given function symbol. Prior to the | |
// introduction of the loader, this was done purely using name | |
diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go | |
index 5bdfdbaee6..612deaa722 100644 | |
--- a/src/cmd/link/internal/wasm/asm.go | |
+++ b/src/cmd/link/internal/wasm/asm.go | |
@@ -6,10 +6,12 @@ package wasm | |
import ( | |
"bytes" | |
+ "cmd/internal/obj" | |
"cmd/internal/objabi" | |
"cmd/link/internal/ld" | |
"cmd/link/internal/loader" | |
"cmd/link/internal/sym" | |
+ "encoding/binary" | |
"internal/buildcfg" | |
"io" | |
"regexp" | |
@@ -44,9 +46,10 @@ func gentext(ctxt *ld.Link, ldr *loader.Loader) { | |
} | |
type wasmFunc struct { | |
- Name string | |
- Type uint32 | |
- Code []byte | |
+ Module string | |
+ Name string | |
+ Type uint32 | |
+ Code []byte | |
} | |
type wasmFuncType struct { | |
@@ -54,6 +57,44 @@ type wasmFuncType struct { | |
Results []byte | |
} | |
+func readWasmImport(b []byte) obj.WasmImport { | |
+ readUint32 := func() uint32 { | |
+ x := binary.LittleEndian.Uint32(b) | |
+ b = b[4:] | |
+ return x | |
+ } | |
+ readInt64 := func() int64 { | |
+ val := binary.LittleEndian.Uint64(b) | |
+ b = b[8:] | |
+ return int64(val) | |
+ } | |
+ readByte := func() byte { | |
+ byte_ := b[0] | |
+ b = b[1:] | |
+ return byte_ | |
+ } | |
+ readString := func() string { | |
+ len_ := readUint32() | |
+ buf := b[:len_] | |
+ b = b[len_:] | |
+ return string(buf) | |
+ } | |
+ wi := obj.WasmImport{} | |
+ wi.Module = readString() | |
+ wi.Name = readString() | |
+ wi.Params = make([]obj.WasmField, readUint32()) | |
+ for i := range wi.Params { | |
+ wi.Params[i].Type = obj.WasmFieldType(readByte()) | |
+ wi.Params[i].Offset = readInt64() | |
+ } | |
+ wi.Results = make([]obj.WasmField, readUint32()) | |
+ for i := range wi.Results { | |
+ wi.Results[i].Type = obj.WasmFieldType(readByte()) | |
+ wi.Results[i].Offset = readInt64() | |
+ } | |
+ return wi | |
+} | |
+ | |
var wasmFuncTypes = map[string]*wasmFuncType{ | |
"_rt0_wasm_js": {Params: []byte{}}, // | |
"wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv | |
@@ -130,22 +171,27 @@ func asmb2(ctxt *ld.Link, ldr *loader.Loader) { | |
} | |
// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript) | |
- hostImports := []*wasmFunc{ | |
- { | |
- Name: "debug", | |
- Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), | |
- }, | |
- } | |
+ // we store the import index of each imported function, so the R_WASMIMPORT relocation | |
+ // can write the correct index after a `call` instruction | |
hostImportMap := make(map[loader.Sym]int64) | |
+ // these are added as import statements to the top of the WebAssembly binary | |
+ var hostImports []*wasmFunc | |
+ | |
for _, fn := range ctxt.Textp { | |
relocs := ldr.Relocs(fn) | |
for ri := 0; ri < relocs.Count(); ri++ { | |
r := relocs.At(ri) | |
if r.Type() == objabi.R_WASMIMPORT { | |
- hostImportMap[r.Sym()] = int64(len(hostImports)) | |
+ wasmImportSym := ldr.GetFuncWasmImportSym(fn) | |
+ wi := readWasmImport(ldr.Data(wasmImportSym)) | |
+ hostImportMap[fn] = int64(len(hostImports)) | |
hostImports = append(hostImports, &wasmFunc{ | |
- Name: ldr.SymName(r.Sym()), | |
- Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), | |
+ Module: wi.Module, | |
+ Name: wi.Name, | |
+ Type: lookupType(&wasmFuncType{ | |
+ Params: fieldsToTypes(wi.Params), | |
+ Results: fieldsToTypes(wi.Results), | |
+ }, &types), | |
}) | |
} | |
} | |
@@ -282,7 +328,7 @@ func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) { | |
writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports | |
for _, fn := range hostImports { | |
- writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js | |
+ writeName(ctxt.Out, fn.Module) | |
writeName(ctxt.Out, fn.Name) | |
ctxt.Out.WriteByte(0x00) // func import | |
writeUleb128(ctxt.Out, uint64(fn.Type)) | |
@@ -604,3 +650,20 @@ func writeSleb128(w io.ByteWriter, v int64) { | |
w.WriteByte(c) | |
} | |
} | |
+ | |
+func fieldsToTypes(fields []obj.WasmField) []byte { | |
+ b := make([]byte, len(fields)) | |
+ for i, f := range fields { | |
+ switch f.Type { | |
+ case obj.WasmI32, obj.WasmPtr: | |
+ b[i] = I32 | |
+ case obj.WasmI64: | |
+ b[i] = I64 | |
+ case obj.WasmF32: | |
+ b[i] = F32 | |
+ case obj.WasmF64: | |
+ b[i] = F64 | |
+ } | |
+ } | |
+ return b | |
+} | |
diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go | |
index 0ca3512baf..b2bcebcdb0 100644 | |
--- a/src/runtime/lock_js.go | |
+++ b/src/runtime/lock_js.go | |
@@ -7,10 +7,6 @@ | |
package runtime | |
-import ( | |
- _ "unsafe" | |
-) | |
- | |
// js/wasm has no support for threads yet. There is no preemption. | |
const ( | |
@@ -225,9 +221,11 @@ func pause(newsp uintptr) | |
// scheduleTimeoutEvent tells the WebAssembly environment to trigger an event after ms milliseconds. | |
// It returns a timer id that can be used with clearTimeoutEvent. | |
+//go:wasmimport go runtime.scheduleTimeoutEvent | |
func scheduleTimeoutEvent(ms int64) int32 | |
// clearTimeoutEvent clears a timeout event scheduled by scheduleTimeoutEvent. | |
+//go:wasmimport go runtime.clearTimeoutEvent | |
func clearTimeoutEvent(id int32) | |
// handleEvent gets invoked on a call from JavaScript into Go. It calls the event handler of the syscall/js package | |
@@ -258,7 +256,6 @@ func handleEvent() { | |
var eventHandler func() | |
-//go:linkname setEventHandler syscall/js.setEventHandler | |
func setEventHandler(fn func()) { | |
eventHandler = fn | |
} | |
diff --git a/src/runtime/mem_js.go b/src/runtime/mem_js.go | |
index fe940360c0..e3cccf50f6 100644 | |
--- a/src/runtime/mem_js.go | |
+++ b/src/runtime/mem_js.go | |
@@ -79,6 +79,7 @@ func growMemory(pages int32) int32 | |
// resetMemoryDataView signals the JS front-end that WebAssembly's memory.grow instruction has been used. | |
// This allows the front-end to replace the old DataView object with a new one. | |
+//go:wasmimport go runtime.resetMemoryDataView | |
func resetMemoryDataView() | |
func sysMap(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) { | |
diff --git a/src/runtime/os_js.go b/src/runtime/os_js.go | |
index 52b64e7602..bbc372a936 100644 | |
--- a/src/runtime/os_js.go | |
+++ b/src/runtime/os_js.go | |
@@ -27,6 +27,7 @@ func closefd(fd int32) int32 { panic("not implemented") | |
func read(fd int32, p unsafe.Pointer, n int32) int32 { panic("not implemented") } | |
//go:noescape | |
+//go:wasmimport go runtime.wasmWrite | |
func wasmWrite(fd uintptr, p unsafe.Pointer, n int32) | |
func usleep(usec uint32) | |
@@ -117,6 +118,7 @@ func crash() { | |
*(*int32)(nil) = 0 | |
} | |
+//go:wasmimport go runtime.getRandomData | |
func getRandomData(r []byte) | |
func goenvs() { | |
diff --git a/src/runtime/stubs3.go b/src/runtime/stubs3.go | |
index b895be4c70..3f1253a09b 100644 | |
--- a/src/runtime/stubs3.go | |
+++ b/src/runtime/stubs3.go | |
@@ -7,4 +7,5 @@ | |
package runtime | |
+//go:wasmimport go runtime.nanotime1 | |
func nanotime1() int64 | |
diff --git a/src/runtime/sys_wasm.go b/src/runtime/sys_wasm.go | |
index 057ed4ccd9..bd7f4e8a9a 100644 | |
--- a/src/runtime/sys_wasm.go | |
+++ b/src/runtime/sys_wasm.go | |
@@ -24,6 +24,7 @@ func wasmDiv() | |
func wasmTruncS() | |
func wasmTruncU() | |
+//go:wasmimport go runtime.wasmExit | |
func wasmExit(code int32) | |
// adjust Gobuf as it if executed a call to fn with context ctxt | |
diff --git a/src/runtime/timestub2.go b/src/runtime/timestub2.go | |
index 800a2a94e0..4836c71321 100644 | |
--- a/src/runtime/timestub2.go | |
+++ b/src/runtime/timestub2.go | |
@@ -13,4 +13,5 @@ | |
package runtime | |
+//go:wasmimport go runtime.walltime | |
func walltime() (sec int64, nsec int32) | |
diff --git a/src/syscall/js/func.go b/src/syscall/js/func.go | |
index da4cf68774..810aae48c6 100644 | |
--- a/src/syscall/js/func.go | |
+++ b/src/syscall/js/func.go | |
@@ -6,7 +6,10 @@ | |
package js | |
-import "sync" | |
+import ( | |
+ "sync" | |
+ _ "unsafe" | |
+) | |
var ( | |
funcsMu sync.Mutex | |
@@ -62,6 +65,7 @@ func (c Func) Release() { | |
} | |
// setEventHandler is defined in the runtime package. | |
+//go:linkname setEventHandler runtime.setEventHandler | |
func setEventHandler(fn func()) | |
func init() { | |
diff --git a/src/syscall/js/js.go b/src/syscall/js/js.go | |
index a48bbd4dd7..3744424a86 100644 | |
--- a/src/syscall/js/js.go | |
+++ b/src/syscall/js/js.go | |
@@ -69,6 +69,7 @@ func makeValue(r ref) Value { | |
return Value{ref: r, gcPtr: gcPtr} | |
} | |
+//go:wasmimport go syscall/js.finalizeRef | |
func finalizeRef(r ref) | |
func predefValue(id uint32, typeFlag byte) Value { | |
@@ -220,6 +221,8 @@ func ValueOf(x interface{}) Value { | |
} | |
} | |
+//go:wasmimport go syscall/js.stringVal | |
+//go:noescape | |
func stringVal(x string) ref | |
// Type represents the JavaScript type of a Value. | |
@@ -303,6 +306,8 @@ func (v Value) Get(p string) Value { | |
return r | |
} | |
+//go:wasmimport go syscall/js.valueGet | |
+//go:noescape | |
func valueGet(v ref, p string) ref | |
// Set sets the JavaScript property p of value v to ValueOf(x). | |
@@ -317,6 +322,8 @@ func (v Value) Set(p string, x interface{}) { | |
runtime.KeepAlive(xv) | |
} | |
+//go:wasmimport go syscall/js.valueSet | |
+//go:noescape | |
func valueSet(v ref, p string, x ref) | |
// Delete deletes the JavaScript property p of value v. | |
@@ -329,6 +336,8 @@ func (v Value) Delete(p string) { | |
runtime.KeepAlive(v) | |
} | |
+//go:wasmimport go syscall/js.valueDelete | |
+//go:noescape | |
func valueDelete(v ref, p string) | |
// Index returns JavaScript index i of value v. | |
@@ -342,6 +351,7 @@ func (v Value) Index(i int) Value { | |
return r | |
} | |
+//go:wasmimport go syscall/js.valueIndex | |
func valueIndex(v ref, i int) ref | |
// SetIndex sets the JavaScript index i of value v to ValueOf(x). | |
@@ -356,6 +366,7 @@ func (v Value) SetIndex(i int, x interface{}) { | |
runtime.KeepAlive(xv) | |
} | |
+//go:wasmimport go syscall/js.valueSetIndex | |
func valueSetIndex(v ref, i int, x ref) | |
func makeArgs(args []interface{}) ([]Value, []ref) { | |
@@ -380,6 +391,7 @@ func (v Value) Length() int { | |
return r | |
} | |
+//go:wasmimport go syscall/js.valueLength | |
func valueLength(v ref) int | |
// Call does a JavaScript call to the method m of value v with the given arguments. | |
@@ -402,6 +414,8 @@ func (v Value) Call(m string, args ...interface{}) Value { | |
return makeValue(res) | |
} | |
+//go:wasmimport go syscall/js.valueCall | |
+//go:noescape | |
func valueCall(v ref, m string, args []ref) (ref, bool) | |
// Invoke does a JavaScript call of the value v with the given arguments. | |
@@ -421,6 +435,8 @@ func (v Value) Invoke(args ...interface{}) Value { | |
return makeValue(res) | |
} | |
+//go:wasmimport go syscall/js.valueInvoke | |
+//go:noescape | |
func valueInvoke(v ref, args []ref) (ref, bool) | |
// New uses JavaScript's "new" operator with value v as constructor and the given arguments. | |
@@ -440,6 +456,8 @@ func (v Value) New(args ...interface{}) Value { | |
return makeValue(res) | |
} | |
+//go:wasmimport go syscall/js.valueNew | |
+//go:noescape | |
func valueNew(v ref, args []ref) (ref, bool) | |
func (v Value) isNumber() bool { | |
@@ -539,8 +557,11 @@ func jsString(v Value) string { | |
return string(b) | |
} | |
+//go:wasmimport go syscall/js.valuePrepareString | |
func valuePrepareString(v ref) (ref, int) | |
+//go:wasmimport go syscall/js.valueLoadString | |
+//go:noescape | |
func valueLoadString(v ref, b []byte) | |
// InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator. | |
@@ -551,6 +572,7 @@ func (v Value) InstanceOf(t Value) bool { | |
return r | |
} | |
+//go:wasmimport go syscall/js.valueInstanceOf | |
func valueInstanceOf(v ref, t ref) bool | |
// A ValueError occurs when a Value method is invoked on | |
@@ -577,6 +599,8 @@ func CopyBytesToGo(dst []byte, src Value) int { | |
return n | |
} | |
+//go:wasmimport go syscall/js.copyBytesToGo | |
+//go:noescape | |
func copyBytesToGo(dst []byte, src ref) (int, bool) | |
// CopyBytesToJS copies bytes from src to dst. | |
@@ -591,4 +615,6 @@ func CopyBytesToJS(dst Value, src []byte) int { | |
return n | |
} | |
+//go:wasmimport go syscall/js.copyBytesToJS | |
+//go:noescape | |
func copyBytesToJS(dst ref, src []byte) (int, bool) | |
diff --git a/src/syscall/js/js_js.s b/src/syscall/js/js_js.s | |
deleted file mode 100644 | |
index 47ad6b83e5..0000000000 | |
--- a/src/syscall/js/js_js.s | |
+++ /dev/null | |
@@ -1,69 +0,0 @@ | |
-// Copyright 2018 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. | |
- | |
-#include "textflag.h" | |
- | |
-TEXT ·finalizeRef(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·stringVal(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueGet(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueSet(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueDelete(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueIndex(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueSetIndex(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueCall(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueInvoke(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueNew(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueLength(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valuePrepareString(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueLoadString(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·valueInstanceOf(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·copyBytesToGo(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
- | |
-TEXT ·copyBytesToJS(SB), NOSPLIT, $0 | |
- CallImport | |
- RET | |
diff --git a/src/syscall/js/js_test.go b/src/syscall/js/js_test.go | |
index 5fc9107d40..b651751138 100644 | |
--- a/src/syscall/js/js_test.go | |
+++ b/src/syscall/js/js_test.go | |
@@ -44,6 +44,19 @@ var dummys = js.Global().Call("eval", `({ | |
objBooleanFalse: new Boolean(false), | |
})`) | |
+//go:wasmimport _gotest add | |
+func testAdd(a, b uint32) uint32 | |
+ | |
+func TestWasmImport(t *testing.T) { | |
+ a := uint32(3) | |
+ b := uint32(5) | |
+ expected := a + b | |
+ actual := testAdd(a, b) | |
+ if actual != expected { | |
+ t.Errorf("Expected %v, got: %v", expected, actual) | |
+ } | |
+} | |
+ | |
func TestBool(t *testing.T) { | |
want := true | |
o := dummys.Get("someBool") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment