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:
Roman Khimov 2021-08-21 19:09:44 +03:00
parent ed35cf8f12
commit 930653418d
8 changed files with 228 additions and 270 deletions

View file

@ -1917,8 +1917,9 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa
if vm.HasFailed() { if vm.HasFailed() {
return 0, fmt.Errorf("%w: vm execution has failed: %v", ErrVerificationFailed, err) return 0, fmt.Errorf("%w: vm execution has failed: %v", ErrVerificationFailed, err)
} }
resEl := vm.Estack().Pop() estack := vm.Estack()
if resEl != nil { if estack.Len() > 0 {
resEl := estack.Pop()
res, err := resEl.Item().TryBool() res, err := resEl.Item().TryBool()
if err != nil { if err != nil {
return 0, fmt.Errorf("%w: invalid return value", ErrVerificationFailed) return 0, fmt.Errorf("%w: invalid return value", ErrVerificationFailed)

50
pkg/vm/bench_test.go Normal file
View 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)
})
}
}

View file

@ -282,10 +282,11 @@ func (c *Context) IsDeployed() bool {
// getContextScriptHash returns script hash of the invocation stack element // getContextScriptHash returns script hash of the invocation stack element
// number n. // number n.
func (v *VM) getContextScriptHash(n int) util.Uint160 { func (v *VM) getContextScriptHash(n int) util.Uint160 {
element := v.Istack().Peek(n) istack := v.Istack()
if element == nil { if istack.Len() <= n {
return util.Uint160{} return util.Uint160{}
} }
element := istack.Peek(n)
ctxIface := element.Value() ctxIface := element.Value()
ctx := ctxIface.(*Context) ctx := ctxIface.(*Context)
return ctx.ScriptHash() return ctx.ScriptHash()

View file

@ -20,7 +20,7 @@ func TestVM_Debug(t *testing.T) {
require.NoError(t, v.Run()) require.NoError(t, v.Run())
require.Equal(t, 5, v.Context().NextIP()) require.Equal(t, 5, v.Context().NextIP())
require.NoError(t, v.Run()) 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()) require.Equal(t, big.NewInt(5), v.estack.Top().Value())
}) })
t.Run("StepInto", func(t *testing.T) { 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.Equal(t, 3, v.Context().NextIP())
require.NoError(t, v.StepOut()) require.NoError(t, v.StepOut())
require.Equal(t, 2, v.Context().NextIP()) 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()) require.Equal(t, big.NewInt(5), v.estack.Top().Value())
}) })
t.Run("StepOver", func(t *testing.T) { t.Run("StepOver", func(t *testing.T) {
v := load(prog) v := load(prog)
require.NoError(t, v.StepOver()) require.NoError(t, v.StepOver())
require.Equal(t, 2, v.Context().NextIP()) 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()) require.Equal(t, big.NewInt(5), v.estack.Top().Value())
}) })
} }

View file

