land/vendor/github.com/go-interpreter/wagon/wasm/init_expr.go

173 lines
3.5 KiB
Go

// Copyright 2017 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 wasm
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"github.com/go-interpreter/wagon/wasm/leb128"
)
const (
i32Const byte = 0x41
i64Const byte = 0x42
f32Const byte = 0x43
f64Const byte = 0x44
getGlobal byte = 0x23
end byte = 0x0b
)
var ErrEmptyInitExpr = errors.New("wasm: Initializer expression produces no value")
type InvalidInitExprOpError byte
func (e InvalidInitExprOpError) Error() string {
return fmt.Sprintf("wasm: Invalid opcode in initializer expression: %#x", byte(e))
}
type InvalidGlobalIndexError uint32
func (e InvalidGlobalIndexError) Error() string {
return fmt.Sprintf("wasm: Invalid index to global index space: %#x", uint32(e))
}
func readInitExpr(r io.Reader) ([]byte, error) {
b := make([]byte, 1)
buf := new(bytes.Buffer)
r = io.TeeReader(r, buf)
outer:
for {
_, err := io.ReadFull(r, b)
if err != nil {
return nil, err
}
switch b[0] {
case i32Const:
_, err := leb128.ReadVarint32(r)
if err != nil {
return nil, err
}
case i64Const:
_, err := leb128.ReadVarint64(r)
if err != nil {
return nil, err
}
case f32Const:
if _, err := readU32(r); err != nil {
return nil, err
}
case f64Const:
if _, err := readU64(r); err != nil {
return nil, err
}
case getGlobal:
_, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
case end:
break outer
default:
return nil, InvalidInitExprOpError(b[0])
}
}
if buf.Len() == 0 {
return nil, ErrEmptyInitExpr
}
return buf.Bytes(), nil
}
// ExecInitExpr executes an initializer expression and returns an interface{} value
// which can either be int32, int64, float32 or float64.
// It returns an error if the expression is invalid, and nil when the expression
// yields no value.
func (m *Module) ExecInitExpr(expr []byte) (interface{}, error) {
var stack []uint64
var lastVal ValueType
r := bytes.NewReader(expr)
if r.Len() == 0 {
return nil, ErrEmptyInitExpr
}
for {
b, err := r.ReadByte()
if err == io.EOF {
break
} else if err != nil {
return nil, err
}
switch b {
case i32Const:
i, err := leb128.ReadVarint32(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeI32
case i64Const:
i, err := leb128.ReadVarint64(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeI64
case f32Const:
i, err := readU32(r)
if err != nil {
return nil, err
}
stack = append(stack, uint64(i))
lastVal = ValueTypeF32
case f64Const:
i, err := readU64(r)
if err != nil {
return nil, err
}
stack = append(stack, i)
lastVal = ValueTypeF64
case getGlobal:
index, err := leb128.ReadVarUint32(r)
if err != nil {
return nil, err
}
globalVar := m.GetGlobal(int(index))
if globalVar == nil {
return nil, InvalidGlobalIndexError(index)
}
lastVal = globalVar.Type.Type
case end:
break
default:
return nil, InvalidInitExprOpError(b)
}
}
if len(stack) == 0 {
return nil, nil
}
v := stack[len(stack)-1]
switch lastVal {
case ValueTypeI32:
return int32(v), nil
case ValueTypeI64:
return int64(v), nil
case ValueTypeF32:
return math.Float32frombits(uint32(v)), nil
case ValueTypeF64:
return math.Float64frombits(uint64(v)), nil
default:
panic(fmt.Sprintf("Invalid value type produced by initializer expression: %d", int8(lastVal)))
}
}