vm.go (5901B)
1 package vm 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 ) 7 8 // NewLine creates a new instruction line for the VM. 9 func NewLine(instructionList []byte, instruction uint16, strargs []string, byteargs []byte, numargs []uint8) []byte { 10 if instructionList == nil { 11 instructionList = []byte{} 12 } 13 b := []byte{0x00, 0x00} 14 binary.BigEndian.PutUint16(b, instruction) 15 for _, arg := range strargs { 16 b = append(b, uint8(len(arg))) 17 b = append(b, []byte(arg)...) 18 } 19 if byteargs != nil { 20 b = append(b, uint8(len(byteargs))) 21 b = append(b, byteargs...) 22 } 23 if numargs != nil { 24 b = append(b, numargs...) 25 } 26 return append(instructionList, b...) 27 } 28 29 // ParseOp verifies and extracts the expected opcode portion of an instruction 30 func ParseOp(b []byte) (Opcode, []byte, error) { 31 op, b, err := opSplit(b) 32 if err != nil { 33 return NOOP, b, err 34 } 35 return op, b, nil 36 } 37 38 // ParseLoad parses and extracts the expected argument portion of a LOAD instruction 39 func ParseLoad(b []byte) (string, uint32, []byte, error) { 40 return parseSymLen(b) 41 } 42 43 // ParseReload parses and extracts the expected argument portion of a RELOAD instruction 44 func ParseReload(b []byte) (string, []byte, error) { 45 return parseSym(b) 46 } 47 48 // ParseMap parses and extracts the expected argument portion of a MAP instruction 49 func ParseMap(b []byte) (string, []byte, error) { 50 return parseSym(b) 51 } 52 53 // ParseMove parses and extracts the expected argument portion of a MOVE instruction 54 func ParseMove(b []byte) (string, []byte, error) { 55 return parseSym(b) 56 } 57 58 // ParseHalt parses and extracts the expected argument portion of a HALT instruction 59 func ParseHalt(b []byte) ([]byte, error) { 60 return parseNoArg(b) 61 } 62 63 // ParseCatch parses and extracts the expected argument portion of a CATCH instruction 64 func ParseCatch(b []byte) (string, uint32, bool, []byte, error) { 65 return parseSymSig(b) 66 } 67 68 // ParseCroak parses and extracts the expected argument portion of a CROAK instruction 69 func ParseCroak(b []byte) (uint32, bool, []byte, error) { 70 return parseSig(b) 71 } 72 73 // ParseInCmp parses and extracts the expected argument portion of a INCMP instruction 74 func ParseInCmp(b []byte) (string, string, []byte, error) { 75 return parseTwoSym(b) 76 } 77 78 // ParseMPrev parses and extracts the expected argument portion of a MPREV instruction 79 func ParseMPrev(b []byte) (string, string, []byte, error) { 80 return parseTwoSym(b) 81 } 82 83 // ParseMNext parses and extracts the expected argument portion of a MNEXT instruction 84 func ParseMNext(b []byte) (string, string, []byte, error) { 85 return parseTwoSym(b) 86 } 87 88 // ParseMSink parses and extracts the expected argument portion of a MSINK instruction 89 func ParseMSink(b []byte) ([]byte, error) { 90 return parseNoArg(b) 91 // if len(b) < 2 { 92 // return 0, 0, b, fmt.Errorf("argument too short") 93 // } 94 // r := uint32(b[0]) 95 // rr := uint32(b[1]) 96 // b = b[2:] 97 // return b, nil 98 } 99 100 // ParseMOut parses and extracts the expected argument portion of a MOUT instruction 101 func ParseMOut(b []byte) (string, string, []byte, error) { 102 return parseTwoSym(b) 103 } 104 105 // noop 106 func parseNoArg(b []byte) ([]byte, error) { 107 return b, nil 108 } 109 110 // parse and extract two length-prefixed string values 111 func parseSym(b []byte) (string, []byte, error) { 112 sym, b, err := instructionSplit(b) 113 if err != nil { 114 return "", b, err 115 } 116 return sym, b, nil 117 } 118 119 // parse and extract two length-prefixed string values 120 func parseTwoSym(b []byte) (string, string, []byte, error) { 121 symOne, b, err := instructionSplit(b) 122 if err != nil { 123 return "", "", b, err 124 } 125 symTwo, b, err := instructionSplit(b) 126 if err != nil { 127 return "", "", b, err 128 } 129 return symOne, symTwo, b, nil 130 } 131 132 // parse and extract one length-prefixed string value, and one length-prefixed integer value 133 func parseSymLen(b []byte) (string, uint32, []byte, error) { 134 sym, b, err := instructionSplit(b) 135 if err != nil { 136 return "", 0, b, err 137 } 138 sz, b, err := intSplit(b) 139 if err != nil { 140 return "", 0, b, err 141 } 142 return sym, sz, b, nil 143 } 144 145 // parse and extract one length-prefixed string value, and one single byte of integer 146 func parseSymSig(b []byte) (string, uint32, bool, []byte, error) { 147 sym, b, err := instructionSplit(b) 148 if err != nil { 149 return "", 0, false, b, err 150 } 151 sig, b, err := intSplit(b) 152 if err != nil { 153 return "", 0, false, b, err 154 } 155 if len(b) == 0 { 156 return "", 0, false, b, fmt.Errorf("instruction too short") 157 } 158 matchmode := b[0] > 0 159 b = b[1:] 160 161 return sym, sig, matchmode, b, nil 162 } 163 164 // parse and extract one single byte of integer 165 func parseSig(b []byte) (uint32, bool, []byte, error) { 166 sig, b, err := intSplit(b) 167 if err != nil { 168 return 0, false, b, err 169 } 170 if len(b) == 0 { 171 return 0, false, b, fmt.Errorf("instruction too short") 172 } 173 matchmode := b[0] > 0 174 b = b[1:] 175 176 return sig, matchmode, b, nil 177 } 178 179 // split bytecode into head and b using length-prefixed integer 180 func intSplit(b []byte) (uint32, []byte, error) { 181 l := uint8(b[0]) 182 sz := uint32(l) 183 b = b[1:] 184 if l > 0 { 185 r := []byte{0, 0, 0, 0} 186 c := 0 187 ll := 4 - l 188 var i uint8 189 for i = 0; i < 4; i++ { 190 if i >= ll { 191 r[i] = b[c] 192 c += 1 193 } 194 } 195 sz = binary.BigEndian.Uint32(r) 196 b = b[l:] 197 } 198 return sz, b, nil 199 } 200 201 // split bytecode into head and b using length-prefixed string 202 func instructionSplit(b []byte) (string, []byte, error) { 203 if len(b) == 0 { 204 return "", nil, fmt.Errorf("argument is empty") 205 } 206 sz := uint8(b[0]) 207 if sz == 0 { 208 return "", nil, fmt.Errorf("zero-length argument") 209 } 210 bSz := len(b) 211 if bSz < int(sz) { 212 return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", bSz, sz) 213 } 214 r := string(b[1:1+sz]) 215 return r, b[1+sz:], nil 216 } 217 218 // split bytecode into head and b using opcode 219 func opSplit(b []byte) (Opcode, []byte, error) { 220 l := len(b) 221 if l < 2 { 222 return 0, b, fmt.Errorf("input size %v too short for opcode", l) 223 } 224 op := binary.BigEndian.Uint16(b) 225 if op > _MAX { 226 return 0, b, fmt.Errorf("invalid opcode %v", op) 227 } 228 return Opcode(op), b[2:], nil 229 }