summaryrefslogtreecommitdiff
path: root/vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-05-27 15:46:15 +0000
committerLibravatar GitHub <noreply@github.com>2024-05-27 17:46:15 +0200
commit1e7b32490dfdccddd04f46d4b0416b48d749d51b (patch)
tree62a11365933a5a11e0800af64cbdf9172e5e6e7a /vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go
parent[chore] Small styling + link issues (#2933) (diff)
downloadgotosocial-1e7b32490dfdccddd04f46d4b0416b48d749d51b.tar.xz
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go')
-rw-r--r--vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go2340
1 files changed, 2340 insertions, 0 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go b/vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go
new file mode 100644
index 000000000..8da689076
--- /dev/null
+++ b/vendor/github.com/tetratelabs/wazero/internal/wasm/func_validation.go
@@ -0,0 +1,2340 @@
+package wasm
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "github.com/tetratelabs/wazero/api"
+ "github.com/tetratelabs/wazero/experimental"
+ "github.com/tetratelabs/wazero/internal/leb128"
+)
+
+// The wazero specific limitation described at RATIONALE.md.
+const maximumValuesOnStack = 1 << 27
+
+// validateFunction validates the instruction sequence of a function.
+// following the specification https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#instructions%E2%91%A2.
+//
+// * idx is the index in the FunctionSection
+// * functions are the function index, which is prefixed by imports. The value is the TypeSection index.
+// * globals are the global index, which is prefixed by imports.
+// * memory is the potentially imported memory and can be nil.
+// * table is the potentially imported table and can be nil.
+// * declaredFunctionIndexes is the set of function indexes declared by declarative element segments which can be acceed by OpcodeRefFunc instruction.
+//
+// Returns an error if the instruction sequence is not valid,
+// or potentially it can exceed the maximum number of values on the stack.
+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
+ }
+ 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
+}
+
+// validateFunctionWithMaxStackValues is like validateFunction, but allows overriding maxStackValues for testing.
+//
+// * stacks is to track the state of Wasm value and control frame stacks at anypoint of execution, and reused to reduce allocation.
+// * maxStackValues is the maximum height of values stack which the target is allowed to reach.
+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 {
+ nonStaticLocals := make(map[Index]struct{})
+ if len(m.NonStaticLocals) > 0 {
+ m.NonStaticLocals[idx] = nonStaticLocals
+ }
+
+ functionType := &m.TypeSection[m.FunctionSection[idx]]
+ code := &m.CodeSection[idx]
+ body := code.Body
+ localTypes := code.LocalTypes
+
+ sts.reset(functionType)
+ valueTypeStack := &sts.vs
+ // We start with the outermost control block which is for function return if the code branches into it.
+ controlBlockStack := &sts.cs
+
+ // Now start walking through all the instructions in the body while tracking
+ // control blocks and value types to check the validity of all instructions.
+ 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)
+ }
+ nonStaticLocals[index] = struct{}{}
+ 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)
+ }
+ nonStaticLocals[index] = struct{}{}
+ 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
+ // Check type soundness.
+ 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
+ }
+ // br instruction is stack-polymorphic.
+ 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)
+ }
+ // Check type soundness.
+ 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
+ }
+ // Push back the result
+ 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)
+ }
+
+ list := make([]uint32, nl)
+ 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
+ list[i] = 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
+ // Check type soundness.
+ 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
+ // Below, we might modify the slice in case of unreachable. Therefore,
+ // we have to copy the content of block result types, otherwise the original
+ // function type might result in invalid value types if the block is the outermost label
+ // which equals the function's type.
+ if lnLabel.op != OpcodeLoop { // Loop operation doesn't require results since the continuation is the beginning of the loop.
+ defaultLabelType = make([]ValueType, len(lnLabel.blockType.Results))
+ copy(defaultLabelType, lnLabel.blockType.Results)
+ } else {
+ defaultLabelType = make([]ValueType, len(lnLabel.blockType.Params))
+ copy(defaultLabelType, lnLabel.blockType.Params)
+ }
+
+ if enabledFeatures.IsEnabled(api.CoreFeatureReferenceTypes) {
+ // As of reference-types proposal, br_table on unreachable state
+ // can choose unknown types for expected parameter types for each label.
+ // https://github.com/WebAssembly/reference-types/pull/116
+ for i := range defaultLabelType {
+ index := len(defaultLabelType) - 1 - i
+ exp := defaultLabelType[index]
+ actual, err := valueTypeStack.pop()
+ if err != nil {
+ return err
+ }
+ if actual == valueTypeUnknown {
+ // Re-assign the expected type to unknown.
+ 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 list {
+ 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)
+ }
+ }
+ }
+
+ // br_table instruction is stack-polymorphic.
+ valueTypeStack.unreachable()
+ } else if op == OpcodeCall {
+ 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")
+ }
+ 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", OpcodeCallName, err)
+ }
+ }
+ for _, exp := range funcType.Results {
+ valueTypeStack.push(exp)
+ }
+ } else if op == OpcodeCallIndirect {
+ pc++
+ typeIndex, num, err := leb128.LoadUint32(body[pc:])
+ if err != nil {
+ return fmt.Errorf("read immediate: %v", err)
+ }
+ pc += num
+
+ if int(typeIndex) >= len(m.TypeSection) {
+ return fmt.Errorf("invalid type index at %s: %d", OpcodeCallIndirectName, 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), OpcodeCallIndirectName)
+ }
+
+ if err = valueTypeStack.popAndVerifyType(ValueTypeI32); err != nil {
+ return fmt.Errorf("cannot pop the offset in table for %s", OpcodeCallIndirectName)
+ }
+ 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", OpcodeCallIndirectName)
+ }
+ }
+ for _, exp := range funcType.Results {
+ valueTypeStack.push(exp)
+ }
+ } 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++
+ // A misc opcode is encoded as an unsigned variable 32-bit integer.
+ 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 the misc opcode is beyond byte range, it is highly likely this is an invalid binary, or
+ // it is due to the new opcode from a new proposal. In the latter case, we have to
+ // change the alias type of OpcodeMisc (which is currently byte) to uint32.
+ 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
+ // Handle opcodes added in bulk-memory-operations/WebAssembly 2.0.
+ switch miscOpcode {
+ case OpcodeMiscDataDrop:
+ if m.DataCountSection == nil {
+ return fmt.Errorf("%s requires data count section", MiscInstructionName(miscOpcode))
+ }
+
+ // We need to read the index to the data section.
+ 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))
+ }
+
+ // We need to read the index to the data section.
+ 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++
+ // memory.copy needs two memory index which are reserved as zero.
+ 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++
+ // Vector instructions come with two bytes where the first byte is always OpcodeVecPrefix,
+ // and the second byte determines the actual instruction.
+ 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:
+ // Read 128-bit = 16 bytes constants
+ 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
+ }
+ // Plus we have to push any block params again.
+ for _, p := range bt.Params {
+ valueTypeStack.push(p)
+ }
+ valueTypeStack.pushStackLimit(len(bt.Params))
+ pc += num
+ } else if op == OpcodeAtomicPrefix {
+ pc++
+ // Atomic instructions come with two bytes where the first byte is always OpcodeAtomicPrefix,
+ // and the second byte determines the actual instruction.
+ 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 {
+ // No memory requirement and no arguments or return, however the immediate byte value must be 0.
+ imm := body[pc]
+ if imm != 0x0 {
+ return fmt.Errorf("invalid immediate value for %s", AtomicInstructionName(atomicOpcode))
+ }
+ continue
+ }
+
+ // All atomic operations except fence (checked above) require memory
+ 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
+ }
+ // Plus we have to push any block params again.
+ 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
+ }
+ // Plus we have to push any block params again.
+ 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
+ // Check the type soundness of the instructions *before* entering this else Op.
+ if err := valueTypeStack.popResults(OpcodeIf, bl.blockType.Results, true); err != nil {
+ return err
+ }
+ // Before entering instructions inside else, we pop all the values pushed by then block.
+ valueTypeStack.resetAtStackLimit()
+ // Plus we have to push any block params again.
+ for _, p := range bl.blockType.Params {
+ valueTypeStack.push(p)
+ }
+ } else if op == OpcodeEnd {
+ bl := controlBlockStack.pop()
+ bl.endAt = pc
+
+ // OpcodeEnd can end a block or the function itself. Check to see what it is:
+
+ ifMissingElse := bl.op == OpcodeIf && bl.elseAt <= bl.startAt
+ if ifMissingElse {
+ // If this is the end of block without else, the number of block's results and params must be same.
+ // Otherwise, the value stack would result in the inconsistent state at runtime.
+ if !bytes.Equal(bl.blockType.Results, bl.blockType.Params) {
+ return typeCountError(false, OpcodeElseName, bl.blockType.Params, bl.blockType.Results)
+ }
+ // -1 skips else, to handle if block without else properly.
+ bl.elseAt = bl.endAt - 1
+ }
+
+ // Determine the block context
+ ctx := "" // the outer-most block: the function return
+ if bl.op == OpcodeIf && !ifMissingElse && bl.elseAt > 0 {
+ ctx = OpcodeElseName
+ } else if bl.op != 0 {
+ ctx = InstructionName(bl.op)
+ }
+
+ // Check return types match
+ if err := valueTypeStack.requireStackValues(false, ctx, bl.blockType.Results, true); err != nil {
+ return err
+ }
+
+ // Put the result types at the end after resetting at the stack limit
+ // since we might have Any type between the limit and the current top.
+ valueTypeStack.resetAtStackLimit()
+ for _, exp := range bl.blockType.Results {
+ valueTypeStack.push(exp)
+ }
+ // We exit if/loop/block, so reset the constraints on the stack manipulation
+ // on values previously pushed by outer blocks.
+ valueTypeStack.popStackLimit()
+ } else if op == OpcodeReturn {
+ // Same formatting as OpcodeEnd on the outer-most block
+ if err := valueTypeStack.requireStackValues(false, "", functionType.Results, false); err != nil {
+ return err
+ }
+ // return instruction is stack-polymorphic.
+ 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 {
+ // unreachable instruction is stack-polymorphic.
+ 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
+}
+
+func (sts *stacks) reset(functionType *FunctionType) {
+ // Reset valueStack for reuse.
+ 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})
+}
+
+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 is used in requireStackValues function to reduce the allocation.
+ requireStackValuesTmp []ValueType
+}
+
+// Only used in the analyzeFunction below.
+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)
+ }
+}
+
+// popAndVerifyType returns an error if the stack value is unexpected.
+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]
+ }
+}
+
+// pushStackLimit pushes the control frame's bottom of the stack.
+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]
+ }
+ // Iterate backwards as we are comparing the desired slice against stack value types.
+ countWanted := len(want)
+
+ // First, check if there are enough values on the stack.
+ 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)
+ }
+
+ // Now, check if there are too many values.
+ 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)
+ }
+ }
+
+ // Finally, check the types of the values:
+ for i, v := range s.requireStackValuesTmp {
+ nextWant := want[countWanted-i-1] // have is in reverse order (stack)
+ if v != nextWant && v != valueTypeUnknown && nextWant != valueTypeUnknown {
+ return typeMismatchError(isParam, context, v, nextWant, i)
+ }
+ }
+ return nil
+}
+
+// typeMismatchError returns an error similar to go compiler's error on type mismatch.
+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())
+}
+
+// typeCountError returns an error similar to go compiler's error on type count mismatch.
+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 is zero when the outermost block
+ op Opcode
+}
+
+// DecodeBlockType decodes the type index from a positive 33-bit signed integer. Negative numbers indicate up to one
+// WebAssembly 1.0 (20191205) compatible result type. Positive numbers are decoded when `enabledFeatures` include
+// CoreFeatureMultiValue and include an index in the Module.TypeSection.
+//
+// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-blocktype
+// See https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/multi-value/Overview.md
+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: // 0x40 in original byte = nil
+ ret = blockType_v_v
+ case -1: // 0x7f in original byte = i32
+ ret = blockType_v_i32
+ case -2: // 0x7e in original byte = i64
+ ret = blockType_v_i64
+ case -3: // 0x7d in original byte = f32
+ ret = blockType_v_f32
+ case -4: // 0x7c in original byte = f64
+ ret = blockType_v_f64
+ case -5: // 0x7b in original byte = v128
+ ret = blockType_v_v128
+ case -16: // 0x70 in original byte = funcref
+ ret = blockType_v_funcref
+ case -17: // 0x6f in original byte = externref
+ 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
+}
+
+// These block types are defined as globals in order to avoid allocations in DecodeBlockType.
+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}
+)
+
+// SplitCallStack returns the input stack resliced to the count of params and
+// results, or errors if it isn't long enough for either.
+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
+}