156 lines
3.9 KiB
Go
156 lines
3.9 KiB
Go
// 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 (
|
|
"bytes"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/go-interpreter/wagon/wasm"
|
|
)
|
|
|
|
func TestHostCall(t *testing.T) {
|
|
const secretValue = 0xdeadbeef
|
|
|
|
var secretVariable int = 0
|
|
|
|
// a host function that can be called by WASM code.
|
|
testHostFunction := func() {
|
|
secretVariable = secretValue
|
|
}
|
|
|
|
m := wasm.NewModule()
|
|
m.Start = &wasm.SectionStartFunction{Index: 0}
|
|
|
|
// A function signature. Both the host and WASM function
|
|
// have the same signature.
|
|
fsig := wasm.FunctionSig{
|
|
Form: 0,
|
|
ParamTypes: []wasm.ValueType{},
|
|
ReturnTypes: []wasm.ValueType{},
|
|
}
|
|
|
|
// List of all function types available in this module.
|
|
// There is only one: (func [] -> [])
|
|
m.Types = &wasm.SectionTypes{
|
|
Entries: []wasm.FunctionSig{fsig, fsig},
|
|
}
|
|
|
|
m.Function = &wasm.SectionFunctions{
|
|
Types: []uint32{0, 0},
|
|
}
|
|
|
|
// The body of the start function, that should only
|
|
// call the host function
|
|
fb := wasm.FunctionBody{
|
|
Module: m,
|
|
Locals: []wasm.LocalEntry{},
|
|
// code should disassemble to:
|
|
// call 1 (which is host)
|
|
// end
|
|
Code: []byte{0x02, 0x00, 0x10, 0x01, 0x0b},
|
|
}
|
|
|
|
// There was no call to `ReadModule` so this part emulates
|
|
// how the module object would look like if the function
|
|
// had been called.
|
|
m.FunctionIndexSpace = []wasm.Function{
|
|
{
|
|
Sig: &fsig,
|
|
Body: &fb,
|
|
},
|
|
{
|
|
Sig: &fsig,
|
|
Host: reflect.ValueOf(testHostFunction),
|
|
},
|
|
}
|
|
|
|
m.Code = &wasm.SectionCode{
|
|
Bodies: []wasm.FunctionBody{fb},
|
|
}
|
|
|
|
// Once called, NewVM will execute the module's main
|
|
// function.
|
|
vm, err := NewVM(m)
|
|
if err != nil {
|
|
t.Fatalf("Error creating VM: %v", vm)
|
|
}
|
|
|
|
if len(vm.funcs) < 1 {
|
|
t.Fatalf("Need at least a start function!")
|
|
}
|
|
|
|
// Only one entry, which should be a function
|
|
if secretVariable != secretValue {
|
|
t.Fatalf("x is %d instead of %d", secretVariable, secretValue)
|
|
}
|
|
}
|
|
|
|
var moduleCallHost = []byte{
|
|
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x06, 0x60, 0x01, 0x7F, 0x00, 0x60,
|
|
0x01, 0x7F, 0x01, 0x7F, 0x60, 0x00, 0x01, 0x7F, 0x60, 0x00, 0x00, 0x60, 0x00, 0x01, 0x7C, 0x60,
|
|
0x01, 0x7F, 0x01, 0x7F, 0x02, 0x0F, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x07, 0x5F, 0x6E, 0x61, 0x74,
|
|
0x69, 0x76, 0x65, 0x00, 0x05, 0x03, 0x02, 0x01, 0x02, 0x04, 0x04, 0x01, 0x70, 0x00, 0x02, 0x06,
|
|
0x10, 0x03, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x01, 0x41, 0x00, 0x0B, 0x7F, 0x00, 0x41, 0x01,
|
|
0x0B, 0x07, 0x09, 0x01, 0x05, 0x5F, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x01, 0x09, 0x01, 0x00, 0x0A,
|
|
0x08, 0x01, 0x06, 0x00, 0x41, 0x00, 0x10, 0x00, 0x0B,
|
|
}
|
|
|
|
func add3(x int32) int32 {
|
|
return x + 3
|
|
}
|
|
|
|
func importer(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(add3),
|
|
Body: &wasm.FunctionBody{},
|
|
},
|
|
}
|
|
m.Export = &wasm.SectionExports{
|
|
Entries: map[string]wasm.ExportEntry{
|
|
"_native": {
|
|
FieldStr: "_naive",
|
|
Kind: wasm.ExternalFunction,
|
|
Index: 0,
|
|
},
|
|
},
|
|
}
|
|
|
|
return m, nil
|
|
}
|
|
|
|
func TestHostSymbolCall(t *testing.T) {
|
|
m, err := wasm.ReadModule(bytes.NewReader(moduleCallHost), importer)
|
|
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)
|
|
}
|
|
rtrns, err := vm.ExecCode(1)
|
|
if err != nil {
|
|
t.Fatalf("Error executing the default function: %v", err)
|
|
}
|
|
if int(rtrns.(uint32)) != 3 {
|
|
t.Fatalf("Did not get the right value. Got %d, wanted %d", rtrns, 3)
|
|
}
|
|
}
|