mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-25 03:56:34 +00:00
d190b3a2e0
A while ago VM serialization format for Integer items was changed but compiler continued to emit Integers in old format. This commit changes compiler behaviour to be compatible with VM.
119 lines
2.7 KiB
Go
119 lines
2.7 KiB
Go
package compiler
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/io"
|
|
"github.com/CityOfZion/neo-go/pkg/vm"
|
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
)
|
|
|
|
// emit a VM Instruction with data to the given buffer.
|
|
func emit(w *io.BinWriter, instr opcode.Opcode, b []byte) {
|
|
emitOpcode(w, instr)
|
|
w.WriteBytes(b)
|
|
}
|
|
|
|
// emitOpcode emits a single VM Instruction the given buffer.
|
|
func emitOpcode(w *io.BinWriter, instr opcode.Opcode) {
|
|
w.WriteBytes([]byte{byte(instr)})
|
|
}
|
|
|
|
// emitBool emits a bool type the given buffer.
|
|
func emitBool(w *io.BinWriter, ok bool) {
|
|
if ok {
|
|
emitOpcode(w, opcode.PUSHT)
|
|
return
|
|
}
|
|
emitOpcode(w, opcode.PUSHF)
|
|
}
|
|
|
|
// emitInt emits a int type to the given buffer.
|
|
func emitInt(w *io.BinWriter, i int64) {
|
|
switch {
|
|
case i == -1:
|
|
emitOpcode(w, opcode.PUSHM1)
|
|
return
|
|
case i == 0:
|
|
emitOpcode(w, opcode.PUSHF)
|
|
return
|
|
case i > 0 && i < 16:
|
|
val := opcode.Opcode(int(opcode.PUSH1) - 1 + int(i))
|
|
emitOpcode(w, val)
|
|
return
|
|
}
|
|
|
|
bInt := big.NewInt(i)
|
|
val := vm.IntToBytes(bInt)
|
|
emitBytes(w, val)
|
|
}
|
|
|
|
// emitString emits a string to the given buffer.
|
|
func emitString(w *io.BinWriter, s string) {
|
|
emitBytes(w, []byte(s))
|
|
}
|
|
|
|
// emitBytes emits a byte array to the given buffer.
|
|
func emitBytes(w *io.BinWriter, b []byte) {
|
|
n := len(b)
|
|
|
|
switch {
|
|
case n <= int(opcode.PUSHBYTES75):
|
|
emit(w, opcode.Opcode(n), b)
|
|
return
|
|
case n < 0x100:
|
|
emit(w, opcode.PUSHDATA1, []byte{byte(n)})
|
|
case n < 0x10000:
|
|
buf := make([]byte, 2)
|
|
binary.LittleEndian.PutUint16(buf, uint16(n))
|
|
emit(w, opcode.PUSHDATA2, buf)
|
|
default:
|
|
buf := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(buf, uint32(n))
|
|
emit(w, opcode.PUSHDATA4, buf)
|
|
if w.Err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
w.WriteBytes(b)
|
|
}
|
|
|
|
// emitSyscall emits the syscall API to the given buffer.
|
|
// Syscall API string cannot be 0.
|
|
func emitSyscall(w *io.BinWriter, api string) {
|
|
if len(api) == 0 {
|
|
w.Err = errors.New("syscall api cannot be of length 0")
|
|
return
|
|
}
|
|
buf := make([]byte, len(api)+1)
|
|
buf[0] = byte(len(api))
|
|
copy(buf[1:], api)
|
|
emit(w, opcode.SYSCALL, buf)
|
|
}
|
|
|
|
// emitCall emits a call Instruction with label to the given buffer.
|
|
func emitCall(w *io.BinWriter, instr opcode.Opcode, label int16) {
|
|
emitJmp(w, instr, label)
|
|
}
|
|
|
|
// emitJmp emits a jump Instruction along with label to the given buffer.
|
|
func emitJmp(w *io.BinWriter, instr opcode.Opcode, label int16) {
|
|
if !isInstrJmp(instr) {
|
|
w.Err = fmt.Errorf("opcode %s is not a jump or call type", instr)
|
|
return
|
|
}
|
|
buf := make([]byte, 2)
|
|
binary.LittleEndian.PutUint16(buf, uint16(label))
|
|
emit(w, instr, buf)
|
|
}
|
|
|
|
func isInstrJmp(instr opcode.Opcode) bool {
|
|
if instr == opcode.JMP || instr == opcode.JMPIFNOT || instr == opcode.JMPIF || instr == opcode.CALL {
|
|
return true
|
|
}
|
|
return false
|
|
}
|