Merge pull request #934 from nspcc-dev/feature/pointer

Implement function pointers in the VM
This commit is contained in:
Roman Khimov 2020-05-12 15:47:01 +03:00 committed by GitHub
commit 8e916f5ab8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 336 additions and 132 deletions

View file

@ -37,6 +37,9 @@ type codegen struct {
// A mapping of func identifiers with their scope.
funcs map[string]*funcScope
// A mapping of lambda functions into their scope.
lambda map[string]*funcScope
// Current funcScope being converted.
scope *funcScope
@ -204,8 +207,8 @@ func (c *codegen) convertGlobals(f ast.Node) {
func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
var (
f *funcScope
ok bool
f *funcScope
ok, isLambda bool
)
f, ok = c.funcs[decl.Name.Name]
@ -215,6 +218,9 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
return
}
c.setLabel(f.label)
} else if f, ok = c.lambda[decl.Name.Name]; ok {
isLambda = ok
c.setLabel(f.label)
} else {
f = c.newFunc(decl)
}
@ -275,6 +281,13 @@ func (c *codegen) convertFuncDecl(file ast.Node, decl *ast.FuncDecl) {
}
f.rng.End = uint16(c.prog.Len() - 1)
if !isLambda {
for _, f := range c.lambda {
c.convertFuncDecl(file, f.decl)
}
c.lambda = make(map[string]*funcScope)
}
}
func (c *codegen) Visit(node ast.Node) ast.Visitor {
@ -521,6 +534,14 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
return nil
case *ast.FuncLit:
l := c.newLabel()
c.newLambda(l, n)
buf := make([]byte, 4)
binary.LittleEndian.PutUint16(buf, l)
emit.Instruction(c.prog.BinWriter, opcode.PUSHA, buf)
return nil
case *ast.BasicLit:
c.emitLoadConst(c.typeInfo.Types[n])
return nil
@ -646,6 +667,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
var (
f *funcScope
ok bool
name string
numArgs = len(n.Args)
isBuiltin = isBuiltin(n.Fun)
)
@ -654,8 +676,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
case *ast.Ident:
f, ok = c.funcs[fun.Name]
if !ok && !isBuiltin {
c.prog.Err = fmt.Errorf("could not resolve function %s", fun.Name)
return nil
name = fun.Name
}
case *ast.SelectorExpr:
// If this is a method call we need to walk the AST to load the struct locally.
@ -700,6 +721,10 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
// Use the ident to check, builtins are not in func scopes.
// We can be sure builtins are of type *ast.Ident.
c.convertBuiltin(n)
case name != "":
// Function was not found thus is can be only an invocation of func-typed variable.
c.emitLoadLocal(name)
emit.Opcode(c.prog.BinWriter, opcode.CALLA)
case isSyscall(f):
c.convertSyscall(n, f.selector.Name, f.name)
default:
@ -1264,6 +1289,15 @@ func (c *codegen) newFunc(decl *ast.FuncDecl) *funcScope {
return f
}
func (c *codegen) newLambda(u uint16, lit *ast.FuncLit) {
name := fmt.Sprintf("lambda@%d", u)
c.lambda[name] = newFuncScope(&ast.FuncDecl{
Name: ast.NewIdent(name),
Type: lit.Type,
Body: lit.Body,
}, u)
}
func (c *codegen) compile(info *buildInfo, pkg *loader.PackageInfo) error {
// Resolve the entrypoint of the program.
main, mainFile := resolveEntryPoint(mainIdent, pkg)
@ -1319,6 +1353,7 @@ func newCodegen(info *buildInfo, pkg *loader.PackageInfo) *codegen {
prog: io.NewBufBinWriter(),
l: []int{},
funcs: map[string]*funcScope{},
lambda: map[string]*funcScope{},
labels: map[labelWithType]uint16{},
typeInfo: &pkg.Info,
@ -1364,7 +1399,7 @@ func (c *codegen) writeJumps(b []byte) error {
case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL,
opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLEL, opcode.JMPLTL,
opcode.CALLL:
opcode.CALLL, opcode.PUSHA:
// we can't use arg returned by ctx.Next() because it is copied
nextIP := ctx.NextIP()
arg := b[nextIP-4:]
@ -1373,7 +1408,12 @@ func (c *codegen) writeJumps(b []byte) error {
if int(index) > len(c.l) {
return fmt.Errorf("unexpected label number: %d (max %d)", index, len(c.l))
}
offset := c.l[index] - nextIP + 5
var offset int
if op == opcode.PUSHA {
offset = c.l[index]
} else {
offset = c.l[index] - nextIP + 5
}
if offset > math.MaxInt32 || offset < math.MinInt32 {
return fmt.Errorf("label offset is too big at the instruction %d: %d (max %d, min %d)",
nextIP-5, offset, math.MaxInt32, math.MinInt32)

View file

@ -42,8 +42,12 @@ type funcScope struct {
}
func newFuncScope(decl *ast.FuncDecl, label uint16) *funcScope {
var name string
if decl.Name != nil {
name = decl.Name.Name
}
return &funcScope{
name: decl.Name.Name,
name: name,
decl: decl,
label: label,
locals: map[string]int{},

View file

@ -111,7 +111,7 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
numtoread = 1
case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL,
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL,
opcode.CALLL, opcode.SYSCALL:
opcode.CALLL, opcode.SYSCALL, opcode.PUSHA:
numtoread = 4
default:
if instr <= opcode.PUSHINT256 {

View file

@ -15,6 +15,7 @@ const (
PUSHINT128 Opcode = 0x04
PUSHINT256 Opcode = 0x05
PUSHA Opcode = 0x0A
PUSHNULL Opcode = 0x0B
PUSHDATA1 Opcode = 0x0C
@ -64,6 +65,7 @@ const (
JMPLEL Opcode = 0x33
CALL Opcode = 0x34
CALLL Opcode = 0x35
CALLA Opcode = 0x36
// Exceptions
ABORT Opcode = 0x37

View file

@ -14,6 +14,7 @@ func _() {
_ = x[PUSHINT64-3]
_ = x[PUSHINT128-4]
_ = x[PUSHINT256-5]
_ = x[PUSHA-10]
_ = x[PUSHNULL-11]
_ = x[PUSHDATA1-12]
_ = x[PUSHDATA2-13]
@ -59,6 +60,7 @@ func _() {
_ = x[JMPLEL-51]
_ = x[CALL-52]
_ = x[CALLL-53]
_ = x[CALLA-54]
_ = x[ABORT-55]
_ = x[ASSERT-56]
_ = x[THROW-58]
@ -141,7 +143,7 @@ func _() {
_ = x[CONVERT-219]
}
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENDUPFROMALTSTACKTOALTSTACKFROMALTSTACKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENDUPFROMALTSTACKTOALTSTACKFROMALTSTACKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8],
@ -150,128 +152,130 @@ var _Opcode_map = map[Opcode]string{
3: _Opcode_name[26:35],
4: _Opcode_name[35:45],
5: _Opcode_name[45:55],
11: _Opcode_name[55:63],
12: _Opcode_name[63:72],
13: _Opcode_name[72:81],
14: _Opcode_name[81:90],
15: _Opcode_name[90:96],
16: _Opcode_name[96:101],
17: _Opcode_name[101:106],
18: _Opcode_name[106:111],
19: _Opcode_name[111:116],
20: _Opcode_name[116:121],
21: _Opcode_name[121:126],
22: _Opcode_name[126:131],
23: _Opcode_name[131:136],
24: _Opcode_name[136:141],
25: _Opcode_name[141:146],
26: _Opcode_name[146:152],
27: _Opcode_name[152:158],
28: _Opcode_name[158:164],
29: _Opcode_name[164:170],
30: _Opcode_name[170:176],
31: _Opcode_name[176:182],
32: _Opcode_name[182:188],
33: _Opcode_name[188:191],
34: _Opcode_name[191:194],
35: _Opcode_name[194:198],
36: _Opcode_name[198:203],
37: _Opcode_name[203:209],
38: _Opcode_name[209:217],
39: _Opcode_name[217:226],
40: _Opcode_name[226:231],
41: _Opcode_name[231:237],
42: _Opcode_name[237:242],
43: _Opcode_name[242:248],
44: _Opcode_name[248:253],
45: _Opcode_name[253:259],
46: _Opcode_name[259:264],
47: _Opcode_name[264:270],
48: _Opcode_name[270:275],
49: _Opcode_name[275:281],
50: _Opcode_name[281:286],
51: _Opcode_name[286:292],
52: _Opcode_name[292:296],
53: _Opcode_name[296:301],
55: _Opcode_name[301:306],
56: _Opcode_name[306:312],
58: _Opcode_name[312:317],
64: _Opcode_name[317:320],
65: _Opcode_name[320:327],
67: _Opcode_name[327:332],
69: _Opcode_name[332:336],
70: _Opcode_name[336:339],
72: _Opcode_name[339:344],
73: _Opcode_name[344:349],
74: _Opcode_name[349:352],
75: _Opcode_name[352:356],
77: _Opcode_name[356:360],
78: _Opcode_name[360:364],
80: _Opcode_name[364:368],
81: _Opcode_name[368:376],
82: _Opcode_name[376:380],
83: _Opcode_name[380:388],
84: _Opcode_name[388:396],
85: _Opcode_name[396:404],
106: _Opcode_name[404:419],
107: _Opcode_name[419:429],
108: _Opcode_name[429:441],
126: _Opcode_name[441:444],
127: _Opcode_name[444:450],
128: _Opcode_name[450:454],
129: _Opcode_name[454:459],
144: _Opcode_name[459:465],
145: _Opcode_name[465:468],
146: _Opcode_name[468:470],
147: _Opcode_name[470:473],
151: _Opcode_name[473:478],
152: _Opcode_name[478:486],
153: _Opcode_name[486:490],
154: _Opcode_name[490:493],
155: _Opcode_name[493:499],
156: _Opcode_name[499:502],
157: _Opcode_name[502:505],
158: _Opcode_name[505:508],
159: _Opcode_name[508:511],
160: _Opcode_name[511:514],
161: _Opcode_name[514:517],
162: _Opcode_name[517:520],
168: _Opcode_name[520:523],
169: _Opcode_name[523:526],
170: _Opcode_name[526:529],
171: _Opcode_name[529:536],
172: _Opcode_name[536:542],
177: _Opcode_name[542:544],
179: _Opcode_name[544:552],
180: _Opcode_name[552:563],
181: _Opcode_name[563:565],
182: _Opcode_name[565:568],
183: _Opcode_name[568:570],
184: _Opcode_name[570:573],
185: _Opcode_name[573:576],
186: _Opcode_name[576:579],
187: _Opcode_name[579:585],
192: _Opcode_name[585:589],
193: _Opcode_name[589:595],
194: _Opcode_name[595:604],
195: _Opcode_name[604:612],
196: _Opcode_name[612:621],
197: _Opcode_name[621:631],
198: _Opcode_name[631:640],
200: _Opcode_name[640:646],
202: _Opcode_name[646:650],
203: _Opcode_name[650:656],
204: _Opcode_name[656:660],
205: _Opcode_name[660:666],
206: _Opcode_name[666:674],
207: _Opcode_name[674:680],
208: _Opcode_name[680:687],
209: _Opcode_name[687:699],
210: _Opcode_name[699:705],
211: _Opcode_name[705:715],
216: _Opcode_name[715:721],
217: _Opcode_name[721:727],
219: _Opcode_name[727:734],
10: _Opcode_name[55:60],
11: _Opcode_name[60:68],
12: _Opcode_name[68:77],
13: _Opcode_name[77:86],
14: _Opcode_name[86:95],
15: _Opcode_name[95:101],
16: _Opcode_name[101:106],
17: _Opcode_name[106:111],
18: _Opcode_name[111:116],
19: _Opcode_name[116:121],
20: _Opcode_name[121:126],
21: _Opcode_name[126:131],
22: _Opcode_name[131:136],
23: _Opcode_name[136:141],
24: _Opcode_name[141:146],
25: _Opcode_name[146:151],
26: _Opcode_name[151:157],
27: _Opcode_name[157:163],
28: _Opcode_name[163:169],
29: _Opcode_name[169:175],
30: _Opcode_name[175:181],
31: _Opcode_name[181:187],
32: _Opcode_name[187:193],
33: _Opcode_name[193:196],
34: _Opcode_name[196:199],
35: _Opcode_name[199:203],
36: _Opcode_name[203:208],
37: _Opcode_name[208:214],
38: _Opcode_name[214:222],
39: _Opcode_name[222:231],
40: _Opcode_name[231:236],
41: _Opcode_name[236:242],
42: _Opcode_name[242:247],
43: _Opcode_name[247:253],
44: _Opcode_name[253:258],
45: _Opcode_name[258:264],
46: _Opcode_name[264:269],
47: _Opcode_name[269:275],
48: _Opcode_name[275:280],
49: _Opcode_name[280:286],
50: _Opcode_name[286:291],
51: _Opcode_name[291:297],
52: _Opcode_name[297:301],
53: _Opcode_name[301:306],
54: _Opcode_name[306:311],
55: _Opcode_name[311:316],
56: _Opcode_name[316:322],
58: _Opcode_name[322:327],
64: _Opcode_name[327:330],
65: _Opcode_name[330:337],
67: _Opcode_name[337:342],
69: _Opcode_name[342:346],
70: _Opcode_name[346:349],
72: _Opcode_name[349:354],
73: _Opcode_name[354:359],
74: _Opcode_name[359:362],
75: _Opcode_name[362:366],
77: _Opcode_name[366:370],
78: _Opcode_name[370:374],
80: _Opcode_name[374:378],
81: _Opcode_name[378:386],
82: _Opcode_name[386:390],
83: _Opcode_name[390:398],
84: _Opcode_name[398:406],
85: _Opcode_name[406:414],
106: _Opcode_name[414:429],
107: _Opcode_name[429:439],
108: _Opcode_name[439:451],
126: _Opcode_name[451:454],
127: _Opcode_name[454:460],
128: _Opcode_name[460:464],
129: _Opcode_name[464:469],
144: _Opcode_name[469:475],
145: _Opcode_name[475:478],
146: _Opcode_name[478:480],
147: _Opcode_name[480:483],
151: _Opcode_name[483:488],
152: _Opcode_name[488:496],
153: _Opcode_name[496:500],
154: _Opcode_name[500:503],
155: _Opcode_name[503:509],
156: _Opcode_name[509:512],
157: _Opcode_name[512:515],
158: _Opcode_name[515:518],
159: _Opcode_name[518:521],
160: _Opcode_name[521:524],
161: _Opcode_name[524:527],
162: _Opcode_name[527:530],
168: _Opcode_name[530:533],
169: _Opcode_name[533:536],
170: _Opcode_name[536:539],
171: _Opcode_name[539:546],
172: _Opcode_name[546:552],
177: _Opcode_name[552:554],
179: _Opcode_name[554:562],
180: _Opcode_name[562:573],
181: _Opcode_name[573:575],
182: _Opcode_name[575:578],
183: _Opcode_name[578:580],
184: _Opcode_name[580:583],
185: _Opcode_name[583:586],
186: _Opcode_name[586:589],
187: _Opcode_name[589:595],
192: _Opcode_name[595:599],
193: _Opcode_name[599:605],
194: _Opcode_name[605:614],
195: _Opcode_name[614:622],
196: _Opcode_name[622:631],
197: _Opcode_name[631:641],
198: _Opcode_name[641:650],
200: _Opcode_name[650:656],
202: _Opcode_name[656:660],
203: _Opcode_name[660:666],
204: _Opcode_name[666:670],
205: _Opcode_name[670:676],
206: _Opcode_name[676:684],
207: _Opcode_name[684:690],
208: _Opcode_name[690:697],
209: _Opcode_name[697:709],
210: _Opcode_name[709:715],
211: _Opcode_name[715:725],
216: _Opcode_name[725:731],
217: _Opcode_name[731:737],
219: _Opcode_name[737:744],
}
func (i Opcode) String() string {

View file

@ -10,7 +10,9 @@ import (
"math/big"
"reflect"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
@ -857,3 +859,84 @@ func (i *InteropItem) Convert(typ StackItemType) (StackItem, error) {
func (i *InteropItem) MarshalJSON() ([]byte, error) {
return json.Marshal(i.value)
}
// PointerItem represents VM-level instruction pointer.
type PointerItem struct {
pos int
script []byte
hash util.Uint160
}
// NewPointerItem returns new pointer on the specified position.
func NewPointerItem(pos int, script []byte) *PointerItem {
return &PointerItem{
pos: pos,
script: script,
hash: hash.Hash160(script),
}
}
// String implements StackItem interface.
func (p *PointerItem) String() string {
return "Pointer"
}
// Value implements StackItem interface.
func (p *PointerItem) Value() interface{} {
return p.pos
}
// Dup implements StackItem interface.
func (p *PointerItem) Dup() StackItem {
return &PointerItem{
pos: p.pos,
script: p.script,
hash: p.hash,
}
}
// Bool implements StackItem interface.
func (p *PointerItem) Bool() bool {
return true
}
// TryBytes implements StackItem interface.
func (p *PointerItem) TryBytes() ([]byte, error) {
return nil, errors.New("can't convert Pointer to ByteArray")
}
// TryInteger implements StackItem interface.
func (p *PointerItem) TryInteger() (*big.Int, error) {
return nil, errors.New("can't convert Pointer to Integer")
}
// Equals implements StackItem interface.
func (p *PointerItem) Equals(s StackItem) bool {
if p == s {
return true
}
ptr, ok := s.(*PointerItem)
return ok && p.pos == ptr.pos && p.hash == ptr.hash
}
// ToContractParameter implements StackItem interface.
func (p *PointerItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
return smartcontract.NewParameter(smartcontract.AnyType)
}
// Type implements StackItem interface.
func (p *PointerItem) Type() StackItemType {
return PointerT
}
// Convert implements StackItem interface.
func (p *PointerItem) Convert(typ StackItemType) (StackItem, error) {
switch typ {
case PointerT:
return p, nil
case BooleanT:
return NewBoolItem(p.Bool()), nil
default:
return nil, errInvalidConversion
}
}

View file

@ -127,6 +127,10 @@ var stringerTestCases = []struct {
input: NewInteropItem(nil),
result: "InteropItem",
},
{
input: NewPointerItem(0, nil),
result: "Pointer",
},
}
func TestStringer(t *testing.T) {
@ -314,6 +318,32 @@ var equalsTestCases = map[string][]struct {
result: true,
},
},
"pointer": {
{
item1: NewPointerItem(0, []byte{}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{1}),
result: true,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(2, []byte{1}),
result: false,
},
{
item1: NewPointerItem(1, []byte{1}),
item2: NewPointerItem(1, []byte{2}),
result: false,
},
{
item1: NewPointerItem(0, []byte{}),
item2: NewBigIntegerItem(big.NewInt(0)),
result: false,
},
},
}
func TestEquals(t *testing.T) {

View file

@ -198,6 +198,9 @@ func (v *VM) PrintOps() {
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.CALL:
offset := int16(binary.LittleEndian.Uint16(parameter))
desc = fmt.Sprintf("%d (%d/%x)", ctx.ip+int(offset), offset, parameter)
case opcode.PUSHA:
offset := int32(binary.LittleEndian.Uint32(parameter))
desc = fmt.Sprintf("%d (%x)", offset, parameter)
case opcode.SYSCALL:
desc = fmt.Sprintf("%q", parameter)
default:
@ -529,6 +532,14 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
case opcode.PUSHDATA1, opcode.PUSHDATA2, opcode.PUSHDATA4:
v.estack.PushVal(parameter)
case opcode.PUSHA:
n := int32(binary.LittleEndian.Uint32(parameter))
if n < 0 || int(n) > len(ctx.prog) {
panic(fmt.Sprintf("invalid pointer offset (%d)", n))
}
ptr := NewPointerItem(int(n), ctx.prog)
v.estack.PushVal(ptr)
case opcode.PUSHNULL:
v.estack.PushVal(NullItem{})
@ -1134,6 +1145,17 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
offset := v.getJumpOffset(newCtx, parameter, 0)
v.jumpIf(newCtx, offset, true)
case opcode.CALLA:
ptr := v.estack.Pop().Item().(*PointerItem)
if ptr.hash != ctx.ScriptHash() {
panic("invalid script in pointer")
}
newCtx := ctx.Copy()
newCtx.rvcount = -1
v.istack.PushVal(newCtx)
v.jumpIf(newCtx, ptr.pos, true)
case opcode.SYSCALL:
interopID := GetInteropID(parameter)
ifunc := v.GetInteropByID(interopID)

View file

@ -266,6 +266,7 @@ func TestCONVERT(t *testing.T) {
NewArrayItem(arr), NewArrayItem(nil),
NewStructItem(arr), NewStructItem(nil),
NewMapItem(), m, NewInteropItem(struct{}{}),
NewPointerItem(0, []byte{}),
}
for i := range trueCases {
t.Run(getName(trueCases[i], BooleanT), testBool(trueCases[i], NewBoolItem(true)))
@ -329,9 +330,12 @@ func TestCONVERT(t *testing.T) {
t.Run("Map->Map", testCONVERT(MapT, m, m))
ptr := NewPointerItem(1, []byte{1})
t.Run("Pointer->Pointer", testCONVERT(PointerT, ptr, ptr))
t.Run("Null->", func(t *testing.T) {
types := []StackItemType{
BooleanT, ByteArrayT, IntegerT, ArrayT, StructT, MapT, InteropT,
BooleanT, ByteArrayT, IntegerT, ArrayT, StructT, MapT, InteropT, PointerT,
}
for i := range types {
t.Run(types[i].String(), testCONVERT(types[i], NullItem{}, NullItem{}))
@ -905,6 +909,21 @@ func TestJMPs(t *testing.T) {
}
}
func TestPUSHA(t *testing.T) {
t.Run("Negative", getTestFuncForVM(makeProgram(opcode.PUSHA, 0xFF, 0xFF, 0xFF, 0xFF), nil))
t.Run("TooBig", getTestFuncForVM(makeProgram(opcode.PUSHA, 10, 0, 0, 0), nil))
t.Run("Good", func(t *testing.T) {
prog := makeProgram(opcode.PUSHA, 2, 0, 0, 0)
runWithArgs(t, prog, NewPointerItem(2, prog))
})
}
func TestCALLA(t *testing.T) {
prog := makeProgram(opcode.CALLA, opcode.PUSH2, opcode.ADD, opcode.RET, opcode.PUSH3, opcode.RET)
t.Run("InvalidScript", getTestFuncForVM(prog, nil, NewPointerItem(4, []byte{1})))
t.Run("Good", getTestFuncForVM(prog, 5, NewPointerItem(4, prog)))
}
func TestNOT(t *testing.T) {
prog := makeProgram(opcode.NOT)
t.Run("Bool", getTestFuncForVM(prog, true, false))