Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(import) Support instance context API #26

Merged
merged 3 commits into from
Jun 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions wasmer/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type cWasmerImportExportKind C.wasmer_import_export_kind
type cWasmerImportExportValue C.wasmer_import_export_value
type cWasmerImportFuncT C.wasmer_import_func_t
type cWasmerImportT C.wasmer_import_t
type cWasmerInstanceContextT C.wasmer_instance_context_t
type cWasmerInstanceT C.wasmer_instance_t
type cWasmerMemoryT C.wasmer_memory_t
type cWasmerResultT C.wasmer_result_t
Expand Down Expand Up @@ -141,6 +142,10 @@ func cWasmerImportFuncDestroy(function *cWasmerImportFuncT) {
C.wasmer_import_func_destroy((*C.wasmer_import_func_t)(function))
}

func cWasmerInstanceContextMemory(instanceContext *cWasmerInstanceContextT) *cWasmerMemoryT {
return (*cWasmerMemoryT)(C.wasmer_instance_context_memory((*C.wasmer_instance_context_t)(instanceContext), 0))
}

func cGoString(string *cChar) string {
return C.GoString((*C.char)(string))
}
Expand Down
21 changes: 21 additions & 0 deletions wasmer/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,24 @@ func (imports *Imports) Close() {
}
}
}

// InstanceContext represents a way to access instance API from within
// an imported context.
type InstanceContext struct {
context *cWasmerInstanceContextT
memory Memory
}

// IntoInstanceContext casts the first `context unsafe.Pointer`
// argument of an imported function into an `InstanceContext`.
func IntoInstanceContext(instanceContext unsafe.Pointer) InstanceContext {
context := (*cWasmerInstanceContextT)(instanceContext)
memory := newMemory(cWasmerInstanceContextMemory(context))

return InstanceContext{context, memory}
}

// Memory returns the current instance memory.
func (instanceContext *InstanceContext) Memory() *Memory {
return &instanceContext.memory
}
4 changes: 4 additions & 0 deletions wasmer/test/import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ func TestImportBadInput(t *testing.T) {
func TestImportBadOutput(t *testing.T) {
testImportBadOutput(t)
}

func TestImportInstanceContext(t *testing.T) {
testImportInstanceContext(t)
}
86 changes: 58 additions & 28 deletions wasmer/test/imports.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package wasmertest
// extern int32_t badInstanceContext(int32_t x);
// extern int32_t badInput(void *ctx, char x);
// extern char badOutput(void *ctx);
// extern void logMessage(void *ctx, int32_t pointer, int32_t length);
import "C"
import (
"github.com/stretchr/testify/assert"
Expand All @@ -17,45 +18,25 @@ import (
"unsafe"
)

//export sum
func sum(context unsafe.Pointer, x int32, y int32) int32 {
return x + y
}

//export missingContext
func missingContext() int32 {
return 7
}

//export badInstanceContext
func badInstanceContext(x int32) int32 {
return x + 7
}

//export badInput
func badInput(context unsafe.Pointer, x C.char) int32 {
return 7
}

//export badOutput
func badOutput(context unsafe.Pointer) C.char {
return 'a'
}

func getImportedFunctionBytes() []byte {
func getImportedFunctionBytes(wasmFile ...string) []byte {
_, filename, _, _ := runtime.Caller(0)
modulePath := path.Join(path.Dir(filename), "testdata", "examples", "imported_function.wasm")
modulePath := path.Join(path.Dir(filename), "testdata", path.Join(wasmFile...))

bytes, _ := wasm.ReadBytes(modulePath)

return bytes
}

//export sum
func sum(context unsafe.Pointer, x int32, y int32) int32 {
return x + y
}

func testImport(t *testing.T) {
imports, err := wasm.NewImports().Append("sum", sum, C.sum)
assert.NoError(t, err)

instance, err := wasm.NewInstanceWithImports(getImportedFunctionBytes(), imports)
instance, err := wasm.NewInstanceWithImports(getImportedFunctionBytes("examples", "imported_function.wasm"), imports)
assert.NoError(t, err)

defer instance.Close()
Expand All @@ -69,22 +50,71 @@ func testImport(t *testing.T) {
assert.NoError(t, err)
}

//export missingContext
func missingContext() int32 {
return 7
}

func testImportMissingInstanceContext(t *testing.T) {
_, err := wasm.NewImports().Append("foo", missingContext, C.missingContext)
assert.EqualError(t, err, "Imported function `foo` must at least have one argument for the instance context.")
}

//export badInstanceContext
func badInstanceContext(x int32) int32 {
return x + 7
}

func testImportBadTypeForInstanceContext(t *testing.T) {
_, err := wasm.NewImports().Append("foo", badInstanceContext, C.badInstanceContext)
assert.EqualError(t, err, "The instance context of the `foo` imported function must be of kind `unsafe.Pointer`; given `int32`; is it missing?")
}

//export badInput
func badInput(context unsafe.Pointer, x C.char) int32 {
return 7
}

func testImportBadInput(t *testing.T) {
_, err := wasm.NewImports().Append("foo", badInput, C.badInput)
assert.EqualError(t, err, "Invalid input type for the `foo` imported function; given `int8`; only accept `int32`, `int64`, `float32`, and `float64`.")
}

//export badOutput
func badOutput(context unsafe.Pointer) C.char {
return 'a'
}

func testImportBadOutput(t *testing.T) {
_, err := wasm.NewImports().Append("foo", badOutput, C.badOutput)
assert.EqualError(t, err, "Invalid output type for the `foo` imported function; given `int8`; only accept `int32`, `int64`, `float32`, and `float64`.")
}

var loggedMessage = ""

//export logMessage
func logMessage(context unsafe.Pointer, pointer int32, length int32) {
var instanceContext = wasm.IntoInstanceContext(context)
var memory = instanceContext.Memory().Data()

loggedMessage = string(memory[pointer : pointer+length])
}

func testImportInstanceContext(t *testing.T) {
imports, err := wasm.NewImports().Append("log_message", logMessage, C.logMessage)
assert.NoError(t, err)

instance, err := wasm.NewInstanceWithImports(getImportedFunctionBytes("log.wasm"), imports)
assert.NoError(t, err)

defer instance.Close()

doSomething, exists := instance.Exports["do_something"]
assert.Equal(t, true, exists)

result, err := doSomething()

assert.Equal(t, wasm.TypeVoid, result.GetType())
assert.NoError(t, err)
assert.Equal(t, "hello", loggedMessage)
}
10 changes: 10 additions & 0 deletions wasmer/test/testdata/log.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extern "C" {
fn log_message(pointer: *const u8, length: u32);
}

#[no_mangle]
pub extern "C" fn do_something() {
let message = "hello";

unsafe { log_message(message.as_ptr(), message.len() as u32) }
}
Binary file added wasmer/test/testdata/log.wasm
Binary file not shown.