Skip to content

Commit

Permalink
Merge pull request #3677 from ixje/expose-slots
Browse files Browse the repository at this point in the history
vm: make slots public
  • Loading branch information
roman-khimov authored Nov 15, 2024
2 parents 5707bdd + 7ec0c11 commit 895a0ae
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 33 deletions.
12 changes: 11 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,14 @@ event-based waiter fallbacks to the old way of block monitoring with
`block_added` notifications subscription.

Removal of stale RPC server compatibility code from `waiter.EventWaiter` is
scheduled for Jun-Jul 2024 (~0.107.0 release).
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.
14 changes: 11 additions & 3 deletions cli/vm/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,18 +695,26 @@ 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")
}
fmt.Fprintln(c.App.Writer, rawSlot)
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 {
Expand Down
27 changes: 23 additions & 4 deletions pkg/vm/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ type scriptContext struct {
// Evaluation stack pointer.
estack *Stack

static slot
static Slot

// Script hash of the prog.
scriptHash util.Uint160
Expand Down Expand Up @@ -65,8 +65,8 @@ type Context struct {

sc *scriptContext

local slot
arguments slot
local Slot
arguments Slot

// Exception context stack.
tryStack Stack
Expand Down Expand Up @@ -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 "[]"
}
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion pkg/vm/json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
22 changes: 11 additions & 11 deletions pkg/vm/slot.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ 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")
}
*s = make([]stackitem.Item, n)
*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
}
Expand All @@ -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])
Expand Down
6 changes: 3 additions & 3 deletions pkg/vm/slot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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))
}
20 changes: 10 additions & 10 deletions pkg/vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,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)
}
}

Expand All @@ -719,11 +719,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))
Expand All @@ -735,11 +735,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))
Expand All @@ -751,11 +751,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())
Expand Down Expand Up @@ -1700,15 +1700,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)
Expand Down

0 comments on commit 895a0ae

Please sign in to comment.