From 31be35012e2ae697ed886b2d89b79720af35b823 Mon Sep 17 00:00:00 2001 From: evandigby Date: Sun, 22 Jul 2018 15:47:25 -0700 Subject: [PATCH 1/3] added BufferAt for getting slices directly from memory --- exec/vm.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/exec/vm.go b/exec/vm.go index 00b033a7..4b1b34e4 100644 --- a/exec/vm.go +++ b/exec/vm.go @@ -456,6 +456,20 @@ func (proc *Process) WriteAt(p []byte, off int64) (int, error) { return length, err } +// ErrOutOfBounds is returned when you attempt to buffer memory out of the bounds of process memory +var ErrOutOfBounds = errors.New("offset and length out of memory bounds") + +// BufferAt returns a slice pointing at the process memory at offset "off" for length "len" +func (proc *Process) BufferAt(offset, length int64) ([]byte, error) { + mem := proc.vm.Memory() + + if len(mem) < int(offset+length) { + return nil, ErrOutOfBounds + } + + return mem[offset : offset+length], nil +} + // Terminate stops the execution of the current module. func (proc *Process) Terminate() { proc.vm.abort = true From fb5d8ab044cff94136a63bea5a6861e470e63b53 Mon Sep 17 00:00:00 2001 From: evandigby Date: Sun, 22 Jul 2018 15:48:12 -0700 Subject: [PATCH 2/3] wip: adding go runtime module --- modules/go.go | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 modules/go.go diff --git a/modules/go.go b/modules/go.go new file mode 100644 index 00000000..3144fe34 --- /dev/null +++ b/modules/go.go @@ -0,0 +1,206 @@ +package modules + +import ( + "crypto/rand" + "encoding/binary" + "errors" + "fmt" + "reflect" + "time" + + "github.com/go-interpreter/wagon/exec" + "github.com/go-interpreter/wagon/wasm" +) + +var ( + // ErrGoMemKindMismatch is Returned when importing a module generated by Go and the 'mem' is not of kind "ExternalMemory" + ErrGoMemKindMismatch = errors.New("expected import 'mem' to be of kind wasm.ExternalMemory") +) + +// Go is the Go WASM runtime module (emulates wasm_exec.js shipped with Go) +type Go struct { + *wasm.Module + + timeOrigin time.Time +} + +func stub(name string) func(proc *exec.Process, sp int32) { + return func(proc *exec.Process, sp int32) { + fmt.Println("called", name, "with val", sp) + } +} + +const ( + goExportIndexDebug = iota + goExportIndexRuntimeWasmExit + goExportIndexRuntimeWasmWrite + goExportIndexRuntimeNanotime + goExportIndexRuntimeWalltime + goExportIndexRuntimeScheduleCallback + goExportIndexRuntimeClearScheduledCallback + goExportIndexRuntimeGetRandomData + goExportIndexSyscallJSStringVal + goExportIndexSyscallJSValueGet + goExportIndexSyscallJSValueCall + goExportIndexSyscallJSValueNew + goExportIndexSyscallJSValuePrepareString + goExportIndexSyscallJSValueLoadString + + goExportIndexLength +) + +// NewGo creates a new Go WASM runtime module +func NewGo() (*Go, error) { + g := &Go{ + Module: wasm.NewModule(), + timeOrigin: time.Now(), + } + + g.Types.Entries = make([]wasm.FunctionSig, goExportIndexLength) + g.FunctionIndexSpace = make([]wasm.Function, goExportIndexLength) + g.Export.Entries = map[string]wasm.ExportEntry{} + + g.mapFunc("debug", goExportIndexDebug, stub("debug"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.wasmExit", goExportIndexRuntimeWasmExit, g.exportExit, []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.wasmWrite", goExportIndexRuntimeWasmWrite, g.exportWasmWrite, []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.nanotime", goExportIndexRuntimeNanotime, g.exportNanotime, []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.walltime", goExportIndexRuntimeWalltime, stub("runtime.walltime"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.scheduleCallback", goExportIndexRuntimeScheduleCallback, stub("runtime.scheduleCallback"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.clearScheduledCallback", goExportIndexRuntimeClearScheduledCallback, stub("runtime.clearScheduledCallback"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("runtime.getRandomData", goExportIndexRuntimeGetRandomData, g.exportGetRandomData, []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.stringVal", goExportIndexSyscallJSStringVal, stub("syscall/js.stringVal"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.valueGet", goExportIndexSyscallJSValueGet, g.exportValueGet, []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.valueCall", goExportIndexSyscallJSValueCall, stub("syscall/js.valueCall"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.valueNew", goExportIndexSyscallJSValueNew, stub("syscall/js.valueNew"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.valuePrepareString", goExportIndexSyscallJSValuePrepareString, stub("syscall/js.valuePrepareString"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.mapFunc("syscall/js.valueLoadString", goExportIndexSyscallJSValueLoadString, stub("syscall/js.valueLoadString"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + + return g, nil +} + +// BufferAt is anything that can return a buffer at an offset and length +type BufferAt interface { + BufferAt(offset, length int64) ([]byte, error) +} + +func (g *Go) setInt32(ba BufferAt, addr int32, v int32) { + buf, err := ba.BufferAt(int64(addr), 8) + if err != nil { + panic(err) + } + binary.LittleEndian.PutUint32(buf, uint32(v)) +} + +func (g *Go) setInt64(ba BufferAt, addr int32, v int64) { + buf, err := ba.BufferAt(int64(addr), 8) + if err != nil { + panic(err) + } + binary.LittleEndian.PutUint64(buf, uint64(v)) +} + +func (g *Go) getInt64(ba BufferAt, addr int32) int64 { + buf, err := ba.BufferAt(int64(addr), 8) + if err != nil { + panic(err) + } + return int64(binary.LittleEndian.Uint64(buf)) +} + +func (g *Go) getInt32(ba BufferAt, addr int32) int32 { + buf, err := ba.BufferAt(int64(addr), 8) + if err != nil { + panic(err) + } + return int32(binary.LittleEndian.Uint32(buf)) +} + +func (g *Go) loadSlice(ba BufferAt, sp int32) ([]byte, error) { + offset := g.getInt64(ba, sp) + length := g.getInt64(ba, sp+8) + + return ba.BufferAt(offset, length) +} + +func (g *Go) loadString(ba BufferAt, sp int32) (string, error) { + buf, err := g.loadSlice(ba, sp) + if err != nil { + return "", err + } + return string(buf), nil +} + +func (g *Go) exportValueLoadString(proc *exec.Process, sp int32) { + +} + +func (g *Go) exportValueNew(proc *exec.Process, sp int32) { + +} + +func (g *Go) exportValueGet(proc *exec.Process, sp int32) { + s, err := g.loadString(proc, sp+16) + if err != nil { + panic(err) + } + + fmt.Println("Get", s) +} + +func (g *Go) exportWalltime(proc *exec.Process, sp int32) { + n := time.Now() + + nsec := n.UnixNano() + secs := nsec / 1e9 + nsec = nsec - (secs * 1e9) + g.setInt64(proc, sp+8, secs) + g.setInt32(proc, sp+16, int32(nsec)) +} + +func (g *Go) exportNanotime(proc *exec.Process, sp int32) { + g.setInt64(proc, sp+8, time.Since(g.timeOrigin).Nanoseconds()) +} + +func (g *Go) exportGetRandomData(proc *exec.Process, sp int32) { + s, err := g.loadSlice(proc, sp) + if err != nil { + panic(err) + } + rand.Read(s) +} + +func (g *Go) exportWasmWrite(proc *exec.Process, sp int32) { + p := g.getInt64(proc, sp+16) + n := g.getInt32(proc, sp+24) + + buf, err := proc.BufferAt(p, int64(n)) + if err != nil { + panic(err) + } + + fmt.Print(string(buf)) +} + +func (g *Go) exportExit(proc *exec.Process, sp int32) { + proc.Terminate() +} + +func (g *Go) mapFunc(name string, i int, f interface{}, params []wasm.ValueType, returns []wasm.ValueType) { + g.Types.Entries[i] = wasm.FunctionSig{ + Form: 0, + ParamTypes: params, + ReturnTypes: returns, + } + + g.FunctionIndexSpace[i] = wasm.Function{ + Sig: &g.Types.Entries[i], + Host: reflect.ValueOf(f), + Body: &wasm.FunctionBody{}, + } + + g.Export.Entries[name] = wasm.ExportEntry{ + FieldStr: name, + Kind: wasm.ExternalFunction, + Index: uint32(i), + } +} From 43543289af4bfa8c6f6819bfb5c67c9103d7f4c7 Mon Sep 17 00:00:00 2001 From: Evan Digby Date: Tue, 24 Jul 2018 16:27:48 -0700 Subject: [PATCH 3/3] wip building JS environment --- modules/go.go | 422 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 346 insertions(+), 76 deletions(-) diff --git a/modules/go.go b/modules/go.go index 3144fe34..ef938606 100644 --- a/modules/go.go +++ b/modules/go.go @@ -3,8 +3,9 @@ package modules import ( "crypto/rand" "encoding/binary" - "errors" "fmt" + "log" + "math" "reflect" "time" @@ -12,16 +13,16 @@ import ( "github.com/go-interpreter/wagon/wasm" ) -var ( - // ErrGoMemKindMismatch is Returned when importing a module generated by Go and the 'mem' is not of kind "ExternalMemory" - ErrGoMemKindMismatch = errors.New("expected import 'mem' to be of kind wasm.ExternalMemory") -) +type ref uint64 // Go is the Go WASM runtime module (emulates wasm_exec.js shipped with Go) type Go struct { *wasm.Module timeOrigin time.Time + + values map[ref]Value + valueIndex ref } func stub(name string) func(proc *exec.Process, sp int32) { @@ -30,89 +31,212 @@ func stub(name string) func(proc *exec.Process, sp int32) { } } +type Value struct { + ref ref + v interface{} +} + +type jsObject map[string]interface{} + +func (o jsObject) Get(v string) interface{} { + return o[v] +} + +func newJSError(err error) jsObject { + return jsObject{"message": err.Error()} +} + +type jsInt8Array []int8 + +func (a jsInt8Array) New(args ...interface{}) interface{} { + a = make(jsInt8Array, len(args)) + for i, v := range args { + a[i] = v.(int8) + } + return a +} + +type jsArray []interface{} + +func (a jsArray) New(args ...interface{}) interface{} { + return jsArray(args) +} + +type getter interface { + Get(v string) interface{} +} + +type newer interface { + New(args ...interface{}) interface{} +} + +var ( + nan = struct{}{} + undefined = struct{}{} + jsGlobal = jsObject{ + "Array": jsArray{}, + "Int8Array": jsInt8Array{}, + } +) + const ( - goExportIndexDebug = iota - goExportIndexRuntimeWasmExit - goExportIndexRuntimeWasmWrite - goExportIndexRuntimeNanotime - goExportIndexRuntimeWalltime - goExportIndexRuntimeScheduleCallback - goExportIndexRuntimeClearScheduledCallback - goExportIndexRuntimeGetRandomData - goExportIndexSyscallJSStringVal - goExportIndexSyscallJSValueGet - goExportIndexSyscallJSValueCall - goExportIndexSyscallJSValueNew - goExportIndexSyscallJSValuePrepareString - goExportIndexSyscallJSValueLoadString - - goExportIndexLength + jsValueNaN = 0 + jsValueUndefined = 1 + jsValueNull = 2 + jsValueTrue = 3 + jsValueFalse = 4 + jsValueGlobal = 5 + jsValueMemory = 6 // WebAssembly linear memory + jsVsGo = 7 // instance of the Go class in JavaScript ) +var defaultValues = map[ref]Value{ + jsValueNaN: Value{ref: jsValueNaN, v: nan}, + jsValueUndefined: Value{ref: jsValueUndefined, v: undefined}, + jsValueNull: Value{ref: jsValueNull, v: nil}, + jsValueTrue: Value{ref: jsValueTrue, v: true}, + jsValueFalse: Value{ref: jsValueFalse, v: false}, + jsValueGlobal: Value{ref: jsValueGlobal, v: jsGlobal}, + jsValueMemory: Value{ref: jsValueMemory, v: jsValueMemory}, + jsVsGo: Value{ref: jsVsGo, v: jsVsGo}, +} + // NewGo creates a new Go WASM runtime module func NewGo() (*Go, error) { g := &Go{ Module: wasm.NewModule(), timeOrigin: time.Now(), + values: defaultValues, + valueIndex: ref(len(defaultValues)), } - g.Types.Entries = make([]wasm.FunctionSig, goExportIndexLength) - g.FunctionIndexSpace = make([]wasm.Function, goExportIndexLength) - g.Export.Entries = map[string]wasm.ExportEntry{} - - g.mapFunc("debug", goExportIndexDebug, stub("debug"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.wasmExit", goExportIndexRuntimeWasmExit, g.exportExit, []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.wasmWrite", goExportIndexRuntimeWasmWrite, g.exportWasmWrite, []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.nanotime", goExportIndexRuntimeNanotime, g.exportNanotime, []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.walltime", goExportIndexRuntimeWalltime, stub("runtime.walltime"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.scheduleCallback", goExportIndexRuntimeScheduleCallback, stub("runtime.scheduleCallback"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.clearScheduledCallback", goExportIndexRuntimeClearScheduledCallback, stub("runtime.clearScheduledCallback"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("runtime.getRandomData", goExportIndexRuntimeGetRandomData, g.exportGetRandomData, []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.stringVal", goExportIndexSyscallJSStringVal, stub("syscall/js.stringVal"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.valueGet", goExportIndexSyscallJSValueGet, g.exportValueGet, []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.valueCall", goExportIndexSyscallJSValueCall, stub("syscall/js.valueCall"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.valueNew", goExportIndexSyscallJSValueNew, stub("syscall/js.valueNew"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.valuePrepareString", goExportIndexSyscallJSValuePrepareString, stub("syscall/js.valuePrepareString"), []wasm.ValueType{wasm.ValueTypeI32}, nil) - g.mapFunc("syscall/js.valueLoadString", goExportIndexSyscallJSValueLoadString, stub("syscall/js.valueLoadString"), []wasm.ValueType{wasm.ValueTypeI32}, nil) + g.loadExports() return g, nil } +func (g *Go) loadExports() { + exports := []struct { + Name string + Func func(proc *exec.Process, sp int32) + Params []wasm.ValueType + Returns []wasm.ValueType + }{ + {Name: "debug", Func: stub("debug"), Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.wasmExit", Func: g.exportExit, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.wasmWrite", Func: g.exportWasmWrite, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.nanotime", Func: g.exportNanotime, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.walltime", Func: g.exportWalltime, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.scheduleCallback", Func: stub("runtime.scheduleCallback"), Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.clearScheduledCallback", Func: stub("runtime.clearScheduledCallback"), Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "runtime.getRandomData", Func: g.exportGetRandomData, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.stringVal", Func: g.exportStringVal, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.valueGet", Func: g.exportValueGet, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.valueCall", Func: g.exportValueCall, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.valueNew", Func: g.exportValueNew, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.valuePrepareString", Func: g.exportValuePrepareString, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + {Name: "syscall/js.valueLoadString", Func: g.exportValueLoadString, Params: []wasm.ValueType{wasm.ValueTypeI32}}, + } + + g.Export.Entries = map[string]wasm.ExportEntry{} + + for i, e := range exports { + g.Types.Entries = append(g.Types.Entries, + wasm.FunctionSig{ + Form: 0, + ParamTypes: e.Params, + ReturnTypes: e.Returns, + }) + + g.FunctionIndexSpace = append(g.FunctionIndexSpace, wasm.Function{ + Sig: &g.Types.Entries[i], + Host: reflect.ValueOf(e.Func), + Body: &wasm.FunctionBody{}, + }) + + g.Export.Entries[e.Name] = wasm.ExportEntry{ + FieldStr: e.Name, + Kind: wasm.ExternalFunction, + Index: uint32(i), + } + } +} + // BufferAt is anything that can return a buffer at an offset and length type BufferAt interface { BufferAt(offset, length int64) ([]byte, error) } -func (g *Go) setInt32(ba BufferAt, addr int32, v int32) { +func (g *Go) setUInt8(ba BufferAt, addr int32, v uint8) { buf, err := ba.BufferAt(int64(addr), 8) if err != nil { panic(err) } - binary.LittleEndian.PutUint32(buf, uint32(v)) + buf[0] = byte(v) } -func (g *Go) setInt64(ba BufferAt, addr int32, v int64) { +func (g *Go) setUInt32(ba BufferAt, addr int32, v uint32) { buf, err := ba.BufferAt(int64(addr), 8) if err != nil { panic(err) } - binary.LittleEndian.PutUint64(buf, uint64(v)) + binary.LittleEndian.PutUint32(buf, v) } -func (g *Go) getInt64(ba BufferAt, addr int32) int64 { +func (g *Go) setRef(ba BufferAt, addr int32, v ref) { + g.setUInt32(ba, addr, uint32(v)) +} + +func (g *Go) setInt32(ba BufferAt, addr int32, v int32) { + g.setUInt32(ba, addr, uint32(v)) +} + +func (g *Go) setUInt64(ba BufferAt, addr int32, v uint64) { buf, err := ba.BufferAt(int64(addr), 8) if err != nil { panic(err) } - return int64(binary.LittleEndian.Uint64(buf)) + binary.LittleEndian.PutUint64(buf, v) } -func (g *Go) getInt32(ba BufferAt, addr int32) int32 { +func (g *Go) setInt64(ba BufferAt, addr int32, v int64) { + g.setUInt64(ba, addr, uint64(v)) +} + +func (g *Go) setFloat64(ba BufferAt, addr int32, v float64) { + g.setUInt64(ba, addr, uint64(v)) +} + +func (g *Go) getUInt64(ba BufferAt, addr int32) uint64 { buf, err := ba.BufferAt(int64(addr), 8) if err != nil { panic(err) } - return int32(binary.LittleEndian.Uint32(buf)) + return binary.LittleEndian.Uint64(buf) +} + +func (g *Go) getInt64(ba BufferAt, addr int32) int64 { + return int64(g.getUInt64(ba, addr)) +} + +func (g *Go) getUInt32(ba BufferAt, addr int32) uint32 { + buf, err := ba.BufferAt(int64(addr), 8) + if err != nil { + panic(err) + } + return binary.LittleEndian.Uint32(buf) +} + +func (g *Go) getFloat64(ba BufferAt, addr int32) float64 { + return float64(g.getUInt64(ba, addr)) +} + +func (g *Go) getRef(ba BufferAt, addr int32) ref { + return ref(g.getUInt32(ba, addr)) +} + +func (g *Go) getInt32(ba BufferAt, addr int32) int32 { + return int32(g.getUInt32(ba, addr)) } func (g *Go) loadSlice(ba BufferAt, sp int32) ([]byte, error) { @@ -122,35 +246,200 @@ func (g *Go) loadSlice(ba BufferAt, sp int32) ([]byte, error) { return ba.BufferAt(offset, length) } +func (g *Go) setSlice(ba BufferAt, sp int32, v []byte) { + buf, err := g.loadSlice(ba, sp) + if err != nil { + panic(err) + } + + copy(buf, v) +} + func (g *Go) loadString(ba BufferAt, sp int32) (string, error) { buf, err := g.loadSlice(ba, sp) if err != nil { return "", err } + fmt.Println("loaded string", string(buf)) return string(buf), nil } +func (g *Go) loadValue(ba BufferAt, addr int32) Value { + r := g.getRef(ba, addr) + fmt.Println("getting id", r) + if int(r) > len(g.values) { + return g.values[jsValueUndefined] // this is how javascript acts when index out of bounds occurs + } + return g.values[r] +} + +func (g *Go) loadSliceOfValues(ba BufferAt, addr int32) []interface{} { + arrayAddr := g.getInt64(ba, addr) + length := g.getInt64(ba, addr+8) + + array := make([]interface{}, length) + for i := int64(0); i < length; i++ { + id := g.getRef(ba, int32(arrayAddr+i*4)) + array[i] = g.values[id] + } + return array +} + +func (g *Go) storeValue(ba BufferAt, addr int32, v interface{}) { + const nanHead = 0x7FF80000 + + fmt.Println("storeValue", addr, v) + + if v == nil { + g.setUInt32(ba, addr+4, nanHead) + g.setUInt32(ba, addr, jsValueNull) + return + } + + var r ref + switch tv := v.(type) { + case float64: + if math.IsNaN(tv) { + g.setUInt32(ba, addr+4, nanHead) + g.setUInt32(ba, addr, 0) + return + } + g.setFloat64(ba, addr, tv) + case bool: + g.setUInt32(ba, addr+4, nanHead) + if tv { + g.setUInt32(ba, addr, jsValueTrue) + } else { + g.setUInt32(ba, addr, jsValueFalse) + } + return + case Value: + if tv == g.values[jsValueUndefined] { + g.setInt32(ba, addr, 0) + return + } + + if tv == g.values[jsValueNull] { + g.setInt32(ba, addr, 1) + return + } + + r = tv.ref + default: + r = g.valueIndex + g.values[r] = Value{ + ref: r, + v: tv, + } + g.valueIndex++ + } + + const ( + typeFlagString = 1 + typeFlagSymbol = 2 + typeFlagFunction = 3 + ) + + var tf uint32 + switch reflect.TypeOf(v).Kind() { + case reflect.String: + tf = typeFlagString + case reflect.Func: + tf = typeFlagFunction + } + + g.setUInt32(ba, addr+4, nanHead|tf) + g.setRef(ba, addr, r) +} + +func (g *Go) exportValuePrepareString(proc *exec.Process, sp int32) { + fmt.Println("exportValuePrepareString") + str, err := g.loadString(proc, sp+8) + if err != nil { + panic(err) + } + g.storeValue(proc, sp+16, str) + g.setInt64(proc, sp+24, int64(len(str))) +} + func (g *Go) exportValueLoadString(proc *exec.Process, sp int32) { + fmt.Println("exportValueLoadString") + str := g.loadValue(proc, sp+8) + g.setSlice(proc, sp+8, []byte(str.v.(string))) // will panic if not a string value +} +func (g *Go) exportStringVal(proc *exec.Process, sp int32) { + fmt.Println("exportStringVal") + s, err := g.loadString(proc, sp+8) + if err != nil { + panic(err) + } + g.storeValue(proc, sp+24, s) } func (g *Go) exportValueNew(proc *exec.Process, sp int32) { + fmt.Println("exportValueNew") + v := g.loadValue(proc, sp+8) + args := g.loadSliceOfValues(proc, sp+16) + + n, ok := v.v.(newer) + if !ok { + err := fmt.Errorf("value %v of type %q is not a newer", v, reflect.TypeOf(v)) + + g.storeValue(proc, sp+40, newJSError(err)) + g.setUInt8(proc, sp+48, 0) + return + } + g.storeValue(proc, sp+40, n.New(args...)) + g.setUInt8(proc, sp+48, 1) +} + +func (g *Go) exportValueCall(proc *exec.Process, sp int32) { + fmt.Println("exportValueCall") + + v := g.loadValue(proc, sp+8) + f, err := g.loadString(proc, sp+16) + if err != nil { + panic(err) + } + + _, ok := v.v.(getter) + if !ok { + fmt.Println(g.values) + log.Printf("value %v of type %v is not a getter wanting %q", v.ref, reflect.TypeOf(v.v), f) + proc.Terminate() + return + } + + args := g.loadSliceOfValues(proc, sp+32) + + fmt.Printf("Calling %q on %v with args %v\n", f, v, args) } func (g *Go) exportValueGet(proc *exec.Process, sp int32) { + fmt.Println("exportValueGet") + v := g.loadValue(proc, sp+8) + s, err := g.loadString(proc, sp+16) if err != nil { panic(err) } - fmt.Println("Get", s) + gtr, ok := v.v.(getter) + if !ok { + fmt.Println(g.values) + log.Printf("value %v of type %v is not a getter wanting %q", v.ref, reflect.TypeOf(v.v), s) + proc.Terminate() + return + } + + g.storeValue(proc, sp+32, gtr.Get(s)) } func (g *Go) exportWalltime(proc *exec.Process, sp int32) { - n := time.Now() - - nsec := n.UnixNano() + fmt.Println("exportWalltime") + nsec := time.Now().UnixNano() secs := nsec / 1e9 nsec = nsec - (secs * 1e9) g.setInt64(proc, sp+8, secs) @@ -158,10 +447,12 @@ func (g *Go) exportWalltime(proc *exec.Process, sp int32) { } func (g *Go) exportNanotime(proc *exec.Process, sp int32) { + fmt.Println("exportNanotime") g.setInt64(proc, sp+8, time.Since(g.timeOrigin).Nanoseconds()) } func (g *Go) exportGetRandomData(proc *exec.Process, sp int32) { + fmt.Println("exportGetRandomData") s, err := g.loadSlice(proc, sp) if err != nil { panic(err) @@ -170,37 +461,16 @@ func (g *Go) exportGetRandomData(proc *exec.Process, sp int32) { } func (g *Go) exportWasmWrite(proc *exec.Process, sp int32) { - p := g.getInt64(proc, sp+16) - n := g.getInt32(proc, sp+24) - - buf, err := proc.BufferAt(p, int64(n)) + fmt.Println("exportWasmWrite") + s, err := g.loadString(proc, sp+16) if err != nil { panic(err) } - fmt.Print(string(buf)) + fmt.Print(s) } func (g *Go) exportExit(proc *exec.Process, sp int32) { + fmt.Println("exportExit") proc.Terminate() } - -func (g *Go) mapFunc(name string, i int, f interface{}, params []wasm.ValueType, returns []wasm.ValueType) { - g.Types.Entries[i] = wasm.FunctionSig{ - Form: 0, - ParamTypes: params, - ReturnTypes: returns, - } - - g.FunctionIndexSpace[i] = wasm.Function{ - Sig: &g.Types.Entries[i], - Host: reflect.ValueOf(f), - Body: &wasm.FunctionBody{}, - } - - g.Export.Entries[name] = wasm.ExportEntry{ - FieldStr: name, - Kind: wasm.ExternalFunction, - Index: uint32(i), - } -}