This repository has been archived by the owner on May 11, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 147
Pass the VM pointer to host go functions #59
Closed
gballet
wants to merge
1
commit into
go-interpreter:master
from
gballet:call-gofuncs-with-vm-and-module
Closed
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,10 +15,10 @@ import ( | |
func TestHostCall(t *testing.T) { | ||
const secretValue = 0xdeadbeef | ||
|
||
var secretVariable int = 0 | ||
var secretVariable int | ||
|
||
// a host function that can be called by WASM code. | ||
testHostFunction := func() { | ||
testHostFunction := func(proc *Process) { | ||
secretVariable = secretValue | ||
} | ||
|
||
|
@@ -99,11 +99,11 @@ var moduleCallHost = []byte{ | |
0x08, 0x01, 0x06, 0x00, 0x41, 0x00, 0x10, 0x00, 0x0B, | ||
} | ||
|
||
func add3(x int32) int32 { | ||
func add3(proc *Process, x int32) int32 { | ||
return x + 3 | ||
} | ||
|
||
func importer(name string) (*wasm.Module, error) { | ||
func importer(name string, f func(*Process, int32) int32) (*wasm.Module, error) { | ||
m := wasm.NewModule() | ||
m.Types = &wasm.SectionTypes{ | ||
// List of all function types available in this module. | ||
|
@@ -119,7 +119,44 @@ func importer(name string) (*wasm.Module, error) { | |
m.FunctionIndexSpace = []wasm.Function{ | ||
{ | ||
Sig: &m.Types.Entries[0], | ||
Host: reflect.ValueOf(add3), | ||
Host: reflect.ValueOf(f), | ||
Body: &wasm.FunctionBody{}, | ||
}, | ||
} | ||
m.Export = &wasm.SectionExports{ | ||
Entries: map[string]wasm.ExportEntry{ | ||
"_native": { | ||
FieldStr: "_naive", | ||
Kind: wasm.ExternalFunction, | ||
Index: 0, | ||
}, | ||
}, | ||
} | ||
|
||
return m, nil | ||
} | ||
|
||
func invalidAdd3(x int32) int32 { | ||
return x + 3 | ||
} | ||
|
||
func invalidImporter(name string) (*wasm.Module, error) { | ||
m := wasm.NewModule() | ||
m.Types = &wasm.SectionTypes{ | ||
// List of all function types available in this module. | ||
// There is only one: (func [int32] -> [int32]) | ||
Entries: []wasm.FunctionSig{ | ||
{ | ||
Form: 0, | ||
ParamTypes: []wasm.ValueType{wasm.ValueTypeI32}, | ||
ReturnTypes: []wasm.ValueType{wasm.ValueTypeI32}, | ||
}, | ||
}, | ||
} | ||
m.FunctionIndexSpace = []wasm.Function{ | ||
{ | ||
Sig: &m.Types.Entries[0], | ||
Host: reflect.ValueOf(invalidAdd3), | ||
Body: &wasm.FunctionBody{}, | ||
}, | ||
} | ||
|
@@ -137,7 +174,7 @@ func importer(name string) (*wasm.Module, error) { | |
} | ||
|
||
func TestHostSymbolCall(t *testing.T) { | ||
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), importer) | ||
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), func(n string) (*wasm.Module, error) { return importer(n, add3) }) | ||
if err != nil { | ||
t.Fatalf("Could not read module: %v", err) | ||
} | ||
|
@@ -153,3 +190,50 @@ func TestHostSymbolCall(t *testing.T) { | |
t.Fatalf("Did not get the right value. Got %d, wanted %d", rtrns, 3) | ||
} | ||
} | ||
|
||
func TestGoFunctionCallChecksForFirstArgument(t *testing.T) { | ||
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), invalidImporter) | ||
if err != nil { | ||
t.Fatalf("Could not read module: %v", err) | ||
} | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("This code should have panicked.") | ||
} else { | ||
if r != "exec: the first argument of a host function was int32, expected ptr" { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks brittle. |
||
t.Errorf("This should have panicked because of the wrong type being used as a first argument, and it panicked because of %v", r) | ||
} | ||
} | ||
}() | ||
vm, err := NewVM(m) | ||
if err != nil { | ||
t.Fatalf("Could not instantiate vm: %v", err) | ||
} | ||
_, err = vm.ExecCode(1) | ||
if err != nil { | ||
t.Fatalf("Error executing the default function: %v", err) | ||
} | ||
} | ||
|
||
func terminate(proc *Process, x int32) int32 { | ||
proc.Terminate() | ||
return 3 | ||
} | ||
|
||
func TestHostTerminate(t *testing.T) { | ||
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), func(n string) (*wasm.Module, error) { return importer(n, terminate) }) | ||
if err != nil { | ||
t.Fatalf("Could not read module: %v", err) | ||
} | ||
vm, err := NewVM(m) | ||
if err != nil { | ||
t.Fatalf("Could not instantiate vm: %v", err) | ||
} | ||
_, err = vm.ExecCode(1) | ||
if err != nil { | ||
t.Fatalf("Error executing the default function: %v", err) | ||
} | ||
if vm.abort == false || vm.ctx.pc > 0xa { | ||
t.Fatalf("Terminate did not abort execution: abort=%v, pc=%#x", vm.abort, vm.ctx.pc) | ||
} | ||
} |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// Copyright 2018 The go-interpreter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package exec | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
var ( | ||
smallMemoryVM = &VM{memory: []byte{1, 2, 3}} | ||
emptyMemoryVM = &VM{memory: []byte{}} | ||
smallMemoryProcess = &Process{vm: smallMemoryVM} | ||
emptyMemoryProcess = &Process{vm: emptyMemoryVM} | ||
tooBigABuffer = []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} | ||
) | ||
|
||
func TestNormalWrite(t *testing.T) { | ||
vm := &VM{memory: make([]byte, 300)} | ||
proc := &Process{vm: vm} | ||
n, err := proc.WriteAt(tooBigABuffer, 0) | ||
if err != nil { | ||
t.Fatalf("Found an error when writing: %v", err) | ||
} | ||
if n != len(tooBigABuffer) { | ||
t.Fatalf("Number of written bytes was %d, should have been %d", n, len(tooBigABuffer)) | ||
} | ||
} | ||
|
||
func TestWriteBoundary(t *testing.T) { | ||
n, err := smallMemoryProcess.WriteAt(tooBigABuffer, 0) | ||
if err == nil { | ||
t.Fatal("Should have reported an error and didn't") | ||
} | ||
if n != len(smallMemoryVM.memory) { | ||
t.Fatalf("Number of written bytes was %d, should have been 0", n) | ||
} | ||
} | ||
|
||
func TestReadBoundary(t *testing.T) { | ||
buf := make([]byte, 300) | ||
n, err := smallMemoryProcess.ReadAt(buf, 0) | ||
if err == nil { | ||
t.Fatal("Should have reported an error and didn't") | ||
} | ||
if n != len(smallMemoryVM.memory) { | ||
t.Fatalf("Number of written bytes was %d, should have been 0", n) | ||
} | ||
} | ||
|
||
func TestReadEmpty(t *testing.T) { | ||
buf := make([]byte, 300) | ||
n, err := emptyMemoryProcess.ReadAt(buf, 0) | ||
if err == nil { | ||
t.Fatal("Should have reported an error and didn't") | ||
} | ||
if n != 0 { | ||
t.Fatalf("Number of written bytes was %d, should have been 0", n) | ||
} | ||
} | ||
|
||
func TestReadOffset(t *testing.T) { | ||
buf0 := make([]byte, 2) | ||
n0, err := smallMemoryProcess.ReadAt(buf0, 0) | ||
if err != nil { | ||
t.Fatalf("Error reading 1-byte buffer: %v", err) | ||
} | ||
if n0 != 2 { | ||
t.Fatalf("Read %d bytes, expected 2", n0) | ||
} | ||
|
||
buf1 := make([]byte, 1) | ||
n1, err := smallMemoryProcess.ReadAt(buf1, 1) | ||
if err != nil { | ||
t.Fatalf("Error reading 1-byte buffer: %v", err) | ||
} | ||
if n1 != 1 { | ||
t.Fatalf("Read %d bytes, expected 1.", n0) | ||
} | ||
|
||
if buf0[1] != buf1[0] { | ||
t.Fatal("Read two different bytes from what should be the same location") | ||
} | ||
} | ||
|
||
func TestWriteEmpty(t *testing.T) { | ||
n, err := emptyMemoryProcess.WriteAt(tooBigABuffer, 0) | ||
if err == nil { | ||
t.Fatal("Should have reported an error and didn't") | ||
} | ||
if n != 0 { | ||
t.Fatalf("Number of written bytes was %d, should have been 0", n) | ||
} | ||
} | ||
|
||
func TestWriteOffset(t *testing.T) { | ||
vm := &VM{memory: make([]byte, 300)} | ||
proc := &Process{vm: vm} | ||
|
||
n, err := proc.WriteAt(tooBigABuffer, 2) | ||
if err != nil { | ||
t.Fatalf("error writing to buffer: %v", err) | ||
} | ||
if n != len(tooBigABuffer) { | ||
t.Fatalf("Number of written bytes was %d, should have been %d", n, len(tooBigABuffer)) | ||
} | ||
|
||
if vm.memory[0] != 0 || vm.memory[1] != 0 || vm.memory[2] != tooBigABuffer[0] { | ||
t.Fatal("Writing at offset didn't work") | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could modify
importer
to take thef func(*Process, int32) int32)
argument, close around it and return the usualfunc(string) (*wasm.Module, error)
importer function...(just a suggestion. it might make things less obvious, though...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought of that, and I think this is not the right approach: I can only imagine one real-life use case when a host function needs not access the memory: reading integer value from a system-specific file (like
/proc/$$/fd
or/dev/urandom
) and that is by no means the most general use case, so it feels like an unnecessary complexity to me.Furthermore, the spec specifies that the store should be passed to host functions so it seems normal to do that in any case, just as the spec expects an interpreter to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok.