|
|
|
|
@@ -330,7 +330,7 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, config wazero.R
|
|
|
|
|
for _, f := range files {
|
|
|
|
|
filename := f.Name()
|
|
|
|
|
if strings.HasSuffix(filename, ".json") {
|
|
|
|
|
jsonfiles = append(jsonfiles, testdataPath(filename))
|
|
|
|
|
jsonfiles = append(jsonfiles, filename)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -339,161 +339,166 @@ func Run(t *testing.T, testDataFS embed.FS, ctx context.Context, config wazero.R
|
|
|
|
|
require.True(t, len(jsonfiles) > 1, "len(jsonfiles)=%d (not greater than one)", len(jsonfiles))
|
|
|
|
|
|
|
|
|
|
for _, f := range jsonfiles {
|
|
|
|
|
raw, err := testDataFS.ReadFile(f)
|
|
|
|
|
RunJson(t, testDataFS, f, ctx, config)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RunJson runs the test case described by the given spectest JSON file name in the testDataFS file system.
|
|
|
|
|
func RunJson(t *testing.T, testDataFS embed.FS, f string, ctx context.Context, config wazero.RuntimeConfig) {
|
|
|
|
|
raw, err := testDataFS.ReadFile(testdataPath(f))
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
var base testbase
|
|
|
|
|
require.NoError(t, json.Unmarshal(raw, &base))
|
|
|
|
|
|
|
|
|
|
wastName := basename(base.SourceFile)
|
|
|
|
|
|
|
|
|
|
t.Run(wastName, func(t *testing.T) {
|
|
|
|
|
r := wazero.NewRuntimeWithConfig(ctx, config)
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, r.Close(ctx))
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
_, err := r.InstantiateWithConfig(ctx, spectestWasm, wazero.NewModuleConfig())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
var base testbase
|
|
|
|
|
require.NoError(t, json.Unmarshal(raw, &base))
|
|
|
|
|
modules := make(map[string]api.Module)
|
|
|
|
|
var lastInstantiatedModule api.Module
|
|
|
|
|
for i := 0; i < len(base.Commands); i++ {
|
|
|
|
|
c := &base.Commands[i]
|
|
|
|
|
t.Run(fmt.Sprintf("%s/line:%d", c.CommandType, c.Line), func(t *testing.T) {
|
|
|
|
|
msg := fmt.Sprintf("%s:%d %s", wastName, c.Line, c.CommandType)
|
|
|
|
|
switch c.CommandType {
|
|
|
|
|
case "module":
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
|
|
|
|
|
wastName := basename(base.SourceFile)
|
|
|
|
|
|
|
|
|
|
t.Run(wastName, func(t *testing.T) {
|
|
|
|
|
r := wazero.NewRuntimeWithConfig(ctx, config)
|
|
|
|
|
defer func() {
|
|
|
|
|
require.NoError(t, r.Close(ctx))
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
_, err := r.InstantiateWithConfig(ctx, spectestWasm, wazero.NewModuleConfig())
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
|
|
modules := make(map[string]api.Module)
|
|
|
|
|
var lastInstantiatedModule api.Module
|
|
|
|
|
for i := 0; i < len(base.Commands); i++ {
|
|
|
|
|
c := &base.Commands[i]
|
|
|
|
|
t.Run(fmt.Sprintf("%s/line:%d", c.CommandType, c.Line), func(t *testing.T) {
|
|
|
|
|
msg := fmt.Sprintf("%s:%d %s", wastName, c.Line, c.CommandType)
|
|
|
|
|
switch c.CommandType {
|
|
|
|
|
case "module":
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
|
|
|
|
|
var registeredName string
|
|
|
|
|
if next := i + 1; next < len(base.Commands) && base.Commands[next].CommandType == "register" {
|
|
|
|
|
registeredName = base.Commands[next].As
|
|
|
|
|
i++ // Skip the entire "register" command.
|
|
|
|
|
}
|
|
|
|
|
mod, err := r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig().WithName(registeredName))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
if c.Name != "" {
|
|
|
|
|
modules[c.Name] = mod
|
|
|
|
|
}
|
|
|
|
|
lastInstantiatedModule = mod
|
|
|
|
|
case "assert_return", "action":
|
|
|
|
|
m := lastInstantiatedModule
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
m = modules[c.Action.Module]
|
|
|
|
|
}
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args, exps := c.getAssertReturnArgsExps()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
fn := m.ExportedFunction(c.Action.Field)
|
|
|
|
|
results, err := fn.Call(ctx, args...)
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
require.Equal(t, len(exps), len(results), msg)
|
|
|
|
|
laneTypes := map[int]string{}
|
|
|
|
|
for i, expV := range c.Exps {
|
|
|
|
|
if expV.ValType == "v128" {
|
|
|
|
|
laneTypes[i] = expV.LaneType
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
matched, valuesMsg := valuesEq(results, exps, fn.Definition().ResultTypes(), laneTypes)
|
|
|
|
|
require.True(t, matched, msg+"\n"+valuesMsg)
|
|
|
|
|
case "get":
|
|
|
|
|
_, exps := c.getAssertReturnArgsExps()
|
|
|
|
|
require.Equal(t, 1, len(exps))
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
global := m.ExportedGlobal(c.Action.Field)
|
|
|
|
|
require.NotNil(t, global)
|
|
|
|
|
require.Equal(t, exps[0], global.Get(), msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_malformed":
|
|
|
|
|
if c.ModuleType != "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
}
|
|
|
|
|
case "assert_trap":
|
|
|
|
|
m := lastInstantiatedModule
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
m = modules[c.Action.Module]
|
|
|
|
|
}
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args := c.getAssertReturnArgs()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
_, err := m.ExportedFunction(c.Action.Field).Call(ctx, args...)
|
|
|
|
|
require.ErrorIs(t, err, c.expectedError(), msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_invalid":
|
|
|
|
|
if c.ModuleType == "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
t.Skip()
|
|
|
|
|
}
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
case "assert_exhaustion":
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args := c.getAssertReturnArgs()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
_, err := lastInstantiatedModule.ExportedFunction(c.Action.Field).Call(ctx, args...)
|
|
|
|
|
require.ErrorIs(t, err, wasmruntime.ErrRuntimeStackOverflow, msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_unlinkable":
|
|
|
|
|
if c.ModuleType == "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
t.Skip()
|
|
|
|
|
}
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
case "assert_uninstantiable":
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
if c.Text == "out of bounds table access" {
|
|
|
|
|
// This is not actually an instantiation error, but assert_trap in the original wast, but wast2json translates it to assert_uninstantiable.
|
|
|
|
|
// Anyway, this spectest case expects the error due to active element offset ouf of bounds
|
|
|
|
|
// "after" instantiation while retaining function instances used for elements.
|
|
|
|
|
// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
|
|
|
|
|
//
|
|
|
|
|
// In practice, such a module instance can be used for invoking functions without any issue. In addition, we have to
|
|
|
|
|
// retain functions after the expected "instantiation" failure, so in wazero we choose to not raise error in that case.
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
} else {
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported command type: %s", c)
|
|
|
|
|
var registeredName string
|
|
|
|
|
if next := i + 1; next < len(base.Commands) && base.Commands[next].CommandType == "register" {
|
|
|
|
|
registeredName = base.Commands[next].As
|
|
|
|
|
i++ // Skip the entire "register" command.
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
mod, err := r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig().WithName(registeredName))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
if c.Name != "" {
|
|
|
|
|
modules[c.Name] = mod
|
|
|
|
|
}
|
|
|
|
|
lastInstantiatedModule = mod
|
|
|
|
|
case "assert_return", "action":
|
|
|
|
|
m := lastInstantiatedModule
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
m = modules[c.Action.Module]
|
|
|
|
|
}
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args, exps := c.getAssertReturnArgsExps()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
fn := m.ExportedFunction(c.Action.Field)
|
|
|
|
|
results, err := fn.Call(ctx, args...)
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
require.Equal(t, len(exps), len(results), msg)
|
|
|
|
|
laneTypes := map[int]string{}
|
|
|
|
|
for i, expV := range c.Exps {
|
|
|
|
|
if expV.ValType == "v128" {
|
|
|
|
|
laneTypes[i] = expV.LaneType
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
matched, valuesMsg := valuesEq(results, exps, fn.Definition().ResultTypes(), laneTypes)
|
|
|
|
|
require.True(t, matched, msg+"\n"+valuesMsg)
|
|
|
|
|
case "get":
|
|
|
|
|
_, exps := c.getAssertReturnArgsExps()
|
|
|
|
|
require.Equal(t, 1, len(exps))
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
global := m.ExportedGlobal(c.Action.Field)
|
|
|
|
|
require.NotNil(t, global)
|
|
|
|
|
require.Equal(t, exps[0], global.Get(), msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_malformed":
|
|
|
|
|
if c.ModuleType != "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
}
|
|
|
|
|
case "assert_trap":
|
|
|
|
|
m := lastInstantiatedModule
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
m = modules[c.Action.Module]
|
|
|
|
|
}
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args := c.getAssertReturnArgs()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
_, err := m.ExportedFunction(c.Action.Field).Call(ctx, args...)
|
|
|
|
|
require.ErrorIs(t, err, c.expectedError(), msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_invalid":
|
|
|
|
|
if c.ModuleType == "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
t.Skip()
|
|
|
|
|
}
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
case "assert_exhaustion":
|
|
|
|
|
switch c.Action.ActionType {
|
|
|
|
|
case "invoke":
|
|
|
|
|
args := c.getAssertReturnArgs()
|
|
|
|
|
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
|
|
|
|
|
if c.Action.Module != "" {
|
|
|
|
|
msg += " in module " + c.Action.Module
|
|
|
|
|
}
|
|
|
|
|
_, err := lastInstantiatedModule.ExportedFunction(c.Action.Field).Call(ctx, args...)
|
|
|
|
|
require.ErrorIs(t, err, wasmruntime.ErrRuntimeStackOverflow, msg)
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported action type type: %v", c)
|
|
|
|
|
}
|
|
|
|
|
case "assert_unlinkable":
|
|
|
|
|
if c.ModuleType == "text" {
|
|
|
|
|
// We don't support direct loading of wast yet.
|
|
|
|
|
t.Skip()
|
|
|
|
|
}
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
case "assert_uninstantiable":
|
|
|
|
|
buf, err := testDataFS.ReadFile(testdataPath(c.Filename))
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
_, err = r.InstantiateWithConfig(ctx, buf, wazero.NewModuleConfig())
|
|
|
|
|
if c.Text == "out of bounds table access" {
|
|
|
|
|
// This is not actually an instantiation error, but assert_trap in the original wast, but wast2json translates it to assert_uninstantiable.
|
|
|
|
|
// Anyway, this spectest case expects the error due to active element offset ouf of bounds
|
|
|
|
|
// "after" instantiation while retaining function instances used for elements.
|
|
|
|
|
// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/linking.wast#L264-L274
|
|
|
|
|
//
|
|
|
|
|
// In practice, such a module instance can be used for invoking functions without any issue. In addition, we have to
|
|
|
|
|
// retain functions after the expected "instantiation" failure, so in wazero we choose to not raise error in that case.
|
|
|
|
|
require.NoError(t, err, msg)
|
|
|
|
|
} else {
|
|
|
|
|
require.Error(t, err, msg)
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
t.Fatalf("unsupported command type: %s", c)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// basename avoids filepath.Base to ensure a forward slash is used even in Windows.
|
|
|
|
|
|