go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

vm.go (6447B)


      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 
    180 // split bytecode into head and b using length-prefixed bitfield
    181 func byteSplit(b []byte) ([]byte, []byte, error) {
    182 	bitFieldSize := b[0]
    183 	bitField := b[1:1+bitFieldSize]
    184 	b = b[1+bitFieldSize:]
    185 	return bitField, b, nil
    186 }
    187 
    188 // split bytecode into head and b using length-prefixed integer
    189 func intSplit(b []byte) (uint32, []byte, error) {
    190 	l := uint8(b[0])
    191 	sz := uint32(l)
    192 	b = b[1:]
    193 	if l > 0 {
    194 		r := []byte{0, 0, 0, 0}
    195 		c := 0
    196 		ll := 4 - l
    197 		var i uint8
    198 		for i = 0; i < 4; i++ {
    199 			if i >= ll {
    200 				r[i] = b[c]
    201 				c += 1
    202 			}
    203 		}
    204 		sz = binary.BigEndian.Uint32(r)
    205 		b = b[l:]
    206 	}
    207 	return sz, b, nil
    208 }
    209 
    210 // split bytecode into head and b using length-prefixed string
    211 func instructionSplit(b []byte) (string, []byte, error) {
    212 	if len(b) == 0 {
    213 		return "", nil, fmt.Errorf("argument is empty")
    214 	}
    215 	sz := uint8(b[0])
    216 	if sz == 0 {
    217 		return "", nil, fmt.Errorf("zero-length argument")
    218 	}
    219 	bSz := uint8(len(b))
    220 	if bSz < sz {
    221 		return "", nil, fmt.Errorf("corrupt instruction, len %v less than symbol length: %v", bSz, sz)
    222 	}
    223 	r := string(b[1:1+sz])
    224 	return r, b[1+sz:], nil
    225 }
    226 
    227 // check if the start of the given bytecode contains a valid opcode, extract and return it
    228 func opCheck(b []byte, opIn Opcode) ([]byte, error) {
    229 	var bb []byte
    230 	op, bb, err := opSplit(b)
    231 	if err != nil {
    232 		return b, err
    233 	}
    234 	b = bb
    235 	if op != opIn {
    236 		return b, fmt.Errorf("not a %v instruction", op)
    237 	}
    238 	return b, nil
    239 }
    240 
    241 // split bytecode into head and b using opcode
    242 func opSplit(b []byte) (Opcode, []byte, error) {
    243 	l := len(b)
    244 	if l < 2 {
    245 		return 0, b, fmt.Errorf("input size %v too short for opcode", l)
    246 	}
    247 	op := binary.BigEndian.Uint16(b)
    248 	if op > _MAX {
    249 		return 0, b, fmt.Errorf("invalid opcode %v", op)
    250 	}
    251 	return Opcode(op), b[2:], nil
    252 }