forked from TrueCloudLab/neoneo-go
vm: rework stack as a simple slice
Double-linked list is quite expensive to manage especially given that it requires microallocations for each Element. It can be replaced by simple slice which is much more effective for simple push/pop operations that are very typical in a VM. I've worried a little about more complex operations like XDROP/1024 or REVERSEN/1024 because these require copying quite substantial number of elements, but turns out these work fine too. At the moment Element is kept as a convenient wrapper for Bytes/BigInt/Bool/etc methods, but it can be changed in future. Many other potential optimizations are also possible now. Complex scripts: name old time/op new time/op delta ScriptFibonacci-8 1.11ms ± 2% 0.85ms ± 2% -23.40% (p=0.000 n=10+10) ScriptNestedRefCount-8 1.46ms ± 2% 1.16ms ± 1% -20.65% (p=0.000 n=10+10) ScriptPushPop/4-8 1.81µs ± 1% 1.54µs ± 4% -14.96% (p=0.000 n=8+10) ScriptPushPop/16-8 4.88µs ± 2% 3.91µs ± 2% -19.87% (p=0.000 n=9+9) ScriptPushPop/128-8 31.9µs ± 9% 26.7µs ± 3% -16.28% (p=0.000 n=9+8) ScriptPushPop/1024-8 235µs ± 1% 192µs ± 3% -18.31% (p=0.000 n=9+10) name old alloc/op new alloc/op delta ScriptFibonacci-8 392kB ± 0% 123kB ± 0% -68.68% (p=0.000 n=8+8) ScriptNestedRefCount-8 535kB ± 0% 266kB ± 0% -50.38% (p=0.000 n=6+10) ScriptPushPop/4-8 352B ± 0% 160B ± 0% -54.55% (p=0.000 n=10+10) ScriptPushPop/16-8 1.41kB ± 0% 0.64kB ± 0% -54.55% (p=0.000 n=10+10) ScriptPushPop/128-8 11.3kB ± 0% 8.7kB ± 0% -22.73% (p=0.000 n=10+10) ScriptPushPop/1024-8 90.1kB ± 0% 73.2kB ± 0% -18.75% (p=0.000 n=10+10) name old allocs/op new allocs/op delta ScriptFibonacci-8 9.14k ± 0% 3.53k ± 0% -61.41% (p=0.000 n=10+10) ScriptNestedRefCount-8 17.4k ± 0% 11.8k ± 0% -32.35% (p=0.000 n=10+10) ScriptPushPop/4-8 12.0 ± 0% 8.0 ± 0% -33.33% (p=0.000 n=10+10) ScriptPushPop/16-8 48.0 ± 0% 32.0 ± 0% -33.33% (p=0.000 n=10+10) ScriptPushPop/128-8 384 ± 0% 259 ± 0% -32.55% (p=0.000 n=10+10) ScriptPushPop/1024-8 3.07k ± 0% 2.05k ± 0% -33.14% (p=0.000 n=10+10) Some stack-management opcodes: name old time/op new time/op delta Opcodes/XDROP/0/1-8 255ns ± 9% 273ns ±11% +6.92% (p=0.016 n=11+10) Opcodes/XDROP/0/1024-8 362ns ± 2% 365ns ± 8% ~ (p=0.849 n=10+11) Opcodes/XDROP/1024/1024-8 3.20µs ± 2% 1.99µs ±12% -37.69% (p=0.000 n=11+11) Opcodes/XDROP/2047/2048-8 6.55µs ± 3% 1.75µs ± 5% -73.26% (p=0.000 n=10+11) Opcodes/DUP/null-8 414ns ± 6% 245ns ±12% -40.88% (p=0.000 n=11+11) Opcodes/DUP/boolean-8 411ns ± 8% 245ns ± 6% -40.31% (p=0.000 n=11+11) Opcodes/DUP/integer/small-8 684ns ± 8% 574ns ± 3% -16.02% (p=0.000 n=11+10) Opcodes/DUP/integer/big-8 675ns ± 6% 601ns ±10% -10.98% (p=0.000 n=11+11) Opcodes/DUP/bytearray/small-8 675ns ±10% 566ns ±10% -16.22% (p=0.000 n=11+11) Opcodes/DUP/bytearray/big-8 6.39µs ±11% 6.13µs ± 3% ~ (p=0.148 n=10+10) Opcodes/DUP/buffer/small-8 412ns ± 5% 261ns ± 8% -36.55% (p=0.000 n=9+11) Opcodes/DUP/buffer/big-8 586ns ±10% 337ns ± 7% -42.53% (p=0.000 n=11+11) Opcodes/DUP/struct/small-8 458ns ±12% 256ns ±12% -44.09% (p=0.000 n=11+11) Opcodes/DUP/struct/big-8 489ns ± 7% 274ns ± 5% -44.06% (p=0.000 n=10+10) Opcodes/DUP/pointer-8 586ns ± 7% 494ns ± 7% -15.67% (p=0.000 n=11+11) Opcodes/OVER/null-8 450ns ±14% 264ns ±10% -41.30% (p=0.000 n=11+11) Opcodes/OVER/boolean-8 450ns ±14% 264ns ±10% -41.31% (p=0.000 n=11+11) Opcodes/OVER/integer/small-8 716ns ± 9% 604ns ± 6% -15.65% (p=0.000 n=11+11) Opcodes/OVER/integer/big-8 696ns ± 5% 634ns ± 6% -8.89% (p=0.000 n=10+11) Opcodes/OVER/bytearray/small-8 693ns ± 1% 539ns ± 9% -22.18% (p=0.000 n=9+10) Opcodes/OVER/bytearray/big-8 6.33µs ± 2% 6.16µs ± 4% -2.79% (p=0.004 n=8+10) Opcodes/OVER/buffer/small-8 415ns ± 4% 263ns ± 8% -36.76% (p=0.000 n=9+11) Opcodes/OVER/buffer/big-8 587ns ± 5% 342ns ± 7% -41.70% (p=0.000 n=11+11) Opcodes/OVER/struct/small-8 446ns ±14% 257ns ± 8% -42.42% (p=0.000 n=11+11) Opcodes/OVER/struct/big-8 607ns ±26% 278ns ± 7% -54.25% (p=0.000 n=11+11) Opcodes/OVER/pointer-8 645ns ±12% 476ns ±10% -26.21% (p=0.000 n=11+11) Opcodes/PICK/2/null-8 460ns ±11% 264ns ± 9% -42.68% (p=0.000 n=11+11) Opcodes/PICK/2/boolean-8 460ns ± 4% 260ns ± 4% -43.37% (p=0.000 n=8+11) Opcodes/PICK/2/integer/small-8 725ns ± 7% 557ns ± 4% -23.19% (p=0.000 n=11+10) Opcodes/PICK/2/integer/big-8 722ns ±12% 582ns ± 6% -19.51% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/small-8 705ns ± 6% 545ns ± 4% -22.69% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/big-8 7.17µs ±36% 6.37µs ± 8% ~ (p=0.065 n=11+11) Opcodes/PICK/2/buffer/small-8 427ns ± 8% 253ns ± 8% -40.82% (p=0.000 n=11+11) Opcodes/PICK/2/buffer/big-8 590ns ± 3% 331ns ± 6% -43.83% (p=0.000 n=11+11) Opcodes/PICK/2/struct/small-8 428ns ± 8% 254ns ± 7% -40.64% (p=0.000 n=11+11) Opcodes/PICK/2/struct/big-8 489ns ±15% 283ns ± 7% -42.11% (p=0.000 n=11+11) Opcodes/PICK/2/pointer-8 553ns ± 7% 414ns ± 8% -25.18% (p=0.000 n=11+11) Opcodes/PICK/1024/null-8 531ns ± 4% 327ns ± 6% -38.49% (p=0.000 n=10+10) Opcodes/PICK/1024/boolean-8 527ns ± 5% 318ns ± 5% -39.78% (p=0.000 n=11+9) Opcodes/PICK/1024/integer/small-8 861ns ± 4% 683ns ± 4% -20.66% (p=0.000 n=11+11) Opcodes/PICK/1024/integer/big-8 882ns ± 4% 1060ns ±47% ~ (p=0.748 n=11+11) Opcodes/PICK/1024/bytearray/small-8 850ns ± 4% 671ns ± 5% -21.12% (p=0.000 n=10+11) Opcodes/PICK/1024/bytearray/big-8 6.32µs ±26% 6.75µs ± 4% +6.86% (p=0.019 n=10+11) Opcodes/PICK/1024/buffer/small-8 530ns ± 6% 324ns ± 5% -38.86% (p=0.000 n=10+11) Opcodes/PICK/1024/buffer/big-8 570ns ± 4% 417ns ±45% -26.82% (p=0.001 n=11+10) Opcodes/PICK/1024/struct/small-8 1.11µs ±122% 0.34µs ±11% -69.38% (p=0.000 n=11+10) Opcodes/PICK/1024/pointer-8 693ns ± 5% 568ns ±31% -18.10% (p=0.002 n=10+10) Opcodes/TUCK/null-8 450ns ±10% 275ns ± 8% -38.93% (p=0.000 n=11+11) Opcodes/TUCK/boolean-8 449ns ±13% 268ns ± 9% -40.16% (p=0.000 n=11+10) Opcodes/TUCK/integer/small-8 716ns ± 7% 599ns ± 7% -16.30% (p=0.000 n=11+11) Opcodes/TUCK/integer/big-8 718ns ± 8% 613ns ±11% -14.55% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/small-8 700ns ±12% 558ns ± 7% -20.39% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/big-8 5.88µs ± 7% 6.37µs ± 3% +8.31% (p=0.000 n=10+11) Opcodes/TUCK/buffer/small-8 425ns ± 6% 258ns ±12% -39.28% (p=0.000 n=11+11) Opcodes/TUCK/buffer/big-8 553ns ±19% 334ns ± 6% -39.57% (p=0.000 n=11+11) Opcodes/TUCK/struct/small-8 474ns ± 3% 263ns ±12% -44.51% (p=0.000 n=10+11) Opcodes/TUCK/struct/big-8 641ns ±24% 284ns ± 8% -55.63% (p=0.000 n=11+11) Opcodes/TUCK/pointer-8 635ns ±13% 468ns ±16% -26.31% (p=0.000 n=11+11) Opcodes/SWAP/null-8 227ns ±31% 212ns ±11% ~ (p=0.847 n=11+11) Opcodes/SWAP/integer-8 233ns ±32% 210ns ±14% ~ (p=0.072 n=10+11) Opcodes/SWAP/big_bytes-8 263ns ±39% 211ns ±11% ~ (p=0.056 n=11+11) Opcodes/ROT/null-8 308ns ±68% 223ns ±12% ~ (p=0.519 n=11+11) Opcodes/ROT/integer-8 226ns ±25% 228ns ± 9% ~ (p=0.705 n=10+11) Opcodes/ROT/big_bytes-8 215ns ±18% 218ns ± 7% ~ (p=0.756 n=10+11) Opcodes/ROLL/4/null-8 269ns ±10% 295ns ± 9% +9.42% (p=0.002 n=10+11) Opcodes/ROLL/4/integer-8 344ns ±48% 280ns ± 2% ~ (p=0.882 n=11+9) Opcodes/ROLL/4/big_bytes-8 276ns ±13% 288ns ± 4% +4.38% (p=0.046 n=9+11) Opcodes/ROLL/1024/null-8 4.21µs ±70% 1.01µs ± 9% -76.15% (p=0.000 n=11+11) Opcodes/ROLL/1024/integer-8 4.78µs ±82% 0.71µs ± 3% -85.06% (p=0.000 n=11+11) Opcodes/ROLL/1024/big_bytes-8 3.28µs ± 5% 1.35µs ±36% -58.91% (p=0.000 n=9+11) Opcodes/REVERSE3/null-8 219ns ± 9% 224ns ± 9% ~ (p=0.401 n=11+11) Opcodes/REVERSE3/integer-8 261ns ±28% 220ns ± 6% -15.67% (p=0.015 n=11+11) Opcodes/REVERSE3/big_bytes-8 245ns ±31% 218ns ± 7% ~ (p=0.051 n=10+11) Opcodes/REVERSE4/null-8 223ns ±10% 218ns ± 6% ~ (p=0.300 n=11+11) Opcodes/REVERSE4/integer-8 233ns ±10% 220ns ± 7% -5.74% (p=0.016 n=11+11) Opcodes/REVERSE4/big_bytes-8 225ns ±10% 220ns ± 7% ~ (p=0.157 n=10+11) Opcodes/REVERSEN/5/null-8 281ns ±12% 277ns ± 4% ~ (p=0.847 n=11+11) Opcodes/REVERSEN/5/integer-8 280ns ±11% 275ns ± 5% ~ (p=0.243 n=11+11) Opcodes/REVERSEN/5/big_bytes-8 283ns ± 9% 276ns ± 7% ~ (p=0.133 n=11+11) Opcodes/REVERSEN/1024/null-8 4.85µs ± 6% 1.94µs ± 6% -60.07% (p=0.000 n=10+11) Opcodes/REVERSEN/1024/integer-8 4.97µs ± 7% 1.99µs ±22% -59.88% (p=0.000 n=11+11) Opcodes/REVERSEN/1024/big_bytes-8 5.11µs ±10% 2.00µs ± 4% -60.87% (p=0.000 n=10+9) Opcodes/PACK/1-8 1.22µs ± 7% 0.95µs ± 6% -22.17% (p=0.000 n=10+11) Opcodes/PACK/255-8 11.1µs ± 4% 10.2µs ± 6% -7.96% (p=0.000 n=11+11) Opcodes/PACK/1024-8 38.9µs ± 4% 37.4µs ± 9% ~ (p=0.173 n=10+11) Opcodes/UNPACK/1-8 1.32µs ±34% 0.96µs ± 6% -27.57% (p=0.000 n=10+11) Opcodes/UNPACK/255-8 27.2µs ±14% 16.0µs ±13% -41.04% (p=0.000 n=11+11) Opcodes/UNPACK/1024-8 102µs ±10% 64µs ±16% -37.33% (p=0.000 n=10+11) name old alloc/op new alloc/op delta Opcodes/XDROP/0/1-8 0.00B 0.00B ~ (all equal) Opcodes/XDROP/0/1024-8 0.00B 0.00B ~ (all equal) Opcodes/XDROP/1024/1024-8 0.00B 0.00B ~ (all equal) Opcodes/XDROP/2047/2048-8 0.00B 0.00B ~ (all equal) Opcodes/DUP/null-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/boolean-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/integer/small-8 96.0B ± 0% 48.0B ± 0% -50.00% (p=0.000 n=11+11) Opcodes/DUP/integer/big-8 104B ± 0% 56B ± 0% -46.15% (p=0.000 n=11+11) Opcodes/DUP/bytearray/small-8 88.0B ± 0% 40.0B ± 0% -54.55% (p=0.000 n=11+11) Opcodes/DUP/bytearray/big-8 65.6kB ± 0% 65.6kB ± 0% -0.07% (p=0.000 n=10+9) Opcodes/DUP/buffer/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/buffer/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/struct/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/struct/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/DUP/pointer-8 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=11+11) Opcodes/OVER/null-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/boolean-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/integer/small-8 96.0B ± 0% 48.0B ± 0% -50.00% (p=0.000 n=11+11) Opcodes/OVER/integer/big-8 104B ± 0% 56B ± 0% -46.15% (p=0.000 n=11+11) Opcodes/OVER/bytearray/small-8 88.0B ± 0% 40.0B ± 0% -54.55% (p=0.000 n=11+11) Opcodes/OVER/bytearray/big-8 65.6kB ± 0% 65.6kB ± 0% -0.07% (p=0.000 n=9+11) Opcodes/OVER/buffer/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/buffer/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/struct/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/struct/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/OVER/pointer-8 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=11+11) Opcodes/PICK/2/null-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/boolean-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/integer/small-8 96.0B ± 0% 48.0B ± 0% -50.00% (p=0.000 n=11+11) Opcodes/PICK/2/integer/big-8 104B ± 0% 56B ± 0% -46.15% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/small-8 88.0B ± 0% 40.0B ± 0% -54.55% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/big-8 65.6kB ± 0% 65.6kB ± 0% -0.07% (p=0.001 n=9+11) Opcodes/PICK/2/buffer/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/buffer/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/struct/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/struct/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/pointer-8 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=11+11) Opcodes/PICK/1024/null-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/boolean-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/integer/small-8 96.0B ± 0% 48.0B ± 0% -50.00% (p=0.000 n=11+11) Opcodes/PICK/1024/integer/big-8 104B ± 0% 56B ± 0% -46.15% (p=0.000 n=11+11) Opcodes/PICK/1024/bytearray/small-8 88.0B ± 0% 40.0B ± 0% -54.55% (p=0.000 n=11+11) Opcodes/PICK/1024/bytearray/big-8 65.6kB ± 0% 65.6kB ± 0% -0.07% (p=0.000 n=11+11) Opcodes/PICK/1024/buffer/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/buffer/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/struct/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/pointer-8 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=11+11) Opcodes/TUCK/null-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/boolean-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/integer/small-8 96.0B ± 0% 48.0B ± 0% -50.00% (p=0.000 n=11+11) Opcodes/TUCK/integer/big-8 104B ± 0% 56B ± 0% -46.15% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/small-8 88.0B ± 0% 40.0B ± 0% -54.55% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/big-8 65.6kB ± 0% 65.6kB ± 0% -0.07% (p=0.000 n=10+11) Opcodes/TUCK/buffer/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/buffer/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/struct/small-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/struct/big-8 48.0B ± 0% 0.0B -100.00% (p=0.000 n=11+11) Opcodes/TUCK/pointer-8 112B ± 0% 64B ± 0% -42.86% (p=0.000 n=11+11) Opcodes/SWAP/null-8 0.00B 0.00B ~ (all equal) Opcodes/SWAP/integer-8 0.00B 0.00B ~ (all equal) Opcodes/SWAP/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/ROT/null-8 0.00B 0.00B ~ (all equal) Opcodes/ROT/integer-8 0.00B 0.00B ~ (all equal) Opcodes/ROT/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/4/null-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/4/integer-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/4/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/1024/null-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/1024/integer-8 0.00B 0.00B ~ (all equal) Opcodes/ROLL/1024/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE3/null-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE3/integer-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE3/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE4/null-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE4/integer-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSE4/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/5/null-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/5/integer-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/5/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/1024/null-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/1024/integer-8 0.00B 0.00B ~ (all equal) Opcodes/REVERSEN/1024/big_bytes-8 0.00B 0.00B ~ (all equal) Opcodes/PACK/1-8 144B ± 0% 96B ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PACK/255-8 4.22kB ± 0% 4.18kB ± 0% -1.14% (p=0.000 n=11+11) Opcodes/PACK/1024-8 16.5kB ± 0% 16.5kB ± 0% -0.29% (p=0.000 n=11+11) Opcodes/UNPACK/1-8 168B ± 0% 72B ± 0% -57.14% (p=0.000 n=11+11) Opcodes/UNPACK/255-8 12.4kB ± 0% 7.8kB ± 0% -37.28% (p=0.000 n=11+11) Opcodes/UNPACK/1024-8 49.3kB ± 0% 52.8kB ± 0% +7.18% (p=0.000 n=11+11) name old allocs/op new allocs/op delta Opcodes/XDROP/0/1-8 0.00 0.00 ~ (all equal) Opcodes/XDROP/0/1024-8 0.00 0.00 ~ (all equal) Opcodes/XDROP/1024/1024-8 0.00 0.00 ~ (all equal) Opcodes/XDROP/2047/2048-8 0.00 0.00 ~ (all equal) Opcodes/DUP/null-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/boolean-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/integer/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/DUP/integer/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/DUP/bytearray/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/DUP/bytearray/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/DUP/buffer/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/buffer/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/struct/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/struct/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/DUP/pointer-8 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=11+11) Opcodes/OVER/null-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/boolean-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/integer/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/OVER/integer/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/OVER/bytearray/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/OVER/bytearray/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/OVER/buffer/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/buffer/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/struct/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/struct/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/OVER/pointer-8 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=11+11) Opcodes/PICK/2/null-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/boolean-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/integer/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/2/integer/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/2/bytearray/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/2/buffer/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/buffer/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/struct/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/struct/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/2/pointer-8 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=11+11) Opcodes/PICK/1024/null-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/boolean-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/integer/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/1024/integer/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/1024/bytearray/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/1024/bytearray/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/PICK/1024/buffer/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/buffer/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/struct/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/PICK/1024/pointer-8 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=11+11) Opcodes/TUCK/null-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/boolean-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/integer/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/TUCK/integer/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/small-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/TUCK/bytearray/big-8 3.00 ± 0% 2.00 ± 0% -33.33% (p=0.000 n=11+11) Opcodes/TUCK/buffer/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/buffer/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/struct/small-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/struct/big-8 1.00 ± 0% 0.00 -100.00% (p=0.000 n=11+11) Opcodes/TUCK/pointer-8 2.00 ± 0% 1.00 ± 0% -50.00% (p=0.000 n=11+11) Opcodes/SWAP/null-8 0.00 0.00 ~ (all equal) Opcodes/SWAP/integer-8 0.00 0.00 ~ (all equal) Opcodes/SWAP/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/ROT/null-8 0.00 0.00 ~ (all equal) Opcodes/ROT/integer-8 0.00 0.00 ~ (all equal) Opcodes/ROT/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/4/null-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/4/integer-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/4/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/1024/null-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/1024/integer-8 0.00 0.00 ~ (all equal) Opcodes/ROLL/1024/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE3/null-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE3/integer-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE3/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE4/null-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE4/integer-8 0.00 0.00 ~ (all equal) Opcodes/REVERSE4/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/5/null-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/5/integer-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/5/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/1024/null-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/1024/integer-8 0.00 0.00 ~ (all equal) Opcodes/REVERSEN/1024/big_bytes-8 0.00 0.00 ~ (all equal) Opcodes/PACK/1-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.000 n=11+11) Opcodes/PACK/255-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.000 n=11+11) Opcodes/PACK/1024-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.000 n=11+11) Opcodes/UNPACK/1-8 5.00 ± 0% 3.00 ± 0% -40.00% (p=0.000 n=11+11) Opcodes/UNPACK/255-8 259 ± 0% 7 ± 0% -97.30% (p=0.000 n=11+11) Opcodes/UNPACK/1024-8 1.03k ± 0% 0.01k ± 0% -98.93% (p=0.000 n=11+11)
This commit is contained in:
parent
ed35cf8f12
commit
930653418d
8 changed files with 228 additions and 270 deletions
|
@ -1917,8 +1917,9 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
|
|||
if vm.HasFailed() {
|
||||
return 0, fmt.Errorf("%w: vm execution has failed: %v", ErrVerificationFailed, err)
|
||||
}
|
||||
resEl := vm.Estack().Pop()
|
||||
if resEl != nil {
|
||||
estack := vm.Estack()
|
||||
if estack.Len() > 0 {
|
||||
resEl := estack.Pop()
|
||||
res, err := resEl.Item().TryBool()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%w: invalid return value", ErrVerificationFailed)
|
||||
|
|
50
pkg/vm/bench_test.go
Normal file
50
pkg/vm/bench_test.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func benchScript(t *testing.B, script []byte) {
|
||||
for n := 0; n < t.N; n++ {
|
||||
t.StopTimer()
|
||||
vm := load(script)
|
||||
t.StartTimer()
|
||||
err := vm.Run()
|
||||
t.StopTimer()
|
||||
require.NoError(t, err)
|
||||
t.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
// Shared as is by @ixje once upon a time (compiled from Python).
|
||||
func BenchmarkScriptFibonacci(t *testing.B) {
|
||||
var script = []byte{87, 5, 0, 16, 112, 17, 113, 105, 104, 18, 192, 114, 16, 115, 34, 28, 104, 105, 158, 116, 106, 108, 75,
|
||||
217, 48, 38, 5, 139, 34, 5, 207, 34, 3, 114, 105, 112, 108, 113, 107, 17, 158, 115, 107, 12, 2, 94, 1,
|
||||
219, 33, 181, 36, 222, 106, 64}
|
||||
benchScript(t, script)
|
||||
}
|
||||
|
||||
func BenchmarkScriptNestedRefCount(t *testing.B) {
|
||||
b64script := "whBNEcARTRHAVgEB/gGdYBFNEU0SwFMSwFhKJPNFUUVFRQ=="
|
||||
script, err := base64.StdEncoding.DecodeString(b64script)
|
||||
require.NoError(t, err)
|
||||
benchScript(t, script)
|
||||
}
|
||||
|
||||
func BenchmarkScriptPushPop(t *testing.B) {
|
||||
for _, i := range []int{4, 16, 128, 1024} {
|
||||
t.Run(strconv.Itoa(i), func(t *testing.B) {
|
||||
var script = make([]byte, i*2)
|
||||
for p := 0; p < i; p++ {
|
||||
script[p] = byte(opcode.PUSH1)
|
||||
script[i+p] = byte(opcode.DROP)
|
||||
}
|
||||
benchScript(t, script)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -282,10 +282,11 @@ func (c *Context) IsDeployed() bool {
|
|||
// getContextScriptHash returns script hash of the invocation stack element
|
||||
// number n.
|
||||
func (v *VM) getContextScriptHash(n int) util.Uint160 {
|
||||
element := v.Istack().Peek(n)
|
||||
if element == nil {
|
||||
istack := v.Istack()
|
||||
if istack.Len() <= n {
|
||||
return util.Uint160{}
|
||||
}
|
||||
element := istack.Peek(n)
|
||||
ctxIface := element.Value()
|
||||
ctx := ctxIface.(*Context)
|
||||
return ctx.ScriptHash()
|
||||
|
|
|
@ -20,7 +20,7 @@ func TestVM_Debug(t *testing.T) {
|
|||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 5, v.Context().NextIP())
|
||||
require.NoError(t, v.Run())
|
||||
require.Equal(t, 1, v.estack.len)
|
||||
require.Equal(t, 1, v.estack.Len())
|
||||
require.Equal(t, big.NewInt(5), v.estack.Top().Value())
|
||||
})
|
||||
t.Run("StepInto", func(t *testing.T) {
|
||||
|
@ -29,14 +29,14 @@ func TestVM_Debug(t *testing.T) {
|
|||
require.Equal(t, 3, v.Context().NextIP())
|
||||
require.NoError(t, v.StepOut())
|
||||
require.Equal(t, 2, v.Context().NextIP())
|
||||
require.Equal(t, 1, v.estack.len)
|
||||
require.Equal(t, 1, v.estack.Len())
|
||||
require.Equal(t, big.NewInt(5), v.estack.Top().Value())
|
||||
})
|
||||
t.Run("StepOver", func(t *testing.T) {
|
||||
v := load(prog)
|
||||
require.NoError(t, v.StepOver())
|
||||
require.Equal(t, 2, v.Context().NextIP())
|
||||
require.Equal(t, 1, v.estack.len)
|
||||
require.Equal(t, 1, v.estack.Len())
|
||||
require.Equal(t, big.NewInt(5), v.estack.Top().Value())
|
||||
})
|
||||
}
|
||||
|
|
272
pkg/vm/stack.go
272
pkg/vm/stack.go
|
@ -9,70 +9,36 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
)
|
||||
|
||||
// Stack implementation for the neo-go virtual machine. The stack implements
|
||||
// a double linked list where its semantics are first in first out.
|
||||
// To simplify the implementation, internally a Stack s is implemented as a
|
||||
// ring, such that &s.top is both the next element of the last element s.Back()
|
||||
// and the previous element of the first element s.Top().
|
||||
//
|
||||
// s.Push(0)
|
||||
// s.Push(1)
|
||||
// s.Push(2)
|
||||
//
|
||||
// [ 2 ] > top
|
||||
// [ 1 ]
|
||||
// [ 0 ] > back
|
||||
//
|
||||
// s.Pop() > 2
|
||||
//
|
||||
// [ 1 ]
|
||||
// [ 0 ]
|
||||
// Stack implementation for the neo-go virtual machine. The stack with its LIFO
|
||||
// semantics is emulated from simple slice where the top of the stack corresponds
|
||||
// to the latest element of this slice. Pushes are appends to this slice, pops are
|
||||
// slice resizes.
|
||||
|
||||
// Element represents an element in the double linked list (the stack),
|
||||
// which will hold the underlying stackitem.Item.
|
||||
// Element represents an element on the stack, technically it's a wrapper around
|
||||
// stackitem.Item interface to provide some API simplification for VM.
|
||||
type Element struct {
|
||||
value stackitem.Item
|
||||
next, prev *Element
|
||||
stack *Stack
|
||||
value stackitem.Item
|
||||
}
|
||||
|
||||
// NewElement returns a new Element object, with its underlying value inferred
|
||||
// to the corresponding type.
|
||||
func NewElement(v interface{}) *Element {
|
||||
return &Element{
|
||||
value: stackitem.Make(v),
|
||||
}
|
||||
}
|
||||
|
||||
// Next returns the next element in the stack.
|
||||
func (e *Element) Next() *Element {
|
||||
if elem := e.next; e.stack != nil && elem != &e.stack.top {
|
||||
return elem
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Prev returns the previous element in the stack.
|
||||
func (e *Element) Prev() *Element {
|
||||
if elem := e.prev; e.stack != nil && elem != &e.stack.top {
|
||||
return elem
|
||||
}
|
||||
return nil
|
||||
func NewElement(v interface{}) Element {
|
||||
return Element{stackitem.Make(v)}
|
||||
}
|
||||
|
||||
// Item returns Item contained in the element.
|
||||
func (e *Element) Item() stackitem.Item {
|
||||
func (e Element) Item() stackitem.Item {
|
||||
return e.value
|
||||
}
|
||||
|
||||
// Value returns value of the Item contained in the element.
|
||||
func (e *Element) Value() interface{} {
|
||||
func (e Element) Value() interface{} {
|
||||
return e.value.Value()
|
||||
}
|
||||
|
||||
// BigInt attempts to get the underlying value of the element as a big integer.
|
||||
// Will panic if the assertion failed which will be caught by the VM.
|
||||
func (e *Element) BigInt() *big.Int {
|
||||
func (e Element) BigInt() *big.Int {
|
||||
val, err := e.value.TryInteger()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -82,7 +48,7 @@ func (e *Element) BigInt() *big.Int {
|
|||
|
||||
// Bool converts an underlying value of the element to a boolean if it's
|
||||
// possible to do so, it will panic otherwise.
|
||||
func (e *Element) Bool() bool {
|
||||
func (e Element) Bool() bool {
|
||||
b, err := e.value.TryBool()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -92,7 +58,7 @@ func (e *Element) Bool() bool {
|
|||
|
||||
// Bytes attempts to get the underlying value of the element as a byte array.
|
||||
// Will panic if the assertion failed which will be caught by the VM.
|
||||
func (e *Element) Bytes() []byte {
|
||||
func (e Element) Bytes() []byte {
|
||||
bs, err := e.value.TryBytes()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -102,7 +68,7 @@ func (e *Element) Bytes() []byte {
|
|||
|
||||
// BytesOrNil attempts to get the underlying value of the element as a byte array or nil.
|
||||
// Will panic if the assertion failed which will be caught by the VM.
|
||||
func (e *Element) BytesOrNil() []byte {
|
||||
func (e Element) BytesOrNil() []byte {
|
||||
if _, ok := e.value.(stackitem.Null); ok {
|
||||
return nil
|
||||
}
|
||||
|
@ -115,7 +81,7 @@ func (e *Element) BytesOrNil() []byte {
|
|||
|
||||
// String attempts to get string from the element value.
|
||||
// It is assumed to be use in interops and panics if string is not a valid UTF-8 byte sequence.
|
||||
func (e *Element) String() string {
|
||||
func (e Element) String() string {
|
||||
s, err := stackitem.ToString(e.value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -126,7 +92,7 @@ func (e *Element) String() string {
|
|||
// Array attempts to get the underlying value of the element as an array of
|
||||
// other items. Will panic if the item type is different which will be caught
|
||||
// by the VM.
|
||||
func (e *Element) Array() []stackitem.Item {
|
||||
func (e Element) Array() []stackitem.Item {
|
||||
switch t := e.value.(type) {
|
||||
case *stackitem.Array:
|
||||
return t.Value().([]stackitem.Item)
|
||||
|
@ -139,7 +105,7 @@ func (e *Element) Array() []stackitem.Item {
|
|||
|
||||
// Interop attempts to get the underlying value of the element
|
||||
// as an interop item.
|
||||
func (e *Element) Interop() *stackitem.Interop {
|
||||
func (e Element) Interop() *stackitem.Interop {
|
||||
switch t := e.value.(type) {
|
||||
case *stackitem.Interop:
|
||||
return t
|
||||
|
@ -148,12 +114,11 @@ func (e *Element) Interop() *stackitem.Interop {
|
|||
}
|
||||
}
|
||||
|
||||
// Stack represents a Stack backed by a double linked list.
|
||||
// Stack represents a Stack backed by a slice of Elements.
|
||||
type Stack struct {
|
||||
top Element
|
||||
name string
|
||||
len int
|
||||
refs *refCounter
|
||||
elems []Element
|
||||
name string
|
||||
refs *refCounter
|
||||
}
|
||||
|
||||
// NewStack returns a new stack name by the given name.
|
||||
|
@ -163,64 +128,43 @@ func NewStack(n string) *Stack {
|
|||
|
||||
func newStack(n string, refc *refCounter) *Stack {
|
||||
s := new(Stack)
|
||||
s.elems = make([]Element, 0, 16) // Most of uses are expected to fit into 16 elements.
|
||||
initStack(s, n, refc)
|
||||
return s
|
||||
}
|
||||
func initStack(s *Stack, n string, refc *refCounter) {
|
||||
s.name = n
|
||||
s.refs = refc
|
||||
s.top.next = &s.top
|
||||
s.top.prev = &s.top
|
||||
s.len = 0
|
||||
s.Clear()
|
||||
}
|
||||
|
||||
// Clear clears all elements on the stack and set its length to 0.
|
||||
func (s *Stack) Clear() {
|
||||
s.top.next = &s.top
|
||||
s.top.prev = &s.top
|
||||
s.len = 0
|
||||
if s.elems != nil {
|
||||
s.elems = s.elems[:0]
|
||||
}
|
||||
}
|
||||
|
||||
// Len returns the number of elements that are on the stack.
|
||||
func (s *Stack) Len() int {
|
||||
return s.len
|
||||
}
|
||||
|
||||
// insert inserts the element after element (at) on the stack.
|
||||
func (s *Stack) insert(e, at *Element) *Element {
|
||||
// If we insert an element that is already popped from this stack,
|
||||
// we need to clean it up, there are still pointers referencing to it.
|
||||
if e.stack == s {
|
||||
e = NewElement(e.value)
|
||||
}
|
||||
|
||||
n := at.next
|
||||
at.next = e
|
||||
e.prev = at
|
||||
e.next = n
|
||||
n.prev = e
|
||||
e.stack = s
|
||||
s.len++
|
||||
|
||||
s.refs.Add(e.value)
|
||||
|
||||
return e
|
||||
return len(s.elems)
|
||||
}
|
||||
|
||||
// InsertAt inserts the given item (n) deep on the stack.
|
||||
// Be very careful using it and _always_ check both e and n before invocation
|
||||
// as it will silently do wrong things otherwise.
|
||||
func (s *Stack) InsertAt(e *Element, n int) *Element {
|
||||
before := s.Peek(n - 1)
|
||||
if before == nil {
|
||||
return nil
|
||||
}
|
||||
return s.insert(e, before)
|
||||
// Be very careful using it and _always_ check n before invocation
|
||||
// as it will panic otherwise.
|
||||
func (s *Stack) InsertAt(e Element, n int) {
|
||||
l := len(s.elems)
|
||||
s.elems = append(s.elems, e)
|
||||
copy(s.elems[l-n+1:], s.elems[l-n:l])
|
||||
s.elems[l-n] = e
|
||||
s.refs.Add(e.value)
|
||||
}
|
||||
|
||||
// Push pushes the given element on the stack.
|
||||
func (s *Stack) Push(e *Element) {
|
||||
s.insert(e, &s.top)
|
||||
func (s *Stack) Push(e Element) {
|
||||
s.elems = append(s.elems, e)
|
||||
s.refs.Add(e.value)
|
||||
}
|
||||
|
||||
// PushVal pushes the given value on the stack. It will infer the
|
||||
|
@ -229,63 +173,49 @@ func (s *Stack) PushVal(v interface{}) {
|
|||
s.Push(NewElement(v))
|
||||
}
|
||||
|
||||
// Pop removes and returns the element on top of the stack.
|
||||
func (s *Stack) Pop() *Element {
|
||||
return s.Remove(s.Top())
|
||||
// Pop removes and returns the element on top of the stack. Panics if stack is
|
||||
// empty.
|
||||
func (s *Stack) Pop() Element {
|
||||
l := len(s.elems)
|
||||
e := s.elems[l-1]
|
||||
s.elems = s.elems[:l-1]
|
||||
s.refs.Remove(e.value)
|
||||
return e
|
||||
}
|
||||
|
||||
// Top returns the element on top of the stack. Nil if the stack
|
||||
// is empty.
|
||||
func (s *Stack) Top() *Element {
|
||||
if s.len == 0 {
|
||||
return nil
|
||||
func (s *Stack) Top() Element {
|
||||
if len(s.elems) == 0 {
|
||||
return Element{}
|
||||
}
|
||||
return s.top.next
|
||||
return s.elems[len(s.elems)-1]
|
||||
}
|
||||
|
||||
// Back returns the element at the end of the stack. Nil if the stack
|
||||
// is empty.
|
||||
func (s *Stack) Back() *Element {
|
||||
if s.len == 0 {
|
||||
return nil
|
||||
func (s *Stack) Back() Element {
|
||||
if len(s.elems) == 0 {
|
||||
return Element{}
|
||||
}
|
||||
return s.top.prev
|
||||
return s.elems[0]
|
||||
}
|
||||
|
||||
// Peek returns the element (n) far in the stack beginning from
|
||||
// the top of the stack.
|
||||
// n = 0 => will return the element on top of the stack.
|
||||
func (s *Stack) Peek(n int) *Element {
|
||||
i := 0
|
||||
for e := s.Top(); e != nil; e = e.Next() {
|
||||
if n == i {
|
||||
return e
|
||||
}
|
||||
i++
|
||||
}
|
||||
return nil
|
||||
// the top of the stack. For n == 0 it's effectively the same as Top,
|
||||
// but it'll panic if the stack is empty.
|
||||
func (s *Stack) Peek(n int) Element {
|
||||
n = len(s.elems) - n - 1
|
||||
return s.elems[n]
|
||||
}
|
||||
|
||||
// RemoveAt removes the element (n) deep on the stack beginning
|
||||
// from the top of the stack.
|
||||
func (s *Stack) RemoveAt(n int) *Element {
|
||||
return s.Remove(s.Peek(n))
|
||||
}
|
||||
|
||||
// Remove removes and returns the given element from the stack.
|
||||
func (s *Stack) Remove(e *Element) *Element {
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
e.next = nil // avoid memory leaks.
|
||||
e.prev = nil // avoid memory leaks.
|
||||
e.stack = nil
|
||||
s.len--
|
||||
|
||||
// from the top of the stack. Panics if called with out of bounds n.
|
||||
func (s *Stack) RemoveAt(n int) Element {
|
||||
l := len(s.elems)
|
||||
e := s.elems[l-1-n]
|
||||
s.elems = append(s.elems[:l-1-n], s.elems[l-n:]...)
|
||||
s.refs.Remove(e.value)
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
|
@ -293,15 +223,9 @@ func (s *Stack) Remove(e *Element) *Element {
|
|||
// Dup is used for copying elements on to the top of its own stack.
|
||||
// s.Push(s.Peek(0)) // will result in unexpected behaviour.
|
||||
// s.Push(s.Dup(0)) // is the correct approach.
|
||||
func (s *Stack) Dup(n int) *Element {
|
||||
func (s *Stack) Dup(n int) Element {
|
||||
e := s.Peek(n)
|
||||
if e == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Element{
|
||||
value: e.value.Dup(),
|
||||
}
|
||||
return Element{e.value.Dup()}
|
||||
}
|
||||
|
||||
// Iter iterates over all the elements int the stack, starting from the top
|
||||
|
@ -309,9 +233,9 @@ func (s *Stack) Dup(n int) *Element {
|
|||
// s.Iter(func(elem *Element) {
|
||||
// // do something with the element.
|
||||
// })
|
||||
func (s *Stack) Iter(f func(*Element)) {
|
||||
for e := s.Top(); e != nil; e = e.Next() {
|
||||
f(e)
|
||||
func (s *Stack) Iter(f func(Element)) {
|
||||
for i := len(s.elems) - 1; i >= 0; i-- {
|
||||
f(s.elems[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,9 +244,9 @@ func (s *Stack) Iter(f func(*Element)) {
|
|||
// s.IterBack(func(elem *Element) {
|
||||
// // do something with the element.
|
||||
// })
|
||||
func (s *Stack) IterBack(f func(*Element)) {
|
||||
for e := s.Back(); e != nil; e = e.Prev() {
|
||||
f(e)
|
||||
func (s *Stack) IterBack(f func(Element)) {
|
||||
for i := 0; i < len(s.elems); i++ {
|
||||
f(s.elems[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -331,37 +255,27 @@ func (s *Stack) Swap(n1, n2 int) error {
|
|||
if n1 < 0 || n2 < 0 {
|
||||
return errors.New("negative index")
|
||||
}
|
||||
if n1 >= s.len || n2 >= s.len {
|
||||
l := len(s.elems)
|
||||
if n1 >= l || n2 >= l {
|
||||
return errors.New("too big index")
|
||||
}
|
||||
if n1 == n2 {
|
||||
return nil
|
||||
}
|
||||
s.swap(n1, n2)
|
||||
s.elems[l-n1-1], s.elems[l-n2-1] = s.elems[l-n2-1], s.elems[l-n1-1]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stack) swap(n1, n2 int) {
|
||||
a := s.Peek(n1)
|
||||
b := s.Peek(n2)
|
||||
a.value, b.value = b.value, a.value
|
||||
}
|
||||
|
||||
// ReverseTop reverses top n items of the stack.
|
||||
func (s *Stack) ReverseTop(n int) error {
|
||||
l := len(s.elems)
|
||||
if n < 0 {
|
||||
return errors.New("negative index")
|
||||
} else if n > s.len {
|
||||
} else if n > l {
|
||||
return errors.New("too big index")
|
||||
} else if n <= 1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
a, b := s.Peek(0), s.Peek(n-1)
|
||||
for i := 0; i < n/2; i++ {
|
||||
a.value, b.value = b.value, a.value
|
||||
a = a.Next()
|
||||
b = b.Prev()
|
||||
for i, j := l-n, l-1; i <= j; i, j = i+1, j-1 {
|
||||
s.elems[i], s.elems[j] = s.elems[j], s.elems[i]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -373,24 +287,16 @@ func (s *Stack) Roll(n int) error {
|
|||
if n < 0 {
|
||||
return errors.New("negative index")
|
||||
}
|
||||
if n >= s.len {
|
||||
l := len(s.elems)
|
||||
if n >= l {
|
||||
return errors.New("too big index")
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
top := s.Peek(0)
|
||||
e := s.Peek(n)
|
||||
|
||||
e.prev.next = e.next
|
||||
e.next.prev = e.prev
|
||||
|
||||
top.prev = e
|
||||
e.next = top
|
||||
|
||||
e.prev = &s.top
|
||||
s.top.next = e
|
||||
|
||||
e := s.elems[l-1-n]
|
||||
copy(s.elems[l-1-n:], s.elems[l-n:])
|
||||
s.elems[l-1] = e
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -399,10 +305,10 @@ func (s *Stack) Roll(n int) error {
|
|||
func (s *Stack) PopSigElements() ([][]byte, error) {
|
||||
var num int
|
||||
var elems [][]byte
|
||||
item := s.Pop()
|
||||
if item == nil {
|
||||
if s.Len() == 0 {
|
||||
return nil, fmt.Errorf("nothing on the stack")
|
||||
}
|
||||
item := s.Pop()
|
||||
switch item.value.(type) {
|
||||
case *stackitem.Array:
|
||||
num = len(item.Array())
|
||||
|
@ -432,8 +338,8 @@ func (s *Stack) PopSigElements() ([][]byte, error) {
|
|||
|
||||
// ToArray converts stack to an array of stackitems with top item being the last.
|
||||
func (s *Stack) ToArray() []stackitem.Item {
|
||||
items := make([]stackitem.Item, 0, s.len)
|
||||
s.IterBack(func(e *Element) {
|
||||
items := make([]stackitem.Item, 0, len(s.elems))
|
||||
s.IterBack(func(e Element) {
|
||||
items = append(items, e.Item())
|
||||
})
|
||||
return items
|
||||
|
|
|
@ -76,9 +76,6 @@ func TestRemoveAt(t *testing.T) {
|
|||
|
||||
elem := s.RemoveAt(8)
|
||||
assert.Equal(t, elems[1], elem)
|
||||
assert.Nil(t, elem.prev)
|
||||
assert.Nil(t, elem.next)
|
||||
assert.Nil(t, elem.stack)
|
||||
|
||||
// Test if the pointers are moved.
|
||||
assert.Equal(t, elems[0], s.Peek(8))
|
||||
|
@ -147,8 +144,6 @@ func TestRemoveLastElement(t *testing.T) {
|
|||
}
|
||||
elem := s.RemoveAt(1)
|
||||
assert.Equal(t, elems[0], elem)
|
||||
assert.Nil(t, elem.prev)
|
||||
assert.Nil(t, elem.next)
|
||||
assert.Equal(t, 1, s.Len())
|
||||
}
|
||||
|
||||
|
@ -163,7 +158,7 @@ func TestIterAfterRemove(t *testing.T) {
|
|||
s.RemoveAt(0)
|
||||
|
||||
i := 0
|
||||
s.Iter(func(elem *Element) {
|
||||
s.Iter(func(_ Element) {
|
||||
i++
|
||||
})
|
||||
assert.Equal(t, len(elems)-1, i)
|
||||
|
@ -180,15 +175,16 @@ func TestIteration(t *testing.T) {
|
|||
}
|
||||
assert.Equal(t, len(elems), s.Len())
|
||||
|
||||
iteratedElems := make([]*Element, 0)
|
||||
iteratedElems := make([]Element, 0)
|
||||
|
||||
s.Iter(func(elem *Element) {
|
||||
s.Iter(func(elem Element) {
|
||||
iteratedElems = append(iteratedElems, elem)
|
||||
})
|
||||
|
||||
// Top to bottom order of iteration.
|
||||
poppedElems := make([]*Element, 0)
|
||||
for elem := s.Pop(); elem != nil; elem = s.Pop() {
|
||||
poppedElems = append(poppedElems, elem)
|
||||
poppedElems := make([]Element, 0)
|
||||
for s.Len() != 0 {
|
||||
poppedElems = append(poppedElems, s.Pop())
|
||||
}
|
||||
assert.Equal(t, poppedElems, iteratedElems)
|
||||
}
|
||||
|
@ -204,9 +200,9 @@ func TestBackIteration(t *testing.T) {
|
|||
}
|
||||
assert.Equal(t, len(elems), s.Len())
|
||||
|
||||
iteratedElems := make([]*Element, 0)
|
||||
iteratedElems := make([]Element, 0)
|
||||
|
||||
s.IterBack(func(elem *Element) {
|
||||
s.IterBack(func(elem Element) {
|
||||
iteratedElems = append(iteratedElems, elem)
|
||||
})
|
||||
// Bottom to the top order of iteration.
|
||||
|
@ -331,6 +327,25 @@ func TestRoll(t *testing.T) {
|
|||
assert.Equal(t, int64(1), s.Pop().BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestInsertAt(t *testing.T) {
|
||||
s := NewStack("stack")
|
||||
s.PushVal(1)
|
||||
s.PushVal(2)
|
||||
s.PushVal(3)
|
||||
s.PushVal(4)
|
||||
s.PushVal(5)
|
||||
|
||||
e := s.Dup(1) // it's `4`
|
||||
s.InsertAt(e, 3)
|
||||
|
||||
assert.Equal(t, int64(5), s.Peek(0).BigInt().Int64())
|
||||
assert.Equal(t, int64(4), s.Peek(1).BigInt().Int64())
|
||||
assert.Equal(t, int64(3), s.Peek(2).BigInt().Int64())
|
||||
assert.Equal(t, int64(4), s.Peek(3).BigInt().Int64())
|
||||
assert.Equal(t, int64(2), s.Peek(4).BigInt().Int64())
|
||||
assert.Equal(t, int64(1), s.Peek(5).BigInt().Int64())
|
||||
}
|
||||
|
||||
func TestPopSigElements(t *testing.T) {
|
||||
s := NewStack("test")
|
||||
|
||||
|
@ -369,8 +384,8 @@ func TestPopSigElements(t *testing.T) {
|
|||
assert.Equal(t, z, [][]byte{b1, b2})
|
||||
}
|
||||
|
||||
func makeElements(n int) []*Element {
|
||||
elems := make([]*Element, n)
|
||||
func makeElements(n int) []Element {
|
||||
elems := make([]Element, n)
|
||||
for i := 0; i < n; i++ {
|
||||
elems[i] = NewElement(i)
|
||||
}
|
||||
|
|
101
pkg/vm/vm.go
101
pkg/vm/vm.go
|
@ -328,11 +328,10 @@ func (v *VM) Context() *Context {
|
|||
// PopResult is used to pop the first item of the evaluation stack. This allows
|
||||
// us to test compiler and vm in a bi-directional way.
|
||||
func (v *VM) PopResult() interface{} {
|
||||
e := v.estack.Pop()
|
||||
if e != nil {
|
||||
return e.Value()
|
||||
if v.estack.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
return v.estack.Pop().Value()
|
||||
}
|
||||
|
||||
// Stack returns json formatted representation of the given stack.
|
||||
|
@ -448,8 +447,8 @@ func (v *VM) StepOut() error {
|
|||
v.state = NoneState
|
||||
}
|
||||
|
||||
expSize := v.istack.len
|
||||
for v.state == NoneState && v.istack.len >= expSize {
|
||||
expSize := v.istack.Len()
|
||||
for v.state == NoneState && v.istack.Len() >= expSize {
|
||||
err = v.StepInto()
|
||||
}
|
||||
if v.state == NoneState {
|
||||
|
@ -470,10 +469,10 @@ func (v *VM) StepOver() error {
|
|||
v.state = NoneState
|
||||
}
|
||||
|
||||
expSize := v.istack.len
|
||||
expSize := v.istack.Len()
|
||||
for {
|
||||
err = v.StepInto()
|
||||
if !(v.state == NoneState && v.istack.len > expSize) {
|
||||
if !(v.state == NoneState && v.istack.Len() > expSize) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -739,20 +738,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
v.estack.Pop()
|
||||
|
||||
case opcode.NIP:
|
||||
elem := v.estack.RemoveAt(1)
|
||||
if elem == nil {
|
||||
if v.estack.Len() < 2 {
|
||||
panic("no second element found")
|
||||
}
|
||||
_ = v.estack.RemoveAt(1)
|
||||
|
||||
case opcode.XDROP:
|
||||
n := int(v.estack.Pop().BigInt().Int64())
|
||||
if n < 0 {
|
||||
panic("invalid length")
|
||||
}
|
||||
e := v.estack.RemoveAt(n)
|
||||
if e == nil {
|
||||
if v.estack.Len() < n+1 {
|
||||
panic("bad index")
|
||||
}
|
||||
_ = v.estack.RemoveAt(n)
|
||||
|
||||
case opcode.CLEAR:
|
||||
v.estack.Clear()
|
||||
|
@ -761,10 +760,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
v.estack.Push(v.estack.Dup(0))
|
||||
|
||||
case opcode.OVER:
|
||||
a := v.estack.Dup(1)
|
||||
if a == nil {
|
||||
if v.estack.Len() < 2 {
|
||||
panic("no second element found")
|
||||
}
|
||||
a := v.estack.Dup(1)
|
||||
v.estack.Push(a)
|
||||
|
||||
case opcode.PICK:
|
||||
|
@ -772,20 +771,17 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
if n < 0 {
|
||||
panic("negative stack item returned")
|
||||
}
|
||||
a := v.estack.Dup(n)
|
||||
if a == nil {
|
||||
if v.estack.Len() < n+1 {
|
||||
panic("no nth element found")
|
||||
}
|
||||
a := v.estack.Dup(n)
|
||||
v.estack.Push(a)
|
||||
|
||||
case opcode.TUCK:
|
||||
a := v.estack.Dup(0)
|
||||
if a == nil {
|
||||
panic("no top-level element found")
|
||||
}
|
||||
if v.estack.Len() < 2 {
|
||||
panic("can't TUCK with a one-element stack")
|
||||
panic("too short stack to TUCK")
|
||||
}
|
||||
a := v.estack.Dup(0)
|
||||
v.estack.InsertAt(a, 2)
|
||||
|
||||
case opcode.SWAP:
|
||||
|
@ -821,10 +817,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
|
||||
// Bit operations.
|
||||
case opcode.INVERT:
|
||||
// inplace
|
||||
e := v.estack.Peek(0)
|
||||
i := e.BigInt()
|
||||
e.value = stackitem.Make(new(big.Int).Not(i))
|
||||
i := v.estack.Pop().BigInt()
|
||||
v.estack.PushVal(new(big.Int).Not(i))
|
||||
|
||||
case opcode.AND:
|
||||
b := v.estack.Pop().BigInt()
|
||||
|
@ -842,14 +836,11 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
v.estack.PushVal(new(big.Int).Xor(b, a))
|
||||
|
||||
case opcode.EQUAL, opcode.NOTEQUAL:
|
||||
if v.estack.Len() < 2 {
|
||||
panic("need a pair of elements on the stack")
|
||||
}
|
||||
b := v.estack.Pop()
|
||||
if b == nil {
|
||||
panic("no top-level element found")
|
||||
}
|
||||
a := v.estack.Pop()
|
||||
if a == nil {
|
||||
panic("no second-to-the-top element found")
|
||||
}
|
||||
v.estack.PushVal(a.value.Equals(b.value) == (op == opcode.EQUAL))
|
||||
|
||||
// Numeric operations.
|
||||
|
@ -1100,7 +1091,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
if index < 0 {
|
||||
panic("invalid key")
|
||||
}
|
||||
v.estack.Push(&Element{value: t.Value().([]stackitem.MapElement)[index].Value.Dup()})
|
||||
v.estack.Push(Element{value: t.Value().([]stackitem.MapElement)[index].Value.Dup()})
|
||||
default:
|
||||
arr := obj.Bytes()
|
||||
if index < 0 || index >= len(arr) {
|
||||
|
@ -1318,13 +1309,13 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
}
|
||||
|
||||
case opcode.NEWMAP:
|
||||
v.estack.Push(&Element{value: stackitem.NewMap()})
|
||||
v.estack.Push(Element{value: stackitem.NewMap()})
|
||||
|
||||
case opcode.KEYS:
|
||||
item := v.estack.Pop()
|
||||
if item == nil {
|
||||
if v.estack.Len() == 0 {
|
||||
panic("no argument")
|
||||
}
|
||||
item := v.estack.Pop()
|
||||
|
||||
m, ok := item.value.(*stackitem.Map)
|
||||
if !ok {
|
||||
|
@ -1338,10 +1329,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
v.estack.PushVal(arr)
|
||||
|
||||
case opcode.VALUES:
|
||||
item := v.estack.Pop()
|
||||
if item == nil {
|
||||
if v.estack.Len() == 0 {
|
||||
panic("no argument")
|
||||
}
|
||||
item := v.estack.Pop()
|
||||
|
||||
var arr []stackitem.Item
|
||||
switch t := item.value.(type) {
|
||||
|
@ -1363,13 +1354,13 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
|
|||
v.estack.PushVal(arr)
|
||||
|
||||
case opcode.HASKEY:
|
||||
if v.estack.Len() < 2 {
|
||||
panic("not enough arguments")
|
||||
}
|
||||
key := v.estack.Pop()
|
||||
validateMapKey(key)
|
||||
|
||||
c := v.estack.Pop()
|
||||
if c == nil {
|
||||
panic("no value found")
|
||||
}
|
||||
switch t := c.value.(type) {
|
||||
case *stackitem.Array, *stackitem.Struct:
|
||||
index := key.BigInt().Int64()
|
||||
|
@ -1559,16 +1550,15 @@ func calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
|
|||
}
|
||||
|
||||
func (v *VM) handleException() {
|
||||
pop := 0
|
||||
ictxv := v.istack.Peek(0)
|
||||
ictx := ictxv.Value().(*Context)
|
||||
for ictx != nil {
|
||||
e := ictx.tryStack.Peek(0)
|
||||
for e != nil {
|
||||
for pop := 0; pop < v.istack.Len(); pop++ {
|
||||
ictxv := v.istack.Peek(pop)
|
||||
ictx := ictxv.Value().(*Context)
|
||||
for j := 0; j < ictx.tryStack.Len(); j++ {
|
||||
e := ictx.tryStack.Peek(j)
|
||||
ectx := e.Value().(*exceptionHandlingContext)
|
||||
if ectx.State == eFinally || (ectx.State == eCatch && !ectx.HasFinally()) {
|
||||
ictx.tryStack.Pop()
|
||||
e = ictx.tryStack.Peek(0)
|
||||
j = -1
|
||||
continue
|
||||
}
|
||||
for i := 0; i < pop; i++ {
|
||||
|
@ -1586,12 +1576,6 @@ func (v *VM) handleException() {
|
|||
}
|
||||
return
|
||||
}
|
||||
pop++
|
||||
ictxv = ictxv.Next()
|
||||
if ictxv == nil {
|
||||
break
|
||||
}
|
||||
ictx = ictxv.Value().(*Context)
|
||||
}
|
||||
throwUnhandledException(v.uncaughtException)
|
||||
}
|
||||
|
@ -1753,17 +1737,18 @@ func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
|
|||
return items
|
||||
}
|
||||
|
||||
func validateMapKey(key *Element) {
|
||||
if key == nil {
|
||||
func validateMapKey(key Element) {
|
||||
item := key.Item()
|
||||
if item == nil {
|
||||
panic("no key found")
|
||||
}
|
||||
if err := stackitem.IsValidMapKey(key.Item()); err != nil {
|
||||
if err := stackitem.IsValidMapKey(item); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (v *VM) checkInvocationStackSize() {
|
||||
if v.istack.len >= MaxInvocationStackSize {
|
||||
if v.istack.Len() >= MaxInvocationStackSize {
|
||||
panic("invocation stack is too big")
|
||||
}
|
||||
}
|
||||
|
@ -1785,7 +1770,7 @@ func (v *VM) GetCallingScriptHash() util.Uint160 {
|
|||
|
||||
// GetEntryScriptHash implements ScriptHashGetter interface.
|
||||
func (v *VM) GetEntryScriptHash() util.Uint160 {
|
||||
return v.getContextScriptHash(v.istack.len - 1)
|
||||
return v.getContextScriptHash(v.istack.Len() - 1)
|
||||
}
|
||||
|
||||
// GetCurrentScriptHash implements ScriptHashGetter interface.
|
||||
|
|
|
@ -1176,7 +1176,7 @@ func TestPICKITEMDupMap(t *testing.T) {
|
|||
vm := load(prog)
|
||||
m := stackitem.NewMap()
|
||||
m.Add(stackitem.Make(42), stackitem.Make(-1))
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
runVM(t, vm)
|
||||
assert.Equal(t, 2, vm.estack.Len())
|
||||
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64())
|
||||
|
@ -1245,7 +1245,7 @@ func TestSETITEMBigMapGood(t *testing.T) {
|
|||
for i := 0; i < MaxStackSize-3; i++ {
|
||||
m.Add(stackitem.Make(i), stackitem.Make(i))
|
||||
}
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
vm.estack.PushVal(0)
|
||||
vm.estack.PushVal(0)
|
||||
|
||||
|
@ -1274,7 +1274,7 @@ func TestKEYSMap(t *testing.T) {
|
|||
m := stackitem.NewMap()
|
||||
m.Add(stackitem.Make(5), stackitem.Make(6))
|
||||
m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make(6))
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
|
||||
runVM(t, vm)
|
||||
assert.Equal(t, 1, vm.estack.Len())
|
||||
|
@ -1298,7 +1298,7 @@ func TestVALUESMap(t *testing.T) {
|
|||
m := stackitem.NewMap()
|
||||
m.Add(stackitem.Make(5), stackitem.Make([]byte{2, 3}))
|
||||
m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]stackitem.Item{}))
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
|
||||
runVM(t, vm)
|
||||
assert.Equal(t, 1, vm.estack.Len())
|
||||
|
@ -1880,7 +1880,7 @@ func TestREVERSEITEMSGoodStruct(t *testing.T) {
|
|||
for i := range elements {
|
||||
arr[i] = stackitem.Make(elements[i])
|
||||
}
|
||||
vm.estack.Push(&Element{value: stackitem.NewStruct(arr)})
|
||||
vm.estack.Push(Element{value: stackitem.NewStruct(arr)})
|
||||
|
||||
runVM(t, vm)
|
||||
assert.Equal(t, 2, vm.estack.Len())
|
||||
|
@ -1944,8 +1944,8 @@ func TestREMOVEMap(t *testing.T) {
|
|||
m := stackitem.NewMap()
|
||||
m.Add(stackitem.Make(5), stackitem.Make(3))
|
||||
m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]byte{2, 3}))
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(&Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
vm.estack.Push(Element{value: m})
|
||||
vm.estack.PushVal(stackitem.Make(5))
|
||||
|
||||
runVM(t, vm)
|
||||
|
|
Loading…
Reference in a new issue