vm.go (5912B)
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 // 95 // r := uint32(b[0]) 96 // rr := uint32(b[1]) 97 // b = b[2:] 98 // return b, nil 99 } 100 101 // ParseMOut parses and extracts the expected argument portion of a MOUT instruction 102 func ParseMOut(b []byte) (string, string, []byte, error) { 103 return parseTwoSym(b) 104 } 105 106 // noop 107 func parseNoArg(b []byte) ([]byte, error) { 108 return b, nil 109 } 110 111 // parse and extract two length-prefixed string values 112 func parseSym(b []byte) (string, []byte, error) { 113 sym, b, err := instructionSplit(b) 114 if err != nil { 115 return "", b, err 116 } 117 return sym, b, nil 118 } 119 120 // parse and extract two length-prefixed string values 121 func parseTwoSym(b []byte) (string, string, []byte, error) { 122 symOne, b, err := instructionSplit(b) 123 if err != nil { 124 return "", "", b, err 125 } 126 symTwo, b, err := instructionSplit(b) 127 if err != nil { 128 return "", "", b, err 129 } 130 return symOne, symTwo, b, nil 131 } 132 133 // parse and extract one length-prefixed string value, and one length-prefixed integer value 134 func parseSymLen(b []byte) (string, uint32, []byte, error) { 135 sym, b, err := instructionSplit(b) 136 if err != nil { 137 return "", 0, b, err 138 } 139 sz, b, err := intSplit(b) 140 if err != nil { 141 return "", 0, b, err 142 } 143 return sym, sz, b, nil 144 } 145 146 // parse and extract one length-prefixed string value, and one single byte of integer 147 func parseSymSig(b []byte) (string, uint32, bool, []byte, error) { 148 sym, b, err := instructionSplit(b) 149 if err != nil { 150 return "", 0, false, b, err 151 } 152 sig, b, err := intSplit(b) 153 if err != nil { 154 return "", 0, false, b, err 155 } 156 if len(b) == 0 { 157 return "", 0, false, b, fmt.Errorf("instruction too short") 158 } 159 matchmode := b[0] > 0 160 b = b[1:] 161 162 return sym, sig, matchmode, b, nil 163 } 164 165 // parse and extract one single byte of integer 166 func parseSig(b []byte) (uint32, bool, []byte, error) { 167 sig, b, err := intSplit(b) 168 if err != nil { 169 return 0, false, b, err 170 } 171 if len(b) == 0 { 172 return 0, false, b, fmt.Errorf("instruction too short") 173 } 174 matchmode := b[0] > 0 175 b = b[1:] 176 177 return sig, matchmode, b, nil 178 } 179 180 // split bytecode into head and b using length-prefixed integer 181 func intSplit(b []byte) (uint32, []byte, error) { 182 l := uint8(b[0]) 183 sz := uint32(l) 184 b = b[1:] 185 if l > 0 { 186 r := []byte{0, 0, 0, 0} 187 c := 0 188 ll := 4 - l 189 var i uint8 190 for i = 0; i < 4; i++ { 191 if i >= ll { 192 r[i] = b[c] 193 c += 1 194 } 195 } 196 sz = binary.BigEndian.Uint32(r) 197 b = b[l:] 198 } 199 return sz, b, nil 200 } 201 202 // split bytecode into head and b using length-prefixed string 203 func instructionSplit(b []byte) (string, []byte, error) { 204 if len(b) == 0 { 205 return "", nil, fmt.Errorf("argument is empty") 206 } 207 sz := uint8(b[0]) 208 if sz == 0 { 209 return "", nil, fmt.Errorf("zero-length argument") 210 } 211 bSz := len(b) 212 if bSz < int(sz) { 213 return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", bSz, sz) 214 } 215 r := string(b[1 : 1+sz]) 216 return r, b[1+sz:], nil 217 } 218 219 // split bytecode into head and b using opcode 220 func opSplit(b []byte) (Opcode, []byte, error) { 221 l := len(b) 222 if l < 2 { 223 return 0, b, fmt.Errorf("input size %v too short for opcode", l) 224 } 225 op := binary.BigEndian.Uint16(b) 226 if op > _MAX { 227 return 0, b, fmt.Errorf("invalid opcode %v", op) 228 } 229 return Opcode(op), b[2:], nil 230 }