forked from TrueCloudLab/neoneo-go
vm: support exceptions
Implement 3 new instructions: TRY,ENDTRY,ENDFINALLY. 1. TRY marks the start of the block where exceptions are catched. It has 2 arguments which are relative offsets of exception handler and the end of the whole try/catch construction. 2. ENDTRY denotes either end of try or catch block. 3. ENDFINALLY denotes end of finally block which is executed irregardless of whether an exception has occured.
This commit is contained in:
parent
c0d7b9d234
commit
797324cb04
6 changed files with 415 additions and 139 deletions
|
@ -33,6 +33,9 @@ type Context struct {
|
|||
local *Slot
|
||||
arguments *Slot
|
||||
|
||||
// Exception context stack pointer.
|
||||
tryStack *Stack
|
||||
|
||||
// Script hash of the prog.
|
||||
scriptHash util.Uint160
|
||||
|
||||
|
@ -103,14 +106,18 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
|
|||
case opcode.JMP, opcode.JMPIF, opcode.JMPIFNOT, opcode.JMPEQ, opcode.JMPNE,
|
||||
opcode.JMPGT, opcode.JMPGE, opcode.JMPLT, opcode.JMPLE,
|
||||
opcode.CALL, opcode.ISTYPE, opcode.CONVERT, opcode.NEWARRAYT,
|
||||
opcode.ENDTRY,
|
||||
opcode.INITSSLOT, opcode.LDSFLD, opcode.STSFLD, opcode.LDARG, opcode.STARG, opcode.LDLOC, opcode.STLOC:
|
||||
numtoread = 1
|
||||
case opcode.INITSLOT:
|
||||
case opcode.INITSLOT, opcode.TRY:
|
||||
numtoread = 2
|
||||
case opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.JMPEQL, opcode.JMPNEL,
|
||||
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL,
|
||||
opcode.ENDTRYL,
|
||||
opcode.CALLL, opcode.SYSCALL, opcode.PUSHA:
|
||||
numtoread = 4
|
||||
case opcode.TRYL:
|
||||
numtoread = 8
|
||||
default:
|
||||
if instr <= opcode.PUSHINT256 {
|
||||
numtoread = 1 << instr
|
||||
|
|
85
pkg/vm/exception.go
Normal file
85
pkg/vm/exception.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// exceptionHandlingState represents state of the exception handling process.
|
||||
type exceptionHandlingState byte
|
||||
|
||||
const (
|
||||
eTry exceptionHandlingState = iota
|
||||
eCatch
|
||||
eFinally
|
||||
)
|
||||
|
||||
// exceptionHandlingContext represents context of the exception handling process.
|
||||
type exceptionHandlingContext struct {
|
||||
CatchOffset int
|
||||
FinallyOffset int
|
||||
EndOffset int
|
||||
State exceptionHandlingState
|
||||
}
|
||||
|
||||
func newExceptionHandlingContext(cOffset, fOffset int) *exceptionHandlingContext {
|
||||
return &exceptionHandlingContext{
|
||||
CatchOffset: cOffset,
|
||||
FinallyOffset: fOffset,
|
||||
EndOffset: -1,
|
||||
State: eTry,
|
||||
}
|
||||
}
|
||||
|
||||
// HasCatch returns true iff context has `catch` block.
|
||||
func (c *exceptionHandlingContext) HasCatch() bool { return c.CatchOffset >= 0 }
|
||||
|
||||
// HasFinally returns true iff context has `finally` block.
|
||||
func (c *exceptionHandlingContext) HasFinally() bool { return c.FinallyOffset >= 0 }
|
||||
|
||||
// String implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) String() string {
|
||||
return "exception handling context"
|
||||
}
|
||||
|
||||
// Value implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Value() interface{} {
|
||||
return c
|
||||
}
|
||||
|
||||
// Dup implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Dup() stackitem.Item {
|
||||
return c
|
||||
}
|
||||
|
||||
// Bool implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Bool() bool {
|
||||
panic("can't convert exceptionHandlingContext to Bool")
|
||||
}
|
||||
|
||||
// TryBytes implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) TryBytes() ([]byte, error) {
|
||||
return nil, errors.New("can't convert exceptionHandlingContext to ByteArray")
|
||||
}
|
||||
|
||||
// TryInteger implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) TryInteger() (*big.Int, error) {
|
||||
return nil, errors.New("can't convert exceptionHandlingContext to Integer")
|
||||
}
|
||||
|
||||
// Type implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Type() stackitem.Type {
|
||||
panic("exceptionHandlingContext cannot appear on evaluation stack")
|
||||
}
|
||||
|
||||
// Convert implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Convert(_ stackitem.Type) (stackitem.Item, error) {
|
||||
panic("exceptionHandlingContext cannot be converted to anything")
|
||||
}
|
||||
|
||||
// Equals implements stackitem.Item interface.
|
||||
func (c *exceptionHandlingContext) Equals(s stackitem.Item) bool {
|
||||
return c == s
|
||||
}
|
|
@ -68,9 +68,14 @@ const (
|
|||
CALLA Opcode = 0x36
|
||||
|
||||
// Exceptions
|
||||
ABORT Opcode = 0x37
|
||||
ASSERT Opcode = 0x38
|
||||
THROW Opcode = 0x3A
|
||||
ABORT Opcode = 0x37
|
||||
ASSERT Opcode = 0x38
|
||||
THROW Opcode = 0x3A
|
||||
TRY Opcode = 0x3B
|
||||
TRYL Opcode = 0x3C // TRY_L
|
||||
ENDTRY Opcode = 0x3D
|
||||
ENDTRYL Opcode = 0x3E // ENDTRY_L
|
||||
ENDFINALLY Opcode = 0x3F
|
||||
|
||||
RET Opcode = 0x40
|
||||
SYSCALL Opcode = 0x41
|
||||
|
|
|
@ -64,6 +64,11 @@ func _() {
|
|||
_ = x[ABORT-55]
|
||||
_ = x[ASSERT-56]
|
||||
_ = x[THROW-58]
|
||||
_ = x[TRY-59]
|
||||
_ = x[TRYL-60]
|
||||
_ = x[ENDTRY-61]
|
||||
_ = x[ENDTRYL-62]
|
||||
_ = x[ENDFINALLY-63]
|
||||
_ = x[RET-64]
|
||||
_ = x[SYSCALL-65]
|
||||
_ = x[DEPTH-67]
|
||||
|
@ -191,7 +196,7 @@ func _() {
|
|||
_ = x[CONVERT-219]
|
||||
}
|
||||
|
||||
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
|
||||
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHAPUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMP_LJMPIFJMPIF_LJMPIFNOTJMPIFNOT_LJMPEQJMPEQ_LJMPNEJMPNE_LJMPGTJMPGT_LJMPGEJMPGE_LJMPLTJMPLT_LJMPLEJMPLE_LCALLCALL_LCALLAABORTASSERTTHROWTRYTRY_LENDTRYENDTRY_LENDFINALLYRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPROTROLLREVERSE3REVERSE4REVERSENINITSSLOTINITSLOTLDSFLD0LDSFLD1LDSFLD2LDSFLD3LDSFLD4LDSFLD5LDSFLD6LDSFLDSTSFLD0STSFLD1STSFLD2STSFLD3STSFLD4STSFLD5STSFLD6STSFLDLDLOC0LDLOC1LDLOC2LDLOC3LDLOC4LDLOC5LDLOC6LDLOCSTLOC0STLOC1STLOC2STLOC3STLOC4STLOC5STLOC6STLOCLDARG0LDARG1LDARG2LDARG3LDARG4LDARG5LDARG6LDARGSTARG0STARG1STARG2STARG3STARG4STARG5STARG6STARGNEWBUFFERMEMCPYCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAY_TNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
|
||||
|
||||
var _Opcode_map = map[Opcode]string{
|
||||
0: _Opcode_name[0:8],
|
||||
|
@ -248,131 +253,136 @@ var _Opcode_map = map[Opcode]string{
|
|||
55: _Opcode_name[321:326],
|
||||
56: _Opcode_name[326:332],
|
||||
58: _Opcode_name[332:337],
|
||||
64: _Opcode_name[337:340],
|
||||
65: _Opcode_name[340:347],
|
||||
67: _Opcode_name[347:352],
|
||||
69: _Opcode_name[352:356],
|
||||
70: _Opcode_name[356:359],
|
||||
72: _Opcode_name[359:364],
|
||||
73: _Opcode_name[364:369],
|
||||
74: _Opcode_name[369:372],
|
||||
75: _Opcode_name[372:376],
|
||||
77: _Opcode_name[376:380],
|
||||
78: _Opcode_name[380:384],
|
||||
80: _Opcode_name[384:388],
|
||||
81: _Opcode_name[388:391],
|
||||
82: _Opcode_name[391:395],
|
||||
83: _Opcode_name[395:403],
|
||||
84: _Opcode_name[403:411],
|
||||
85: _Opcode_name[411:419],
|
||||
86: _Opcode_name[419:428],
|
||||
87: _Opcode_name[428:436],
|
||||
88: _Opcode_name[436:443],
|
||||
89: _Opcode_name[443:450],
|
||||
90: _Opcode_name[450:457],
|
||||
91: _Opcode_name[457:464],
|
||||
92: _Opcode_name[464:471],
|
||||
93: _Opcode_name[471:478],
|
||||
94: _Opcode_name[478:485],
|
||||
95: _Opcode_name[485:491],
|
||||
96: _Opcode_name[491:498],
|
||||
97: _Opcode_name[498:505],
|
||||
98: _Opcode_name[505:512],
|
||||
99: _Opcode_name[512:519],
|
||||
100: _Opcode_name[519:526],
|
||||
101: _Opcode_name[526:533],
|
||||
102: _Opcode_name[533:540],
|
||||
103: _Opcode_name[540:546],
|
||||
104: _Opcode_name[546:552],
|
||||
105: _Opcode_name[552:558],
|
||||
106: _Opcode_name[558:564],
|
||||
107: _Opcode_name[564:570],
|
||||
108: _Opcode_name[570:576],
|
||||
109: _Opcode_name[576:582],
|
||||
110: _Opcode_name[582:588],
|
||||
111: _Opcode_name[588:593],
|
||||
112: _Opcode_name[593:599],
|
||||
113: _Opcode_name[599:605],
|
||||
114: _Opcode_name[605:611],
|
||||
115: _Opcode_name[611:617],
|
||||
116: _Opcode_name[617:623],
|
||||
117: _Opcode_name[623:629],
|
||||
118: _Opcode_name[629:635],
|
||||
119: _Opcode_name[635:640],
|
||||
120: _Opcode_name[640:646],
|
||||
121: _Opcode_name[646:652],
|
||||
122: _Opcode_name[652:658],
|
||||
123: _Opcode_name[658:664],
|
||||
124: _Opcode_name[664:670],
|
||||
125: _Opcode_name[670:676],
|
||||
126: _Opcode_name[676:682],
|
||||
127: _Opcode_name[682:687],
|
||||
128: _Opcode_name[687:693],
|
||||
129: _Opcode_name[693:699],
|
||||
130: _Opcode_name[699:705],
|
||||
131: _Opcode_name[705:711],
|
||||
132: _Opcode_name[711:717],
|
||||
133: _Opcode_name[717:723],
|
||||
134: _Opcode_name[723:729],
|
||||
135: _Opcode_name[729:734],
|
||||
136: _Opcode_name[734:743],
|
||||
137: _Opcode_name[743:749],
|
||||
139: _Opcode_name[749:752],
|
||||
140: _Opcode_name[752:758],
|
||||
141: _Opcode_name[758:762],
|
||||
142: _Opcode_name[762:767],
|
||||
144: _Opcode_name[767:773],
|
||||
145: _Opcode_name[773:776],
|
||||
146: _Opcode_name[776:778],
|
||||
147: _Opcode_name[778:781],
|
||||
151: _Opcode_name[781:786],
|
||||
152: _Opcode_name[786:794],
|
||||
153: _Opcode_name[794:798],
|
||||
154: _Opcode_name[798:801],
|
||||
155: _Opcode_name[801:807],
|
||||
156: _Opcode_name[807:810],
|
||||
157: _Opcode_name[810:813],
|
||||
158: _Opcode_name[813:816],
|
||||
159: _Opcode_name[816:819],
|
||||
160: _Opcode_name[819:822],
|
||||
161: _Opcode_name[822:825],
|
||||
162: _Opcode_name[825:828],
|
||||
168: _Opcode_name[828:831],
|
||||
169: _Opcode_name[831:834],
|
||||
170: _Opcode_name[834:837],
|
||||
171: _Opcode_name[837:844],
|
||||
172: _Opcode_name[844:850],
|
||||
177: _Opcode_name[850:852],
|
||||
179: _Opcode_name[852:860],
|
||||
180: _Opcode_name[860:871],
|
||||
181: _Opcode_name[871:873],
|
||||
182: _Opcode_name[873:876],
|
||||
183: _Opcode_name[876:878],
|
||||
184: _Opcode_name[878:881],
|
||||
185: _Opcode_name[881:884],
|
||||
186: _Opcode_name[884:887],
|
||||
187: _Opcode_name[887:893],
|
||||
192: _Opcode_name[893:897],
|
||||
193: _Opcode_name[897:903],
|
||||
194: _Opcode_name[903:912],
|
||||
195: _Opcode_name[912:920],
|
||||
196: _Opcode_name[920:930],
|
||||
197: _Opcode_name[930:940],
|
||||
198: _Opcode_name[940:949],
|
||||
200: _Opcode_name[949:955],
|
||||
202: _Opcode_name[955:959],
|
||||
203: _Opcode_name[959:965],
|
||||
204: _Opcode_name[965:969],
|
||||
205: _Opcode_name[969:975],
|
||||
206: _Opcode_name[975:983],
|
||||
207: _Opcode_name[983:989],
|
||||
208: _Opcode_name[989:996],
|
||||
209: _Opcode_name[996:1008],
|
||||
210: _Opcode_name[1008:1014],
|
||||
211: _Opcode_name[1014:1024],
|
||||
216: _Opcode_name[1024:1030],
|
||||
217: _Opcode_name[1030:1036],
|
||||
219: _Opcode_name[1036:1043],
|
||||
59: _Opcode_name[337:340],
|
||||
60: _Opcode_name[340:345],
|
||||
61: _Opcode_name[345:351],
|
||||
62: _Opcode_name[351:359],
|
||||
63: _Opcode_name[359:369],
|
||||
64: _Opcode_name[369:372],
|
||||
65: _Opcode_name[372:379],
|
||||
67: _Opcode_name[379:384],
|
||||
69: _Opcode_name[384:388],
|
||||
70: _Opcode_name[388:391],
|
||||
72: _Opcode_name[391:396],
|
||||
73: _Opcode_name[396:401],
|
||||
74: _Opcode_name[401:404],
|
||||
75: _Opcode_name[404:408],
|
||||
77: _Opcode_name[408:412],
|
||||
78: _Opcode_name[412:416],
|
||||
80: _Opcode_name[416:420],
|
||||
81: _Opcode_name[420:423],
|
||||
82: _Opcode_name[423:427],
|
||||
83: _Opcode_name[427:435],
|
||||
84: _Opcode_name[435:443],
|
||||
85: _Opcode_name[443:451],
|
||||
86: _Opcode_name[451:460],
|
||||
87: _Opcode_name[460:468],
|
||||
88: _Opcode_name[468:475],
|
||||
89: _Opcode_name[475:482],
|
||||
90: _Opcode_name[482:489],
|
||||
91: _Opcode_name[489:496],
|
||||
92: _Opcode_name[496:503],
|
||||
93: _Opcode_name[503:510],
|
||||
94: _Opcode_name[510:517],
|
||||
95: _Opcode_name[517:523],
|
||||
96: _Opcode_name[523:530],
|
||||
97: _Opcode_name[530:537],
|
||||
98: _Opcode_name[537:544],
|
||||
99: _Opcode_name[544:551],
|
||||
100: _Opcode_name[551:558],
|
||||
101: _Opcode_name[558:565],
|
||||
102: _Opcode_name[565:572],
|
||||
103: _Opcode_name[572:578],
|
||||
104: _Opcode_name[578:584],
|
||||
105: _Opcode_name[584:590],
|
||||
106: _Opcode_name[590:596],
|
||||
107: _Opcode_name[596:602],
|
||||
108: _Opcode_name[602:608],
|
||||
109: _Opcode_name[608:614],
|
||||
110: _Opcode_name[614:620],
|
||||
111: _Opcode_name[620:625],
|
||||
112: _Opcode_name[625:631],
|
||||
113: _Opcode_name[631:637],
|
||||
114: _Opcode_name[637:643],
|
||||
115: _Opcode_name[643:649],
|
||||
116: _Opcode_name[649:655],
|
||||
117: _Opcode_name[655:661],
|
||||
118: _Opcode_name[661:667],
|
||||
119: _Opcode_name[667:672],
|
||||
120: _Opcode_name[672:678],
|
||||
121: _Opcode_name[678:684],
|
||||
122: _Opcode_name[684:690],
|
||||
123: _Opcode_name[690:696],
|
||||
124: _Opcode_name[696:702],
|
||||
125: _Opcode_name[702:708],
|
||||
126: _Opcode_name[708:714],
|
||||
127: _Opcode_name[714:719],
|
||||
128: _Opcode_name[719:725],
|
||||
129: _Opcode_name[725:731],
|
||||
130: _Opcode_name[731:737],
|
||||
131: _Opcode_name[737:743],
|
||||
132: _Opcode_name[743:749],
|
||||
133: _Opcode_name[749:755],
|
||||
134: _Opcode_name[755:761],
|
||||
135: _Opcode_name[761:766],
|
||||
136: _Opcode_name[766:775],
|
||||
137: _Opcode_name[775:781],
|
||||
139: _Opcode_name[781:784],
|
||||
140: _Opcode_name[784:790],
|
||||
141: _Opcode_name[790:794],
|
||||
142: _Opcode_name[794:799],
|
||||
144: _Opcode_name[799:805],
|
||||
145: _Opcode_name[805:808],
|
||||
146: _Opcode_name[808:810],
|
||||
147: _Opcode_name[810:813],
|
||||
151: _Opcode_name[813:818],
|
||||
152: _Opcode_name[818:826],
|
||||
153: _Opcode_name[826:830],
|
||||
154: _Opcode_name[830:833],
|
||||
155: _Opcode_name[833:839],
|
||||
156: _Opcode_name[839:842],
|
||||
157: _Opcode_name[842:845],
|
||||
158: _Opcode_name[845:848],
|
||||
159: _Opcode_name[848:851],
|
||||
160: _Opcode_name[851:854],
|
||||
161: _Opcode_name[854:857],
|
||||
162: _Opcode_name[857:860],
|
||||
168: _Opcode_name[860:863],
|
||||
169: _Opcode_name[863:866],
|
||||
170: _Opcode_name[866:869],
|
||||
171: _Opcode_name[869:876],
|
||||
172: _Opcode_name[876:882],
|
||||
177: _Opcode_name[882:884],
|
||||
179: _Opcode_name[884:892],
|
||||
180: _Opcode_name[892:903],
|
||||
181: _Opcode_name[903:905],
|
||||
182: _Opcode_name[905:908],
|
||||
183: _Opcode_name[908:910],
|
||||
184: _Opcode_name[910:913],
|
||||
185: _Opcode_name[913:916],
|
||||
186: _Opcode_name[916:919],
|
||||
187: _Opcode_name[919:925],
|
||||
192: _Opcode_name[925:929],
|
||||
193: _Opcode_name[929:935],
|
||||
194: _Opcode_name[935:944],
|
||||
195: _Opcode_name[944:952],
|
||||
196: _Opcode_name[952:962],
|
||||
197: _Opcode_name[962:972],
|
||||
198: _Opcode_name[972:981],
|
||||
200: _Opcode_name[981:987],
|
||||
202: _Opcode_name[987:991],
|
||||
203: _Opcode_name[991:997],
|
||||
204: _Opcode_name[997:1001],
|
||||
205: _Opcode_name[1001:1007],
|
||||
206: _Opcode_name[1007:1015],
|
||||
207: _Opcode_name[1015:1021],
|
||||
208: _Opcode_name[1021:1028],
|
||||
209: _Opcode_name[1028:1040],
|
||||
210: _Opcode_name[1040:1046],
|
||||
211: _Opcode_name[1046:1056],
|
||||
216: _Opcode_name[1056:1062],
|
||||
217: _Opcode_name[1062:1068],
|
||||
219: _Opcode_name[1068:1075],
|
||||
}
|
||||
|
||||
func (i Opcode) String() string {
|
||||
|
|
114
pkg/vm/vm.go
114
pkg/vm/vm.go
|
@ -71,6 +71,8 @@ type VM struct {
|
|||
istack *Stack // invocation stack.
|
||||
estack *Stack // execution stack.
|
||||
|
||||
uncaughtException stackitem.Item // exception being handled
|
||||
|
||||
refs *refCounter
|
||||
|
||||
gasConsumed int64
|
||||
|
@ -193,13 +195,12 @@ func (v *VM) PrintOps() {
|
|||
opcode.JMPL, opcode.JMPIFL, opcode.JMPIFNOTL, opcode.CALLL,
|
||||
opcode.JMPEQL, opcode.JMPNEL,
|
||||
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLEL, opcode.JMPLTL,
|
||||
opcode.PUSHA:
|
||||
offset, rOffset, err := v.calcJumpOffset(ctx, parameter)
|
||||
if err != nil {
|
||||
desc = fmt.Sprintf("ERROR: %v", err)
|
||||
} else {
|
||||
desc = fmt.Sprintf("%d (%d/%x)", offset, rOffset, parameter)
|
||||
}
|
||||
opcode.PUSHA, opcode.ENDTRY, opcode.ENDTRYL:
|
||||
desc = v.getOffsetDesc(ctx, parameter)
|
||||
case opcode.TRY, opcode.TRYL:
|
||||
catchP, finallyP := getTryParams(instr, parameter)
|
||||
desc = fmt.Sprintf("catch %s, finally %s",
|
||||
v.getOffsetDesc(ctx, catchP), v.getOffsetDesc(ctx, finallyP))
|
||||
case opcode.INITSSLOT:
|
||||
desc = fmt.Sprint(parameter[0])
|
||||
case opcode.INITSLOT:
|
||||
|
@ -223,6 +224,14 @@ func (v *VM) PrintOps() {
|
|||
w.Flush()
|
||||
}
|
||||
|
||||
func (v *VM) getOffsetDesc(ctx *Context, parameter []byte) string {
|
||||
offset, rOffset, err := v.calcJumpOffset(ctx, parameter)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("ERROR: %v", err)
|
||||
}
|
||||
return fmt.Sprintf("%d (%d/%x)", offset, rOffset, parameter)
|
||||
}
|
||||
|
||||
// AddBreakPoint adds a breakpoint to the current context.
|
||||
func (v *VM) AddBreakPoint(n int) {
|
||||
ctx := v.Context()
|
||||
|
@ -271,6 +280,7 @@ func (v *VM) LoadScript(b []byte) {
|
|||
func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) {
|
||||
ctx := NewContext(b)
|
||||
ctx.estack = v.estack
|
||||
ctx.tryStack = NewStack("exception")
|
||||
ctx.callFlag = f
|
||||
v.istack.PushVal(ctx)
|
||||
}
|
||||
|
@ -1357,7 +1367,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
// unlucky ^^
|
||||
|
||||
case opcode.THROW:
|
||||
panic("THROW")
|
||||
v.throw(v.estack.Pop().Item())
|
||||
|
||||
case opcode.ABORT:
|
||||
panic("ABORT")
|
||||
|
@ -1367,6 +1377,43 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
panic("ASSERT failed")
|
||||
}
|
||||
|
||||
case opcode.TRY, opcode.TRYL:
|
||||
catchP, finallyP := getTryParams(op, parameter)
|
||||
cOffset := v.getJumpOffset(ctx, catchP)
|
||||
fOffset := v.getJumpOffset(ctx, finallyP)
|
||||
if cOffset == 0 && fOffset == 0 {
|
||||
panic("invalid offset for TRY*")
|
||||
} else if cOffset == ctx.ip {
|
||||
cOffset = -1
|
||||
} else if fOffset == ctx.ip {
|
||||
fOffset = -1
|
||||
}
|
||||
eCtx := newExceptionHandlingContext(cOffset, fOffset)
|
||||
ctx.tryStack.PushVal(eCtx)
|
||||
|
||||
case opcode.ENDTRY, opcode.ENDTRYL:
|
||||
eCtx := ctx.tryStack.Peek(0).Value().(*exceptionHandlingContext)
|
||||
if eCtx.State == eFinally {
|
||||
panic("invalid exception handling state during ENDTRY*")
|
||||
}
|
||||
eOffset := v.getJumpOffset(ctx, parameter)
|
||||
if eCtx.HasFinally() {
|
||||
eCtx.State = eFinally
|
||||
eCtx.EndOffset = eOffset
|
||||
eOffset = eCtx.FinallyOffset
|
||||
} else {
|
||||
ctx.tryStack.Pop()
|
||||
}
|
||||
v.jump(ctx, eOffset)
|
||||
|
||||
case opcode.ENDFINALLY:
|
||||
if v.uncaughtException != nil {
|
||||
v.handleException()
|
||||
return
|
||||
}
|
||||
eCtx := ctx.tryStack.Pop().Value().(*exceptionHandlingContext)
|
||||
v.jump(ctx, eCtx.EndOffset)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("unknown opcode %s", op.String()))
|
||||
}
|
||||
|
@ -1386,6 +1433,15 @@ func (v *VM) unloadContext(ctx *Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// getTryParams splits TRY(L) instruction parameter into offsets for catch and finally blocks.
|
||||
func getTryParams(op opcode.Opcode, p []byte) ([]byte, []byte) {
|
||||
i := 1
|
||||
if op == opcode.TRYL {
|
||||
i = 4
|
||||
}
|
||||
return p[:i], p[i:]
|
||||
}
|
||||
|
||||
// getJumpCondition performs opcode specific comparison of a and b
|
||||
func getJumpCondition(op opcode.Opcode, a, b *big.Int) bool {
|
||||
cmp := a.Cmp(b)
|
||||
|
@ -1407,6 +1463,11 @@ func getJumpCondition(op opcode.Opcode, a, b *big.Int) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func (v *VM) throw(item stackitem.Item) {
|
||||
v.uncaughtException = item
|
||||
v.handleException()
|
||||
}
|
||||
|
||||
// jump performs jump to the offset.
|
||||
func (v *VM) jump(ctx *Context, offset int) {
|
||||
ctx.nextip = offset
|
||||
|
@ -1424,6 +1485,8 @@ func (v *VM) getJumpOffset(ctx *Context, parameter []byte) int {
|
|||
return offset
|
||||
}
|
||||
|
||||
// calcJumpOffset returns absolute and relative offset of JMP/CALL/TRY instructions
|
||||
// either in short (1-byte) or long (4-byte) form.
|
||||
func (v *VM) calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
|
||||
var rOffset int32
|
||||
switch l := len(parameter); l {
|
||||
|
@ -1432,7 +1495,8 @@ func (v *VM) calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
|
|||
case 4:
|
||||
rOffset = int32(binary.LittleEndian.Uint32(parameter))
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("invalid JMP* parameter length: %d", l)
|
||||
_, curr := ctx.CurrInstr()
|
||||
return 0, 0, fmt.Errorf("invalid %s parameter length: %d", curr, l)
|
||||
}
|
||||
offset := ctx.ip + int(rOffset)
|
||||
if offset < 0 || offset > len(ctx.prog) {
|
||||
|
@ -1442,6 +1506,38 @@ func (v *VM) calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
|
|||
return offset, int(rOffset), nil
|
||||
}
|
||||
|
||||
func (v *VM) handleException() {
|
||||
pop := 0
|
||||
ictx := v.istack.Peek(0).Value().(*Context)
|
||||
for ictx != nil {
|
||||
e := ictx.tryStack.Peek(pop)
|
||||
for e != nil {
|
||||
ectx := e.Value().(*exceptionHandlingContext)
|
||||
if ectx.State == eFinally || (ectx.State == eCatch && !ectx.HasFinally()) {
|
||||
ictx.tryStack.Pop()
|
||||
e = ictx.tryStack.Peek(0)
|
||||
continue
|
||||
}
|
||||
for i := 0; i < pop; i++ {
|
||||
ctx := v.istack.Pop().Value().(*Context)
|
||||
v.unloadContext(ctx)
|
||||
}
|
||||
if ectx.State == eTry && ectx.HasCatch() {
|
||||
ectx.State = eCatch
|
||||
v.estack.PushVal(v.uncaughtException)
|
||||
v.uncaughtException = nil
|
||||
v.jump(ictx, ectx.CatchOffset)
|
||||
} else {
|
||||
ectx.State = eFinally
|
||||
v.jump(ictx, ectx.FinallyOffset)
|
||||
}
|
||||
return
|
||||
}
|
||||
pop++
|
||||
ictx = v.istack.Peek(pop).Value().(*Context)
|
||||
}
|
||||
}
|
||||
|
||||
// CheckMultisigPar checks if sigs contains sufficient valid signatures.
|
||||
func CheckMultisigPar(v *VM, curve elliptic.Curve, h []byte, pkeys [][]byte, sigs [][]byte) bool {
|
||||
if len(sigs) == 1 {
|
||||
|
|
|
@ -1241,6 +1241,79 @@ func TestNEWBUFFER(t *testing.T) {
|
|||
t.Run("TooBig", getTestFuncForVM(prog, nil, stackitem.MaxSize+1))
|
||||
}
|
||||
|
||||
func getTRYProgram(tryBlock, catchBlock, finallyBlock []byte) []byte {
|
||||
hasCatch := catchBlock != nil
|
||||
hasFinally := finallyBlock != nil
|
||||
tryOffset := len(tryBlock) + 3 + 2 // try args + endtry args
|
||||
|
||||
var (
|
||||
catchLen, finallyLen int
|
||||
catchOffset, finallyOffset int
|
||||
)
|
||||
if hasCatch {
|
||||
catchLen = len(catchBlock) + 2 // endtry args
|
||||
catchOffset = tryOffset
|
||||
}
|
||||
if hasFinally {
|
||||
finallyLen = len(finallyBlock) + 1 // endfinally
|
||||
finallyOffset = tryOffset + catchLen
|
||||
}
|
||||
prog := []byte{byte(opcode.TRY), byte(catchOffset), byte(finallyOffset)}
|
||||
prog = append(prog, tryBlock...)
|
||||
prog = append(prog, byte(opcode.ENDTRY), byte(catchLen+finallyLen+2))
|
||||
if hasCatch {
|
||||
prog = append(prog, catchBlock...)
|
||||
prog = append(prog, byte(opcode.ENDTRY), byte(finallyLen+2))
|
||||
}
|
||||
if hasFinally {
|
||||
prog = append(prog, finallyBlock...)
|
||||
prog = append(prog, byte(opcode.ENDFINALLY))
|
||||
}
|
||||
prog = append(prog, byte(opcode.RET))
|
||||
return prog
|
||||
}
|
||||
|
||||
func getTRYTestFunc(result interface{}, tryBlock, catchBlock, finallyBlock []byte) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
prog := getTRYProgram(tryBlock, catchBlock, finallyBlock)
|
||||
runWithArgs(t, prog, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTRY(t *testing.T) {
|
||||
throw := []byte{byte(opcode.PUSH13), byte(opcode.THROW)}
|
||||
push1 := []byte{byte(opcode.PUSH1)}
|
||||
add5 := []byte{byte(opcode.PUSH5), byte(opcode.ADD)}
|
||||
add9 := []byte{byte(opcode.PUSH9), byte(opcode.ADD)}
|
||||
t.Run("NoCatch", func(t *testing.T) {
|
||||
t.Run("NoFinally", getTRYTestFunc(nil, push1, nil, nil))
|
||||
t.Run("WithFinally", getTRYTestFunc(10, push1, nil, add9))
|
||||
t.Run("Throw", getTRYTestFunc(nil, throw, nil, add9))
|
||||
})
|
||||
t.Run("WithCatch", func(t *testing.T) {
|
||||
t.Run("NoFinally", func(t *testing.T) {
|
||||
t.Run("Simple", getTRYTestFunc(1, push1, add5, nil))
|
||||
t.Run("Throw", getTRYTestFunc(18, throw, add5, nil))
|
||||
t.Run("Abort", getTRYTestFunc(nil, []byte{byte(opcode.ABORT)}, push1, nil))
|
||||
t.Run("ThrowInCatch", getTRYTestFunc(nil, throw, throw, nil))
|
||||
})
|
||||
t.Run("WithFinally", func(t *testing.T) {
|
||||
t.Run("Simple", getTRYTestFunc(10, push1, add5, add9))
|
||||
t.Run("Throw", getTRYTestFunc(27, throw, add5, add9))
|
||||
})
|
||||
})
|
||||
t.Run("Nested", func(t *testing.T) {
|
||||
t.Run("ReThrowInTry", func(t *testing.T) {
|
||||
inner := getTRYProgram(throw, []byte{byte(opcode.THROW)}, nil)
|
||||
getTRYTestFunc(27, inner, add5, add9)(t)
|
||||
})
|
||||
t.Run("ThrowInFinally", func(t *testing.T) {
|
||||
inner := getTRYProgram(throw, add5, []byte{byte(opcode.THROW)})
|
||||
getTRYTestFunc(32, inner, add5, add9)(t)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestMEMCPY(t *testing.T) {
|
||||
prog := makeProgram(opcode.MEMCPY)
|
||||
t.Run("Good", func(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue