package vm import "fmt" import "github.com/pkg/errors" const GlobalMemoryEntries = 16 type State struct { Running bool Error error Functions [][]*Instruction FunctionIndex int64 InstructionIndex int64 ComparisonResult bool GlobalMemory [GlobalMemoryEntries]uint64 Stack []*StackFrame } func NewState(byteCodes [][]byte) (*State, error) { state := &State{} for i, byteCode := range byteCodes { instrs := []*Instruction{} for start := 0; start < len(byteCode); start += InstructionBytes { chunk := byteCode[start : start+InstructionBytes] instr, err := NewInstruction(chunk) if err != nil { return nil, errors.Wrapf(err, "At function index %d, byte offset %d", i, start) } instrs = append(instrs, instr) } instrs = append(instrs, &Instruction{ OpCode: OpReturn, }) state.Functions = append(state.Functions, instrs) } return state, nil } func (state *State) StackFrame() *StackFrame { return state.Stack[len(state.Stack)-1] } func (state *State) Function() []*Instruction { return state.Functions[state.FunctionIndex] } func (state *State) Execute() { state.setHandlers() state.call(0) state.Running = true for state.Running { state.ProcessInstruction() } } func (state *State) setError(err error) { state.Error = err state.Running = false } func (state *State) setHandlers() { for _, fnc := range state.Functions { for _, instr := range fnc { handler, found := OpHandlers[instr.OpCode] if !found { state.setError(fmt.Errorf("Invalid OpCode: 0x%08x", instr.OpCode)) return } instr.opHandler = handler } } } func (state *State) ProcessInstruction() { fnc := state.Function() instr := fnc[state.InstructionIndex] state.InstructionIndex += 1 instr.opHandler(state, instr) } func (state *State) ReadUnsigned(op *Operand) uint64 { switch op.Type { case Literal: return op.Value case FunctionMemoryIndex: if op.Value >= FunctionMemoryEntries { state.setError(fmt.Errorf("Invalid function memory index: %016x", op.Value)) return 0 } return state.StackFrame().FunctionMemory[op.Value] case GlobalMemoryIndex: if op.Value >= GlobalMemoryEntries { state.setError(fmt.Errorf("Invalid global memory index: %016x", op.Value)) return 0 } return state.GlobalMemory[op.Value] default: state.setError(fmt.Errorf("Unknown operand type: 0x%02x", op.Type)) return 0 } } func (state *State) ReadSigned(op *Operand) int64 { return int64(state.ReadUnsigned(op)) } func (state *State) WriteUnsigned(op *Operand, value uint64) { switch op.Type { case Literal: state.setError(fmt.Errorf("Write to literal operand")) case FunctionMemoryIndex: if op.Value >= FunctionMemoryEntries { state.setError(fmt.Errorf("Invalid function memory index: %016x", op.Value)) return } state.StackFrame().FunctionMemory[op.Value] = value case GlobalMemoryIndex: if op.Value >= GlobalMemoryEntries { state.setError(fmt.Errorf("Invalid global memory index: %016x", op.Value)) return } state.GlobalMemory[op.Value] = value default: state.setError(fmt.Errorf("Unknown operand type: 0x%02x", op.Type)) } } func (state *State) WriteSigned(op *Operand, value int64) { state.WriteUnsigned(op, uint64(value)) } func (state *State) call(functionOffset int64) { if state.FunctionIndex+functionOffset >= int64(len(state.Functions)) { state.setError(fmt.Errorf("Invalid function call index: %d + %d = %d", state.FunctionIndex, functionOffset, state.FunctionIndex+functionOffset)) return } stackFrame := &StackFrame{ PreviousFunctionIndex: state.FunctionIndex, PreviousInstructionIndex: state.InstructionIndex, } state.Stack = append(state.Stack, stackFrame) state.FunctionIndex += functionOffset state.InstructionIndex = 0 } func (state *State) ret() { state.FunctionIndex = state.StackFrame().PreviousFunctionIndex state.InstructionIndex = state.StackFrame().PreviousInstructionIndex state.Stack = state.Stack[:len(state.Stack)-1] if len(state.Stack) == 0 { state.Running = false } }