package vm import "fmt" import "github.com/pkg/errors" const globalMemoryEntries = 16 type State struct { running bool err error functions [][]*Instruction functionIndex int64 instructionIndex int64 comparisonResult bool globalMemory *memory stack []*stackFrame } func NewState(functions [][]*Instruction) (*State, error) { return &State{ functions: functions, globalMemory: newMemory(globalMemoryEntries), }, nil } func NewStateFromByteCode(byteCodes [][]byte) (*State, error) { functions := [][]*Instruction{} for i, byteCode := range byteCodes { instrs, err := NewInstructionsFromByteCode(byteCode) if err != nil { return nil, errors.Wrapf(err, "At function index %d", i) } functions = append(functions, instrs) } return NewState(functions) } func (state *State) Execute() { state.setHandlers() state.call(0) state.running = true for state.running { state.processInstruction() } } 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) setError(err error) { state.err = 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: value, err := state.globalMemory.readUnsigned(op.Value) if err != nil { state.setError(err) } return 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: err := state.globalMemory.writeUnsigned(op.Value, value) if err != nil { state.setError(err) } 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 } } func (state *State) jump(instructionOffset int64) { // -1 accounts for the +1 processInstruction() state.instructionIndex += instructionOffset - 1 }