171 lines
4.0 KiB
Go
171 lines
4.0 KiB
Go
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
|
|
}
|
|
}
|