vm: expose Context slots, hide Set/ClearRefs on Slot, deprecate Dump*Slot methods

Signed-off-by: ixje <erik@coz.io>
This commit is contained in:
ixje 2024-11-15 11:05:42 +01:00 committed by Erik van den Brink
parent dda2cafdf8
commit 7ec0c1155c
7 changed files with 70 additions and 33 deletions

View file

@ -69,3 +69,13 @@ event-based waiter fallbacks to the old way of block monitoring with
Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
scheduled for Jun-Jul 2024 (~0.107.0 release).
## Dump*Slot() methods of `vm.Context`
The following new methods have been exposed to give access to VM context slot contents
with greater flexibility:
- `ArgumentsSlot`
- `LocalsSlot`
- `StaticsSlot`.
Removal of the `Dump*Slot()` methods are scheduled for the 0.108.0 release.

View file

@ -648,11 +648,11 @@ func handleSlots(c *cli.Context) error {
var rawSlot string
switch c.Command.Name {
case "sslot":
rawSlot = vmCtx.DumpStaticSlot()
rawSlot = dumpSlot(vmCtx.StaticsSlot())
case "lslot":
rawSlot = vmCtx.DumpLocalSlot()
rawSlot = dumpSlot(vmCtx.LocalsSlot())
case "aslot":
rawSlot = vmCtx.DumpArgumentsSlot()
rawSlot = dumpSlot(vmCtx.ArgumentsSlot())
default:
return errors.New("unknown slot")
}
@ -660,6 +660,14 @@ func handleSlots(c *cli.Context) error {
return nil
}
func dumpSlot(s *vm.Slot) string {
if s == nil {
return "[]"
}
b, _ := json.MarshalIndent(s, "", " ")
return string(b)
}
// prepareVM retrieves --historic flag from context (if set) and resets app state
// (to the specified historic height if given).
func prepareVM(c *cli.Context, tx *transaction.Transaction) error {

View file

@ -30,7 +30,7 @@ type scriptContext struct {
// Evaluation stack pointer.
estack *Stack
static slot
static Slot
// Script hash of the prog.
scriptHash util.Uint160
@ -65,8 +65,8 @@ type Context struct {
sc *scriptContext
local slot
arguments slot
local Slot
arguments Slot
// Exception context stack.
tryStack Stack
@ -278,22 +278,26 @@ func (c *Context) IsDeployed() bool {
}
// DumpStaticSlot returns json formatted representation of the given slot.
// Deprecated: to be removed in next version. Use [Context.StaticsSlot] instead.
func (c *Context) DumpStaticSlot() string {
return dumpSlot(&c.sc.static)
}
// DumpLocalSlot returns json formatted representation of the given slot.
// Deprecated: to be removed in next version. Use [Context.LocalsSlot] instead.
func (c *Context) DumpLocalSlot() string {
return dumpSlot(&c.local)
}
// DumpArgumentsSlot returns json formatted representation of the given slot.
// Deprecated: to be removed in next version. Use [Context.ArgumentsSlot] instead.
func (c *Context) DumpArgumentsSlot() string {
return dumpSlot(&c.arguments)
}
// dumpSlot returns json formatted representation of the given slot.
func dumpSlot(s *slot) string {
// Deprecated: to be removed in next version.
func dumpSlot(s *Slot) string {
if s == nil || *s == nil {
return "[]"
}
@ -354,3 +358,18 @@ func DynamicOnUnload(v *VM, ctx *Context, commit bool) error {
func (c *Context) BreakPoints() []int {
return slices.Clone(c.sc.breakPoints)
}
// ArgumentsSlot returns the arguments slot of Context.
func (c *Context) ArgumentsSlot() *Slot {
return &c.arguments
}
// LocalsSlot returns the local slot of Context.
func (c *Context) LocalsSlot() *Slot {
return &c.local
}
// StaticsSlot returns the static slot of Context.
func (c *Context) StaticsSlot() *Slot {
return &c.sc.static
}

View file

@ -240,7 +240,7 @@ func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
compareItemArrays(t, expected, actual.Len(), func(i int) stackitem.Item { return actual.Peek(i).Item() })
}
func compareSlots(t *testing.T, expected []vmUTStackItem, actual slot) {
func compareSlots(t *testing.T, expected []vmUTStackItem, actual Slot) {
if actual == nil && len(expected) == 0 {
return
}

View file

@ -6,11 +6,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// slot is a fixed-size slice of stack items.
type slot []stackitem.Item
// Slot is a fixed-size slice of stack items.
type Slot []stackitem.Item
// init sets static slot size to n. It is intended to be used only by INITSSLOT.
func (s *slot) init(n int, rc *refCounter) {
func (s *Slot) init(n int, rc *refCounter) {
if *s != nil {
panic("already initialized")
}
@ -18,8 +18,8 @@ func (s *slot) init(n int, rc *refCounter) {
*rc += refCounter(n) // Virtual "Null" elements.
}
// Set sets i-th storage slot.
func (s slot) Set(i int, item stackitem.Item, refs *refCounter) {
// set sets i-th storage slot.
func (s Slot) set(i int, item stackitem.Item, refs *refCounter) {
if s[i] == item {
return
}
@ -29,30 +29,30 @@ func (s slot) Set(i int, item stackitem.Item, refs *refCounter) {
}
// Get returns the item contained in the i-th slot.
func (s slot) Get(i int) stackitem.Item {
func (s Slot) Get(i int) stackitem.Item {
if item := s[i]; item != nil {
return item
}
return stackitem.Null{}
}
// ClearRefs removes all slot variables from the reference counter.
func (s slot) ClearRefs(refs *refCounter) {
// clearRefs removes all slot variables from the reference counter.
func (s Slot) clearRefs(refs *refCounter) {
for _, item := range s {
refs.Remove(item)
}
}
// Size returns the slot size.
func (s slot) Size() int {
func (s Slot) Size() int {
if s == nil {
panic("not initialized")
return 0
}
return len(s)
}
// MarshalJSON implements the JSON marshalling interface.
func (s slot) MarshalJSON() ([]byte, error) {
func (s Slot) MarshalJSON() ([]byte, error) {
arr := make([]json.RawMessage, len(s))
for i := range s {
data, err := stackitem.ToJSONWithTypes(s[i])

View file

@ -10,8 +10,8 @@ import (
func TestSlot_Get(t *testing.T) {
rc := newRefCounter()
var s slot
require.Panics(t, func() { s.Size() })
var s Slot
require.Equal(t, 0, s.Size())
s.init(3, rc)
require.Equal(t, 3, s.Size())
@ -21,7 +21,7 @@ func TestSlot_Get(t *testing.T) {
item := s.Get(2)
require.Equal(t, stackitem.Null{}, item)
s.Set(1, stackitem.NewBigInteger(big.NewInt(42)), rc)
s.set(1, stackitem.NewBigInteger(big.NewInt(42)), rc)
require.Equal(t, stackitem.NewBigInteger(big.NewInt(42)), s.Get(1))
require.Equal(t, 3, int(*rc))
}

View file

@ -697,7 +697,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
sz := int(parameter[1])
ctx.arguments.init(sz, &v.refs)
for i := range sz {
ctx.arguments.Set(i, v.estack.Pop().Item(), &v.refs)
ctx.arguments.set(i, v.estack.Pop().Item(), &v.refs)
}
}
@ -711,11 +711,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.STSFLD0, opcode.STSFLD1, opcode.STSFLD2, opcode.STSFLD3, opcode.STSFLD4, opcode.STSFLD5, opcode.STSFLD6:
item := v.estack.Pop().Item()
ctx.sc.static.Set(int(op-opcode.STSFLD0), item, &v.refs)
ctx.sc.static.set(int(op-opcode.STSFLD0), item, &v.refs)
case opcode.STSFLD:
item := v.estack.Pop().Item()
ctx.sc.static.Set(int(parameter[0]), item, &v.refs)
ctx.sc.static.set(int(parameter[0]), item, &v.refs)
case opcode.LDLOC0, opcode.LDLOC1, opcode.LDLOC2, opcode.LDLOC3, opcode.LDLOC4, opcode.LDLOC5, opcode.LDLOC6:
item := ctx.local.Get(int(op - opcode.LDLOC0))
@ -727,11 +727,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.STLOC0, opcode.STLOC1, opcode.STLOC2, opcode.STLOC3, opcode.STLOC4, opcode.STLOC5, opcode.STLOC6:
item := v.estack.Pop().Item()
ctx.local.Set(int(op-opcode.STLOC0), item, &v.refs)
ctx.local.set(int(op-opcode.STLOC0), item, &v.refs)
case opcode.STLOC:
item := v.estack.Pop().Item()
ctx.local.Set(int(parameter[0]), item, &v.refs)
ctx.local.set(int(parameter[0]), item, &v.refs)
case opcode.LDARG0, opcode.LDARG1, opcode.LDARG2, opcode.LDARG3, opcode.LDARG4, opcode.LDARG5, opcode.LDARG6:
item := ctx.arguments.Get(int(op - opcode.LDARG0))
@ -743,11 +743,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.STARG0, opcode.STARG1, opcode.STARG2, opcode.STARG3, opcode.STARG4, opcode.STARG5, opcode.STARG6:
item := v.estack.Pop().Item()
ctx.arguments.Set(int(op-opcode.STARG0), item, &v.refs)
ctx.arguments.set(int(op-opcode.STARG0), item, &v.refs)
case opcode.STARG:
item := v.estack.Pop().Item()
ctx.arguments.Set(int(parameter[0]), item, &v.refs)
ctx.arguments.set(int(parameter[0]), item, &v.refs)
case opcode.NEWBUFFER:
n := toInt(v.estack.Pop().BigInt())
@ -1692,15 +1692,15 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
func (v *VM) unloadContext(ctx *Context) {
if ctx.local != nil {
ctx.local.ClearRefs(&v.refs)
ctx.local.clearRefs(&v.refs)
}
if ctx.arguments != nil {
ctx.arguments.ClearRefs(&v.refs)
ctx.arguments.clearRefs(&v.refs)
}
currCtx := v.Context()
if currCtx == nil || ctx.sc != currCtx.sc {
if ctx.sc.static != nil {
ctx.sc.static.ClearRefs(&v.refs)
ctx.sc.static.clearRefs(&v.refs)
}
if ctx.sc.onUnload != nil {
err := ctx.sc.onUnload(v, ctx, v.uncaughtException == nil)