vm: allow to initialize static slot in method

This commit is contained in:
Evgenii Stratonikov 2020-07-24 11:54:12 +03:00
parent 28e0661f82
commit 6ecd1ae437
5 changed files with 37 additions and 14 deletions

View file

@ -228,7 +228,7 @@ func compareStacks(t *testing.T, expected []vmUTStackItem, actual *Stack) {
} }
func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) { func compareSlots(t *testing.T, expected []vmUTStackItem, actual *Slot) {
if actual == nil && len(expected) == 0 { if actual.storage == nil && len(expected) == 0 {
return return
} }
require.NotNil(t, actual) require.NotNil(t, actual)

View file

@ -10,16 +10,25 @@ type Slot struct {
refs *refCounter refs *refCounter
} }
// newSlot returns new slot of n items. // newSlot returns new slot with the provided reference counter.
func newSlot(n int, refs *refCounter) *Slot { func newSlot(refs *refCounter) *Slot {
return &Slot{ return &Slot{
storage: make([]stackitem.Item, n),
refs: refs, refs: refs,
} }
} }
// init sets static slot size to n. It is intended to be used only by INITSSLOT.
func (s *Slot) init(n int) {
if s.storage != nil {
panic("already initialized")
}
s.storage = make([]stackitem.Item, n)
}
func (v *VM) newSlot(n int) *Slot { func (v *VM) newSlot(n int) *Slot {
return newSlot(n, v.refs) s := newSlot(v.refs)
s.init(n)
return s
} }
// Set sets i-th storage slot. // Set sets i-th storage slot.
@ -43,12 +52,17 @@ func (s *Slot) Get(i int) stackitem.Item {
return stackitem.Null{} return stackitem.Null{}
} }
// Size returns slot size.
func (s *Slot) Size() int { return len(s.storage) }
// Clear removes all slot variables from reference counter. // Clear removes all slot variables from reference counter.
func (s *Slot) Clear() { func (s *Slot) Clear() {
for _, item := range s.storage { for _, item := range s.storage {
s.refs.Remove(item) s.refs.Remove(item)
} }
} }
// Size returns slot size.
func (s *Slot) Size() int {
if s.storage == nil {
panic("not initialized")
}
return len(s.storage)
}

View file

@ -9,8 +9,11 @@ import (
) )
func TestSlot_Get(t *testing.T) { func TestSlot_Get(t *testing.T) {
s := newSlot(3, newRefCounter()) s := newSlot(newRefCounter())
require.NotNil(t, s) require.NotNil(t, s)
require.Panics(t, func() { s.Size() })
s.init(3)
require.Equal(t, 3, s.Size()) require.Equal(t, 3, s.Size())
// Null is the default // Null is the default

View file

@ -282,6 +282,7 @@ func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) {
ctx.estack = v.estack ctx.estack = v.estack
ctx.tryStack = NewStack("exception") ctx.tryStack = NewStack("exception")
ctx.callFlag = f ctx.callFlag = f
ctx.static = newSlot(v.refs)
v.istack.PushVal(ctx) v.istack.PushVal(ctx)
} }
@ -568,13 +569,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushVal(result) v.estack.PushVal(result)
case opcode.INITSSLOT: case opcode.INITSSLOT:
if ctx.static != nil {
panic("already initialized")
}
if parameter[0] == 0 { if parameter[0] == 0 {
panic("zero argument") panic("zero argument")
} }
ctx.static = v.newSlot(int(parameter[0])) ctx.static.init(int(parameter[0]))
case opcode.INITSLOT: case opcode.INITSLOT:
if ctx.local != nil || ctx.arguments != nil { if ctx.local != nil || ctx.arguments != nil {

View file

@ -2596,6 +2596,14 @@ func TestSLOTOpcodes(t *testing.T) {
t.Run("Local", getTestFuncForVM(makeProgram(opcode.INITSLOT, 8, 0, opcode.STLOC, 7, opcode.LDLOC, 7), 42, 42)) t.Run("Local", getTestFuncForVM(makeProgram(opcode.INITSLOT, 8, 0, opcode.STLOC, 7, opcode.LDLOC, 7), 42, 42))
t.Run("Argument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 2, opcode.STARG, 1, opcode.LDARG, 1), 42, 42, 1, 2)) t.Run("Argument", getTestFuncForVM(makeProgram(opcode.INITSLOT, 0, 2, opcode.STARG, 1, opcode.LDARG, 1), 42, 42, 1, 2))
}) })
t.Run("InitStaticSlotInMethod", func(t *testing.T) {
prog := makeProgram(
opcode.CALL, 4, opcode.LDSFLD0, opcode.RET,
opcode.INITSSLOT, 1, opcode.PUSH12, opcode.STSFLD0, opcode.RET,
)
runWithArgs(t, prog, 12)
})
} }
func makeProgram(opcodes ...opcode.Opcode) []byte { func makeProgram(opcodes ...opcode.Opcode) []byte {