diff --git a/convert/convert.go b/convert/convert.go index 21306e9..d6e80f7 100644 --- a/convert/convert.go +++ b/convert/convert.go @@ -44,3 +44,18 @@ func IntToString(i int) string { return string(result) } + +// WasmPtrToBytes returns a slice of bytes from WebAssembly compatible numeric types +// representing its pointer and length. +func WasmPtrToBytes(ptr uint32, size uint32) []byte { + return unsafe.Slice((*byte)(unsafe.Pointer(uintptr(ptr))), size) +} + +// BytesToWasmPtr returns a pointer and size pair for the given string in a way +// compatible with WebAssembly numeric types. +// The returned pointer aliases the string hence the string must be kept alive +// until ptr is no longer needed. +func BytesToWasmPtr(b []byte) (uint32, uint32) { + ptr := uintptr(unsafe.Pointer(unsafe.SliceData(b))) + return uint32(ptr), uint32(len(b)) +} diff --git a/engine/engine_test.go b/engine/engine_test.go index dea2992..55a357d 100644 --- a/engine/engine_test.go +++ b/engine/engine_test.go @@ -1,8 +1,6 @@ package engine -import ( - "testing" -) +import "testing" func TestEngine(t *testing.T) { t.Run("cannot init without interpreter", func(t *testing.T) { @@ -53,3 +51,7 @@ func (i *mockInterpreter) DefineFunc(modulename, funcname string, f interface{}) func (i *mockInterpreter) MemoryData(ptr, sz uint32) ([]byte, error) { return nil, nil } + +func (i *mockInterpreter) References() *ExternalReferences { + return nil +} diff --git a/engine/interp.go b/engine/interp.go index bfbc81d..b1572a2 100644 --- a/engine/interp.go +++ b/engine/interp.go @@ -15,4 +15,6 @@ type Interpreter interface { DefineFunc(module, name string, f interface{}) error // MemoryData returns a slice of memory data from the memory managed by the host. MemoryData(ptr, sz uint32) ([]byte, error) + // References are the external references managed by the host module. + References() *ExternalReferences } diff --git a/engine/references.go b/engine/references.go index 0f8594a..78bc944 100644 --- a/engine/references.go +++ b/engine/references.go @@ -12,8 +12,8 @@ type ExternalReferences struct { } // NewReferences creates a new ExternalReferences store. -func NewReferences() *ExternalReferences { - return &ExternalReferences{ +func NewReferences() ExternalReferences { + return ExternalReferences{ refs: make(map[int32]uintptr), } } diff --git a/go.mod b/go.mod index 0ddbb18..5ab870a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.0 replace github.com/tetratelabs/wazero => github.com/orsinium-forks/wazero v0.0.0-20240217173836-b12c024bcbe4 require ( - github.com/hybridgroup/wasman v0.0.0-20240228124029-ecd9fd3f900a + github.com/hybridgroup/wasman v0.0.0-20240229144219-f3288962ab34 github.com/tetratelabs/wazero v1.6.0 github.com/urfave/cli/v2 v2.27.1 tinygo.org/x/tinyfs v0.3.1-0.20231212053859-32ae3f6bbad9 diff --git a/go.sum b/go.sum index 45ce827..ada22ad 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHH github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/hybridgroup/wasman v0.0.0-20240228124029-ecd9fd3f900a h1:6q1Gn1MR643sBn4PnvgX40jCCXmAyjuNXFfk8wirpds= github.com/hybridgroup/wasman v0.0.0-20240228124029-ecd9fd3f900a/go.mod h1:rLavUo4P0xVcDeDnViYEpPQPoACmp1py9UTLPY/R7Lg= +github.com/hybridgroup/wasman v0.0.0-20240229144219-f3288962ab34 h1:XGX7Qt+ylSTF7VrwfXePwXAy2cK2iB6At+Ni/PcaNok= +github.com/hybridgroup/wasman v0.0.0-20240229144219-f3288962ab34/go.mod h1:rLavUo4P0xVcDeDnViYEpPQPoACmp1py9UTLPY/R7Lg= github.com/orsinium-forks/wazero v0.0.0-20240217173836-b12c024bcbe4 h1:MUh9e2izck9aROiwDsDm24UU7kHieYM2911U1t+NASs= github.com/orsinium-forks/wazero v0.0.0-20240217173836-b12c024bcbe4/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= diff --git a/interp/tester/interp.go b/interp/tester/interp.go index 1b14081..ab70a53 100644 --- a/interp/tester/interp.go +++ b/interp/tester/interp.go @@ -2,6 +2,7 @@ package tester import ( "testing" + "unsafe" "github.com/hybridgroup/mechanoid/engine" ) @@ -11,6 +12,14 @@ func InitTest(t *testing.T, i engine.Interpreter) { if err != nil { t.Errorf("Interpreter.Init() failed: %v", err) } + + if i.Name() == "" { + t.Errorf("Interpreter.Name() failed: %v", i.Name()) + } + + if i.References() == nil { + t.Errorf("Interpreter.References() failed: %v", i.References()) + } } func LoadTest(t *testing.T, i engine.Interpreter) { @@ -51,3 +60,53 @@ func HaltTest(t *testing.T, i engine.Interpreter) { t.Errorf("Interpreter.Halt() failed: %v", err) } } + +func ReferencesTest(t *testing.T, i engine.Interpreter) { + err := i.Init() + if err != nil { + t.Errorf("Interpreter.Init() failed: %v", err) + } + if i.References() == nil { + t.Errorf("Interpreter.References() failed: %v", i.References()) + } + + var id1, id2 int32 + thing1 := &testingType{ + val1: "hello", + val2: "world", + } + thing2 := &testingType{ + val1: "hola", + val2: "mundo", + } + + t.Run("add references", func(t *testing.T) { + id1 = i.References().Add(unsafe.Pointer(&thing1)) + id2 = i.References().Add(unsafe.Pointer(&thing2)) + + if id1 == id2 { + t.Errorf("id1 and id2 should not be the same") + } + }) + + t.Run("get references", func(t *testing.T) { + if i.References().Get(id1) != uintptr(unsafe.Pointer(&thing1)) { + t.Errorf("refs.Get(id1) failed") + } + if i.References().Get(id2) != uintptr(unsafe.Pointer(&thing2)) { + t.Errorf("refs.Get(id2) failed") + } + }) + + t.Run("remove references", func(t *testing.T) { + i.References().Remove(id1) + i.References().Remove(id2) + + if i.References().Get(id1) != uintptr(0) { + t.Errorf("refs.Get(id1) failed") + } + if i.References().Get(id2) != uintptr(0) { + t.Errorf("refs.Get(id2) failed") + } + }) +} diff --git a/interp/wasman/interp.go b/interp/wasman/interp.go index 6dc6eab..1186f09 100644 --- a/interp/wasman/interp.go +++ b/interp/wasman/interp.go @@ -14,6 +14,8 @@ type Interpreter struct { module *wasmaneng.Module instance *wasmaneng.Instance Memory []byte + + references engine.ExternalReferences } func (i *Interpreter) Name() string { @@ -34,6 +36,8 @@ func (i *Interpreter) Init() error { } } + i.references = engine.NewReferences() + return nil } @@ -100,6 +104,8 @@ func (i *Interpreter) DefineFunc(moduleName, funcName string, f any) error { return wasmaneng.DefineFunc20(i.linker, moduleName, funcName, tf) case func(uint32, uint32) uint32: return wasmaneng.DefineFunc21(i.linker, moduleName, funcName, tf) + case func(uint32, uint32, uint32) uint32: + return wasmaneng.DefineFunc31(i.linker, moduleName, funcName, tf) default: return engine.ErrInvalidFuncType } @@ -115,3 +121,8 @@ func (i *Interpreter) MemoryData(ptr, sz uint32) ([]byte, error) { return i.instance.Memory.Value[ptr : ptr+sz], nil } + +// References are the external references managed by the host module. +func (i *Interpreter) References() *engine.ExternalReferences { + return &i.references +} diff --git a/interp/wasman/interp_test.go b/interp/wasman/interp_test.go index 2eb0476..f6b6266 100644 --- a/interp/wasman/interp_test.go +++ b/interp/wasman/interp_test.go @@ -29,6 +29,10 @@ func TestHalt(t *testing.T) { tester.HaltTest(t, &Interpreter{}) } +func TestExternalReferences(t *testing.T) { + tester.ReferencesTest(t, &Interpreter{}) +} + func TestDefineFunc(t *testing.T) { t.Skip("TODO: implement TestDefineFunc") } diff --git a/interp/wazero/interp.go b/interp/wazero/interp.go index f35dab7..58bb25d 100644 --- a/interp/wazero/interp.go +++ b/interp/wazero/interp.go @@ -14,6 +14,8 @@ type Interpreter struct { runtime wazero.Runtime defs map[string]map[string]any module api.Module + + references engine.ExternalReferences } func (i *Interpreter) Name() string { @@ -26,6 +28,7 @@ func (i *Interpreter) Init() error { conf = conf.WithDebugInfoEnabled(false) conf = conf.WithMemoryLimitPages(1) i.runtime = wazero.NewRuntimeWithConfig(ctx, conf) + i.references = engine.NewReferences() return nil } @@ -95,6 +98,11 @@ func (i *Interpreter) MemoryData(ptr, sz uint32) ([]byte, error) { return data, nil } +// References are the external references managed by the host module. +func (i *Interpreter) References() *engine.ExternalReferences { + return &i.references +} + // A fake RandSource for having fewer memory allocations. // // Should not be used with modules that do need an access to random functions. diff --git a/interp/wazero/interp_test.go b/interp/wazero/interp_test.go index 5a723ca..a908456 100644 --- a/interp/wazero/interp_test.go +++ b/interp/wazero/interp_test.go @@ -29,6 +29,10 @@ func TestHalt(t *testing.T) { tester.HaltTest(t, &Interpreter{}) } +func TestExternalReferences(t *testing.T) { + tester.ReferencesTest(t, &Interpreter{}) +} + func TestDefineFunc(t *testing.T) { t.Skip("TODO: implement TestDefineFunc") }