mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-26 09:42:22 +00:00
Add Context stack Item
This commit is contained in:
parent
04c56b514c
commit
1ff0caf40e
3 changed files with 164 additions and 0 deletions
137
pkg/vm/stack/context.go
Normal file
137
pkg/vm/stack/context.go
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context represent the current execution context of the VM.
|
||||||
|
// context will be treated as stack item
|
||||||
|
// and placed onto the invocation stack
|
||||||
|
type Context struct {
|
||||||
|
*abstractItem
|
||||||
|
|
||||||
|
// Instruction pointer.
|
||||||
|
ip int
|
||||||
|
|
||||||
|
// The raw program script.
|
||||||
|
prog []byte
|
||||||
|
|
||||||
|
// Breakpoints
|
||||||
|
breakPoints []int
|
||||||
|
|
||||||
|
// Evaluation Stack
|
||||||
|
Estack RandomAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext return a new Context object.
|
||||||
|
func NewContext(b []byte) *Context {
|
||||||
|
return &Context{
|
||||||
|
abstractItem: &abstractItem{},
|
||||||
|
ip: -1,
|
||||||
|
prog: b,
|
||||||
|
breakPoints: []int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context overrides the default implementation
|
||||||
|
// to return a context item
|
||||||
|
func (c *Context) Context() (*Context, error) {
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next return the next instruction to execute.
|
||||||
|
func (c *Context) Next() Instruction {
|
||||||
|
c.ip++
|
||||||
|
if c.ip >= len(c.prog) {
|
||||||
|
return RET
|
||||||
|
}
|
||||||
|
return Instruction(c.prog[c.ip])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, Instruction) {
|
||||||
|
if c.ip < 0 {
|
||||||
|
return c.ip, Instruction(0x00)
|
||||||
|
}
|
||||||
|
return c.ip, Instruction(c.prog[c.ip])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns an new exact copy of c.
|
||||||
|
func (c *Context) Copy() *Context {
|
||||||
|
return &Context{
|
||||||
|
ip: c.ip,
|
||||||
|
prog: c.prog,
|
||||||
|
breakPoints: c.breakPoints,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program returns the loaded program.
|
||||||
|
func (c *Context) Program() []byte {
|
||||||
|
return c.prog
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) readUint32() uint32 {
|
||||||
|
start, end := c.IP(), c.IP()+4
|
||||||
|
if end > len(c.prog) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val := binary.LittleEndian.Uint32(c.prog[start:end])
|
||||||
|
c.ip += 4
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) readUint16() uint16 {
|
||||||
|
start, end := c.IP(), c.IP()+2
|
||||||
|
if end > len(c.prog) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val := binary.LittleEndian.Uint16(c.prog[start:end])
|
||||||
|
c.ip += 2
|
||||||
|
return val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) readByte() byte {
|
||||||
|
return c.readBytes(1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) readBytes(n int) []byte {
|
||||||
|
start, end := c.IP(), c.IP()+n
|
||||||
|
if end > len(c.prog) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make([]byte, n)
|
||||||
|
copy(out, c.prog[start:end])
|
||||||
|
c.ip += n
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) readVarBytes() []byte {
|
||||||
|
n := c.readByte()
|
||||||
|
return c.readBytes(int(n))
|
||||||
|
}
|
20
pkg/vm/stack/context_test.go
Normal file
20
pkg/vm/stack/context_test.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNextInstruction(t *testing.T) {
|
||||||
|
// PUSHBYTES1 2
|
||||||
|
builder := NewBuilder()
|
||||||
|
builder.EmitBytes([]byte{0x02}) //[]byte{0x01, 0x02}
|
||||||
|
|
||||||
|
ctx := NewContext(builder.Bytes())
|
||||||
|
op := ctx.Next()
|
||||||
|
byt := ctx.readByte()
|
||||||
|
|
||||||
|
assert.Equal(t, PUSHBYTES1, op)
|
||||||
|
assert.Equal(t, byte(2), byt)
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ type Item interface {
|
||||||
Boolean() (*Boolean, error)
|
Boolean() (*Boolean, error)
|
||||||
ByteArray() (*ByteArray, error)
|
ByteArray() (*ByteArray, error)
|
||||||
Array() (*Array, error)
|
Array() (*Array, error)
|
||||||
|
Context() (*Context, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Represents an `abstract` stack item
|
// Represents an `abstract` stack item
|
||||||
|
@ -40,3 +41,9 @@ func (a *abstractItem) ByteArray() (*ByteArray, error) {
|
||||||
func (a *abstractItem) Array() (*Array, error) {
|
func (a *abstractItem) Array() (*Array, error) {
|
||||||
return nil, errors.New("This stack item is not an array")
|
return nil, errors.New("This stack item is not an array")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context is the default implementation for a stackItem
|
||||||
|
// Implements Item interface
|
||||||
|
func (a *abstractItem) Context() (*Context, error) {
|
||||||
|
return nil, errors.New("This stack item is not of type context")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue