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
}
if sslot != 0 {
v.Context().static.init(sslot)
v.Context().static.init(sslot, &v.refs)
}
if slotloc != 0 && slotarg != 0 {
v.Context().local.init(slotloc)
v.Context().arguments.init(slotarg)
v.Context().local.init(slotloc, &v.refs)
v.Context().arguments.init(slotarg, &v.refs)
}
for i := range items {
item, ok := items[i].(stackitem.Item)

View file

@ -10,11 +10,12 @@ import (
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) {
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.
@ -26,6 +27,8 @@ func (s slot) Set(i int, item stackitem.Item, refs *refCounter) {
s[i] = item
if old != nil {
refs.Remove(old)
} else {
*refs-- // Not really existing, but counted Null element.
}
refs.Add(item)
}
@ -38,8 +41,8 @@ func (s slot) Get(i int) stackitem.Item {
return stackitem.Null{}
}
// Clear removes all slot variables from the reference counter.
func (s slot) Clear(refs *refCounter) {
// ClearRefs removes all slot variables from the reference counter.
func (s slot) ClearRefs(refs *refCounter) {
for _, item := range s {
refs.Remove(item)
}

View file

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

@ -616,7 +616,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if parameter[0] == 0 {
panic("zero argument")
}
ctx.static.init(int(parameter[0]))
ctx.static.init(int(parameter[0]), &v.refs)
case opcode.INITSLOT:
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")
}
if parameter[0] > 0 {
ctx.local.init(int(parameter[0]))
ctx.local.init(int(parameter[0]), &v.refs)
}
if parameter[1] > 0 {
sz := int(parameter[1])
ctx.arguments.init(sz)
ctx.arguments.init(sz, &v.refs)
for i := 0; i < sz; i++ {
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) {
if ctx.local != nil {
ctx.local.Clear(&v.refs)
ctx.local.ClearRefs(&v.refs)
}
if ctx.arguments != nil {
ctx.arguments.Clear(&v.refs)
ctx.arguments.ClearRefs(&v.refs)
}
currCtx := v.Context()
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
size int
}{
{opcode.PUSH2, 1},
{opcode.NEWARRAY, 3}, // array + 2 items
{opcode.PUSH2, 2}, // 1 from INITSSLOT and 1 for integer 2
{opcode.NEWARRAY, 4}, // array + 2 items
{opcode.STSFLD0, 3},
{opcode.LDSFLD0, 4},
{opcode.NEWMAP, 5},