@ -9,70 +9,36 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Stack implementation for the neo-go virtual machine. The stack implements // Stack implementation for the neo-go virtual machine. The stack with its LIFO
// a double linked list where its semantics are first in first out. // semantics is emulated from simple slice where the top of the stack corresponds
// To simplify the implementation, internally a Stack s is implemented as a // to the latest element of this slice. Pushes are appends to this slice, pops are
// ring, such that &s.top is both the next element of the last element s.Back() // slice resizes.
// 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 ]
// Element represents an element in the double linked list (the stack), // Element represents an element on the stack, technically it's a wrapper around
// which will hold the underlying stackitem.Item. // stackitem.Item interface to provide some API simplification for VM.
type Element struct { type Element struct {
value stackitem.Item value stackitem.Item
next, prev *Element
stack *Stack
} }
// NewElement returns a new Element object, with its underlying value inferred // NewElement returns a new Element object, with its underlying value inferred
// to the corresponding type. // to the corresponding type.
func NewElement(v interface{}) *Element { func NewElement(v interface{}) Element {
return &Element{ return Element{stackitem.Make(v)}
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
} }
// Item returns Item contained in the element. // Item returns Item contained in the element.
func (e *Element) Item() stackitem.Item { func (e Element) Item() stackitem.Item {
return e.value return e.value
} }
// Value returns value of the Item contained in the element. // Value returns value of the Item contained in the element.
func (e *Element) Value() interface{} { func (e Element) Value() interface{} {
return e.value.Value() return e.value.Value()
} }
// BigInt attempts to get the underlying value of the element as a big integer. // 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. // 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() val, err := e.value.TryInteger()
if err != nil { if err != nil {
panic(err) 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 // Bool converts an underlying value of the element to a boolean if it's
// possible to do so, it will panic otherwise. // possible to do so, it will panic otherwise.
func (e *Element) Bool() bool { func (e Element) Bool() bool {
b, err := e.value.TryBool() b, err := e.value.TryBool()
if err != nil { if err != nil {
panic(err) 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. // 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. // 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() bs, err := e.value.TryBytes()
if err != nil { if err != nil {
panic(err) 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. // 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. // 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 { if _, ok := e.value.(stackitem.Null); ok {
return nil return nil
} }
@ -115,7 +81,7 @@ func (e *Element) BytesOrNil() []byte {
// String attempts to get string from the element value. // 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. // 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) s, err := stackitem.ToString(e.value)
if err != nil { if err != nil {
panic(err) 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 // 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 // other items. Will panic if the item type is different which will be caught
// by the VM. // by the VM.
func (e *Element) Array() []stackitem.Item { func (e Element) Array() []stackitem.Item {
switch t := e.value.(type) { switch t := e.value.(type) {
case *stackitem.Array: case *stackitem.Array:
return t.Value().([]stackitem.Item) 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 // Interop attempts to get the underlying value of the element
// as an interop item. // as an interop item.
func (e *Element) Interop() *stackitem.Interop { func (e Element) Interop() *stackitem.Interop {
switch t := e.value.(type) { switch t := e.value.(type) {
case *stackitem.Interop: case *stackitem.Interop:
return t return t
@ -148,11 +114,10 @@ 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 { type Stack struct {
top Element elems []Element
name string name string
len int
refs *refCounter refs *refCounter
} }
@ -163,64 +128,43 @@ func NewStack(n string) *Stack {
func newStack(n string, refc *refCounter) *Stack { func newStack(n string, refc *refCounter) *Stack {
s := new(Stack) s := new(Stack)
s.elems = make([]Element, 0, 16) // Most of uses are expected to fit into 16 elements.
initStack(s, n, refc) initStack(s, n, refc)
return s return s
} }
func initStack(s *Stack, n string, refc *refCounter) { func initStack(s *Stack, n string, refc *refCounter) {
s.name = n s.name = n
s.refs = refc s.refs = refc
s.top.next = &s.top s.Clear()
s.top.prev = &s.top
s.len = 0
} }
// Clear clears all elements on the stack and set its length to 0. // Clear clears all elements on the stack and set its length to 0.
func (s *Stack) Clear() { func (s *Stack) Clear() {
s.top.next = &s.top if s.elems != nil {
s.top.prev = &s.top s.elems = s.elems[:0]
s.len = 0 }
} }
// Len returns the number of elements that are on the stack. // Len returns the number of elements that are on the stack.
func (s *Stack) Len() int { func (s *Stack) Len() int {
return s.len return len(s.elems)
}
// 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
} }
// InsertAt inserts the given item (n) deep on the stack. // InsertAt inserts the given item (n) deep on the stack.
// Be very careful using it and _always_ check both e and n before invocation // Be very careful using it and _always_ check n before invocation
// as it will silently do wrong things otherwise. // as it will panic otherwise.
func (s *Stack) InsertAt(e *Element, n int) *Element { func (s *Stack) InsertAt(e Element, n int) {
before := s.Peek(n - 1) l := len(s.elems)
if before == nil { s.elems = append(s.elems, e)
return nil copy(s.elems[l-n+1:], s.elems[l-n:l])
} s.elems[l-n] = e
return s.insert(e, before) s.refs.Add(e.value)
} }
// Push pushes the given element on the stack. // Push pushes the given element on the stack.
func (s *Stack) Push(e *Element) { func (s *Stack) Push(e Element) {
s.insert(e, &s.top) s.elems = append(s.elems, e)
s.refs.Add(e.value)
} }
// PushVal pushes the given value on the stack. It will infer the // 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)) s.Push(NewElement(v))
} }
// Pop removes and returns the element on top of the stack. // Pop removes and returns the element on top of the stack. Panics if stack is
func (s *Stack) Pop() *Element { // empty.
return s.Remove(s.Top()) 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 // Top returns the element on top of the stack. Nil if the stack
// is empty. // is empty.
func (s *Stack) Top() *Element { func (s *Stack) Top() Element {
if s.len == 0 { if len(s.elems) == 0 {
return nil 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 // Back returns the element at the end of the stack. Nil if the stack
// is empty. // is empty.
func (s *Stack) Back() *Element { func (s *Stack) Back() Element {
if s.len == 0 { if len(s.elems) == 0 {
return nil return Element{}
} }
return s.top.prev return s.elems[0]
} }
// Peek returns the element (n) far in the stack beginning from // Peek returns the element (n) far in the stack beginning from
// the top of the stack. // the top of the stack. For n == 0 it's effectively the same as Top,
// n = 0 => will return the element on top of the stack. // but it'll panic if the stack is empty.
func (s *Stack) Peek(n int) *Element { func (s *Stack) Peek(n int) Element {
i := 0 n = len(s.elems) - n - 1
for e := s.Top(); e != nil; e = e.Next() { return s.elems[n]
if n == i {
return e
}
i++
}
return nil
} }
// RemoveAt removes the element (n) deep on the stack beginning // RemoveAt removes the element (n) deep on the stack beginning
// from the top of the stack. // from the top of the stack. Panics if called with out of bounds n.
func (s *Stack) RemoveAt(n int) *Element { func (s *Stack) RemoveAt(n int) Element {
return s.Remove(s.Peek(n)) l := len(s.elems)
} e := s.elems[l-1-n]
s.elems = append(s.elems[:l-1-n], s.elems[l-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--
s.refs.Remove(e.value) s.refs.Remove(e.value)
return e 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. // 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.Peek(0)) // will result in unexpected behaviour.
// s.Push(s.Dup(0)) // is the correct approach. // 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) e := s.Peek(n)
if e == nil { return Element{e.value.Dup()}
return nil
}
return &Element{
value: e.value.Dup(),
}
} }
// Iter iterates over all the elements int the stack, starting from the top // 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) { // s.Iter(func(elem *Element) {
// // do something with the element. // // do something with the element.
// }) // })
func (s *Stack) Iter(f func(*Element)) { func (s *Stack) Iter(f func(Element)) {
for e := s.Top(); e != nil; e = e.Next() { for i := len(s.elems) - 1; i >= 0; i-- {
f(e) f(s.elems[i])
} }
} }
@ -320,9 +244,9 @@ func (s *Stack) Iter(f func(*Element)) {
// s.IterBack(func(elem *Element) { // s.IterBack(func(elem *Element) {
// // do something with the element. // // do something with the element.
// }) // })
func (s *Stack) IterBack(f func(*Element)) { func (s *Stack) IterBack(f func(Element)) {
for e := s.Back(); e != nil; e = e.Prev() { for i := 0; i < len(s.elems); i++ {
f(e) f(s.elems[i])
} }
} }
@ -331,37 +255,27 @@ func (s *Stack) Swap(n1, n2 int) error {
if n1 < 0 || n2 < 0 { if n1 < 0 || n2 < 0 {
return errors.New("negative index") 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") return errors.New("too big index")
} }
if n1 == n2 { s.elems[l-n1-1], s.elems[l-n2-1] = s.elems[l-n2-1], s.elems[l-n1-1]
return nil return nil
} }
s.swap(n1, n2)
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. // ReverseTop reverses top n items of the stack.
func (s *Stack) ReverseTop(n int) error { func (s *Stack) ReverseTop(n int) error {
l := len(s.elems)
if n < 0 { if n < 0 {
return errors.New("negative index") return errors.New("negative index")
} else if n > s.len { } else if n > l {
return errors.New("too big index") return errors.New("too big index")
} else if n <= 1 { } else if n <= 1 {
return nil return nil
} }
a, b := s.Peek(0), s.Peek(n-1) for i, j := l-n, l-1; i <= j; i, j = i+1, j-1 {
for i := 0; i < n/2; i++ { s.elems[i], s.elems[j] = s.elems[j], s.elems[i]
a.value, b.value = b.value, a.value
a = a.Next()
b = b.Prev()
} }
return nil return nil
} }
@ -373,24 +287,16 @@ func (s *Stack) Roll(n int) error {
if n < 0 { if n < 0 {
return errors.New("negative index") return errors.New("negative index")
} }
if n >= s.len { l := len(s.elems)
if n >= l {
return errors.New("too big index") return errors.New("too big index")
} }
if n == 0 { if n == 0 {
return nil return nil
} }
top := s.Peek(0) e := s.elems[l-1-n]
e := s.Peek(n) copy(s.elems[l-1-n:], s.elems[l-n:])
s.elems[l-1] = e
e.prev.next = e.next
e.next.prev = e.prev
top.prev = e
e.next = top
e.prev = &s.top
s.top.next = e
return nil return nil
} }
@ -399,10 +305,10 @@ func (s *Stack) Roll(n int) error {
func (s *Stack) PopSigElements() ([][]byte, error) { func (s *Stack) PopSigElements() ([][]byte, error) {
var num int var num int
var elems [][]byte var elems [][]byte
item := s.Pop() if s.Len() == 0 {
if item == nil {
return nil, fmt.Errorf("nothing on the stack") return nil, fmt.Errorf("nothing on the stack")
} }
item := s.Pop()
switch item.value.(type) { switch item.value.(type) {
case *stackitem.Array: case *stackitem.Array:
num = len(item.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. // ToArray converts stack to an array of stackitems with top item being the last.
func (s *Stack) ToArray() []stackitem.Item { func (s *Stack) ToArray() []stackitem.Item {
items := make([]stackitem.Item, 0, s.len) items := make([]stackitem.Item, 0, len(s.elems))
s.IterBack(func(e *Element) { s.IterBack(func(e Element) {
items = append(items, e.Item()) items = append(items, e.Item())
}) })
return items return items

View file

@ -76,9 +76,6 @@ func TestRemoveAt(t *testing.T) {
elem := s.RemoveAt(8) elem := s.RemoveAt(8)
assert.Equal(t, elems[1], elem) 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. // Test if the pointers are moved.
assert.Equal(t, elems[0], s.Peek(8)) assert.Equal(t, elems[0], s.Peek(8))
@ -147,8 +144,6 @@ func TestRemoveLastElement(t *testing.T) {
} }
elem := s.RemoveAt(1) elem := s.RemoveAt(1)
assert.Equal(t, elems[0], elem) assert.Equal(t, elems[0], elem)
assert.Nil(t, elem.prev)
assert.Nil(t, elem.next)
assert.Equal(t, 1, s.Len()) assert.Equal(t, 1, s.Len())
} }
@ -163,7 +158,7 @@ func TestIterAfterRemove(t *testing.T) {
s.RemoveAt(0) s.RemoveAt(0)
i := 0 i := 0
s.Iter(func(elem *Element) { s.Iter(func(_ Element) {
i++ i++
}) })
assert.Equal(t, len(elems)-1, i) assert.Equal(t, len(elems)-1, i)
@ -180,15 +175,16 @@ func TestIteration(t *testing.T) {
} }
assert.Equal(t, len(elems), s.Len()) 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) iteratedElems = append(iteratedElems, elem)
}) })
// Top to bottom order of iteration. // Top to bottom order of iteration.
poppedElems := make([]*Element, 0) poppedElems := make([]Element, 0)
for elem := s.Pop(); elem != nil; elem = s.Pop() { for s.Len() != 0 {
poppedElems = append(poppedElems, elem) poppedElems = append(poppedElems, s.Pop())
} }
assert.Equal(t, poppedElems, iteratedElems) assert.Equal(t, poppedElems, iteratedElems)
} }
@ -204,9 +200,9 @@ func TestBackIteration(t *testing.T) {
} }
assert.Equal(t, len(elems), s.Len()) 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) iteratedElems = append(iteratedElems, elem)
}) })
// Bottom to the top order of iteration. // 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()) 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) { func TestPopSigElements(t *testing.T) {
s := NewStack("test") s := NewStack("test")
@ -369,8 +384,8 @@ func TestPopSigElements(t *testing.T) {
assert.Equal(t, z, [][]byte{b1, b2}) assert.Equal(t, z, [][]byte{b1, b2})
} }
func makeElements(n int) []*Element { func makeElements(n int) []Element {
elems := make([]*Element, n) elems := make([]Element, n)
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
elems[i] = NewElement(i) elems[i] = NewElement(i)
} }

View file

@ -328,12 +328,11 @@ func (v *VM) Context() *Context {
// PopResult is used to pop the first item of the evaluation stack. This allows // 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. // us to test compiler and vm in a bi-directional way.
func (v *VM) PopResult() interface{} { func (v *VM) PopResult() interface{} {
e := v.estack.Pop() if v.estack.Len() == 0 {
if e != nil {
return e.Value()
}
return nil return nil
} }
return v.estack.Pop().Value()
}
// Stack returns json formatted representation of the given stack. // Stack returns json formatted representation of the given stack.
func (v *VM) Stack(n string) string { func (v *VM) Stack(n string) string {
@ -448,8 +447,8 @@ func (v *VM) StepOut() error {
v.state = NoneState v.state = NoneState
} }
expSize := v.istack.len expSize := v.istack.Len()
for v.state == NoneState && v.istack.len >= expSize { for v.state == NoneState && v.istack.Len() >= expSize {
err = v.StepInto() err = v.StepInto()
} }
if v.state == NoneState { if v.state == NoneState {
@ -470,10 +469,10 @@ func (v *VM) StepOver() error {
v.state = NoneState v.state = NoneState
} }
expSize := v.istack.len expSize := v.istack.Len()
for { for {
err = v.StepInto() err = v.StepInto()
if !(v.state == NoneState && v.istack.len > expSize) { if !(v.state == NoneState && v.istack.Len() > expSize) {
break break
} }
} }
@ -739,20 +738,20 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.Pop() v.estack.Pop()
case opcode.NIP: case opcode.NIP:
elem := v.estack.RemoveAt(1) if v.estack.Len() < 2 {
if elem == nil {
panic("no second element found") panic("no second element found")
} }
_ = v.estack.RemoveAt(1)
case opcode.XDROP: case opcode.XDROP:
n := int(v.estack.Pop().BigInt().Int64()) n := int(v.estack.Pop().BigInt().Int64())
if n < 0 { if n < 0 {
panic("invalid length") panic("invalid length")
} }
e := v.estack.RemoveAt(n) if v.estack.Len() < n+1 {
if e == nil {
panic("bad index") panic("bad index")
} }
_ = v.estack.RemoveAt(n)
case opcode.CLEAR: case opcode.CLEAR:
v.estack.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)) v.estack.Push(v.estack.Dup(0))
case opcode.OVER: case opcode.OVER:
a := v.estack.Dup(1) if v.estack.Len() < 2 {
if a == nil {
panic("no second element found") panic("no second element found")
} }
a := v.estack.Dup(1)
v.estack.Push(a) v.estack.Push(a)
case opcode.PICK: case opcode.PICK:
@ -772,20 +771,17 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if n < 0 { if n < 0 {
panic("negative stack item returned") panic("negative stack item returned")
} }
a := v.estack.Dup(n) if v.estack.Len() < n+1 {
if a == nil {
panic("no nth element found") panic("no nth element found")
} }
a := v.estack.Dup(n)
v.estack.Push(a) v.estack.Push(a)
case opcode.TUCK: case opcode.TUCK:
a := v.estack.Dup(0)
if a == nil {
panic("no top-level element found")
}
if v.estack.Len() < 2 { 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) v.estack.InsertAt(a, 2)
case opcode.SWAP: case opcode.SWAP:
@ -821,10 +817,8 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
// Bit operations. // Bit operations.
case opcode.INVERT: case opcode.INVERT:
// inplace i := v.estack.Pop().BigInt()
e := v.estack.Peek(0) v.estack.PushVal(new(big.Int).Not(i))
i := e.BigInt()
e.value = stackitem.Make(new(big.Int).Not(i))
case opcode.AND: case opcode.AND:
b := v.estack.Pop().BigInt() 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)) v.estack.PushVal(new(big.Int).Xor(b, a))
case opcode.EQUAL, opcode.NOTEQUAL: case opcode.EQUAL, opcode.NOTEQUAL:
if v.estack.Len() < 2 {
panic("need a pair of elements on the stack")
}
b := v.estack.Pop() b := v.estack.Pop()
if b == nil {
panic("no top-level element found")
}
a := v.estack.Pop() 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)) v.estack.PushVal(a.value.Equals(b.value) == (op == opcode.EQUAL))
// Numeric operations. // Numeric operations.
@ -1100,7 +1091,7 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
if index < 0 { if index < 0 {
panic("invalid key") 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: default:
arr := obj.Bytes() arr := obj.Bytes()
if index < 0 || index >= len(arr) { 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: case opcode.NEWMAP:
v.estack.Push(&Element{value: stackitem.NewMap()}) v.estack.Push(Element{value: stackitem.NewMap()})
case opcode.KEYS: case opcode.KEYS:
item := v.estack.Pop() if v.estack.Len() == 0 {
if item == nil {
panic("no argument") panic("no argument")
} }
item := v.estack.Pop()
m, ok := item.value.(*stackitem.Map) m, ok := item.value.(*stackitem.Map)
if !ok { if !ok {
@ -1338,10 +1329,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
v.estack.PushVal(arr) v.estack.PushVal(arr)
case opcode.VALUES: case opcode.VALUES:
item := v.estack.Pop() if v.estack.Len() == 0 {
if item == nil {
panic("no argument") panic("no argument")
} }
item := v.estack.Pop()
var arr []stackitem.Item var arr []stackitem.Item
switch t := item.value.(type) { 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) v.estack.PushVal(arr)
case opcode.HASKEY: case opcode.HASKEY:
if v.estack.Len() < 2 {
panic("not enough arguments")
}
key := v.estack.Pop() key := v.estack.Pop()
validateMapKey(key) validateMapKey(key)
c := v.estack.Pop() c := v.estack.Pop()
if c == nil {
panic("no value found")
}
switch t := c.value.(type) { switch t := c.value.(type) {
case *stackitem.Array, *stackitem.Struct: case *stackitem.Array, *stackitem.Struct:
index := key.BigInt().Int64() index := key.BigInt().Int64()
@ -1559,16 +1550,15 @@ func calcJumpOffset(ctx *Context, parameter []byte) (int, int, error) {
} }
func (v *VM) handleException() { func (v *VM) handleException() {
pop := 0 for pop := 0; pop < v.istack.Len(); pop++ {
ictxv := v.istack.Peek(0) ictxv := v.istack.Peek(pop)
ictx := ictxv.Value().(*Context) ictx := ictxv.Value().(*Context)
for ictx != nil { for j := 0; j < ictx.tryStack.Len(); j++ {
e := ictx.tryStack.Peek(0) e := ictx.tryStack.Peek(j)
for e != nil {
ectx := e.Value().(*exceptionHandlingContext) ectx := e.Value().(*exceptionHandlingContext)
if ectx.State == eFinally || (ectx.State == eCatch && !ectx.HasFinally()) { if ectx.State == eFinally || (ectx.State == eCatch && !ectx.HasFinally()) {
ictx.tryStack.Pop() ictx.tryStack.Pop()
e = ictx.tryStack.Peek(0) j = -1
continue continue
} }
for i := 0; i < pop; i++ { for i := 0; i < pop; i++ {
@ -1586,12 +1576,6 @@ func (v *VM) handleException() {
} }
return return
} }
pop++
ictxv = ictxv.Next()
if ictxv == nil {
break
}
ictx = ictxv.Value().(*Context)
} }
throwUnhandledException(v.uncaughtException) throwUnhandledException(v.uncaughtException)
} }
@ -1753,17 +1737,18 @@ func makeArrayOfType(n int, typ stackitem.Type) []stackitem.Item {
return items return items
} }
func validateMapKey(key *Element) { func validateMapKey(key Element) {
if key == nil { item := key.Item()
if item == nil {
panic("no key found") panic("no key found")
} }
if err := stackitem.IsValidMapKey(key.Item()); err != nil { if err := stackitem.IsValidMapKey(item); err != nil {
panic(err) panic(err)
} }
} }
func (v *VM) checkInvocationStackSize() { func (v *VM) checkInvocationStackSize() {
if v.istack.len >= MaxInvocationStackSize { if v.istack.Len() >= MaxInvocationStackSize {
panic("invocation stack is too big") panic("invocation stack is too big")
} }
} }
@ -1785,7 +1770,7 @@ func (v *VM) GetCallingScriptHash() util.Uint160 {
// GetEntryScriptHash implements ScriptHashGetter interface. // GetEntryScriptHash implements ScriptHashGetter interface.
func (v *VM) GetEntryScriptHash() util.Uint160 { func (v *VM) GetEntryScriptHash() util.Uint160 {
return v.getContextScriptHash(v.istack.len - 1) return v.getContextScriptHash(v.istack.Len() - 1)
} }
// GetCurrentScriptHash implements ScriptHashGetter interface. // GetCurrentScriptHash implements ScriptHashGetter interface.

View file

@ -1176,7 +1176,7 @@ func TestPICKITEMDupMap(t *testing.T) {
vm := load(prog) vm := load(prog)
m := stackitem.NewMap() m := stackitem.NewMap()
m.Add(stackitem.Make(42), stackitem.Make(-1)) m.Add(stackitem.Make(42), stackitem.Make(-1))
vm.estack.Push(&Element{value: m}) vm.estack.Push(Element{value: m})
runVM(t, vm) runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len()) assert.Equal(t, 2, vm.estack.Len())
assert.Equal(t, int64(1), vm.estack.Pop().BigInt().Int64()) 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++ { for i := 0; i < MaxStackSize-3; i++ {
m.Add(stackitem.Make(i), stackitem.Make(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)
vm.estack.PushVal(0) vm.estack.PushVal(0)
@ -1274,7 +1274,7 @@ func TestKEYSMap(t *testing.T) {
m := stackitem.NewMap() m := stackitem.NewMap()
m.Add(stackitem.Make(5), stackitem.Make(6)) m.Add(stackitem.Make(5), stackitem.Make(6))
m.Add(stackitem.Make([]byte{0, 1}), 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) runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len()) assert.Equal(t, 1, vm.estack.Len())
@ -1298,7 +1298,7 @@ func TestVALUESMap(t *testing.T) {
m := stackitem.NewMap() m := stackitem.NewMap()
m.Add(stackitem.Make(5), stackitem.Make([]byte{2, 3})) m.Add(stackitem.Make(5), stackitem.Make([]byte{2, 3}))
m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]stackitem.Item{})) 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) runVM(t, vm)
assert.Equal(t, 1, vm.estack.Len()) assert.Equal(t, 1, vm.estack.Len())
@ -1880,7 +1880,7 @@ func TestREVERSEITEMSGoodStruct(t *testing.T) {
for i := range elements { for i := range elements {
arr[i] = stackitem.Make(elements[i]) 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) runVM(t, vm)
assert.Equal(t, 2, vm.estack.Len()) assert.Equal(t, 2, vm.estack.Len())
@ -1944,8 +1944,8 @@ func TestREMOVEMap(t *testing.T) {
m := stackitem.NewMap() m := stackitem.NewMap()
m.Add(stackitem.Make(5), stackitem.Make(3)) m.Add(stackitem.Make(5), stackitem.Make(3))
m.Add(stackitem.Make([]byte{0, 1}), stackitem.Make([]byte{2, 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)) vm.estack.PushVal(stackitem.Make(5))
runVM(t, vm) runVM(t, vm)