wazevo: supports for LookupFunction API (#1704)

Signed-off-by: Takeshi Yoneda <t.y.mathetake@gmail.com>
This commit is contained in:
Takeshi Yoneda
2023-09-13 12:36:56 +09:00
committed by GitHub
parent 9c4291a581
commit c9019e6406
9 changed files with 177 additions and 131 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
_ "embed"
"math"
"runtime"
"strconv"
"testing"
"time"
@@ -11,11 +12,14 @@ import (
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental/table"
"github.com/tetratelabs/wazero/internal/engine/wazevo"
"github.com/tetratelabs/wazero/internal/platform"
"github.com/tetratelabs/wazero/internal/testing/binaryencoding"
"github.com/tetratelabs/wazero/internal/testing/proxy"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/internal/wasm"
"github.com/tetratelabs/wazero/internal/wasmruntime"
"github.com/tetratelabs/wazero/sys"
)
@@ -30,43 +34,62 @@ var memoryCapacityPages = uint32(2)
var moduleConfig = wazero.NewModuleConfig()
var tests = map[string]func(t *testing.T, r wazero.Runtime){
"huge stack": testHugeStack,
"unreachable": testUnreachable,
"recursive entry": testRecursiveEntry,
"host func memory": testHostFuncMemory,
"host function with context parameter": testHostFunctionContextParameter,
"host function with nested context": testNestedGoContext,
"host function with numeric parameter": testHostFunctionNumericParameter,
"close module with in-flight calls": testCloseInFlight,
"multiple instantiation from same source": testMultipleInstantiation,
"exported function that grows memory": testMemOps,
"import functions with reference type in signature": testReftypeImports,
"overflow integer addition": testOverflow,
"un-signed extend global": testGlobalExtend,
"user-defined primitive in host func": testUserDefinedPrimitiveHostFunc,
"ensures invocations terminate on module close": testEnsureTerminationOnClose,
"call host function indirectly": callHostFunctionIndirect,
type testCase struct {
f func(t *testing.T, r wazero.Runtime)
wazevoSkip bool
}
var tests = map[string]testCase{
"huge stack": {f: testHugeStack, wazevoSkip: true},
"unreachable": {f: testUnreachable},
"recursive entry": {f: testRecursiveEntry},
"host func memory": {f: testHostFuncMemory},
"host function with context parameter": {f: testHostFunctionContextParameter},
"host function with nested context": {f: testNestedGoContext},
"host function with numeric parameter": {f: testHostFunctionNumericParameter},
"close module with in-flight calls": {f: testCloseInFlight},
"multiple instantiation from same source": {f: testMultipleInstantiation},
"exported function that grows memory": {f: testMemOps, wazevoSkip: true},
"import functions with reference type in signature": {f: testReftypeImports, wazevoSkip: true},
"overflow integer addition": {f: testOverflow},
"un-signed extend global": {f: testGlobalExtend},
"user-defined primitive in host func": {f: testUserDefinedPrimitiveHostFunc},
"ensures invocations terminate on module close": {f: testEnsureTerminationOnClose, wazevoSkip: true},
"call host function indirectly": {f: callHostFunctionIndirect, wazevoSkip: true},
"lookup function": {f: testLookupFunction},
}
func TestEngineCompiler(t *testing.T) {
if !platform.CompilerSupported() {
t.Skip()
}
runAllTests(t, tests, wazero.NewRuntimeConfigCompiler().WithCloseOnContextDone(true))
runAllTests(t, tests, wazero.NewRuntimeConfigCompiler().WithCloseOnContextDone(true), false)
}
func TestEngineInterpreter(t *testing.T) {
runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter().WithCloseOnContextDone(true))
runAllTests(t, tests, wazero.NewRuntimeConfigInterpreter().WithCloseOnContextDone(true), false)
}
func runAllTests(t *testing.T, tests map[string]func(t *testing.T, r wazero.Runtime), config wazero.RuntimeConfig) {
for name, testf := range tests {
name := name // pin
testf := testf // pin
func TestEngineWazevo(t *testing.T) {
if runtime.GOARCH != "arm64" {
t.Skip()
}
config := wazero.NewRuntimeConfigInterpreter()
wazevo.ConfigureWazevo(config)
runAllTests(t, tests, config.WithCloseOnContextDone(true), true)
}
func runAllTests(t *testing.T, tests map[string]testCase, config wazero.RuntimeConfig, isWazevo bool) {
for name, tc := range tests {
name := name
tc := tc
if isWazevo && tc.wazevoSkip {
t.Logf("skipping %s because it is not supported by wazevo", name)
continue
}
t.Run(name, func(t *testing.T) {
t.Parallel()
testf(t, wazero.NewRuntimeWithConfig(testCtx, config))
tc.f(t, wazero.NewRuntimeWithConfig(testCtx, config))
})
}
}
@@ -798,3 +821,61 @@ func testMultipleInstantiation(t *testing.T, r wazero.Runtime) {
require.Equal(t, uint64(1000), after)
}
}
func testLookupFunction(t *testing.T, r wazero.Runtime) {
bin := binaryencoding.EncodeModule(&wasm.Module{
TypeSection: []wasm.FunctionType{{Results: []wasm.ValueType{i32}}},
FunctionSection: []wasm.Index{0, 0, 0},
CodeSection: []wasm.Code{
{Body: []byte{wasm.OpcodeI32Const, 1, wasm.OpcodeEnd}},
{Body: []byte{wasm.OpcodeI32Const, 2, wasm.OpcodeEnd}},
{Body: []byte{wasm.OpcodeI32Const, 3, wasm.OpcodeEnd}},
},
TableSection: []wasm.Table{{Min: 10, Type: wasm.RefTypeFuncref}},
ElementSection: []wasm.ElementSegment{
{
OffsetExpr: wasm.ConstantExpression{
Opcode: wasm.OpcodeI32Const,
Data: []byte{0},
},
TableIndex: 0,
Init: []wasm.Index{2, 0},
},
},
})
inst, err := r.Instantiate(testCtx, bin)
require.NoError(t, err)
t.Run("null reference", func(t *testing.T) {
err = require.CapturePanic(func() {
table.LookupFunction(inst, 0, 3, nil, []wasm.ValueType{i32})
})
require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
})
t.Run("out of range", func(t *testing.T) {
err = require.CapturePanic(func() {
table.LookupFunction(inst, 0, 1000, nil, []wasm.ValueType{i32})
})
require.Equal(t, wasmruntime.ErrRuntimeInvalidTableAccess, err)
})
t.Run("type mismatch", func(t *testing.T) {
err = require.CapturePanic(func() {
table.LookupFunction(inst, 0, 0, []wasm.ValueType{i32}, nil)
})
require.Equal(t, wasmruntime.ErrRuntimeIndirectCallTypeMismatch, err)
})
t.Run("ok", func(t *testing.T) {
f2 := table.LookupFunction(inst, 0, 0, nil, []wasm.ValueType{i32})
res, err := f2.Call(testCtx)
require.NoError(t, err)
require.Equal(t, uint64(3), res[0])
f0 := table.LookupFunction(inst, 0, 1, nil, []wasm.ValueType{i32})
res, err = f0.Call(testCtx)
require.NoError(t, err)
require.Equal(t, uint64(1), res[0])
})
}

View File

@@ -2,32 +2,43 @@ package adhoc
import (
"context"
"runtime"
"sync"
"testing"
"github.com/tetratelabs/wazero"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/internal/engine/wazevo"
"github.com/tetratelabs/wazero/internal/platform"
"github.com/tetratelabs/wazero/internal/testing/hammer"
"github.com/tetratelabs/wazero/internal/testing/require"
"github.com/tetratelabs/wazero/sys"
)
var hammers = map[string]func(t *testing.T, r wazero.Runtime){
var hammers = map[string]testCase{
// Tests here are similar to what's described in /RATIONALE.md, but deviate as they involve blocking functions.
"close importing module while in use": closeImportingModuleWhileInUse,
"close imported module while in use": closeImportedModuleWhileInUse,
"close importing module while in use": {f: closeImportingModuleWhileInUse, wazevoSkip: true},
"close imported module while in use": {f: closeImportedModuleWhileInUse, wazevoSkip: true},
}
func TestEngineCompiler_hammer(t *testing.T) {
if !platform.CompilerSupported() {
t.Skip()
}
runAllTests(t, hammers, wazero.NewRuntimeConfigCompiler())
runAllTests(t, hammers, wazero.NewRuntimeConfigCompiler(), false)
}
func TestEngineInterpreter_hammer(t *testing.T) {
runAllTests(t, hammers, wazero.NewRuntimeConfigInterpreter())
runAllTests(t, hammers, wazero.NewRuntimeConfigInterpreter(), false)
}
func TestEngineWazevo_hammer(t *testing.T) {
if runtime.GOARCH != "arm64" {
t.Skip()
}
c := wazero.NewRuntimeConfigInterpreter()
wazevo.ConfigureWazevo(c)
runAllTests(t, hammers, c, true)
}
func closeImportingModuleWhileInUse(t *testing.T, r wazero.Runtime) {