// 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 ( "fmt" "io" "github.com/go-interpreter/wagon/wasm/leb128" ) // ValueType represents the type of a valid value in Wasm type ValueType int8 const ( ValueTypeI32 ValueType = -0x01 ValueTypeI64 ValueType = -0x02 ValueTypeF32 ValueType = -0x03 ValueTypeF64 ValueType = -0x04 ) var valueTypeStrMap = map[ValueType]string{ ValueTypeI32: "i32", ValueTypeI64: "i64", ValueTypeF32: "f32", ValueTypeF64: "f64", } func (t ValueType) String() string { str, ok := valueTypeStrMap[t] if !ok { str = fmt.Sprintf("", int8(t)) } return str } // TypeFunc represents the value type of a function const TypeFunc int = -0x20 func readValueType(r io.Reader) (ValueType, error) { v, err := leb128.ReadVarint32(r) return ValueType(v), err } // BlockType represents the signature of a structured block type BlockType ValueType // varint7 const BlockTypeEmpty BlockType = -0x40 func readBlockType(r io.Reader) (BlockType, error) { b, err := leb128.ReadVarint32(r) return BlockType(b), err } func (b BlockType) String() string { if b == BlockTypeEmpty { return "" } return ValueType(b).String() } // ElemType describes the type of a table's elements type ElemType int // varint7 // ElemTypeAnyFunc descibres an any_func value const ElemTypeAnyFunc ElemType = -0x10 func readElemType(r io.Reader) (ElemType, error) { b, err := leb128.ReadVarint32(r) return ElemType(b), err } func (t ElemType) String() string { if t == ElemTypeAnyFunc { return "anyfunc" } return "" } // FunctionSig describes the signature of a declared function in a WASM module type FunctionSig struct { // value for the 'func` type constructor Form int8 // The parameter types of the function ParamTypes []ValueType ReturnTypes []ValueType } func (f FunctionSig) String() string { return fmt.Sprintf(" %v>", f.ParamTypes, f.ReturnTypes) } type InvalidTypeConstructorError struct { Wanted int Got int } func (e InvalidTypeConstructorError) Error() string { return fmt.Sprintf("wasm: invalid type constructor: wanted %d, got %d", e.Wanted, e.Got) } func readFunction(r io.Reader) (FunctionSig, error) { f := FunctionSig{} form, err := leb128.ReadVarint32(r) if err != nil { return f, err } f.Form = int8(form) paramCount, err := leb128.ReadVarUint32(r) if err != nil { return f, err } f.ParamTypes = make([]ValueType, paramCount) for i := range f.ParamTypes { f.ParamTypes[i], err = readValueType(r) if err != nil { return f, err } } returnCount, err := leb128.ReadVarUint32(r) if err != nil { return f, err } f.ReturnTypes = make([]ValueType, returnCount) for i := range f.ReturnTypes { vt, err := readValueType(r) if err != nil { return f, err } f.ReturnTypes[i] = vt } return f, nil } // GlobalVar describes the type and mutability of a declared global variable type GlobalVar struct { Type ValueType // Type of the value stored by the variable Mutable bool // Whether the value of the variable can be changed by the set_global operator } func readGlobalVar(r io.Reader) (*GlobalVar, error) { g := &GlobalVar{} var err error g.Type, err = readValueType(r) if err != nil { return nil, err } m, err := leb128.ReadVarUint32(r) if err != nil { return nil, err } g.Mutable = m == 1 return g, nil } // Table describes a table in a Wasm module. type Table struct { // The type of elements ElementType ElemType Limits ResizableLimits } func readTable(r io.Reader) (*Table, error) { t := Table{} var err error t.ElementType, err = readElemType(r) if err != nil { return nil, err } lims, err := readResizableLimits(r) if err != nil { return nil, err } t.Limits = *lims return &t, err } type Memory struct { Limits ResizableLimits } func readMemory(r io.Reader) (*Memory, error) { lim, err := readResizableLimits(r) if err != nil { return nil, err } return &Memory{*lim}, nil } // External describes the kind of the entry being imported or exported. type External uint8 const ( ExternalFunction External = 0 ExternalTable External = 1 ExternalMemory External = 2 ExternalGlobal External = 3 ) func (e External) String() string { switch e { case ExternalFunction: return "function" case ExternalTable: return "table" case ExternalMemory: return "memory" case ExternalGlobal: return "global" default: return "" } } func readExternal(r io.Reader) (External, error) { bytes, err := readBytes(r, 1) return External(bytes[0]), err } // ResizableLimits describe the limit of a table or linear memory. type ResizableLimits struct { Flags uint32 // 1 if the Maximum field is valid Initial uint32 // initial length (in units of table elements or wasm pages) Maximum uint32 // If flags is 1, it describes the maximum size of the table or memory } func readResizableLimits(r io.Reader) (*ResizableLimits, error) { lim := &ResizableLimits{ Maximum: 0, } f, err := leb128.ReadVarUint32(r) if err != nil { return nil, err } lim.Flags = f lim.Initial, err = leb128.ReadVarUint32(r) if err != nil { return nil, err } if lim.Flags&0x1 != 0 { m, err := leb128.ReadVarUint32(r) if err != nil { return nil, err } lim.Maximum = m } return lim, nil }