func NewDisassembly(fn wasm.Function, module *wasm.Module) (*Disassembly, error) {
	code := fn.Body.Code
	instrs, err := Disassemble(code)
	if err != nil {
		return nil, err
	}
	disas := &Disassembly{}
	blockPolymorphicOps := [][]int{{}}
	stackDepths := &stack.Stack{}
	stackDepths.Push(0)
	blockIndices := &stack.Stack{}  
	curIndex := 0
	for _, instr := range instrs {
		logger.Printf("stack top is %d", stackDepths.Top())
		opStr := instr.Op
		op := opStr.Code
		if op == ops.End || op == ops.Else {
			isUnreachable := blockIndices.Len() != len(blockPolymorphicOps)-1
			instr.Unreachable = isUnreachable
		} else {
			instr.Unreachable = !isInstrReachable(blockPolymorphicOps)
		}
		var blockStartIndex uint64
		switch op {
		case ops.End, ops.Else:
			blockStartIndex = blockIndices.Pop()
			if op == ops.Else {
				blockIndices.Push(uint64(curIndex))
			}
		case ops.Block, ops.Loop, ops.If:
			blockIndices.Push(uint64(curIndex))
		}
		if instr.Unreachable {
			continue
		}
		logger.Printf("op: %s, unreachable: %v", opStr.Name, instr.Unreachable)
		if !opStr.Polymorphic {
			top := int(stackDepths.Top())
			top -= len(opStr.Args)
			stackDepths.SetTop(uint64(top))
			if top < 0 {
				panic("underflow during validation")
			}
			if opStr.Returns != wasm.ValueType(wasm.BlockTypeEmpty) {
				top++
				stackDepths.SetTop(uint64(top))
			}
			disas.checkMaxDepth(top)
		}
		switch op {
		case ops.Unreachable:
			pushPolymorphicOp(blockPolymorphicOps, curIndex)
		case ops.Drop:
			stackDepths.SetTop(stackDepths.Top() - 1)
		case ops.Select:
			stackDepths.SetTop(stackDepths.Top() - 2)
		case ops.Return:
			stackDepths.SetTop(stackDepths.Top() - uint64(len(fn.Sig.ReturnTypes)))
			pushPolymorphicOp(blockPolymorphicOps, curIndex)
		case ops.End, ops.Else:
			blockSig := disas.Code[blockStartIndex].Block.Signature
			instr.Block = &BlockInfo{
				Start:     false,
				Signature: blockSig,
			}
			if op == ops.End {
				instr.Block.BlockStartIndex = int(blockStartIndex)
				disas.Code[blockStartIndex].Block.EndIndex = curIndex
			} else {  
				instr.Block.ElseIfIndex = int(blockStartIndex)
				disas.Code[blockStartIndex].Block.IfElseIndex = int(curIndex)
			}
			prevDepthIndex := stackDepths.Len() - 2
			prevDepth := stackDepths.Get(prevDepthIndex)
			if op != ops.Else && blockSig != wasm.BlockTypeEmpty {
				stackDepths.Set(prevDepthIndex, prevDepth+1)
				disas.checkMaxDepth(int(stackDepths.Get(prevDepthIndex)))
			}
			logger.Printf("setting new stack for %s block (%d)", disas.Code[blockStartIndex].Op.Name, blockStartIndex)
			blockPolymorphicOps = blockPolymorphicOps[:len(blockPolymorphicOps)-1]
			stackDepths.Pop()
			if op == ops.Else {
				stackDepths.Push(stackDepths.Top())
				blockPolymorphicOps = append(blockPolymorphicOps, []int{})
			}
		case ops.Block, ops.Loop, ops.If:
			sig := instr.Immediates[0].(wasm.BlockType)
			logger.Printf("if, depth is %d", stackDepths.Top())
			stackDepths.Push(stackDepths.Top())
			blockPolymorphicOps = append(blockPolymorphicOps, []int{})
			instr.Block = &BlockInfo{
				Start:     true,
				Signature: sig,
			}
		case ops.Br, ops.BrIf:
			depth := instr.Immediates[0].(uint32)
			if int(depth) == blockIndices.Len() {
				instr.IsReturn = true
			} else {
				curDepth := stackDepths.Top()
				prevDepth := stackDepths.Get(stackDepths.Len() - 2 - int(depth))
				elemsDiscard := int(curDepth) - int(prevDepth)
				if elemsDiscard < 0 {
					return nil, ErrStackUnderflow
				}
				if elemsDiscard > 1 {
					index := blockIndices.Get(blockIndices.Len() - 1 - int(depth))
					instr.NewStack = &StackInfo{
						StackTopDiff: int64(elemsDiscard),
						PreserveTop:  disas.Code[index].Block.Signature != wasm.BlockTypeEmpty,
					}
				}
			}
			if op == ops.Br {
				pushPolymorphicOp(blockPolymorphicOps, curIndex)
			}
		case ops.BrTable:
			stackDepths.SetTop(stackDepths.Top() - 1)
			targetCount := instr.Immediates[0].(uint32)
			for i := uint32(0); i < targetCount; i++ {
				entry := instr.Immediates[i+1].(uint32)
				var info StackInfo
				if int(entry) == blockIndices.Len() {
					info.IsReturn = true
				} else {
					curDepth := stackDepths.Top()
					branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(entry))
					elemsDiscard := int(curDepth) - int(branchDepth)
					logger.Printf("Curdepth %d branchDepth %d discard %d", curDepth, branchDepth, elemsDiscard)
					if elemsDiscard < 0 {
						return nil, ErrStackUnderflow
					}
					index := blockIndices.Get(blockIndices.Len() - 1 - int(entry))
					info.StackTopDiff = int64(elemsDiscard)
					info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
				}
				instr.Branches = append(instr.Branches, info)
			}
			defaultTarget := instr.Immediates[targetCount+1].(uint32)
			var info StackInfo
			if int(defaultTarget) == blockIndices.Len() {
				info.IsReturn = true
			} else {
				curDepth := stackDepths.Top()
				branchDepth := stackDepths.Get(stackDepths.Len() - 2 - int(defaultTarget))
				elemsDiscard := int(curDepth) - int(branchDepth)
				if elemsDiscard < 0 {
					return nil, ErrStackUnderflow
				}
				index := blockIndices.Get(blockIndices.Len() - 1 - int(defaultTarget))
				info.StackTopDiff = int64(elemsDiscard)
				info.PreserveTop = disas.Code[index].Block.Signature != wasm.BlockTypeEmpty
			}
			instr.Branches = append(instr.Branches, info)
			pushPolymorphicOp(blockPolymorphicOps, curIndex)
		case ops.Call, ops.CallIndirect:
			index := instr.Immediates[0].(uint32)
			var sig *wasm.FunctionSig
			top := int(stackDepths.Top())
			switch op {
			case ops.CallIndirect:
				if module.Types == nil {
					return nil, errors.New("missing types section")
				}
				sig = &module.Types.Entries[index]
				top--
			default:
				sig, err = module.GetFunctionSig(index)
				if err != nil {
					return nil, err
				}
			}
			top -= len(sig.ParamTypes)
			top += len(sig.ReturnTypes)
			stackDepths.SetTop(uint64(top))
			disas.checkMaxDepth(top)
		case ops.GetLocal, ops.SetLocal, ops.TeeLocal, ops.GetGlobal, ops.SetGlobal:
			top := stackDepths.Top()
			switch op {
			case ops.GetLocal, ops.GetGlobal:
				top++
				stackDepths.SetTop(top)
				disas.checkMaxDepth(int(top))
			case ops.SetLocal, ops.SetGlobal:
				top--
				stackDepths.SetTop(top)
			case ops.TeeLocal:
			}
		}
		disas.Code = append(disas.Code, instr)
		curIndex++
	}
	if logging {
		for _, instr := range disas.Code {
			logger.Printf("%v %v", instr.Op.Name, instr.NewStack)
		}
	}
	return disas, nil
}
NewDisassembly disassembles the given function. It also takes the function's
parent module as an argument for locating any other functions referenced by
fn.
package disasm
import (
	"bytes"
	"encoding/binary"
	"errors"
	"io"
	"math"
	"github.com/go-interpreter/wagon/internal/stack"
	"github.com/go-interpreter/wagon/wasm"
	"github.com/go-interpreter/wagon/wasm/leb128"
	ops "github.com/go-interpreter/wagon/wasm/operators"
)
type Instr struct {
	Op ops.Op
	Immediates  []interface{}
	NewStack    *StackInfo  
	Block       *BlockInfo  
	Unreachable bool        
	IsReturn bool
	Branches []StackInfo
}
type StackInfo struct {
	StackTopDiff int64  
	PreserveTop  bool   
	IsReturn     bool   
}
type BlockInfo struct {
	Start     bool            
	Signature wasm.BlockType  
	IfElseIndex int
	ElseIfIndex int
	EndIndex int
	BlockStartIndex int
}
type Disassembly struct {
	Code     []Instr
	MaxDepth int  
}
func (d *Disassembly) checkMaxDepth(depth int) {
	if depth > d.MaxDepth {
		d.MaxDepth = depth
	}
}
func pushPolymorphicOp(indexStack [][]int, index int) {
	indexStack[len(indexStack)-1] = append(indexStack[len(indexStack)-1], index)
}
func isInstrReachable(indexStack [][]int) bool {
	return len(indexStack[len(indexStack)-1]) == 0
}
var ErrStackUnderflow = errors.New("disasm: stack underflow")