vm: implement OnExecHook
Refs #3415 This commit introduces a small new change that implements the Hooks API and more specifically the OnExecHook. This feature can be used to implement test coverage collection, tracing, breakpoints, and etc. To be more specific, this commit: 1. adds a new `hooks` field to the `VM` (this field contains the OnExecHook function) 2. sets the default value of this hook to be a NOP function 3. adds the `VM.SetOnExecHook` method
This commit is contained in:
parent
0ae5e7ea83
commit
5d7fd0a72a
1 changed files with 30 additions and 0 deletions
30
pkg/vm/vm.go
30
pkg/vm/vm.go
|
@ -63,6 +63,15 @@ const (
|
|||
// SyscallHandler is a type for syscall handler.
|
||||
type SyscallHandler = func(*VM, uint32) error
|
||||
|
||||
// OnExecHook is a type for a callback that is invoked
|
||||
// for each executed instruction
|
||||
type OnExecHook = func(scriptHash util.Uint160, offset int, opcode opcode.Opcode)
|
||||
|
||||
// A struct that contains all VM hooks
|
||||
type hooks struct {
|
||||
onExec OnExecHook
|
||||
}
|
||||
|
||||
// VM represents the virtual machine.
|
||||
type VM struct {
|
||||
state vmstate.State
|
||||
|
@ -90,6 +99,10 @@ type VM struct {
|
|||
|
||||
// invTree is a top-level invocation tree (if enabled).
|
||||
invTree *invocations.Tree
|
||||
|
||||
// All registered hooks.
|
||||
// Each hook should never be nil.
|
||||
hooks hooks
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -99,6 +112,10 @@ var (
|
|||
bigTwo = big.NewInt(2)
|
||||
)
|
||||
|
||||
var defaultHooks = hooks{
|
||||
onExec: func(scriptHash util.Uint160, offset int, opcode opcode.Opcode) {},
|
||||
}
|
||||
|
||||
// New returns a new VM object ready to load AVM bytecode scripts.
|
||||
func New() *VM {
|
||||
return NewWithTrigger(trigger.Application)
|
||||
|
@ -109,6 +126,7 @@ func NewWithTrigger(t trigger.Type) *VM {
|
|||
vm := &VM{
|
||||
state: vmstate.None,
|
||||
trigger: t,
|
||||
hooks: defaultHooks,
|
||||
}
|
||||
|
||||
vm.istack = make([]*Context, 0, 8) // Most of invocations use one-two contracts, but they're likely to have internal calls.
|
||||
|
@ -116,6 +134,16 @@ func NewWithTrigger(t trigger.Type) *VM {
|
|||
return vm
|
||||
}
|
||||
|
||||
// SetOnExecHook sets the value of OnExecHook which
|
||||
// will be invoked for each executed instruction.
|
||||
// This function panics if the VM has been started.
|
||||
func (v *VM) SetOnExecHook(hook OnExecHook) {
|
||||
if v.state != vmstate.None {
|
||||
panic("Cannot set onExec hook of a started VM")
|
||||
}
|
||||
v.hooks.onExec = hook
|
||||
}
|
||||
|
||||
// SetPriceGetter registers the given PriceGetterFunc in v.
|
||||
// f accepts vm's Context, current instruction and instruction parameter.
|
||||
func (v *VM) SetPriceGetter(f func(opcode.Opcode, []byte) int64) {
|
||||
|
@ -472,7 +500,9 @@ func (v *VM) Step() error {
|
|||
|
||||
// step executes one instruction in the given context.
|
||||
func (v *VM) step(ctx *Context) error {
|
||||
instruction_offset := v.Context().nextip
|
||||
op, param, err := ctx.Next()
|
||||
v.hooks.onExec(v.GetCurrentScriptHash(), instruction_offset, op)
|
||||
if err != nil {
|
||||
v.state = vmstate.Fault
|
||||
return newError(ctx.ip, op, err)
|
||||
|
|
Loading…
Reference in a new issue