forked from TrueCloudLab/neoneo-go
Merge pull request #948 from nspcc-dev/feature/buffer
Implement NEO3 Buffer type and string functions
This commit is contained in:
commit
e8ad09f30d
14 changed files with 359 additions and 97 deletions
|
@ -133,6 +133,7 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) {
|
||||||
|
|
||||||
switch typ.Kind() {
|
switch typ.Kind() {
|
||||||
case types.Int, types.UntypedInt, types.Uint,
|
case types.Int, types.UntypedInt, types.Uint,
|
||||||
|
types.Int8, types.Uint8,
|
||||||
types.Int16, types.Uint16,
|
types.Int16, types.Uint16,
|
||||||
types.Int32, types.Uint32, types.Int64, types.Uint64:
|
types.Int32, types.Uint32, types.Int64, types.Uint64:
|
||||||
val, _ := constant.Int64Val(t.Value)
|
val, _ := constant.Int64Val(t.Value)
|
||||||
|
@ -143,10 +144,6 @@ func (c *codegen) emitLoadConst(t types.TypeAndValue) {
|
||||||
case types.Bool, types.UntypedBool:
|
case types.Bool, types.UntypedBool:
|
||||||
val := constant.BoolVal(t.Value)
|
val := constant.BoolVal(t.Value)
|
||||||
emit.Bool(c.prog.BinWriter, val)
|
emit.Bool(c.prog.BinWriter, val)
|
||||||
case types.Byte:
|
|
||||||
val, _ := constant.Int64Val(t.Value)
|
|
||||||
b := byte(val)
|
|
||||||
emit.Bytes(c.prog.BinWriter, []byte{b})
|
|
||||||
default:
|
default:
|
||||||
c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t)
|
c.prog.Err = fmt.Errorf("compiler doesn't know how to convert this basic type: %v", t)
|
||||||
return
|
return
|
||||||
|
@ -238,7 +235,8 @@ func (c *codegen) emitDefault(t types.Type) {
|
||||||
if isCompoundSlice(t) {
|
if isCompoundSlice(t) {
|
||||||
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY0)
|
emit.Opcode(c.prog.BinWriter, opcode.NEWARRAY0)
|
||||||
} else {
|
} else {
|
||||||
emit.Bytes(c.prog.BinWriter, []byte{})
|
emit.Int(c.prog.BinWriter, 0)
|
||||||
|
emit.Opcode(c.prog.BinWriter, opcode.NEWBUFFER)
|
||||||
}
|
}
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
emit.Int(c.prog.BinWriter, int64(t.NumFields()))
|
emit.Int(c.prog.BinWriter, int64(t.NumFields()))
|
||||||
|
@ -719,6 +717,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor {
|
||||||
// For now we will assume that there are only byte slice conversions.
|
// For now we will assume that there are only byte slice conversions.
|
||||||
// E.g. []byte("foobar") or []byte(scriptHash).
|
// E.g. []byte("foobar") or []byte(scriptHash).
|
||||||
ast.Walk(c, n.Args[0])
|
ast.Walk(c, n.Args[0])
|
||||||
|
c.emitConvert(vm.BufferT)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1099,7 +1098,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
case "ToBool":
|
case "ToBool":
|
||||||
typ = vm.BooleanT
|
typ = vm.BooleanT
|
||||||
}
|
}
|
||||||
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
c.emitConvert(typ)
|
||||||
case "SHA256":
|
case "SHA256":
|
||||||
emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256")
|
emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256")
|
||||||
case "AppCall":
|
case "AppCall":
|
||||||
|
@ -1124,6 +1123,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
|
||||||
}
|
}
|
||||||
bytes := uint160.BytesBE()
|
bytes := uint160.BytesBE()
|
||||||
emit.Bytes(c.prog.BinWriter, bytes)
|
emit.Bytes(c.prog.BinWriter, bytes)
|
||||||
|
c.emitConvert(vm.BufferT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1149,6 +1149,11 @@ func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
|
||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// emitConvert converts top stack item to the specified type.
|
||||||
|
func (c *codegen) emitConvert(typ vm.StackItemType) {
|
||||||
|
emit.Instruction(c.prog.BinWriter, opcode.CONVERT, []byte{byte(typ)})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
buf := make([]byte, len(lit.Elts))
|
buf := make([]byte, len(lit.Elts))
|
||||||
for i := 0; i < len(lit.Elts); i++ {
|
for i := 0; i < len(lit.Elts); i++ {
|
||||||
|
@ -1157,6 +1162,7 @@ func (c *codegen) convertByteArray(lit *ast.CompositeLit) {
|
||||||
buf[i] = byte(val)
|
buf[i] = byte(val)
|
||||||
}
|
}
|
||||||
emit.Bytes(c.prog.BinWriter, buf)
|
emit.Bytes(c.prog.BinWriter, buf)
|
||||||
|
c.emitConvert(vm.BufferT)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
func (c *codegen) convertMap(lit *ast.CompositeLit) {
|
||||||
|
|
|
@ -36,6 +36,17 @@ func TestShortHandMultiConst(t *testing.T) {
|
||||||
eval(t, src, big.NewInt(6))
|
eval(t, src, big.NewInt(6))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestByteConstant(t *testing.T) {
|
||||||
|
src := `package foo
|
||||||
|
import "github.com/nspcc-dev/neo-go/pkg/interop/convert"
|
||||||
|
const a byte = 0xFF
|
||||||
|
func Main() int64 {
|
||||||
|
x := convert.ToInteger(a)
|
||||||
|
return x+1
|
||||||
|
}`
|
||||||
|
eval(t, src, big.NewInt(0x100))
|
||||||
|
}
|
||||||
|
|
||||||
func TestGlobalsWithFunctionParams(t *testing.T) {
|
func TestGlobalsWithFunctionParams(t *testing.T) {
|
||||||
src := `
|
src := `
|
||||||
package foobar
|
package foobar
|
||||||
|
|
|
@ -38,7 +38,8 @@ func TestConvert(t *testing.T) {
|
||||||
{"bool", "12", true},
|
{"bool", "12", true},
|
||||||
{"bool", "0", false},
|
{"bool", "0", false},
|
||||||
{"bool", "[]byte{0, 1, 0}", true},
|
{"bool", "[]byte{0, 1, 0}", true},
|
||||||
{"bool", "[]byte{0}", false},
|
{"bool", "[]byte{0}", true},
|
||||||
|
{"bool", `""`, false},
|
||||||
{"int64", "true", big.NewInt(1)},
|
{"int64", "true", big.NewInt(1)},
|
||||||
{"int64", "false", big.NewInt(0)},
|
{"int64", "false", big.NewInt(0)},
|
||||||
{"int64", "12", big.NewInt(12)},
|
{"int64", "12", big.NewInt(12)},
|
||||||
|
|
|
@ -180,6 +180,38 @@ var sliceTestCases = []testCase{
|
||||||
vm.NewByteArrayItem([]byte("b")),
|
vm.NewByteArrayItem([]byte("b")),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"byte-slice assignment",
|
||||||
|
`package foo
|
||||||
|
func Main() []byte {
|
||||||
|
a := []byte{0, 1, 2}
|
||||||
|
a[1] = 42
|
||||||
|
return a
|
||||||
|
}`,
|
||||||
|
[]byte{0, 42, 2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"byte-slice assignment after string conversion",
|
||||||
|
`package foo
|
||||||
|
func Main() []byte {
|
||||||
|
a := "abc"
|
||||||
|
b := []byte(a)
|
||||||
|
b[1] = 42
|
||||||
|
return []byte(a)
|
||||||
|
}`,
|
||||||
|
[]byte{0x61, 0x62, 0x63},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"declare and append byte-slice",
|
||||||
|
`package foo
|
||||||
|
func Main() []byte {
|
||||||
|
var a []byte
|
||||||
|
a = append(a, 1)
|
||||||
|
a = append(a, 2)
|
||||||
|
return a
|
||||||
|
}`,
|
||||||
|
[]byte{1, 2},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSliceOperations(t *testing.T) {
|
func TestSliceOperations(t *testing.T) {
|
||||||
|
|
|
@ -48,18 +48,18 @@ type rpcTestCase struct {
|
||||||
check func(t *testing.T, e *executor, result interface{})
|
check func(t *testing.T, e *executor, result interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
const testContractHash = "33f3421677fab7f620bd70582f468b4a18df1e5d"
|
const testContractHash = "1b4357bff5a01bdf2a6581247cf9ed1e24629176"
|
||||||
|
|
||||||
var rpcTestCases = map[string][]rpcTestCase{
|
var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"getapplicationlog": {
|
"getapplicationlog": {
|
||||||
{
|
{
|
||||||
name: "positive",
|
name: "positive",
|
||||||
params: `["3f1579e797fedb83b66a85fe21d427a119d0e25ef662582e56393fd0d70e4691"]`,
|
params: `["396d55aa14b6cd428d793e9e740d24f93f62d7ddcdc0f4fdadd4dfd89bdabd83"]`,
|
||||||
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
|
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
|
||||||
check: func(t *testing.T, e *executor, acc interface{}) {
|
check: func(t *testing.T, e *executor, acc interface{}) {
|
||||||
res, ok := acc.(*result.ApplicationLog)
|
res, ok := acc.(*result.ApplicationLog)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
expectedTxHash, err := util.Uint256DecodeStringLE("3f1579e797fedb83b66a85fe21d427a119d0e25ef662582e56393fd0d70e4691")
|
expectedTxHash, err := util.Uint256DecodeStringLE("396d55aa14b6cd428d793e9e740d24f93f62d7ddcdc0f4fdadd4dfd89bdabd83")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, expectedTxHash, res.TxHash)
|
assert.Equal(t, expectedTxHash, res.TxHash)
|
||||||
assert.Equal(t, 1, len(res.Executions))
|
assert.Equal(t, 1, len(res.Executions))
|
||||||
|
|
BIN
pkg/rpc/server/testdata/test_contract.avm
vendored
BIN
pkg/rpc/server/testdata/test_contract.avm
vendored
Binary file not shown.
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
|
@ -146,10 +146,12 @@ const (
|
||||||
STARG Opcode = 0x87
|
STARG Opcode = 0x87
|
||||||
|
|
||||||
// Splice
|
// Splice
|
||||||
CAT Opcode = 0x8B
|
NEWBUFFER Opcode = 0x88
|
||||||
SUBSTR Opcode = 0x8C
|
MEMCPY Opcode = 0x89
|
||||||
LEFT Opcode = 0x8D
|
CAT Opcode = 0x8B
|
||||||
RIGHT Opcode = 0x8E
|
SUBSTR Opcode = 0x8C
|
||||||
|
LEFT Opcode = 0x8D
|
||||||
|
RIGHT Opcode = 0x8E
|
||||||
|
|
||||||
// Bitwise logic
|
// Bitwise logic
|
||||||
INVERT Opcode = 0x90
|
INVERT Opcode = 0x90
|
||||||
|
|
|
@ -132,6 +132,8 @@ func _() {
|
||||||
_ = x[STARG5-133]
|
_ = x[STARG5-133]
|
||||||
_ = x[STARG6-134]
|
_ = x[STARG6-134]
|
||||||
_ = x[STARG-135]
|
_ = x[STARG-135]
|
||||||
|
_ = x[NEWBUFFER-136]
|
||||||
|
_ = x[MEMCPY-137]
|
||||||
_ = x[CAT-139]
|
_ = x[CAT-139]
|
||||||
_ = x[SUBSTR-140]
|
_ = x[SUBSTR-140]
|
||||||
_ = x[LEFT-141]
|
_ = x[LEFT-141]
|
||||||
|
@ -190,7 +192,7 @@ func _() {
|
||||||
_ = x[CONVERT-219]
|
_ = x[CONVERT-219]
|
||||||
}
|
}
|
||||||
|
|
||||||
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
|
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
|
||||||
|
|
||||||
var _Opcode_map = map[Opcode]string{
|
var _Opcode_map = map[Opcode]string{
|
||||||
0: _Opcode_name[0:8],
|
0: _Opcode_name[0:8],
|
||||||
|
@ -314,62 +316,64 @@ var _Opcode_map = map[Opcode]string{
|
||||||
133: _Opcode_name[712:718],
|
133: _Opcode_name[712:718],
|
||||||
134: _Opcode_name[718:724],
|
134: _Opcode_name[718:724],
|
||||||
135: _Opcode_name[724:729],
|
135: _Opcode_name[724:729],
|
||||||
139: _Opcode_name[729:732],
|
136: _Opcode_name[729:738],
|
||||||
140: _Opcode_name[732:738],
|
137: _Opcode_name[738:744],
|
||||||
141: _Opcode_name[738:742],
|
139: _Opcode_name[744:747],
|
||||||
142: _Opcode_name[742:747],
|
140: _Opcode_name[747:753],
|
||||||
144: _Opcode_name[747:753],
|
141: _Opcode_name[753:757],
|
||||||
145: _Opcode_name[753:756],
|
142: _Opcode_name[757:762],
|
||||||
146: _Opcode_name[756:758],
|
144: _Opcode_name[762:768],
|
||||||
147: _Opcode_name[758:761],
|
145: _Opcode_name[768:771],
|
||||||
151: _Opcode_name[761:766],
|
146: _Opcode_name[771:773],
|
||||||
152: _Opcode_name[766:774],
|
147: _Opcode_name[773:776],
|
||||||
153: _Opcode_name[774:778],
|
151: _Opcode_name[776:781],
|
||||||
154: _Opcode_name[778:781],
|
152: _Opcode_name[781:789],
|
||||||
155: _Opcode_name[781:787],
|
153: _Opcode_name[789:793],
|
||||||
156: _Opcode_name[787:790],
|
154: _Opcode_name[793:796],
|
||||||
157: _Opcode_name[790:793],
|
155: _Opcode_name[796:802],
|
||||||
158: _Opcode_name[793:796],
|
156: _Opcode_name[802:805],
|
||||||
159: _Opcode_name[796:799],
|
157: _Opcode_name[805:808],
|
||||||
160: _Opcode_name[799:802],
|
158: _Opcode_name[808:811],
|
||||||
161: _Opcode_name[802:805],
|
159: _Opcode_name[811:814],
|
||||||
162: _Opcode_name[805:808],
|
160: _Opcode_name[814:817],
|
||||||
168: _Opcode_name[808:811],
|
161: _Opcode_name[817:820],
|
||||||
169: _Opcode_name[811:814],
|
162: _Opcode_name[820:823],
|
||||||
170: _Opcode_name[814:817],
|
168: _Opcode_name[823:826],
|
||||||
171: _Opcode_name[817:824],
|
169: _Opcode_name[826:829],
|
||||||
172: _Opcode_name[824:830],
|
170: _Opcode_name[829:832],
|
||||||
177: _Opcode_name[830:832],
|
171: _Opcode_name[832:839],
|
||||||
179: _Opcode_name[832:840],
|
172: _Opcode_name[839:845],
|
||||||
180: _Opcode_name[840:851],
|
177: _Opcode_name[845:847],
|
||||||
181: _Opcode_name[851:853],
|
179: _Opcode_name[847:855],
|
||||||
182: _Opcode_name[853:856],
|
180: _Opcode_name[855:866],
|
||||||
183: _Opcode_name[856:858],
|
181: _Opcode_name[866:868],
|
||||||
184: _Opcode_name[858:861],
|
182: _Opcode_name[868:871],
|
||||||
185: _Opcode_name[861:864],
|
183: _Opcode_name[871:873],
|
||||||
186: _Opcode_name[864:867],
|
184: _Opcode_name[873:876],
|
||||||
187: _Opcode_name[867:873],
|
185: _Opcode_name[876:879],
|
||||||
192: _Opcode_name[873:877],
|
186: _Opcode_name[879:882],
|
||||||
193: _Opcode_name[877:883],
|
187: _Opcode_name[882:888],
|
||||||
194: _Opcode_name[883:892],
|
192: _Opcode_name[888:892],
|
||||||
195: _Opcode_name[892:900],
|
193: _Opcode_name[892:898],
|
||||||
196: _Opcode_name[900:909],
|
194: _Opcode_name[898:907],
|
||||||
197: _Opcode_name[909:919],
|
195: _Opcode_name[907:915],
|
||||||
198: _Opcode_name[919:928],
|
196: _Opcode_name[915:924],
|
||||||
200: _Opcode_name[928:934],
|
197: _Opcode_name[924:934],
|
||||||
202: _Opcode_name[934:938],
|
198: _Opcode_name[934:943],
|
||||||
203: _Opcode_name[938:944],
|
200: _Opcode_name[943:949],
|
||||||
204: _Opcode_name[944:948],
|
202: _Opcode_name[949:953],
|
||||||
205: _Opcode_name[948:954],
|
203: _Opcode_name[953:959],
|
||||||
206: _Opcode_name[954:962],
|
204: _Opcode_name[959:963],
|
||||||
207: _Opcode_name[962:968],
|
205: _Opcode_name[963:969],
|
||||||
208: _Opcode_name[968:975],
|
206: _Opcode_name[969:977],
|
||||||
209: _Opcode_name[975:987],
|
207: _Opcode_name[977:983],
|
||||||
210: _Opcode_name[987:993],
|
208: _Opcode_name[983:990],
|
||||||
211: _Opcode_name[993:1003],
|
209: _Opcode_name[990:1002],
|
||||||
216: _Opcode_name[1003:1009],
|
210: _Opcode_name[1002:1008],
|
||||||
217: _Opcode_name[1009:1015],
|
211: _Opcode_name[1008:1018],
|
||||||
219: _Opcode_name[1015:1022],
|
216: _Opcode_name[1018:1024],
|
||||||
|
217: _Opcode_name[1024:1030],
|
||||||
|
219: _Opcode_name[1030:1037],
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Opcode) String() string {
|
func (i Opcode) String() string {
|
||||||
|
|
|
@ -89,6 +89,9 @@ func serializeItemTo(item StackItem, w *io.BinWriter, seen map[StackItem]bool) {
|
||||||
case *ByteArrayItem:
|
case *ByteArrayItem:
|
||||||
w.WriteBytes([]byte{byte(ByteArrayT)})
|
w.WriteBytes([]byte{byte(ByteArrayT)})
|
||||||
w.WriteVarBytes(t.value)
|
w.WriteVarBytes(t.value)
|
||||||
|
case *BufferItem:
|
||||||
|
w.WriteBytes([]byte{byte(BufferT)})
|
||||||
|
w.WriteVarBytes(t.value)
|
||||||
case *BoolItem:
|
case *BoolItem:
|
||||||
w.WriteBytes([]byte{byte(BooleanT)})
|
w.WriteBytes([]byte{byte(BooleanT)})
|
||||||
w.WriteBool(t.value)
|
w.WriteBool(t.value)
|
||||||
|
|
|
@ -124,14 +124,15 @@ func convertPrimitive(item StackItem, typ StackItemType) (StackItem, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return NewBigIntegerItem(bi), nil
|
return NewBigIntegerItem(bi), nil
|
||||||
case ByteArrayT:
|
case ByteArrayT, BufferT:
|
||||||
b, err := item.TryBytes()
|
b, err := item.TryBytes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if typ == BufferT {
|
||||||
|
return NewBufferItem(b), nil
|
||||||
|
}
|
||||||
return NewByteArrayItem(b), nil
|
return NewByteArrayItem(b), nil
|
||||||
case BufferT:
|
|
||||||
panic("TODO") // #877
|
|
||||||
case BooleanT:
|
case BooleanT:
|
||||||
return NewBoolItem(item.Bool()), nil
|
return NewBoolItem(item.Bool()), nil
|
||||||
default:
|
default:
|
||||||
|
@ -517,16 +518,17 @@ func (i *ByteArrayItem) Bool() bool {
|
||||||
|
|
||||||
// TryBytes implements StackItem interface.
|
// TryBytes implements StackItem interface.
|
||||||
func (i *ByteArrayItem) TryBytes() ([]byte, error) {
|
func (i *ByteArrayItem) TryBytes() ([]byte, error) {
|
||||||
return i.value, nil
|
val := make([]byte, len(i.value))
|
||||||
|
copy(val, i.value)
|
||||||
|
return val, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TryInteger implements StackItem interface.
|
// TryInteger implements StackItem interface.
|
||||||
func (i *ByteArrayItem) TryInteger() (*big.Int, error) {
|
func (i *ByteArrayItem) TryInteger() (*big.Int, error) {
|
||||||
bi := emit.BytesToInt(i.value)
|
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
||||||
if bi.BitLen() > MaxBigIntegerSizeBits {
|
|
||||||
return nil, errors.New("integer is too big")
|
return nil, errors.New("integer is too big")
|
||||||
}
|
}
|
||||||
return bi, nil
|
return emit.BytesToInt(i.value), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals implements StackItem interface.
|
// Equals implements StackItem interface.
|
||||||
|
@ -940,3 +942,89 @@ func (p *PointerItem) Convert(typ StackItemType) (StackItem, error) {
|
||||||
return nil, errInvalidConversion
|
return nil, errInvalidConversion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BufferItem represents represents Buffer stack item.
|
||||||
|
type BufferItem struct {
|
||||||
|
value []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBufferItem returns a new BufferItem object.
|
||||||
|
func NewBufferItem(b []byte) *BufferItem {
|
||||||
|
return &BufferItem{
|
||||||
|
value: b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements StackItem interface.
|
||||||
|
func (i *BufferItem) Value() interface{} {
|
||||||
|
return i.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// String implements fmt.Stringer interface.
|
||||||
|
func (i *BufferItem) String() string {
|
||||||
|
return "Buffer"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bool implements StackItem interface.
|
||||||
|
func (i *BufferItem) Bool() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryBytes implements StackItem interface.
|
||||||
|
func (i *BufferItem) TryBytes() ([]byte, error) {
|
||||||
|
val := make([]byte, len(i.value))
|
||||||
|
copy(val, i.value)
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TryInteger implements StackItem interface.
|
||||||
|
func (i *BufferItem) TryInteger() (*big.Int, error) {
|
||||||
|
return nil, errors.New("can't convert Buffer to Integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals implements StackItem interface.
|
||||||
|
func (i *BufferItem) Equals(s StackItem) bool {
|
||||||
|
return i == s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dup implements StackItem interface.
|
||||||
|
func (i *BufferItem) Dup() StackItem {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the json.Marshaler interface.
|
||||||
|
func (i *BufferItem) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(hex.EncodeToString(i.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToContractParameter implements StackItem interface.
|
||||||
|
func (i *BufferItem) ToContractParameter(map[StackItem]bool) smartcontract.Parameter {
|
||||||
|
return smartcontract.Parameter{
|
||||||
|
Type: smartcontract.ByteArrayType,
|
||||||
|
Value: i.value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type implements StackItem interface.
|
||||||
|
func (i *BufferItem) Type() StackItemType { return BufferT }
|
||||||
|
|
||||||
|
// Convert implements StackItem interface.
|
||||||
|
func (i *BufferItem) Convert(typ StackItemType) (StackItem, error) {
|
||||||
|
switch typ {
|
||||||
|
case BooleanT:
|
||||||
|
return NewBoolItem(i.Bool()), nil
|
||||||
|
case BufferT:
|
||||||
|
return i, nil
|
||||||
|
case ByteArrayT:
|
||||||
|
val := make([]byte, len(i.value))
|
||||||
|
copy(val, i.value)
|
||||||
|
return NewByteArrayItem(val), nil
|
||||||
|
case IntegerT:
|
||||||
|
if len(i.value) > MaxBigIntegerSizeBits/8 {
|
||||||
|
return nil, errInvalidConversion
|
||||||
|
}
|
||||||
|
return NewBigIntegerItem(emit.BytesToInt(i.value)), nil
|
||||||
|
default:
|
||||||
|
return nil, errInvalidConversion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -374,6 +374,10 @@ var marshalJSONTestCases = []struct {
|
||||||
input: NewByteArrayItem([]byte{1, 2, 3}),
|
input: NewByteArrayItem([]byte{1, 2, 3}),
|
||||||
result: []byte(`"010203"`),
|
result: []byte(`"010203"`),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: NewBufferItem([]byte{1, 2, 3}),
|
||||||
|
result: []byte(`"010203"`),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
|
input: &ArrayItem{value: []StackItem{&BigIntegerItem{value: big.NewInt(3)}, &ByteArrayItem{value: []byte{1, 2, 3}}}},
|
||||||
result: []byte(`[3,"010203"]`),
|
result: []byte(`[3,"010203"]`),
|
||||||
|
@ -432,6 +436,10 @@ var toContractParameterTestCases = []struct {
|
||||||
input: NewByteArrayItem([]byte{0x01, 0x02, 0x03}),
|
input: NewByteArrayItem([]byte{0x01, 0x02, 0x03}),
|
||||||
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
|
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
input: NewBufferItem([]byte{0x01, 0x02, 0x03}),
|
||||||
|
result: smartcontract.Parameter{Type: smartcontract.ByteArrayType, Value: []byte{0x01, 0x02, 0x03}},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
input: NewArrayItem([]StackItem{NewBigIntegerItem(big.NewInt(2)), NewBoolItem(true)}),
|
input: NewArrayItem([]StackItem{NewBigIntegerItem(big.NewInt(2)), NewBoolItem(true)}),
|
||||||
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
|
result: smartcontract.Parameter{Type: smartcontract.ArrayType, Value: []smartcontract.Parameter{
|
||||||
|
|
83
pkg/vm/vm.go
83
pkg/vm/vm.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
@ -634,6 +635,36 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
item := v.estack.Pop().Item()
|
item := v.estack.Pop().Item()
|
||||||
ctx.arguments.Set(int(parameter[0]), item)
|
ctx.arguments.Set(int(parameter[0]), item)
|
||||||
|
|
||||||
|
case opcode.NEWBUFFER:
|
||||||
|
n := toInt(v.estack.Pop().BigInt())
|
||||||
|
if n < 0 || n > MaxItemSize {
|
||||||
|
panic("invalid size")
|
||||||
|
}
|
||||||
|
v.estack.PushVal(NewBufferItem(make([]byte, n)))
|
||||||
|
|
||||||
|
case opcode.MEMCPY:
|
||||||
|
n := toInt(v.estack.Pop().BigInt())
|
||||||
|
if n < 0 {
|
||||||
|
panic("invalid size")
|
||||||
|
}
|
||||||
|
si := toInt(v.estack.Pop().BigInt())
|
||||||
|
if si < 0 {
|
||||||
|
panic("invalid source index")
|
||||||
|
}
|
||||||
|
src := v.estack.Pop().Bytes()
|
||||||
|
if sum := si + n; sum < 0 || sum > len(src) {
|
||||||
|
panic("size is too big")
|
||||||
|
}
|
||||||
|
di := toInt(v.estack.Pop().BigInt())
|
||||||
|
if di < 0 {
|
||||||
|
panic("invalid destination index")
|
||||||
|
}
|
||||||
|
dst := v.estack.Pop().value.(*BufferItem).value
|
||||||
|
if sum := si + n; sum < 0 || sum > len(dst) {
|
||||||
|
panic("size is too big")
|
||||||
|
}
|
||||||
|
copy(dst[di:], src[si:si+n])
|
||||||
|
|
||||||
case opcode.CAT:
|
case opcode.CAT:
|
||||||
b := v.estack.Pop().Bytes()
|
b := v.estack.Pop().Bytes()
|
||||||
a := v.estack.Pop().Bytes()
|
a := v.estack.Pop().Bytes()
|
||||||
|
@ -641,7 +672,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic(fmt.Sprintf("too big item: %d", l))
|
panic(fmt.Sprintf("too big item: %d", l))
|
||||||
}
|
}
|
||||||
ab := append(a, b...)
|
ab := append(a, b...)
|
||||||
v.estack.PushVal(ab)
|
v.estack.PushVal(NewBufferItem(ab))
|
||||||
|
|
||||||
case opcode.SUBSTR:
|
case opcode.SUBSTR:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -657,7 +688,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if last > len(s) {
|
if last > len(s) {
|
||||||
panic("invalid offset")
|
panic("invalid offset")
|
||||||
}
|
}
|
||||||
v.estack.PushVal(s[o:last])
|
v.estack.PushVal(NewBufferItem(s[o:last]))
|
||||||
|
|
||||||
case opcode.LEFT:
|
case opcode.LEFT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -668,7 +699,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
if t := len(s); l > t {
|
if t := len(s); l > t {
|
||||||
l = t
|
l = t
|
||||||
}
|
}
|
||||||
v.estack.PushVal(s[:l])
|
v.estack.PushVal(NewBufferItem(s[:l]))
|
||||||
|
|
||||||
case opcode.RIGHT:
|
case opcode.RIGHT:
|
||||||
l := int(v.estack.Pop().BigInt().Int64())
|
l := int(v.estack.Pop().BigInt().Int64())
|
||||||
|
@ -676,7 +707,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
panic("negative length")
|
panic("negative length")
|
||||||
}
|
}
|
||||||
s := v.estack.Pop().Bytes()
|
s := v.estack.Pop().Bytes()
|
||||||
v.estack.PushVal(s[len(s)-l:])
|
v.estack.PushVal(NewBufferItem(s[len(s)-l:]))
|
||||||
|
|
||||||
case opcode.DEPTH:
|
case opcode.DEPTH:
|
||||||
v.estack.PushVal(v.estack.Len())
|
v.estack.PushVal(v.estack.Len())
|
||||||
|
@ -1100,16 +1131,36 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
t.Add(key.value, item)
|
t.Add(key.value, item)
|
||||||
v.refs.Add(item)
|
v.refs.Add(item)
|
||||||
|
|
||||||
|
case *BufferItem:
|
||||||
|
index := toInt(key.BigInt())
|
||||||
|
if index < 0 || index >= len(t.value) {
|
||||||
|
panic("invalid index")
|
||||||
|
}
|
||||||
|
bi, err := item.TryInteger()
|
||||||
|
b := toInt(bi)
|
||||||
|
if err != nil || b < math.MinInt8 || b > math.MaxUint8 {
|
||||||
|
panic("invalid value")
|
||||||
|
}
|
||||||
|
t.value[index] = byte(b)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
panic(fmt.Sprintf("SETITEM: invalid item type %s", t))
|
||||||
}
|
}
|
||||||
|
|
||||||
case opcode.REVERSEITEMS:
|
case opcode.REVERSEITEMS:
|
||||||
a := v.estack.Pop().Array()
|
item := v.estack.Pop()
|
||||||
if len(a) > 1 {
|
switch t := item.value.(type) {
|
||||||
for i, j := 0, len(a)-1; i <= j; i, j = i+1, j-1 {
|
case *ArrayItem, *StructItem:
|
||||||
|
a := t.Value().([]StackItem)
|
||||||
|
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
|
||||||
a[i], a[j] = a[j], a[i]
|
a[i], a[j] = a[j], a[i]
|
||||||
}
|
}
|
||||||
|
case *BufferItem:
|
||||||
|
for i, j := 0, len(t.value)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
t.value[i], t.value[j] = t.value[j], t.value[i]
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("invalid item type %s", t))
|
||||||
}
|
}
|
||||||
case opcode.REMOVE:
|
case opcode.REMOVE:
|
||||||
key := v.estack.Pop()
|
key := v.estack.Pop()
|
||||||
|
@ -1322,6 +1373,12 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
||||||
v.estack.PushVal(index < int64(len(c.Array())))
|
v.estack.PushVal(index < int64(len(c.Array())))
|
||||||
case *MapItem:
|
case *MapItem:
|
||||||
v.estack.PushVal(t.Has(key.Item()))
|
v.estack.PushVal(t.Has(key.Item()))
|
||||||
|
case *BufferItem:
|
||||||
|
index := key.BigInt().Int64()
|
||||||
|
if index < 0 {
|
||||||
|
panic("negative index")
|
||||||
|
}
|
||||||
|
v.estack.PushVal(index < int64(len(t.value)))
|
||||||
default:
|
default:
|
||||||
panic("wrong collection type")
|
panic("wrong collection type")
|
||||||
}
|
}
|
||||||
|
@ -1576,3 +1633,15 @@ func (v *VM) GetEntryScriptHash() util.Uint160 {
|
||||||
func (v *VM) GetCurrentScriptHash() util.Uint160 {
|
func (v *VM) GetCurrentScriptHash() util.Uint160 {
|
||||||
return v.getContextScriptHash(0)
|
return v.getContextScriptHash(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toInt converts an item to a 32-bit int.
|
||||||
|
func toInt(i *big.Int) int {
|
||||||
|
if !i.IsInt64() {
|
||||||
|
panic("not an int32")
|
||||||
|
}
|
||||||
|
n := i.Int64()
|
||||||
|
if n < math.MinInt32 || n > math.MaxInt32 {
|
||||||
|
panic("not an int32")
|
||||||
|
}
|
||||||
|
return int(n)
|
||||||
|
}
|
||||||
|
|
|
@ -937,6 +937,8 @@ func TestNOT(t *testing.T) {
|
||||||
t.Run("ByteArray0", getTestFuncForVM(prog, true, []byte{0, 0}))
|
t.Run("ByteArray0", getTestFuncForVM(prog, true, []byte{0, 0}))
|
||||||
t.Run("ByteArray1", getTestFuncForVM(prog, false, []byte{0, 1}))
|
t.Run("ByteArray1", getTestFuncForVM(prog, false, []byte{0, 1}))
|
||||||
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
||||||
|
t.Run("Buffer0", getTestFuncForVM(prog, false, NewBufferItem([]byte{})))
|
||||||
|
t.Run("Buffer1", getTestFuncForVM(prog, false, NewBufferItem([]byte{1})))
|
||||||
}
|
}
|
||||||
|
|
||||||
// getBigInt returns 2^a+b
|
// getBigInt returns 2^a+b
|
||||||
|
@ -1056,6 +1058,7 @@ func TestEQUALTrue(t *testing.T) {
|
||||||
prog := makeProgram(opcode.DUP, opcode.EQUAL)
|
prog := makeProgram(opcode.DUP, opcode.EQUAL)
|
||||||
t.Run("Array", getTestFuncForVM(prog, true, []StackItem{}))
|
t.Run("Array", getTestFuncForVM(prog, true, []StackItem{}))
|
||||||
t.Run("Map", getTestFuncForVM(prog, true, NewMapItem()))
|
t.Run("Map", getTestFuncForVM(prog, true, NewMapItem()))
|
||||||
|
t.Run("Buffer", getTestFuncForVM(prog, true, NewBufferItem([]byte{1, 2})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEQUAL(t *testing.T) {
|
func TestEQUAL(t *testing.T) {
|
||||||
|
@ -1066,6 +1069,7 @@ func TestEQUAL(t *testing.T) {
|
||||||
t.Run("IntegerByteArray", getTestFuncForVM(prog, true, []byte{16}, 16))
|
t.Run("IntegerByteArray", getTestFuncForVM(prog, true, []byte{16}, 16))
|
||||||
t.Run("Map", getTestFuncForVM(prog, false, NewMapItem(), NewMapItem()))
|
t.Run("Map", getTestFuncForVM(prog, false, NewMapItem(), NewMapItem()))
|
||||||
t.Run("Array", getTestFuncForVM(prog, false, []StackItem{}, []StackItem{}))
|
t.Run("Array", getTestFuncForVM(prog, false, []StackItem{}, []StackItem{}))
|
||||||
|
t.Run("Buffer", getTestFuncForVM(prog, false, NewBufferItem([]byte{42}), NewBufferItem([]byte{42})))
|
||||||
}
|
}
|
||||||
|
|
||||||
func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interface{}) {
|
func runWithArgs(t *testing.T, prog []byte, result interface{}, args ...interface{}) {
|
||||||
|
@ -1150,6 +1154,26 @@ func TestDECBigResult(t *testing.T) {
|
||||||
checkVMFailed(t, vm)
|
checkVMFailed(t, vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNEWBUFFER(t *testing.T) {
|
||||||
|
prog := makeProgram(opcode.NEWBUFFER)
|
||||||
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte{0, 0, 0}), 3))
|
||||||
|
t.Run("Negative", getTestFuncForVM(prog, nil, -1))
|
||||||
|
t.Run("TooBig", getTestFuncForVM(prog, nil, MaxItemSize+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMEMCPY(t *testing.T) {
|
||||||
|
prog := makeProgram(opcode.MEMCPY)
|
||||||
|
t.Run("Good", func(t *testing.T) {
|
||||||
|
buf := NewBufferItem([]byte{0, 1, 2, 3})
|
||||||
|
runWithArgs(t, prog, NewBufferItem([]byte{0, 6, 7, 3}), buf, buf, 1, []byte{4, 5, 6, 7}, 2, 2)
|
||||||
|
})
|
||||||
|
t.Run("NegativeSize", getTestFuncForVM(prog, nil, NewBufferItem([]byte{0, 1}), 0, []byte{2}, 0, -1))
|
||||||
|
t.Run("NegativeSrcIndex", getTestFuncForVM(prog, nil, NewBufferItem([]byte{0, 1}), 0, []byte{2}, -1, 1))
|
||||||
|
t.Run("NegativeDstIndex", getTestFuncForVM(prog, nil, NewBufferItem([]byte{0, 1}), -1, []byte{2}, 0, 1))
|
||||||
|
t.Run("BigSizeSrc", getTestFuncForVM(prog, nil, NewBufferItem([]byte{0, 1}), 0, []byte{2}, 0, 2))
|
||||||
|
t.Run("BigSizeDst", getTestFuncForVM(prog, nil, NewBufferItem([]byte{0, 1}), 0, []byte{2, 3, 4}, 0, 3))
|
||||||
|
}
|
||||||
|
|
||||||
func TestNEWARRAY0(t *testing.T) {
|
func TestNEWARRAY0(t *testing.T) {
|
||||||
prog := makeProgram(opcode.NEWARRAY0)
|
prog := makeProgram(opcode.NEWARRAY0)
|
||||||
runWithArgs(t, prog, []StackItem{})
|
runWithArgs(t, prog, []StackItem{})
|
||||||
|
@ -1270,6 +1294,7 @@ func TestPICKITEM(t *testing.T) {
|
||||||
t.Run("bad index", getTestFuncForVM(prog, nil, []StackItem{}, 0))
|
t.Run("bad index", getTestFuncForVM(prog, nil, []StackItem{}, 0))
|
||||||
t.Run("Array", getTestFuncForVM(prog, 2, []StackItem{makeStackItem(1), makeStackItem(2)}, 1))
|
t.Run("Array", getTestFuncForVM(prog, 2, []StackItem{makeStackItem(1), makeStackItem(2)}, 1))
|
||||||
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{1, 2}, 1))
|
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{1, 2}, 1))
|
||||||
|
t.Run("Buffer", getTestFuncForVM(prog, 2, NewBufferItem([]byte{1, 2}), 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPICKITEMDupArray(t *testing.T) {
|
func TestPICKITEMDupArray(t *testing.T) {
|
||||||
|
@ -1305,6 +1330,13 @@ func TestPICKITEMMap(t *testing.T) {
|
||||||
runWithArgs(t, prog, 3, m, 5)
|
runWithArgs(t, prog, 3, m, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSETITEMBuffer(t *testing.T) {
|
||||||
|
prog := makeProgram(opcode.DUP, opcode.REVERSE4, opcode.SETITEM)
|
||||||
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte{0, 42, 2}), 42, 1, NewBufferItem([]byte{0, 1, 2})))
|
||||||
|
t.Run("BadIndex", getTestFuncForVM(prog, nil, 42, -1, NewBufferItem([]byte{0, 1, 2})))
|
||||||
|
t.Run("BadValue", getTestFuncForVM(prog, nil, 256, 1, NewBufferItem([]byte{0, 1, 2})))
|
||||||
|
}
|
||||||
|
|
||||||
func TestSETITEMMap(t *testing.T) {
|
func TestSETITEMMap(t *testing.T) {
|
||||||
prog := makeProgram(opcode.SETITEM, opcode.PICKITEM)
|
prog := makeProgram(opcode.SETITEM, opcode.PICKITEM)
|
||||||
m := NewMapItem()
|
m := NewMapItem()
|
||||||
|
@ -1341,6 +1373,7 @@ func TestSIZE(t *testing.T) {
|
||||||
prog := makeProgram(opcode.SIZE)
|
prog := makeProgram(opcode.SIZE)
|
||||||
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
||||||
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{0, 1}))
|
t.Run("ByteArray", getTestFuncForVM(prog, 2, []byte{0, 1}))
|
||||||
|
t.Run("Buffer", getTestFuncForVM(prog, 2, NewBufferItem([]byte{0, 1})))
|
||||||
t.Run("Bool", getTestFuncForVM(prog, 1, false))
|
t.Run("Bool", getTestFuncForVM(prog, 1, false))
|
||||||
t.Run("Array", getTestFuncForVM(prog, 2, []StackItem{makeStackItem(1), makeStackItem([]byte{})}))
|
t.Run("Array", getTestFuncForVM(prog, 2, []StackItem{makeStackItem(1), makeStackItem([]byte{})}))
|
||||||
t.Run("Map", func(t *testing.T) {
|
t.Run("Map", func(t *testing.T) {
|
||||||
|
@ -1416,6 +1449,12 @@ func TestHASKEY(t *testing.T) {
|
||||||
t.Run("True", getTestFuncForVM(prog, true, NewStructItem(arr), 4))
|
t.Run("True", getTestFuncForVM(prog, true, NewStructItem(arr), 4))
|
||||||
t.Run("False", getTestFuncForVM(prog, false, NewStructItem(arr), 5))
|
t.Run("False", getTestFuncForVM(prog, false, NewStructItem(arr), 5))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Buffer", func(t *testing.T) {
|
||||||
|
t.Run("True", getTestFuncForVM(prog, true, NewBufferItem([]byte{5, 5, 5}), 2))
|
||||||
|
t.Run("False", getTestFuncForVM(prog, false, NewBufferItem([]byte{5, 5, 5}), 3))
|
||||||
|
t.Run("Negative", getTestFuncForVM(prog, nil, NewBufferItem([]byte{5, 5, 5}), -1))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHASKEYMap(t *testing.T) {
|
func TestHASKEYMap(t *testing.T) {
|
||||||
|
@ -1734,9 +1773,9 @@ func TestCAT(t *testing.T) {
|
||||||
arg := make([]byte, MaxItemSize/2+1)
|
arg := make([]byte, MaxItemSize/2+1)
|
||||||
runWithArgs(t, prog, nil, arg, arg)
|
runWithArgs(t, prog, nil, arg, arg)
|
||||||
})
|
})
|
||||||
t.Run("Good", getTestFuncForVM(prog, []byte("abcdef"), []byte("abc"), []byte("def")))
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("abcdef")), []byte("abc"), []byte("def")))
|
||||||
t.Run("Int0ByteArray", getTestFuncForVM(prog, []byte{}, 0, []byte{}))
|
t.Run("Int0ByteArray", getTestFuncForVM(prog, NewBufferItem([]byte{}), 0, []byte{}))
|
||||||
t.Run("ByteArrayInt1", getTestFuncForVM(prog, []byte{1}, []byte{}, 1))
|
t.Run("ByteArrayInt1", getTestFuncForVM(prog, NewBufferItem([]byte{1}), []byte{}, 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSUBSTR(t *testing.T) {
|
func TestSUBSTR(t *testing.T) {
|
||||||
|
@ -1744,7 +1783,7 @@ func TestSUBSTR(t *testing.T) {
|
||||||
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
||||||
t.Run("OneArgument", getTestFuncForVM(prog, nil, 1))
|
t.Run("OneArgument", getTestFuncForVM(prog, nil, 1))
|
||||||
t.Run("TwoArguments", getTestFuncForVM(prog, nil, 0, 2))
|
t.Run("TwoArguments", getTestFuncForVM(prog, nil, 0, 2))
|
||||||
t.Run("Good", getTestFuncForVM(prog, []byte("bc"), []byte("abcdef"), 1, 2))
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("bc")), []byte("abcdef"), 1, 2))
|
||||||
t.Run("BadOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), 7, 1))
|
t.Run("BadOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), 7, 1))
|
||||||
t.Run("BigLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, 6))
|
t.Run("BigLen", getTestFuncForVM(prog, nil, []byte("abcdef"), 1, 6))
|
||||||
t.Run("NegativeOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), -1, 3))
|
t.Run("NegativeOffset", getTestFuncForVM(prog, nil, []byte("abcdef"), -1, 3))
|
||||||
|
@ -1767,8 +1806,8 @@ func TestLEFT(t *testing.T) {
|
||||||
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
||||||
t.Run("NoString", getTestFuncForVM(prog, nil, 2))
|
t.Run("NoString", getTestFuncForVM(prog, nil, 2))
|
||||||
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
|
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
|
||||||
t.Run("Good", getTestFuncForVM(prog, "ab", "abcdef", 2))
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("ab")), "abcdef", 2))
|
||||||
t.Run("GoodBigLen", getTestFuncForVM(prog, "abcdef", "abcdef", 8))
|
t.Run("GoodBigLen", getTestFuncForVM(prog, NewBufferItem([]byte("abcdef")), "abcdef", 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRIGHT(t *testing.T) {
|
func TestRIGHT(t *testing.T) {
|
||||||
|
@ -1776,7 +1815,7 @@ func TestRIGHT(t *testing.T) {
|
||||||
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
t.Run("NoArgument", getTestFuncForVM(prog, nil))
|
||||||
t.Run("NoString", getTestFuncForVM(prog, nil, 2))
|
t.Run("NoString", getTestFuncForVM(prog, nil, 2))
|
||||||
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
|
t.Run("NegativeLen", getTestFuncForVM(prog, nil, "abcdef", -1))
|
||||||
t.Run("Good", getTestFuncForVM(prog, "ef", "abcdef", 2))
|
t.Run("Good", getTestFuncForVM(prog, NewBufferItem([]byte("ef")), "abcdef", 2))
|
||||||
t.Run("BadLen", getTestFuncForVM(prog, nil, "abcdef", 8))
|
t.Run("BadLen", getTestFuncForVM(prog, nil, "abcdef", 8))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1838,11 +1877,10 @@ func TestUNPACKGood(t *testing.T) {
|
||||||
assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64())
|
assert.Equal(t, int64(1), vm.estack.Peek(len(elements)+1).BigInt().Int64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestREVERSEITEMSBadNotArray(t *testing.T) {
|
func TestREVERSEITEMS(t *testing.T) {
|
||||||
prog := makeProgram(opcode.REVERSEITEMS)
|
prog := makeProgram(opcode.DUP, opcode.REVERSEITEMS)
|
||||||
vm := load(prog)
|
t.Run("InvalidItem", getTestFuncForVM(prog, nil, 1))
|
||||||
vm.estack.PushVal(1)
|
t.Run("Buffer", getTestFuncForVM(prog, NewBufferItem([]byte{3, 2, 1}), NewBufferItem([]byte{1, 2, 3})))
|
||||||
checkVMFailed(t, vm)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) {
|
func testREVERSEITEMSIssue437(t *testing.T, i1, i2 opcode.Opcode, reversed bool) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue