package wasm
import (
"bytes"
"errors"
"fmt"
"slices"
"strconv"
"strings"
"github.com/tetratelabs/wazero/api"
"github.com/tetratelabs/wazero/experimental"
"github.com/tetratelabs/wazero/internal/leb128"
)
const maximumValuesOnStack = 1 << 27
func (m *Module ) validateFunction (sts *stacks , enabledFeatures api .CoreFeatures , idx Index , functions []Index ,
globals []GlobalType , memory *Memory , tables []Table , declaredFunctionIndexes map [Index ]struct {}, br *bytes .Reader ,
) error {
return m .validateFunctionWithMaxStackValues (sts , enabledFeatures , idx , functions , globals , memory , tables , maximumValuesOnStack , declaredFunctionIndexes , br )
}
func readMemArg(pc uint64 , body []byte ) (align , offset uint32 , read uint64 , err error ) {
align , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
err = fmt .Errorf ("read memory align: %v" , err )
return
}
if align >= 32 {
err = fmt .Errorf ("invalid memory alignment" )
return
}
read += num
offset , num , err = leb128 .LoadUint32 (body [pc +num :])
if err != nil {
err = fmt .Errorf ("read memory offset: %v" , err )
return
}
read += num
return align , offset , read , nil
}
func (m *Module ) validateFunctionWithMaxStackValues (
sts *stacks ,
enabledFeatures api .CoreFeatures ,
idx Index ,
functions []Index ,
globals []GlobalType ,
memory *Memory ,
tables []Table ,
maxStackValues int ,
declaredFunctionIndexes map [Index ]struct {},
br *bytes .Reader ,
) error {
functionType := &m .TypeSection [m .FunctionSection [idx ]]
code := &m .CodeSection [idx ]
body := code .Body
localTypes := code .LocalTypes
sts .reset (functionType )
valueTypeStack := &sts .vs
controlBlockStack := &sts .cs
for pc := uint64 (0 ); pc < uint64 (len (body )); pc ++ {
op := body [pc ]
if false {
var instName string
if op == OpcodeMiscPrefix {
instName = MiscInstructionName (body [pc +1 ])
} else if op == OpcodeVecPrefix {
instName = VectorInstructionName (body [pc +1 ])
} else if op == OpcodeAtomicPrefix {
instName = AtomicInstructionName (body [pc +1 ])
} else {
instName = InstructionName (op )
}
fmt .Printf ("handling %s, stack=%s, blocks: %v\n" , instName , valueTypeStack .stack , controlBlockStack )
}
if len (controlBlockStack .stack ) == 0 {
return fmt .Errorf ("unexpected end of function at pc=%#x" , pc )
}
if OpcodeI32Load <= op && op <= OpcodeI64Store32 {
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , InstructionName (op ))
}
pc ++
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
pc += read - 1
switch op {
case OpcodeI32Load :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeF32Load :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeI32Store :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeF32Store :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI64Load :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeF64Load :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeI64Store :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeF64Store :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI32Load8S :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32Load8U :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Load8S , OpcodeI64Load8U :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI32Store8 :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI64Store8 :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI32Load16S , OpcodeI32Load16U :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Load16S , OpcodeI64Load16U :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI32Store16 :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI64Store16 :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeI64Load32S , OpcodeI64Load32U :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI64Store32 :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
}
} else if OpcodeMemorySize <= op && op <= OpcodeMemoryGrow {
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , InstructionName (op ))
}
pc ++
val , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
}
if val != 0 || num != 1 {
return fmt .Errorf ("memory instruction reserved bytes not zero with 1 byte" )
}
switch Opcode (op ) {
case OpcodeMemoryGrow :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeMemorySize :
valueTypeStack .push (ValueTypeI32 )
}
pc += num - 1
} else if OpcodeI32Const <= op && op <= OpcodeF64Const {
pc ++
switch Opcode (op ) {
case OpcodeI32Const :
_ , num , err := leb128 .LoadInt32 (body [pc :])
if err != nil {
return fmt .Errorf ("read i32 immediate: %s" , err )
}
pc += num - 1
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Const :
_ , num , err := leb128 .LoadInt64 (body [pc :])
if err != nil {
return fmt .Errorf ("read i64 immediate: %v" , err )
}
valueTypeStack .push (ValueTypeI64 )
pc += num - 1
case OpcodeF32Const :
valueTypeStack .push (ValueTypeF32 )
pc += 3
case OpcodeF64Const :
valueTypeStack .push (ValueTypeF64 )
pc += 7
}
} else if OpcodeLocalGet <= op && op <= OpcodeGlobalSet {
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
}
pc += num - 1
switch op {
case OpcodeLocalGet :
inputLen := uint32 (len (functionType .Params ))
if l := uint32 (len (localTypes )) + inputLen ; index >= l {
return fmt .Errorf ("invalid local index for %s %d >= %d(=len(locals)+len(parameters))" ,
OpcodeLocalGetName , index , l )
}
if index < inputLen {
valueTypeStack .push (functionType .Params [index ])
} else {
valueTypeStack .push (localTypes [index -inputLen ])
}
case OpcodeLocalSet :
inputLen := uint32 (len (functionType .Params ))
if l := uint32 (len (localTypes )) + inputLen ; index >= l {
return fmt .Errorf ("invalid local index for %s %d >= %d(=len(locals)+len(parameters))" ,
OpcodeLocalSetName , index , l )
}
var expType ValueType
if index < inputLen {
expType = functionType .Params [index ]
} else {
expType = localTypes [index -inputLen ]
}
if err := valueTypeStack .popAndVerifyType (expType ); err != nil {
return err
}
case OpcodeLocalTee :
inputLen := uint32 (len (functionType .Params ))
if l := uint32 (len (localTypes )) + inputLen ; index >= l {
return fmt .Errorf ("invalid local index for %s %d >= %d(=len(locals)+len(parameters))" ,
OpcodeLocalTeeName , index , l )
}
var expType ValueType
if index < inputLen {
expType = functionType .Params [index ]
} else {
expType = localTypes [index -inputLen ]
}
if err := valueTypeStack .popAndVerifyType (expType ); err != nil {
return err
}
valueTypeStack .push (expType )
case OpcodeGlobalGet :
if index >= uint32 (len (globals )) {
return fmt .Errorf ("invalid index for %s" , OpcodeGlobalGetName )
}
valueTypeStack .push (globals [index ].ValType )
case OpcodeGlobalSet :
if index >= uint32 (len (globals )) {
return fmt .Errorf ("invalid global index" )
} else if !globals [index ].Mutable {
return fmt .Errorf ("%s when not mutable" , OpcodeGlobalSetName )
} else if err := valueTypeStack .popAndVerifyType (
globals [index ].ValType ); err != nil {
return err
}
}
} else if op == OpcodeBr {
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
} else if int (index ) >= len (controlBlockStack .stack ) {
return fmt .Errorf ("invalid %s operation: index out of range" , OpcodeBrName )
}
pc += num - 1
target := &controlBlockStack .stack [len (controlBlockStack .stack )-int (index )-1 ]
var targetResultType []ValueType
if target .op == OpcodeLoop {
targetResultType = target .blockType .Params
} else {
targetResultType = target .blockType .Results
}
if err = valueTypeStack .popResults (op , targetResultType , false ); err != nil {
return err
}
valueTypeStack .unreachable ()
} else if op == OpcodeBrIf {
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
} else if int (index ) >= len (controlBlockStack .stack ) {
return fmt .Errorf (
"invalid ln param given for %s: index=%d with %d for the current label stack length" ,
OpcodeBrIfName , index , len (controlBlockStack .stack ))
}
pc += num - 1
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the required operand for %s" , OpcodeBrIfName )
}
target := &controlBlockStack .stack [len (controlBlockStack .stack )-int (index )-1 ]
var targetResultType []ValueType
if target .op == OpcodeLoop {
targetResultType = target .blockType .Params
} else {
targetResultType = target .blockType .Results
}
if err := valueTypeStack .popResults (op , targetResultType , false ); err != nil {
return err
}
for _ , t := range targetResultType {
valueTypeStack .push (t )
}
} else if op == OpcodeBrTable {
pc ++
br .Reset (body [pc :])
nl , num , err := leb128 .DecodeUint32 (br )
if err != nil {
return fmt .Errorf ("read immediate: %w" , err )
}
sts .ls = sts .ls [:0 ]
for i := uint32 (0 ); i < nl ; i ++ {
l , n , err := leb128 .DecodeUint32 (br )
if err != nil {
return fmt .Errorf ("read immediate: %w" , err )
}
num += n
sts .ls = append (sts .ls , l )
}
ln , n , err := leb128 .DecodeUint32 (br )
if err != nil {
return fmt .Errorf ("read immediate: %w" , err )
} else if int (ln ) >= len (controlBlockStack .stack ) {
return fmt .Errorf (
"invalid ln param given for %s: ln=%d with %d for the current label stack length" ,
OpcodeBrTableName , ln , len (controlBlockStack .stack ))
}
pc += n + num - 1
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the required operand for %s" , OpcodeBrTableName )
}
lnLabel := &controlBlockStack .stack [len (controlBlockStack .stack )-1 -int (ln )]
var defaultLabelType []ValueType
if lnLabel .op != OpcodeLoop {
defaultLabelType = slices .Clone (lnLabel .blockType .Results )
} else {
defaultLabelType = slices .Clone (lnLabel .blockType .Params )
}
if enabledFeatures .IsEnabled (api .CoreFeatureReferenceTypes ) {
for i := range defaultLabelType {
index := len (defaultLabelType ) - 1 - i
exp := defaultLabelType [index ]
actual , err := valueTypeStack .pop ()
if err != nil {
return err
}
if actual == valueTypeUnknown {
defaultLabelType [index ] = valueTypeUnknown
} else if actual != exp {
return typeMismatchError (true , OpcodeBrTableName , actual , exp , i )
}
}
} else {
if err = valueTypeStack .popResults (op , defaultLabelType , false ); err != nil {
return err
}
}
for _ , l := range sts .ls {
if int (l ) >= len (controlBlockStack .stack ) {
return fmt .Errorf ("invalid l param given for %s" , OpcodeBrTableName )
}
label := &controlBlockStack .stack [len (controlBlockStack .stack )-1 -int (l )]
var tableLabelType []ValueType
if label .op != OpcodeLoop {
tableLabelType = label .blockType .Results
} else {
tableLabelType = label .blockType .Params
}
if len (defaultLabelType ) != len (tableLabelType ) {
return fmt .Errorf ("inconsistent block type length for %s at %d; %v (ln=%d) != %v (l=%d)" , OpcodeBrTableName , l , defaultLabelType , ln , tableLabelType , l )
}
for i := range defaultLabelType {
if defaultLabelType [i ] != valueTypeUnknown && defaultLabelType [i ] != tableLabelType [i ] {
return fmt .Errorf ("incosistent block type for %s at %d" , OpcodeBrTableName , l )
}
}
}
valueTypeStack .unreachable ()
} else if op == OpcodeCall || op == OpcodeTailCallReturnCall {
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
}
pc += num - 1
if int (index ) >= len (functions ) {
return fmt .Errorf ("invalid function index" )
}
var opcodeName string
if op == OpcodeCall {
opcodeName = OpcodeCallName
} else {
opcodeName = OpcodeTailCallReturnCallName
}
funcType := &m .TypeSection [functions [index ]]
for i := 0 ; i < len (funcType .Params ); i ++ {
if err := valueTypeStack .popAndVerifyType (funcType .Params [len (funcType .Params )-1 -i ]); err != nil {
return fmt .Errorf ("type mismatch on %s operation param type: %v" , opcodeName , err )
}
}
for _ , exp := range funcType .Results {
valueTypeStack .push (exp )
}
if op == OpcodeTailCallReturnCall {
if err := enabledFeatures .RequireEnabled (experimental .CoreFeaturesTailCall ); err != nil {
return fmt .Errorf ("%s invalid as %v" , OpcodeTailCallReturnCallName , err )
}
if err := valueTypeStack .requireStackValues (false , "" , functionType .Results , false ); err != nil {
return err
}
valueTypeStack .unreachable ()
}
} else if op == OpcodeCallIndirect || op == OpcodeTailCallReturnCallIndirect {
pc ++
typeIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
}
pc += num
var opcodeName string
if op == OpcodeCallIndirect {
opcodeName = OpcodeCallIndirectName
} else {
opcodeName = OpcodeTailCallReturnCallIndirectName
}
if int (typeIndex ) >= len (m .TypeSection ) {
return fmt .Errorf ("invalid type index at %s: %d" , opcodeName , typeIndex )
}
tableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read table index: %v" , err )
}
pc += num - 1
if tableIndex != 0 {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("table index must be zero but was %d: %w" , tableIndex , err )
}
}
if tableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("unknown table index: %d" , tableIndex )
}
table := tables [tableIndex ]
if table .Type != RefTypeFuncref {
return fmt .Errorf ("table is not funcref type but was %s for %s" , RefTypeName (table .Type ), opcodeName )
}
if err = valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the offset in table for %s" , opcodeName )
}
funcType := &m .TypeSection [typeIndex ]
for i := 0 ; i < len (funcType .Params ); i ++ {
if err = valueTypeStack .popAndVerifyType (funcType .Params [len (funcType .Params )-1 -i ]); err != nil {
return fmt .Errorf ("type mismatch on %s operation input type" , opcodeName )
}
}
for _ , exp := range funcType .Results {
valueTypeStack .push (exp )
}
if op == OpcodeTailCallReturnCallIndirect {
if err := enabledFeatures .RequireEnabled (experimental .CoreFeaturesTailCall ); err != nil {
return fmt .Errorf ("%s invalid as %v" , OpcodeTailCallReturnCallIndirectName , err )
}
if err := valueTypeStack .requireStackValues (false , "" , functionType .Results , false ); err != nil {
return err
}
valueTypeStack .unreachable ()
}
} else if OpcodeI32Eqz <= op && op <= OpcodeI64Extend32S {
switch op {
case OpcodeI32Eqz :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeI32EqzName , err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32Eq , OpcodeI32Ne , OpcodeI32LtS ,
OpcodeI32LtU , OpcodeI32GtS , OpcodeI32GtU , OpcodeI32LeS ,
OpcodeI32LeU , OpcodeI32GeS , OpcodeI32GeU :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the 1st i32 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd i32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Eqz :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeI64EqzName , err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Eq , OpcodeI64Ne , OpcodeI64LtS ,
OpcodeI64LtU , OpcodeI64GtS , OpcodeI64GtU ,
OpcodeI64LeS , OpcodeI64LeU , OpcodeI64GeS , OpcodeI64GeU :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the 1st i64 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd i64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeF32Eq , OpcodeF32Ne , OpcodeF32Lt , OpcodeF32Gt , OpcodeF32Le , OpcodeF32Ge :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f32 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd f32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeF64Eq , OpcodeF64Ne , OpcodeF64Lt , OpcodeF64Gt , OpcodeF64Le , OpcodeF64Ge :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f64 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd f64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32Clz , OpcodeI32Ctz , OpcodeI32Popcnt :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the i32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32Add , OpcodeI32Sub , OpcodeI32Mul , OpcodeI32DivS ,
OpcodeI32DivU , OpcodeI32RemS , OpcodeI32RemU , OpcodeI32And ,
OpcodeI32Or , OpcodeI32Xor , OpcodeI32Shl , OpcodeI32ShrS ,
OpcodeI32ShrU , OpcodeI32Rotl , OpcodeI32Rotr :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the 1st operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Clz , OpcodeI64Ctz , OpcodeI64Popcnt :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the i64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI64Add , OpcodeI64Sub , OpcodeI64Mul , OpcodeI64DivS ,
OpcodeI64DivU , OpcodeI64RemS , OpcodeI64RemU , OpcodeI64And ,
OpcodeI64Or , OpcodeI64Xor , OpcodeI64Shl , OpcodeI64ShrS ,
OpcodeI64ShrU , OpcodeI64Rotl , OpcodeI64Rotr :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the 1st i64 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd i64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeF32Abs , OpcodeF32Neg , OpcodeF32Ceil ,
OpcodeF32Floor , OpcodeF32Trunc , OpcodeF32Nearest ,
OpcodeF32Sqrt :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF32Add , OpcodeF32Sub , OpcodeF32Mul ,
OpcodeF32Div , OpcodeF32Min , OpcodeF32Max ,
OpcodeF32Copysign :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f32 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd f32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF64Abs , OpcodeF64Neg , OpcodeF64Ceil ,
OpcodeF64Floor , OpcodeF64Trunc , OpcodeF64Nearest ,
OpcodeF64Sqrt :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeF64Add , OpcodeF64Sub , OpcodeF64Mul ,
OpcodeF64Div , OpcodeF64Min , OpcodeF64Max ,
OpcodeF64Copysign :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the 1st f64 operand for %s: %v" , InstructionName (op ), err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the 2nd f64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeI32WrapI64 :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeI32WrapI64Name , err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32TruncF32S , OpcodeI32TruncF32U :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the f32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI32TruncF64S , OpcodeI32TruncF64U :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the f64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64ExtendI32S , OpcodeI64ExtendI32U :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the i32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI64TruncF32S , OpcodeI64TruncF32U :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the f32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeI64TruncF64S , OpcodeI64TruncF64U :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the f64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeF32ConvertI32S , OpcodeF32ConvertI32U :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the i32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF32ConvertI64S , OpcodeF32ConvertI64U :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the i64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF32DemoteF64 :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeF32DemoteF64Name , err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF64ConvertI32S , OpcodeF64ConvertI32U :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the i32 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeF64ConvertI64S , OpcodeF64ConvertI64U :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the i64 operand for %s: %v" , InstructionName (op ), err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeF64PromoteF32 :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeF64PromoteF32Name , err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeI32ReinterpretF32 :
if err := valueTypeStack .popAndVerifyType (ValueTypeF32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeI32ReinterpretF32Name , err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64ReinterpretF64 :
if err := valueTypeStack .popAndVerifyType (ValueTypeF64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeI64ReinterpretF64Name , err )
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeF32ReinterpretI32 :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeF32ReinterpretI32Name , err )
}
valueTypeStack .push (ValueTypeF32 )
case OpcodeF64ReinterpretI64 :
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeF64ReinterpretI64Name , err )
}
valueTypeStack .push (ValueTypeF64 )
case OpcodeI32Extend8S , OpcodeI32Extend16S :
if err := enabledFeatures .RequireEnabled (api .CoreFeatureSignExtensionOps ); err != nil {
return fmt .Errorf ("%s invalid as %v" , instructionNames [op ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , instructionNames [op ], err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeI64Extend8S , OpcodeI64Extend16S , OpcodeI64Extend32S :
if err := enabledFeatures .RequireEnabled (api .CoreFeatureSignExtensionOps ); err != nil {
return fmt .Errorf ("%s invalid as %v" , instructionNames [op ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , instructionNames [op ], err )
}
valueTypeStack .push (ValueTypeI64 )
default :
return fmt .Errorf ("invalid numeric instruction 0x%x" , op )
}
} else if op >= OpcodeRefNull && op <= OpcodeRefFunc {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("%s invalid as %v" , instructionNames [op ], err )
}
switch op {
case OpcodeRefNull :
pc ++
switch reftype := body [pc ]; reftype {
case ValueTypeExternref :
valueTypeStack .push (ValueTypeExternref )
case ValueTypeFuncref :
valueTypeStack .push (ValueTypeFuncref )
default :
return fmt .Errorf ("unknown type for ref.null: 0x%x" , reftype )
}
case OpcodeRefIsNull :
tp , err := valueTypeStack .pop ()
if err != nil {
return fmt .Errorf ("cannot pop the operand for ref.is_null: %v" , err )
} else if !isReferenceValueType (tp ) && tp != valueTypeUnknown {
return fmt .Errorf ("type mismatch: expected reference type but was %s" , ValueTypeName (tp ))
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeRefFunc :
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read function index for ref.func: %v" , err )
}
if _ , ok := declaredFunctionIndexes [index ]; !ok {
return fmt .Errorf ("undeclared function index %d for ref.func" , index )
}
pc += num - 1
valueTypeStack .push (ValueTypeFuncref )
}
} else if op == OpcodeTableGet || op == OpcodeTableSet {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("%s is invalid as %v" , InstructionName (op ), err )
}
pc ++
tableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("read immediate: %v" , err )
}
if tableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("table of index %d not found" , tableIndex )
}
refType := tables [tableIndex ].Type
if op == OpcodeTableGet {
if err := valueTypeStack .popAndVerifyType (api .ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for table.get: %v" , err )
}
valueTypeStack .push (refType )
} else {
if err := valueTypeStack .popAndVerifyType (refType ); err != nil {
return fmt .Errorf ("cannot pop the operand for table.set: %v" , err )
}
if err := valueTypeStack .popAndVerifyType (api .ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for table.set: %v" , err )
}
}
pc += num - 1
} else if op == OpcodeMiscPrefix {
pc ++
miscOp32 , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read misc opcode: %v" , err )
}
pc += num - 1
miscOpcode := byte (miscOp32 )
if uint32 (byte (miscOp32 )) != miscOp32 {
return fmt .Errorf ("invalid misc opcode: %#x" , miscOp32 )
}
if miscOpcode >= OpcodeMiscI32TruncSatF32S && miscOpcode <= OpcodeMiscI64TruncSatF64U {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureNonTrappingFloatToIntConversion ); err != nil {
return fmt .Errorf ("%s invalid as %v" , miscInstructionNames [miscOpcode ], err )
}
var inType , outType ValueType
switch miscOpcode {
case OpcodeMiscI32TruncSatF32S , OpcodeMiscI32TruncSatF32U :
inType , outType = ValueTypeF32 , ValueTypeI32
case OpcodeMiscI32TruncSatF64S , OpcodeMiscI32TruncSatF64U :
inType , outType = ValueTypeF64 , ValueTypeI32
case OpcodeMiscI64TruncSatF32S , OpcodeMiscI64TruncSatF32U :
inType , outType = ValueTypeF32 , ValueTypeI64
case OpcodeMiscI64TruncSatF64S , OpcodeMiscI64TruncSatF64U :
inType , outType = ValueTypeF64 , ValueTypeI64
}
if err := valueTypeStack .popAndVerifyType (inType ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , miscInstructionNames [miscOpcode ], err )
}
valueTypeStack .push (outType )
} else if miscOpcode >= OpcodeMiscMemoryInit && miscOpcode <= OpcodeMiscTableCopy {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureBulkMemoryOperations ); err != nil {
return fmt .Errorf ("%s invalid as %v" , miscInstructionNames [miscOpcode ], err )
}
var params []ValueType
switch miscOpcode {
case OpcodeMiscDataDrop :
if m .DataCountSection == nil {
return fmt .Errorf ("%s requires data count section" , MiscInstructionName (miscOpcode ))
}
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read data segment index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if int (index ) >= len (m .DataSection ) {
return fmt .Errorf ("index %d out of range of data section(len=%d)" , index , len (m .DataSection ))
}
pc += num - 1
case OpcodeMiscMemoryInit , OpcodeMiscMemoryCopy , OpcodeMiscMemoryFill :
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , MiscInstructionName (miscOpcode ))
}
params = []ValueType {ValueTypeI32 , ValueTypeI32 , ValueTypeI32 }
if miscOpcode == OpcodeMiscMemoryInit {
if m .DataCountSection == nil {
return fmt .Errorf ("%s requires data count section" , MiscInstructionName (miscOpcode ))
}
pc ++
index , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read data segment index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if int (index ) >= len (m .DataSection ) {
return fmt .Errorf ("index %d out of range of data section(len=%d)" , index , len (m .DataSection ))
}
pc += num - 1
}
pc ++
val , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read memory index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if val != 0 || num != 1 {
return fmt .Errorf ("%s reserved byte must be zero encoded with 1 byte" , MiscInstructionName (miscOpcode ))
}
if miscOpcode == OpcodeMiscMemoryCopy {
pc ++
val , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read memory index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if val != 0 || num != 1 {
return fmt .Errorf ("%s reserved byte must be zero encoded with 1 byte" , MiscInstructionName (miscOpcode ))
}
}
case OpcodeMiscTableInit :
params = []ValueType {ValueTypeI32 , ValueTypeI32 , ValueTypeI32 }
pc ++
elementIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read element segment index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if int (elementIndex ) >= len (m .ElementSection ) {
return fmt .Errorf ("index %d out of range of element section(len=%d)" , elementIndex , len (m .ElementSection ))
}
pc += num
tableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read source table index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if tableIndex != 0 {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("source table index must be zero for %s as %v" , MiscInstructionName (miscOpcode ), err )
}
}
if tableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("table of index %d not found" , tableIndex )
}
if m .ElementSection [elementIndex ].Type != tables [tableIndex ].Type {
return fmt .Errorf ("type mismatch for table.init: element type %s does not match table type %s" ,
RefTypeName (m .ElementSection [elementIndex ].Type ),
RefTypeName (tables [tableIndex ].Type ),
)
}
pc += num - 1
case OpcodeMiscElemDrop :
pc ++
elementIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read element segment index for %s: %v" , MiscInstructionName (miscOpcode ), err )
} else if int (elementIndex ) >= len (m .ElementSection ) {
return fmt .Errorf ("index %d out of range of element section(len=%d)" , elementIndex , len (m .ElementSection ))
}
pc += num - 1
case OpcodeMiscTableCopy :
params = []ValueType {ValueTypeI32 , ValueTypeI32 , ValueTypeI32 }
pc ++
dstTableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read destination table index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if dstTableIndex != 0 {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("destination table index must be zero for %s as %v" , MiscInstructionName (miscOpcode ), err )
}
}
if dstTableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("table of index %d not found" , dstTableIndex )
}
pc += num
srcTableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read source table index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if srcTableIndex != 0 {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("source table index must be zero for %s as %v" , MiscInstructionName (miscOpcode ), err )
}
}
if srcTableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("table of index %d not found" , srcTableIndex )
}
if tables [srcTableIndex ].Type != tables [dstTableIndex ].Type {
return fmt .Errorf ("table type mismatch for table.copy: %s (src) != %s (dst)" ,
RefTypeName (tables [srcTableIndex ].Type ), RefTypeName (tables [dstTableIndex ].Type ))
}
pc += num - 1
}
for _ , p := range params {
if err := valueTypeStack .popAndVerifyType (p ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , miscInstructionNames [miscOpcode ], err )
}
}
} else if miscOpcode >= OpcodeMiscTableGrow && miscOpcode <= OpcodeMiscTableFill {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("%s invalid as %v" , miscInstructionNames [miscOpcode ], err )
}
pc ++
tableIndex , num , err := leb128 .LoadUint32 (body [pc :])
if err != nil {
return fmt .Errorf ("failed to read table index for %s: %v" , MiscInstructionName (miscOpcode ), err )
}
if tableIndex >= uint32 (len (tables )) {
return fmt .Errorf ("table of index %d not found" , tableIndex )
}
pc += num - 1
var params , results []ValueType
reftype := tables [tableIndex ].Type
if miscOpcode == OpcodeMiscTableGrow {
params = []ValueType {ValueTypeI32 , reftype }
results = []ValueType {ValueTypeI32 }
} else if miscOpcode == OpcodeMiscTableSize {
results = []ValueType {ValueTypeI32 }
} else if miscOpcode == OpcodeMiscTableFill {
params = []ValueType {ValueTypeI32 , reftype , ValueTypeI32 }
}
for _ , p := range params {
if err := valueTypeStack .popAndVerifyType (p ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , miscInstructionNames [miscOpcode ], err )
}
}
for _ , r := range results {
valueTypeStack .push (r )
}
} else {
return fmt .Errorf ("unknown misc opcode %#x" , miscOpcode )
}
} else if op == OpcodeVecPrefix {
pc ++
vecOpcode := body [pc ]
if err := enabledFeatures .RequireEnabled (api .CoreFeatureSIMD ); err != nil {
return fmt .Errorf ("%s invalid as %v" , vectorInstructionName [vecOpcode ], err )
}
switch vecOpcode {
case OpcodeVecV128Const :
if int (pc +16 ) >= len (body ) {
return fmt .Errorf ("cannot read constant vector value for %s" , vectorInstructionName [vecOpcode ])
}
pc += 16
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128AnyTrue , OpcodeVecI8x16AllTrue , OpcodeVecI16x8AllTrue , OpcodeVecI32x4AllTrue , OpcodeVecI64x2AllTrue ,
OpcodeVecI8x16BitMask , OpcodeVecI16x8BitMask , OpcodeVecI32x4BitMask , OpcodeVecI64x2BitMask :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeVecV128Load , OpcodeVecV128Load8x8s , OpcodeVecV128Load8x8u , OpcodeVecV128Load16x4s , OpcodeVecV128Load16x4u ,
OpcodeVecV128Load32x2s , OpcodeVecV128Load32x2u , OpcodeVecV128Load8Splat , OpcodeVecV128Load16Splat ,
OpcodeVecV128Load32Splat , OpcodeVecV128Load64Splat ,
OpcodeVecV128Load32zero , OpcodeVecV128Load64zero :
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , VectorInstructionName (vecOpcode ))
}
pc ++
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
pc += read - 1
var maxAlign uint32
switch vecOpcode {
case OpcodeVecV128Load :
maxAlign = 128 / 8
case OpcodeVecV128Load8x8s , OpcodeVecV128Load8x8u , OpcodeVecV128Load16x4s , OpcodeVecV128Load16x4u ,
OpcodeVecV128Load32x2s , OpcodeVecV128Load32x2u :
maxAlign = 64 / 8
case OpcodeVecV128Load8Splat :
maxAlign = 1
case OpcodeVecV128Load16Splat :
maxAlign = 16 / 8
case OpcodeVecV128Load32Splat :
maxAlign = 32 / 8
case OpcodeVecV128Load64Splat :
maxAlign = 64 / 8
case OpcodeVecV128Load32zero :
maxAlign = 32 / 8
case OpcodeVecV128Load64zero :
maxAlign = 64 / 8
}
if 1 <<align > maxAlign {
return fmt .Errorf ("invalid memory alignment %d for %s" , align , VectorInstructionName (vecOpcode ))
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , VectorInstructionName (vecOpcode ), err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128Store :
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , VectorInstructionName (vecOpcode ))
}
pc ++
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
pc += read - 1
if 1 <<align > 128 /8 {
return fmt .Errorf ("invalid memory alignment %d for %s" , align , OpcodeVecV128StoreName )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeVecV128StoreName , err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , OpcodeVecV128StoreName , err )
}
case OpcodeVecV128Load8Lane , OpcodeVecV128Load16Lane , OpcodeVecV128Load32Lane , OpcodeVecV128Load64Lane :
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , VectorInstructionName (vecOpcode ))
}
attr := vecLoadLanes [vecOpcode ]
pc ++
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
if 1 <<align > attr .alignMax {
return fmt .Errorf ("invalid memory alignment %d for %s" , align , vectorInstructionName [vecOpcode ])
}
pc += read
if pc >= uint64 (len (body )) {
return fmt .Errorf ("lane for %s not found" , OpcodeVecV128Load64LaneName )
}
lane := body [pc ]
if lane >= attr .laneCeil {
return fmt .Errorf ("invalid lane index %d >= %d for %s" , lane , attr .laneCeil , vectorInstructionName [vecOpcode ])
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128Store8Lane , OpcodeVecV128Store16Lane , OpcodeVecV128Store32Lane , OpcodeVecV128Store64Lane :
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , VectorInstructionName (vecOpcode ))
}
attr := vecStoreLanes [vecOpcode ]
pc ++
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
if 1 <<align > attr .alignMax {
return fmt .Errorf ("invalid memory alignment %d for %s" , align , vectorInstructionName [vecOpcode ])
}
pc += read
if pc >= uint64 (len (body )) {
return fmt .Errorf ("lane for %s not found" , vectorInstructionName [vecOpcode ])
}
lane := body [pc ]
if lane >= attr .laneCeil {
return fmt .Errorf ("invalid lane index %d >= %d for %s" , lane , attr .laneCeil , vectorInstructionName [vecOpcode ])
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
case OpcodeVecI8x16ExtractLaneS ,
OpcodeVecI8x16ExtractLaneU ,
OpcodeVecI16x8ExtractLaneS ,
OpcodeVecI16x8ExtractLaneU ,
OpcodeVecI32x4ExtractLane ,
OpcodeVecI64x2ExtractLane ,
OpcodeVecF32x4ExtractLane ,
OpcodeVecF64x2ExtractLane :
pc ++
if pc >= uint64 (len (body )) {
return fmt .Errorf ("lane for %s not found" , vectorInstructionName [vecOpcode ])
}
attr := vecExtractLanes [vecOpcode ]
lane := body [pc ]
if lane >= attr .laneCeil {
return fmt .Errorf ("invalid lane index %d >= %d for %s" , lane , attr .laneCeil , vectorInstructionName [vecOpcode ])
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (attr .resultType )
case OpcodeVecI8x16ReplaceLane , OpcodeVecI16x8ReplaceLane , OpcodeVecI32x4ReplaceLane ,
OpcodeVecI64x2ReplaceLane , OpcodeVecF32x4ReplaceLane , OpcodeVecF64x2ReplaceLane :
pc ++
if pc >= uint64 (len (body )) {
return fmt .Errorf ("lane for %s not found" , vectorInstructionName [vecOpcode ])
}
attr := vecReplaceLanes [vecOpcode ]
lane := body [pc ]
if lane >= attr .laneCeil {
return fmt .Errorf ("invalid lane index %d >= %d for %s" , lane , attr .laneCeil , vectorInstructionName [vecOpcode ])
}
if err := valueTypeStack .popAndVerifyType (attr .paramType ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecI8x16Splat , OpcodeVecI16x8Splat , OpcodeVecI32x4Splat ,
OpcodeVecI64x2Splat , OpcodeVecF32x4Splat , OpcodeVecF64x2Splat :
tp := vecSplatValueTypes [vecOpcode ]
if err := valueTypeStack .popAndVerifyType (tp ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecI8x16Swizzle , OpcodeVecV128And , OpcodeVecV128Or , OpcodeVecV128Xor , OpcodeVecV128AndNot :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128Bitselect :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128Not :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecV128i8x16Shuffle :
pc ++
if pc +15 >= uint64 (len (body )) {
return fmt .Errorf ("16 lane indexes for %s not found" , vectorInstructionName [vecOpcode ])
}
lanes := body [pc : pc +16 ]
for i , l := range lanes {
if l >= 32 {
return fmt .Errorf ("invalid lane index[%d] %d >= %d for %s" , i , l , 32 , vectorInstructionName [vecOpcode ])
}
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
pc += 15
case OpcodeVecI8x16Shl , OpcodeVecI8x16ShrS , OpcodeVecI8x16ShrU ,
OpcodeVecI16x8Shl , OpcodeVecI16x8ShrS , OpcodeVecI16x8ShrU ,
OpcodeVecI32x4Shl , OpcodeVecI32x4ShrS , OpcodeVecI32x4ShrU ,
OpcodeVecI64x2Shl , OpcodeVecI64x2ShrS , OpcodeVecI64x2ShrU :
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecI8x16Eq , OpcodeVecI8x16Ne , OpcodeVecI8x16LtS , OpcodeVecI8x16LtU , OpcodeVecI8x16GtS ,
OpcodeVecI8x16GtU , OpcodeVecI8x16LeS , OpcodeVecI8x16LeU , OpcodeVecI8x16GeS , OpcodeVecI8x16GeU ,
OpcodeVecI16x8Eq , OpcodeVecI16x8Ne , OpcodeVecI16x8LtS , OpcodeVecI16x8LtU , OpcodeVecI16x8GtS ,
OpcodeVecI16x8GtU , OpcodeVecI16x8LeS , OpcodeVecI16x8LeU , OpcodeVecI16x8GeS , OpcodeVecI16x8GeU ,
OpcodeVecI32x4Eq , OpcodeVecI32x4Ne , OpcodeVecI32x4LtS , OpcodeVecI32x4LtU , OpcodeVecI32x4GtS ,
OpcodeVecI32x4GtU , OpcodeVecI32x4LeS , OpcodeVecI32x4LeU , OpcodeVecI32x4GeS , OpcodeVecI32x4GeU ,
OpcodeVecI64x2Eq , OpcodeVecI64x2Ne , OpcodeVecI64x2LtS , OpcodeVecI64x2GtS , OpcodeVecI64x2LeS ,
OpcodeVecI64x2GeS , OpcodeVecF32x4Eq , OpcodeVecF32x4Ne , OpcodeVecF32x4Lt , OpcodeVecF32x4Gt ,
OpcodeVecF32x4Le , OpcodeVecF32x4Ge , OpcodeVecF64x2Eq , OpcodeVecF64x2Ne , OpcodeVecF64x2Lt ,
OpcodeVecF64x2Gt , OpcodeVecF64x2Le , OpcodeVecF64x2Ge ,
OpcodeVecI32x4DotI16x8S ,
OpcodeVecI8x16NarrowI16x8S , OpcodeVecI8x16NarrowI16x8U , OpcodeVecI16x8NarrowI32x4S , OpcodeVecI16x8NarrowI32x4U :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecI8x16Neg , OpcodeVecI16x8Neg , OpcodeVecI32x4Neg , OpcodeVecI64x2Neg , OpcodeVecF32x4Neg , OpcodeVecF64x2Neg ,
OpcodeVecF32x4Sqrt , OpcodeVecF64x2Sqrt ,
OpcodeVecI8x16Abs , OpcodeVecI8x16Popcnt , OpcodeVecI16x8Abs , OpcodeVecI32x4Abs , OpcodeVecI64x2Abs ,
OpcodeVecF32x4Abs , OpcodeVecF64x2Abs ,
OpcodeVecF32x4Ceil , OpcodeVecF32x4Floor , OpcodeVecF32x4Trunc , OpcodeVecF32x4Nearest ,
OpcodeVecF64x2Ceil , OpcodeVecF64x2Floor , OpcodeVecF64x2Trunc , OpcodeVecF64x2Nearest ,
OpcodeVecI16x8ExtendLowI8x16S , OpcodeVecI16x8ExtendHighI8x16S , OpcodeVecI16x8ExtendLowI8x16U , OpcodeVecI16x8ExtendHighI8x16U ,
OpcodeVecI32x4ExtendLowI16x8S , OpcodeVecI32x4ExtendHighI16x8S , OpcodeVecI32x4ExtendLowI16x8U , OpcodeVecI32x4ExtendHighI16x8U ,
OpcodeVecI64x2ExtendLowI32x4S , OpcodeVecI64x2ExtendHighI32x4S , OpcodeVecI64x2ExtendLowI32x4U , OpcodeVecI64x2ExtendHighI32x4U ,
OpcodeVecI16x8ExtaddPairwiseI8x16S , OpcodeVecI16x8ExtaddPairwiseI8x16U ,
OpcodeVecI32x4ExtaddPairwiseI16x8S , OpcodeVecI32x4ExtaddPairwiseI16x8U ,
OpcodeVecF64x2PromoteLowF32x4Zero , OpcodeVecF32x4DemoteF64x2Zero ,
OpcodeVecF32x4ConvertI32x4S , OpcodeVecF32x4ConvertI32x4U ,
OpcodeVecF64x2ConvertLowI32x4S , OpcodeVecF64x2ConvertLowI32x4U ,
OpcodeVecI32x4TruncSatF32x4S , OpcodeVecI32x4TruncSatF32x4U , OpcodeVecI32x4TruncSatF64x2SZero , OpcodeVecI32x4TruncSatF64x2UZero :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
case OpcodeVecI8x16Add , OpcodeVecI8x16AddSatS , OpcodeVecI8x16AddSatU , OpcodeVecI8x16Sub , OpcodeVecI8x16SubSatS , OpcodeVecI8x16SubSatU ,
OpcodeVecI16x8Add , OpcodeVecI16x8AddSatS , OpcodeVecI16x8AddSatU , OpcodeVecI16x8Sub , OpcodeVecI16x8SubSatS , OpcodeVecI16x8SubSatU , OpcodeVecI16x8Mul ,
OpcodeVecI32x4Add , OpcodeVecI32x4Sub , OpcodeVecI32x4Mul ,
OpcodeVecI64x2Add , OpcodeVecI64x2Sub , OpcodeVecI64x2Mul ,
OpcodeVecF32x4Add , OpcodeVecF32x4Sub , OpcodeVecF32x4Mul , OpcodeVecF32x4Div ,
OpcodeVecF64x2Add , OpcodeVecF64x2Sub , OpcodeVecF64x2Mul , OpcodeVecF64x2Div ,
OpcodeVecI8x16MinS , OpcodeVecI8x16MinU , OpcodeVecI8x16MaxS , OpcodeVecI8x16MaxU ,
OpcodeVecI8x16AvgrU ,
OpcodeVecI16x8MinS , OpcodeVecI16x8MinU , OpcodeVecI16x8MaxS , OpcodeVecI16x8MaxU ,
OpcodeVecI16x8AvgrU ,
OpcodeVecI32x4MinS , OpcodeVecI32x4MinU , OpcodeVecI32x4MaxS , OpcodeVecI32x4MaxU ,
OpcodeVecF32x4Min , OpcodeVecF32x4Max , OpcodeVecF64x2Min , OpcodeVecF64x2Max ,
OpcodeVecF32x4Pmin , OpcodeVecF32x4Pmax , OpcodeVecF64x2Pmin , OpcodeVecF64x2Pmax ,
OpcodeVecI16x8Q15mulrSatS ,
OpcodeVecI16x8ExtMulLowI8x16S , OpcodeVecI16x8ExtMulHighI8x16S , OpcodeVecI16x8ExtMulLowI8x16U , OpcodeVecI16x8ExtMulHighI8x16U ,
OpcodeVecI32x4ExtMulLowI16x8S , OpcodeVecI32x4ExtMulHighI16x8S , OpcodeVecI32x4ExtMulLowI16x8U , OpcodeVecI32x4ExtMulHighI16x8U ,
OpcodeVecI64x2ExtMulLowI32x4S , OpcodeVecI64x2ExtMulHighI32x4S , OpcodeVecI64x2ExtMulLowI32x4U , OpcodeVecI64x2ExtMulHighI32x4U :
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeV128 ); err != nil {
return fmt .Errorf ("cannot pop the operand for %s: %v" , vectorInstructionName [vecOpcode ], err )
}
valueTypeStack .push (ValueTypeV128 )
default :
return fmt .Errorf ("unknown SIMD instruction %s" , vectorInstructionName [vecOpcode ])
}
} else if op == OpcodeBlock {
br .Reset (body [pc +1 :])
bt , num , err := DecodeBlockType (m .TypeSection , br , enabledFeatures )
if err != nil {
return fmt .Errorf ("read block: %w" , err )
}
controlBlockStack .push (pc , 0 , 0 , bt , num , 0 )
if err = valueTypeStack .popParams (op , bt .Params , false ); err != nil {
return err
}
for _ , p := range bt .Params {
valueTypeStack .push (p )
}
valueTypeStack .pushStackLimit (len (bt .Params ))
pc += num
} else if op == OpcodeAtomicPrefix {
pc ++
atomicOpcode := body [pc ]
if err := enabledFeatures .RequireEnabled (experimental .CoreFeaturesThreads ); err != nil {
return fmt .Errorf ("%s invalid as %v" , atomicInstructionName [atomicOpcode ], err )
}
pc ++
if atomicOpcode == OpcodeAtomicFence {
imm := body [pc ]
if imm != 0x0 {
return fmt .Errorf ("invalid immediate value for %s" , AtomicInstructionName (atomicOpcode ))
}
continue
}
if memory == nil {
return fmt .Errorf ("memory must exist for %s" , AtomicInstructionName (atomicOpcode ))
}
align , _ , read , err := readMemArg (pc , body )
if err != nil {
return err
}
pc += read - 1
switch atomicOpcode {
case OpcodeAtomicMemoryNotify :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicMemoryWait32 :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicMemoryWait64 :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Load :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI64Load :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI32Load8U :
if 1 <<align != 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Load16U :
if 1 <<align != 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI64Load8U :
if 1 <<align != 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Load16U :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Load32U :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI32Store :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI64Store :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI32Store8 :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI32Store16 :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI64Store8 :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI64Store16 :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI64Store32 :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
case OpcodeAtomicI32RmwAdd , OpcodeAtomicI32RmwSub , OpcodeAtomicI32RmwAnd , OpcodeAtomicI32RmwOr , OpcodeAtomicI32RmwXor , OpcodeAtomicI32RmwXchg :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Rmw8AddU , OpcodeAtomicI32Rmw8SubU , OpcodeAtomicI32Rmw8AndU , OpcodeAtomicI32Rmw8OrU , OpcodeAtomicI32Rmw8XorU , OpcodeAtomicI32Rmw8XchgU :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Rmw16AddU , OpcodeAtomicI32Rmw16SubU , OpcodeAtomicI32Rmw16AndU , OpcodeAtomicI32Rmw16OrU , OpcodeAtomicI32Rmw16XorU , OpcodeAtomicI32Rmw16XchgU :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI64RmwAdd , OpcodeAtomicI64RmwSub , OpcodeAtomicI64RmwAnd , OpcodeAtomicI64RmwOr , OpcodeAtomicI64RmwXor , OpcodeAtomicI64RmwXchg :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw8AddU , OpcodeAtomicI64Rmw8SubU , OpcodeAtomicI64Rmw8AndU , OpcodeAtomicI64Rmw8OrU , OpcodeAtomicI64Rmw8XorU , OpcodeAtomicI64Rmw8XchgU :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw16AddU , OpcodeAtomicI64Rmw16SubU , OpcodeAtomicI64Rmw16AndU , OpcodeAtomicI64Rmw16OrU , OpcodeAtomicI64Rmw16XorU , OpcodeAtomicI64Rmw16XchgU :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw32AddU , OpcodeAtomicI64Rmw32SubU , OpcodeAtomicI64Rmw32AndU , OpcodeAtomicI64Rmw32OrU , OpcodeAtomicI64Rmw32XorU , OpcodeAtomicI64Rmw32XchgU :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI32RmwCmpxchg :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Rmw8CmpxchgU :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI32Rmw16CmpxchgU :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI32 )
case OpcodeAtomicI64RmwCmpxchg :
if 1 <<align > 64 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw8CmpxchgU :
if 1 <<align > 1 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw16CmpxchgU :
if 1 <<align > 16 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
case OpcodeAtomicI64Rmw32CmpxchgU :
if 1 <<align > 32 /8 {
return fmt .Errorf ("invalid memory alignment" )
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI64 ); err != nil {
return err
}
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return err
}
valueTypeStack .push (ValueTypeI64 )
default :
return fmt .Errorf ("invalid atomic opcode: 0x%x" , atomicOpcode )
}
} else if op == OpcodeLoop {
br .Reset (body [pc +1 :])
bt , num , err := DecodeBlockType (m .TypeSection , br , enabledFeatures )
if err != nil {
return fmt .Errorf ("read block: %w" , err )
}
controlBlockStack .push (pc , 0 , 0 , bt , num , op )
if err = valueTypeStack .popParams (op , bt .Params , false ); err != nil {
return err
}
for _ , p := range bt .Params {
valueTypeStack .push (p )
}
valueTypeStack .pushStackLimit (len (bt .Params ))
pc += num
} else if op == OpcodeIf {
br .Reset (body [pc +1 :])
bt , num , err := DecodeBlockType (m .TypeSection , br , enabledFeatures )
if err != nil {
return fmt .Errorf ("read block: %w" , err )
}
controlBlockStack .push (pc , 0 , 0 , bt , num , op )
if err = valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("cannot pop the operand for 'if': %v" , err )
}
if err = valueTypeStack .popParams (op , bt .Params , false ); err != nil {
return err
}
for _ , p := range bt .Params {
valueTypeStack .push (p )
}
valueTypeStack .pushStackLimit (len (bt .Params ))
pc += num
} else if op == OpcodeElse {
bl := &controlBlockStack .stack [len (controlBlockStack .stack )-1 ]
if bl .op != OpcodeIf {
return fmt .Errorf ("else instruction must be used in if block: %#x" , pc )
}
bl .op = OpcodeElse
bl .elseAt = pc
if err := valueTypeStack .popResults (OpcodeIf , bl .blockType .Results , true ); err != nil {
return err
}
valueTypeStack .resetAtStackLimit ()
for _ , p := range bl .blockType .Params {
valueTypeStack .push (p )
}
} else if op == OpcodeEnd {
bl := controlBlockStack .pop ()
bl .endAt = pc
ifMissingElse := bl .op == OpcodeIf && bl .elseAt <= bl .startAt
if ifMissingElse {
if !bytes .Equal (bl .blockType .Results , bl .blockType .Params ) {
return typeCountError (false , OpcodeElseName , bl .blockType .Params , bl .blockType .Results )
}
bl .elseAt = bl .endAt - 1
}
ctx := ""
if bl .op == OpcodeIf && !ifMissingElse && bl .elseAt > 0 {
ctx = OpcodeElseName
} else if bl .op != 0 {
ctx = InstructionName (bl .op )
}
if err := valueTypeStack .requireStackValues (false , ctx , bl .blockType .Results , true ); err != nil {
return err
}
valueTypeStack .resetAtStackLimit ()
for _ , exp := range bl .blockType .Results {
valueTypeStack .push (exp )
}
valueTypeStack .popStackLimit ()
} else if op == OpcodeReturn {
if err := valueTypeStack .requireStackValues (false , "" , functionType .Results , false ); err != nil {
return err
}
valueTypeStack .unreachable ()
} else if op == OpcodeDrop {
_ , err := valueTypeStack .pop ()
if err != nil {
return fmt .Errorf ("invalid drop: %v" , err )
}
} else if op == OpcodeSelect || op == OpcodeTypedSelect {
if err := valueTypeStack .popAndVerifyType (ValueTypeI32 ); err != nil {
return fmt .Errorf ("type mismatch on 3rd select operand: %v" , err )
}
v1 , err := valueTypeStack .pop ()
if err != nil {
return fmt .Errorf ("invalid select: %v" , err )
}
v2 , err := valueTypeStack .pop ()
if err != nil {
return fmt .Errorf ("invalid select: %v" , err )
}
if op == OpcodeTypedSelect {
if err := enabledFeatures .RequireEnabled (api .CoreFeatureReferenceTypes ); err != nil {
return fmt .Errorf ("%s is invalid as %w" , InstructionName (op ), err )
}
pc ++
if numTypeImmeidates := body [pc ]; numTypeImmeidates != 1 {
return fmt .Errorf ("too many type immediates for %s" , InstructionName (op ))
}
pc ++
tp := body [pc ]
if tp != ValueTypeI32 && tp != ValueTypeI64 && tp != ValueTypeF32 && tp != ValueTypeF64 &&
tp != api .ValueTypeExternref && tp != ValueTypeFuncref && tp != ValueTypeV128 {
return fmt .Errorf ("invalid type %s for %s" , ValueTypeName (tp ), OpcodeTypedSelectName )
}
} else if isReferenceValueType (v1 ) || isReferenceValueType (v2 ) {
return fmt .Errorf ("reference types cannot be used for non typed select instruction" )
}
if v1 != v2 && v1 != valueTypeUnknown && v2 != valueTypeUnknown {
return fmt .Errorf ("type mismatch on 1st and 2nd select operands" )
}
if v1 == valueTypeUnknown {
valueTypeStack .push (v2 )
} else {
valueTypeStack .push (v1 )
}
} else if op == OpcodeUnreachable {
valueTypeStack .unreachable ()
} else if op == OpcodeNop {
} else {
return fmt .Errorf ("invalid instruction 0x%x" , op )
}
}
if len (controlBlockStack .stack ) > 0 {
return fmt .Errorf ("ill-nested block exists" )
}
if valueTypeStack .maximumStackPointer > maxStackValues {
return fmt .Errorf ("function may have %d stack values, which exceeds limit %d" , valueTypeStack .maximumStackPointer , maxStackValues )
}
return nil
}
var vecExtractLanes = [...]struct {
laneCeil byte
resultType ValueType
}{
OpcodeVecI8x16ExtractLaneS : {laneCeil : 16 , resultType : ValueTypeI32 },
OpcodeVecI8x16ExtractLaneU : {laneCeil : 16 , resultType : ValueTypeI32 },
OpcodeVecI16x8ExtractLaneS : {laneCeil : 8 , resultType : ValueTypeI32 },
OpcodeVecI16x8ExtractLaneU : {laneCeil : 8 , resultType : ValueTypeI32 },
OpcodeVecI32x4ExtractLane : {laneCeil : 4 , resultType : ValueTypeI32 },
OpcodeVecI64x2ExtractLane : {laneCeil : 2 , resultType : ValueTypeI64 },
OpcodeVecF32x4ExtractLane : {laneCeil : 4 , resultType : ValueTypeF32 },
OpcodeVecF64x2ExtractLane : {laneCeil : 2 , resultType : ValueTypeF64 },
}
var vecReplaceLanes = [...]struct {
laneCeil byte
paramType ValueType
}{
OpcodeVecI8x16ReplaceLane : {laneCeil : 16 , paramType : ValueTypeI32 },
OpcodeVecI16x8ReplaceLane : {laneCeil : 8 , paramType : ValueTypeI32 },
OpcodeVecI32x4ReplaceLane : {laneCeil : 4 , paramType : ValueTypeI32 },
OpcodeVecI64x2ReplaceLane : {laneCeil : 2 , paramType : ValueTypeI64 },
OpcodeVecF32x4ReplaceLane : {laneCeil : 4 , paramType : ValueTypeF32 },
OpcodeVecF64x2ReplaceLane : {laneCeil : 2 , paramType : ValueTypeF64 },
}
var vecStoreLanes = [...]struct {
alignMax uint32
laneCeil byte
}{
OpcodeVecV128Store64Lane : {alignMax : 64 / 8 , laneCeil : 128 / 64 },
OpcodeVecV128Store32Lane : {alignMax : 32 / 8 , laneCeil : 128 / 32 },
OpcodeVecV128Store16Lane : {alignMax : 16 / 8 , laneCeil : 128 / 16 },
OpcodeVecV128Store8Lane : {alignMax : 1 , laneCeil : 128 / 8 },
}
var vecLoadLanes = [...]struct {
alignMax uint32
laneCeil byte
}{
OpcodeVecV128Load64Lane : {alignMax : 64 / 8 , laneCeil : 128 / 64 },
OpcodeVecV128Load32Lane : {alignMax : 32 / 8 , laneCeil : 128 / 32 },
OpcodeVecV128Load16Lane : {alignMax : 16 / 8 , laneCeil : 128 / 16 },
OpcodeVecV128Load8Lane : {alignMax : 1 , laneCeil : 128 / 8 },
}
var vecSplatValueTypes = [...]ValueType {
OpcodeVecI8x16Splat : ValueTypeI32 ,
OpcodeVecI16x8Splat : ValueTypeI32 ,
OpcodeVecI32x4Splat : ValueTypeI32 ,
OpcodeVecI64x2Splat : ValueTypeI64 ,
OpcodeVecF32x4Splat : ValueTypeF32 ,
OpcodeVecF64x2Splat : ValueTypeF64 ,
}
type stacks struct {
vs valueTypeStack
cs controlBlockStack
ls []uint32
}
func (sts *stacks ) reset (functionType *FunctionType ) {
sts .vs .stack = sts .vs .stack [:0 ]
sts .vs .stackLimits = sts .vs .stackLimits [:0 ]
sts .vs .maximumStackPointer = 0
sts .cs .stack = sts .cs .stack [:0 ]
sts .cs .stack = append (sts .cs .stack , controlBlock {blockType : functionType })
sts .ls = sts .ls [:0 ]
}
type controlBlockStack struct {
stack []controlBlock
}
func (s *controlBlockStack ) pop () *controlBlock {
tail := len (s .stack ) - 1
ret := &s .stack [tail ]
s .stack = s .stack [:tail ]
return ret
}
func (s *controlBlockStack ) push (startAt , elseAt , endAt uint64 , blockType *FunctionType , blockTypeBytes uint64 , op Opcode ) {
s .stack = append (s .stack , controlBlock {
startAt : startAt ,
elseAt : elseAt ,
endAt : endAt ,
blockType : blockType ,
blockTypeBytes : blockTypeBytes ,
op : op ,
})
}
type valueTypeStack struct {
stack []ValueType
stackLimits []int
maximumStackPointer int
requireStackValuesTmp []ValueType
}
const valueTypeUnknown = ValueType (0xFF )
func (s *valueTypeStack ) tryPop () (vt ValueType , limit int , ok bool ) {
if len (s .stackLimits ) > 0 {
limit = s .stackLimits [len (s .stackLimits )-1 ]
}
stackLen := len (s .stack )
if stackLen <= limit {
return
} else if stackLen == limit +1 && s .stack [limit ] == valueTypeUnknown {
vt = valueTypeUnknown
ok = true
return
} else {
vt = s .stack [stackLen -1 ]
s .stack = s .stack [:stackLen -1 ]
ok = true
return
}
}
func (s *valueTypeStack ) pop () (ValueType , error ) {
if vt , limit , ok := s .tryPop (); ok {
return vt , nil
} else {
return 0 , fmt .Errorf ("invalid operation: trying to pop at %d with limit %d" , len (s .stack ), limit )
}
}
func (s *valueTypeStack ) popAndVerifyType (expected ValueType ) error {
have , _ , ok := s .tryPop ()
if !ok {
return fmt .Errorf ("%s missing" , ValueTypeName (expected ))
}
if have != expected && have != valueTypeUnknown && expected != valueTypeUnknown {
return fmt .Errorf ("type mismatch: expected %s, but was %s" , ValueTypeName (expected ), ValueTypeName (have ))
}
return nil
}
func (s *valueTypeStack ) push (v ValueType ) {
s .stack = append (s .stack , v )
if sp := len (s .stack ); sp > s .maximumStackPointer {
s .maximumStackPointer = sp
}
}
func (s *valueTypeStack ) unreachable () {
s .resetAtStackLimit ()
s .stack = append (s .stack , valueTypeUnknown )
}
func (s *valueTypeStack ) resetAtStackLimit () {
if len (s .stackLimits ) != 0 {
s .stack = s .stack [:s .stackLimits [len (s .stackLimits )-1 ]]
} else {
s .stack = s .stack [:0 ]
}
}
func (s *valueTypeStack ) popStackLimit () {
if len (s .stackLimits ) != 0 {
s .stackLimits = s .stackLimits [:len (s .stackLimits )-1 ]
}
}
func (s *valueTypeStack ) pushStackLimit (params int ) {
limit := len (s .stack ) - params
s .stackLimits = append (s .stackLimits , limit )
}
func (s *valueTypeStack ) popParams (oc Opcode , want []ValueType , checkAboveLimit bool ) error {
return s .requireStackValues (true , InstructionName (oc ), want , checkAboveLimit )
}
func (s *valueTypeStack ) popResults (oc Opcode , want []ValueType , checkAboveLimit bool ) error {
return s .requireStackValues (false , InstructionName (oc ), want , checkAboveLimit )
}
func (s *valueTypeStack ) requireStackValues (
isParam bool ,
context string ,
want []ValueType ,
checkAboveLimit bool ,
) error {
limit := 0
if len (s .stackLimits ) > 0 {
limit = s .stackLimits [len (s .stackLimits )-1 ]
}
countWanted := len (want )
s .requireStackValuesTmp = s .requireStackValuesTmp [:0 ]
for i := countWanted - 1 ; i >= 0 ; i -- {
popped , _ , ok := s .tryPop ()
if !ok {
if len (s .requireStackValuesTmp ) > len (want ) {
return typeCountError (isParam , context , s .requireStackValuesTmp , want )
}
return typeCountError (isParam , context , s .requireStackValuesTmp , want )
}
s .requireStackValuesTmp = append (s .requireStackValuesTmp , popped )
}
if checkAboveLimit {
if !(limit == len (s .stack ) || (limit +1 == len (s .stack ) && s .stack [limit ] == valueTypeUnknown )) {
return typeCountError (isParam , context , append (s .stack , want ...), want )
}
}
for i , v := range s .requireStackValuesTmp {
nextWant := want [countWanted -i -1 ]
if v != nextWant && v != valueTypeUnknown && nextWant != valueTypeUnknown {
return typeMismatchError (isParam , context , v , nextWant , i )
}
}
return nil
}
func typeMismatchError(isParam bool , context string , have ValueType , want ValueType , i int ) error {
var ret strings .Builder
ret .WriteString ("cannot use " )
ret .WriteString (ValueTypeName (have ))
if context != "" {
ret .WriteString (" in " )
ret .WriteString (context )
ret .WriteString (" block" )
}
if isParam {
ret .WriteString (" as param" )
} else {
ret .WriteString (" as result" )
}
ret .WriteString ("[" )
ret .WriteString (strconv .Itoa (i ))
ret .WriteString ("] type " )
ret .WriteString (ValueTypeName (want ))
return errors .New (ret .String ())
}
func typeCountError(isParam bool , context string , have []ValueType , want []ValueType ) error {
var ret strings .Builder
if len (have ) > len (want ) {
ret .WriteString ("too many " )
} else {
ret .WriteString ("not enough " )
}
if isParam {
ret .WriteString ("params" )
} else {
ret .WriteString ("results" )
}
if context != "" {
if isParam {
ret .WriteString (" for " )
} else {
ret .WriteString (" in " )
}
ret .WriteString (context )
ret .WriteString (" block" )
}
ret .WriteString ("\n\thave (" )
writeValueTypes (have , &ret )
ret .WriteString (")\n\twant (" )
writeValueTypes (want , &ret )
ret .WriteByte (')' )
return errors .New (ret .String ())
}
func writeValueTypes(vts []ValueType , ret *strings .Builder ) {
switch len (vts ) {
case 0 :
case 1 :
ret .WriteString (ValueTypeName (vts [0 ]))
default :
ret .WriteString (ValueTypeName (vts [0 ]))
for _ , vt := range vts [1 :] {
ret .WriteString (", " )
ret .WriteString (ValueTypeName (vt ))
}
}
}
func (s *valueTypeStack ) String () string {
var typeStrs , limits []string
for _ , v := range s .stack {
var str string
if v == valueTypeUnknown {
str = "unknown"
} else {
str = ValueTypeName (v )
}
typeStrs = append (typeStrs , str )
}
for _ , d := range s .stackLimits {
limits = append (limits , fmt .Sprintf ("%d" , d ))
}
return fmt .Sprintf ("{stack: [%s], limits: [%s]}" ,
strings .Join (typeStrs , ", " ), strings .Join (limits , "," ))
}
type controlBlock struct {
startAt, elseAt, endAt uint64
blockType *FunctionType
blockTypeBytes uint64
op Opcode
}
func DecodeBlockType (types []FunctionType , r *bytes .Reader , enabledFeatures api .CoreFeatures ) (*FunctionType , uint64 , error ) {
raw , num , err := leb128 .DecodeInt33AsInt64 (r )
if err != nil {
return nil , 0 , fmt .Errorf ("decode int33: %w" , err )
}
var ret *FunctionType
switch raw {
case -64 :
ret = blockType_v_v
case -1 :
ret = blockType_v_i32
case -2 :
ret = blockType_v_i64
case -3 :
ret = blockType_v_f32
case -4 :
ret = blockType_v_f64
case -5 :
ret = blockType_v_v128
case -16 :
ret = blockType_v_funcref
case -17 :
ret = blockType_v_externref
default :
if err = enabledFeatures .RequireEnabled (api .CoreFeatureMultiValue ); err != nil {
return nil , num , fmt .Errorf ("block with function type return invalid as %v" , err )
}
if raw < 0 || (raw >= int64 (len (types ))) {
return nil , 0 , fmt .Errorf ("type index out of range: %d" , raw )
}
ret = &types [raw ]
}
return ret , num , err
}
var (
blockType_v_v = &FunctionType {}
blockType_v_i32 = &FunctionType {Results : []ValueType {ValueTypeI32 }, ResultNumInUint64 : 1 }
blockType_v_i64 = &FunctionType {Results : []ValueType {ValueTypeI64 }, ResultNumInUint64 : 1 }
blockType_v_f32 = &FunctionType {Results : []ValueType {ValueTypeF32 }, ResultNumInUint64 : 1 }
blockType_v_f64 = &FunctionType {Results : []ValueType {ValueTypeF64 }, ResultNumInUint64 : 1 }
blockType_v_v128 = &FunctionType {Results : []ValueType {ValueTypeV128 }, ResultNumInUint64 : 2 }
blockType_v_funcref = &FunctionType {Results : []ValueType {ValueTypeFuncref }, ResultNumInUint64 : 1 }
blockType_v_externref = &FunctionType {Results : []ValueType {ValueTypeExternref }, ResultNumInUint64 : 1 }
)
func SplitCallStack (ft *FunctionType , stack []uint64 ) (params []uint64 , results []uint64 , err error ) {
stackLen := len (stack )
if n := ft .ParamNumInUint64 ; n > stackLen {
return nil , nil , fmt .Errorf ("need %d params, but stack size is %d" , n , stackLen )
} else if n > 0 {
params = stack [:n ]
}
if n := ft .ResultNumInUint64 ; n > stackLen {
return nil , nil , fmt .Errorf ("need %d results, but stack size is %d" , n , stackLen )
} else if n > 0 {
results = stack [:n ]
}
return
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .