vm: count initial Null slot variables as well

Match C# behavior. Thanks to @ixje for finding this.
This commit is contained in:
Roman Khimov 2022-05-16 17:42:57 +03:00
parent 18d627e7f7
commit 0d93b353e6
5 changed files with 20 additions and 15 deletions

View file

@ -56,11 +56,11 @@ func opParamSlotsPushVM(op opcode.Opcode, param []byte, sslot int, slotloc int,
return nil return nil
} }
if sslot != 0 { if sslot != 0 {
v.Context().static.init(sslot) v.Context().static.init(sslot, &v.refs)
} }
if slotloc != 0 && slotarg != 0 { if slotloc != 0 && slotarg != 0 {
v.Context().local.init(slotloc) v.Context().local.init(slotloc, &v.refs)
v.Context().arguments.init(slotarg) v.Context().arguments.init(slotarg, &v.refs)
} }
for i := range items { for i := range items {
item, ok := items[i].(stackitem.Item) item, ok := items[i].(stackitem.Item)

View file

@ -10,11 +10,12 @@ import (
type slot []stackitem.Item type slot []stackitem.Item
// init sets static slot size to n. It is intended to be used only by INITSSLOT. // init sets static slot size to n. It is intended to be used only by INITSSLOT.
func (s *slot) init(n int) { func (s *slot) init(n int, rc *refCounter) {
if *s != nil { if *s != nil {
panic("already initialized") panic("already initialized")
} }
*s = make([]stackitem.Item, n) *s = make([]stackitem.Item, n)
*rc += refCounter(n) // Virtual "Null" elements.
} }
// Set sets i-th storage slot. // Set sets i-th storage slot.
@ -26,6 +27,8 @@ func (s slot) Set(i int, item stackitem.Item, refs *refCounter) {
s[i] = item s[i] = item
if old != nil { if old != nil {
refs.Remove(old) refs.Remove(old)
} else {
*refs-- // Not really existing, but counted Null element.
} }
refs.Add(item) refs.Add(item)
} }
@ -38,8 +41,8 @@ func (s slot) Get(i int) stackitem.Item {
return stackitem.Null{} return stackitem.Null{}
} }
// Clear removes all slot variables from the reference counter. // ClearRefs removes all slot variables from the reference counter.
func (s slot) Clear(refs *refCounter) { func (s slot) ClearRefs(refs *refCounter) {
for _, item := range s { for _, item := range s {
refs.Remove(item) refs.Remove(item)
} }

View file

@ -13,8 +13,9 @@ func TestSlot_Get(t *testing.T) {
var s slot var s slot
require.Panics(t, func() { s.Size() }) require.Panics(t, func() { s.Size() })
s.init(3) s.init(3, rc)
require.Equal(t, 3, s.Size()) require.Equal(t, 3, s.Size())
require.Equal(t, 3, int(*rc))
// Null is the default // Null is the default
item := s.Get(2) item := s.Get(2)
@ -22,4 +23,5 @@ func TestSlot_Get(t *testing.T) {
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, stackitem.NewBigInteger(big.NewInt(42)), s.Get(1))
require.Equal(t, 3, int(*rc))
} }

View file

@ -616,7 +616,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if parameter[0] == 0 { if parameter[0] == 0 {
panic("zero argument") panic("zero argument")
} }
ctx.static.init(int(parameter[0])) ctx.static.init(int(parameter[0]), &v.refs)
case opcode.INITSLOT: case opcode.INITSLOT:
if ctx.local != nil || ctx.arguments != nil { if ctx.local != nil || ctx.arguments != nil {
@ -626,11 +626,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic("zero argument") panic("zero argument")
} }
if parameter[0] > 0 { if parameter[0] > 0 {
ctx.local.init(int(parameter[0])) ctx.local.init(int(parameter[0]), &v.refs)
} }
if parameter[1] > 0 { if parameter[1] > 0 {
sz := int(parameter[1]) sz := int(parameter[1])
ctx.arguments.init(sz) ctx.arguments.init(sz, &v.refs)
for i := 0; i < sz; i++ { for i := 0; i < sz; i++ {
ctx.arguments.Set(i, v.estack.Pop().Item(), &v.refs) ctx.arguments.Set(i, v.estack.Pop().Item(), &v.refs)
} }
@ -1582,14 +1582,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
func (v *VM) unloadContext(ctx *Context) { func (v *VM) unloadContext(ctx *Context) {
if ctx.local != nil { if ctx.local != nil {
ctx.local.Clear(&v.refs) ctx.local.ClearRefs(&v.refs)
} }
if ctx.arguments != nil { if ctx.arguments != nil {
ctx.arguments.Clear(&v.refs) ctx.arguments.ClearRefs(&v.refs)
} }
currCtx := v.Context() currCtx := v.Context()
if ctx.static != nil && currCtx != nil && ctx.static != currCtx.static { if ctx.static != nil && currCtx != nil && ctx.static != currCtx.static {
ctx.static.Clear(&v.refs) ctx.static.ClearRefs(&v.refs)
} }
} }

View file

@ -387,8 +387,8 @@ func TestStackLimit(t *testing.T) {
inst opcode.Opcode inst opcode.Opcode
size int size int
}{ }{
{opcode.PUSH2, 1}, {opcode.PUSH2, 2}, // 1 from INITSSLOT and 1 for integer 2
{opcode.NEWARRAY, 3}, // array + 2 items {opcode.NEWARRAY, 4}, // array + 2 items
{opcode.STSFLD0, 3}, {opcode.STSFLD0, 3},
{opcode.LDSFLD0, 4}, {opcode.LDSFLD0, 4},
{opcode.NEWMAP, 5}, {opcode.NEWMAP, 5},