173 lines
3.5 KiB
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)))
|
||
|
}
|
||
|
}
|