Add assert_trap tests. (#28)

This commit is contained in:
Takeshi Yoneda
2021-10-11 12:11:40 +09:00
committed by GitHub
parent 5d555254ac
commit 775c865b67
6 changed files with 106 additions and 29 deletions

View File

@@ -2,10 +2,10 @@ name: Test
on:
pull_request:
branches:
- master
- main
push:
branches:
- master
- main
jobs:
style:

2
NOTICE
View File

@@ -1,2 +1,2 @@
gasm
Gasm
Copyright 2020-2021 Takeshi Yoneda

View File

@@ -1,10 +1,8 @@
# Gasm
A minimal implementation of Wasm Virtual machine purely written in Go.
The VM can be embedded in your Go program without any dependency like cgo, and enables Gophers to write Wasm host environments easily.
A minimal implementation of Wasm Virtual machine purely written in Go. The VM passes all the [Wasm Spec test suites](https://github.com/WebAssembly/spec/tree/master/test/core) and is fully compatible Wasm v1.0 Specification.
The implementation is quite straightforward and I hope this code would be a
good starting point for novices to learn Wasm spec.
The VM can be embedded in your Go program without any dependency like cgo, and enables Gophers to write Wasm host environments easily.
## Example
@@ -13,7 +11,6 @@ func Test_fibonacci(t *testing.T) {
binary, _ := os.ReadFile("wasm/fibonacci.wasm")
mod, _ := wasm.DecodeModule(binary)
vm, _ := wasm.NewVM()
wasi.NewEnvironment().RegisterToVirtualMachine(vm)
vm.InstantiateModule(mod, "test")
for _, c := range []struct {
@@ -32,4 +29,3 @@ func Test_fibonacci(t *testing.T) {
## References
- https://webassembly.github.io/spec/core/index.html
- https://github.com/WebAssembly/WASI/blob/main/phases/snapshot/docs.md

View File

@@ -13,6 +13,7 @@ const (
var (
errOverflow32 = errors.New("overflows a 32-bit integer")
errOverflow33 = errors.New("overflows a 33-bit integer")
errOverflow64 = errors.New("overflows a 64-bit integer")
)
@@ -114,10 +115,6 @@ func DecodeInt33AsInt64(r io.Reader) (ret int64, num uint64, err error) {
}
}
if num > 5 {
return 0, 0, errOverflow64
}
// fixme: can be optimized
if shift < 33 && (b&int33Mask3) == int33Mask3 {
ret |= int33Mask4 << shift
@@ -128,6 +125,15 @@ func DecodeInt33AsInt64(r io.Reader) (ret int64, num uint64, err error) {
if ret&int33Mask5 > 0 {
ret = ret - int33Mask6
}
// Over flow checks.
// fixme: can be optimized.
if num > 5 {
return 0, 0, errOverflow33
} else if unused := b & 0b00100000; num == 5 && ret < 0 && unused != 0b00100000 {
return 0, 0, errOverflow33
} else if num == 5 && ret >= 0 && unused != 0x00 {
return 0, 0, errOverflow33
}
return ret, num, nil
}
@@ -147,12 +153,18 @@ func DecodeInt64(r io.Reader) (ret int64, num uint64, err error) {
shift += 7
num++
if b&0x80 == 0 {
if num > 10 {
return 0, 0, errOverflow64
}
if shift < 64 && (b&int64Mask3) == int64Mask3 {
ret |= int64Mask4 << shift
}
// Over flow checks.
// fixme: can be optimized.
if num > 10 {
return 0, 0, errOverflow64
} else if unused := b & 0b00111110; num == 10 && ret < 0 && unused != 0b00111110 {
return 0, 0, errOverflow64
} else if num == 10 && ret >= 0 && unused != 0x00 {
return 0, 0, errOverflow64
}
return
}
}

View File

@@ -109,6 +109,14 @@ func (c command) String() string {
return "{" + msg + "}"
}
func (c command) getAssertReturnArgs(t *testing.T) []uint64 {
var args []uint64
for _, arg := range c.Action.Args {
args = append(args, arg.ToUint64(t))
}
return args
}
func (c command) getAssertReturnArgsExps(t *testing.T) ([]uint64, []uint64) {
var args, exps []uint64
for _, arg := range c.Action.Args {
@@ -264,7 +272,24 @@ func TestSpecification(t *testing.T) {
}
require.Error(t, err, msg)
case "assert_trap":
// TODO:
moduleName := lastInstanceName
if c.Action.Module != "" {
moduleName = c.Action.Module
}
require.NotNil(t, vm)
switch c.Action.ActionType {
case "invoke":
args := c.getAssertReturnArgs(t)
msg = fmt.Sprintf("%s invoke %s (%s)", msg, c.Action.Field, c.Action.Args)
if c.Action.Module != "" {
msg += " in module " + c.Action.Module
}
assert.Panics(t, func() {
_, _, _ = vm.ExecExportedFunction(moduleName, c.Action.Field, args...)
}, msg)
default:
t.Fatalf("unsupported action type type: %v", c)
}
case "assert_invalid":
// TODO:
case "assert_exhaustion":

View File

@@ -717,26 +717,46 @@ func i32wrapi64(vm *VirtualMachine) {
}
func i32truncf32s(vm *VirtualMachine) {
v := math.Float32frombits(uint32(vm.OperandStack.Pop()))
vm.OperandStack.Push(uint64(int32(math.Trunc(float64(v)))))
v := math.Trunc(float64(math.Float32frombits(uint32(vm.OperandStack.Pop()))))
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < math.MinInt32 || v > math.MaxInt32 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(int32(v)))
vm.ActiveContext.PC++
}
func i32truncf32u(vm *VirtualMachine) {
v := math.Float32frombits(uint32(vm.OperandStack.Pop()))
vm.OperandStack.Push(uint64(uint32(math.Trunc(float64(v)))))
v := math.Trunc(float64(math.Float32frombits(uint32(vm.OperandStack.Pop()))))
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < 0 || v > math.MaxUint32 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(uint32(v)))
vm.ActiveContext.PC++
}
func i32truncf64s(vm *VirtualMachine) {
v := math.Float64frombits(vm.OperandStack.Pop())
vm.OperandStack.Push(uint64(int32(math.Trunc(v))))
v := math.Trunc(math.Float64frombits(vm.OperandStack.Pop()))
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < math.MinInt32 || v > math.MaxInt32 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(int32(v)))
vm.ActiveContext.PC++
}
func i32truncf64u(vm *VirtualMachine) {
v := math.Float64frombits(vm.OperandStack.Pop())
vm.OperandStack.Push(uint64(uint32(math.Trunc(v))))
v := math.Trunc(math.Float64frombits(vm.OperandStack.Pop()))
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < 0 || v > math.MaxUint32 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(uint32(v)))
vm.ActiveContext.PC++
}
@@ -754,25 +774,49 @@ func i64extendi32u(vm *VirtualMachine) {
func i64truncf32s(vm *VirtualMachine) {
v := math.Trunc(float64(math.Float32frombits(uint32(vm.OperandStack.Pop()))))
vm.OperandStack.Push(uint64(int64(v)))
res := int64(v)
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < math.MinInt64 || v > 0 && res < 0 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(res))
vm.ActiveContext.PC++
}
func i64truncf32u(vm *VirtualMachine) {
v := math.Trunc(float64(math.Float32frombits(uint32(vm.OperandStack.Pop()))))
vm.OperandStack.Push(uint64(v))
res := uint64(v)
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < 0 || v > float64(res) {
panic("integer overflow")
}
vm.OperandStack.Push(res)
vm.ActiveContext.PC++
}
func i64truncf64s(vm *VirtualMachine) {
v := math.Trunc(math.Float64frombits(vm.OperandStack.Pop()))
vm.OperandStack.Push(uint64(int64(v)))
res := int64(v)
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < math.MinInt64 || v > 0 && res < 0 {
panic("integer overflow")
}
vm.OperandStack.Push(uint64(res))
vm.ActiveContext.PC++
}
func i64truncf64u(vm *VirtualMachine) {
v := math.Trunc(math.Float64frombits(vm.OperandStack.Pop()))
vm.OperandStack.Push(uint64(v))
res := uint64(v)
if math.IsNaN(v) {
panic("invalid conversion")
} else if v < 0 || v > float64(res) {
panic("integer overflow")
}
vm.OperandStack.Push(res)
vm.ActiveContext.PC++
}