8b3080b972
go vet is not happy about them: pkg/io/binaryReader.go:92:21: method ReadByte() byte should have signature ReadByte() (byte, error) pkg/io/binaryWriter.go:75:21: method WriteByte(u8 byte) should have signature WriteByte(byte) error
144 lines
3 KiB
Go
144 lines
3 KiB
Go
package vm
|
|
|
|
import (
|
|
"errors"
|
|
|
|
"github.com/CityOfZion/neo-go/pkg/io"
|
|
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
|
)
|
|
|
|
// Context represents the current execution context of the VM.
|
|
type Context struct {
|
|
// Instruction pointer.
|
|
ip int
|
|
|
|
// The next instruction pointer.
|
|
nextip int
|
|
|
|
// The raw program script.
|
|
prog []byte
|
|
|
|
// Breakpoints.
|
|
breakPoints []int
|
|
|
|
// Return value count, -1 is unspecified.
|
|
rvcount int
|
|
|
|
// Evaluation stack pointer.
|
|
estack *Stack
|
|
|
|
// Alt stack pointer.
|
|
astack *Stack
|
|
}
|
|
|
|
// NewContext returns a new Context object.
|
|
func NewContext(b []byte) *Context {
|
|
return &Context{
|
|
prog: b,
|
|
breakPoints: []int{},
|
|
rvcount: -1,
|
|
}
|
|
}
|
|
|
|
// Next returns the next instruction to execute with its parameter if any. After
|
|
// its invocation the instruction pointer points to the instruction being
|
|
// returned.
|
|
func (c *Context) Next() (opcode.Opcode, []byte, error) {
|
|
c.ip = c.nextip
|
|
if c.ip >= len(c.prog) {
|
|
return opcode.RET, nil, nil
|
|
}
|
|
r := io.NewBinReaderFromBuf(c.prog[c.ip:])
|
|
|
|
var instrbyte = r.ReadB()
|
|
instr := opcode.Opcode(instrbyte)
|
|
c.nextip++
|
|
|
|
var numtoread int
|
|
switch instr {
|
|
case opcode.PUSHDATA1, opcode.SYSCALL:
|
|
var n = r.ReadB()
|
|
numtoread = int(n)
|
|
c.nextip++
|
|
case opcode.PUSHDATA2:
|
|
var n = r.ReadU16LE()
|
|
numtoread = int(n)
|
|
c.nextip += 2
|
|
case opcode.PUSHDATA4:
|
|
var n = r.ReadU32LE()
|
|
if n > MaxItemSize {
|
|
return instr, nil, errors.New("parameter is too big")
|
|
}
|
|
numtoread = int(n)
|
|
c.nextip += 4
|
|
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL, opcode.CALLED, opcode.CALLEDT:
|
|
numtoread = 2
|
|
case opcode.CALLI:
|
|
numtoread = 4
|
|
case opcode.APPCALL, opcode.TAILCALL:
|
|
numtoread = 20
|
|
case opcode.CALLE, opcode.CALLET:
|
|
numtoread = 22
|
|
default:
|
|
if instr >= opcode.PUSHBYTES1 && instr <= opcode.PUSHBYTES75 {
|
|
numtoread = int(instr)
|
|
} else {
|
|
// No parameters, can just return.
|
|
return instr, nil, nil
|
|
}
|
|
}
|
|
parameter := make([]byte, numtoread)
|
|
r.ReadBytes(parameter)
|
|
if r.Err != nil {
|
|
return instr, nil, errors.New("failed to read instruction parameter")
|
|
}
|
|
c.nextip += numtoread
|
|
return instr, parameter, nil
|
|
}
|
|
|
|
// IP returns the absolute instruction without taking 0 into account.
|
|
// If that program starts the ip = 0 but IP() will return 1, cause its
|
|
// the first instruction.
|
|
func (c *Context) IP() int {
|
|
return c.ip + 1
|
|
}
|
|
|
|
// LenInstr returns the number of instructions loaded.
|
|
func (c *Context) LenInstr() int {
|
|
return len(c.prog)
|
|
}
|
|
|
|
// CurrInstr returns the current instruction and opcode.
|
|
func (c *Context) CurrInstr() (int, opcode.Opcode) {
|
|
return c.ip, opcode.Opcode(c.prog[c.ip])
|
|
}
|
|
|
|
// Copy returns an new exact copy of c.
|
|
func (c *Context) Copy() *Context {
|
|
ctx := new(Context)
|
|
*ctx = *c
|
|
return ctx
|
|
}
|
|
|
|
// Program returns the loaded program.
|
|
func (c *Context) Program() []byte {
|
|
return c.prog
|
|
}
|
|
|
|
// Value implements StackItem interface.
|
|
func (c *Context) Value() interface{} {
|
|
return c
|
|
}
|
|
|
|
func (c *Context) atBreakPoint() bool {
|
|
for _, n := range c.breakPoints {
|
|
if n == c.ip {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (c *Context) String() string {
|
|
return "execution context"
|
|
}
|