Add assert_trap tests. (#28)
This commit is contained in:
4
.github/workflows/commit.yaml
vendored
4
.github/workflows/commit.yaml
vendored
@@ -2,10 +2,10 @@ name: Test
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
style:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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++
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user