2018-01-15 18:42:40 +00:00
|
|
|
import
|
2018-01-24 13:31:24 +00:00
|
|
|
strformat, strutils, sequtils, macros,
|
2018-01-15 18:42:40 +00:00
|
|
|
value, ../errors, ../validation, ../utils_numeric, ../constants, ../logging
|
|
|
|
|
|
|
|
type
|
|
|
|
|
|
|
|
Stack* = ref object of RootObj
|
|
|
|
## VM Stack
|
|
|
|
logger*: Logger
|
|
|
|
values*: seq[Value]
|
|
|
|
|
|
|
|
template ensureStackLimit: untyped =
|
|
|
|
if len(stack.values) > 1023:
|
|
|
|
raise newException(FullStack, "Stack limit reached")
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc len*(stack: Stack): int =
|
2018-01-15 18:42:40 +00:00
|
|
|
len(stack.values)
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc push*(stack: var Stack; value: Value) =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Push an item onto the stack
|
|
|
|
ensureStackLimit()
|
2018-02-06 19:20:06 +00:00
|
|
|
if value.kind == VInt:
|
|
|
|
validateGte(value.i, 0)
|
|
|
|
else:
|
|
|
|
validateStackItem(value.b)
|
2018-01-15 18:42:40 +00:00
|
|
|
|
|
|
|
stack.values.add(value)
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc push*(stack: var Stack; value: int) =
|
|
|
|
## Push an integer onto the stack
|
|
|
|
ensureStackLimit()
|
2018-02-06 19:20:06 +00:00
|
|
|
validateGte(value, 0)
|
|
|
|
|
2018-01-22 22:23:07 +00:00
|
|
|
stack.values.add(Value(kind: VInt, i: value.int256))
|
2018-01-16 17:05:20 +00:00
|
|
|
|
|
|
|
proc push*(stack: var Stack; value: Int256) =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Push an integer onto the stack
|
|
|
|
ensureStackLimit()
|
2018-02-06 19:20:06 +00:00
|
|
|
validateGte(value, 0)
|
2018-01-15 18:42:40 +00:00
|
|
|
|
|
|
|
stack.values.add(Value(kind: VInt, i: value))
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
proc push*(stack: var Stack; value: string) =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Push a binary onto the stack
|
|
|
|
ensureStackLimit()
|
2018-02-06 19:20:06 +00:00
|
|
|
validateStackItem(value)
|
2018-01-15 18:42:40 +00:00
|
|
|
|
|
|
|
stack.values.add(Value(kind: VBinary, b: value))
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc internalPop(stack: var Stack; numItems: int): seq[Value] =
|
2018-01-15 18:42:40 +00:00
|
|
|
if len(stack) < numItems:
|
|
|
|
result = @[]
|
|
|
|
else:
|
|
|
|
result = stack.values[^numItems .. ^1]
|
|
|
|
stack.values = stack.values[0 ..< ^numItems]
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
template toType(i: Int256, _: typedesc[Int256]): Int256 =
|
2018-01-15 18:42:40 +00:00
|
|
|
i
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
template toType(i: Int256, _: typedesc[string]): string =
|
2018-01-15 18:42:40 +00:00
|
|
|
intToBigEndian(i)
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
template toType(b: string, _: typedesc[Int256]): Int256 =
|
2018-01-15 18:42:40 +00:00
|
|
|
bigEndianToInt(b)
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
template toType(b: string, _: typedesc[string]): string =
|
2018-01-15 18:42:40 +00:00
|
|
|
b
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] =
|
2018-01-15 18:42:40 +00:00
|
|
|
result = @[]
|
|
|
|
if len(stack) < numItems:
|
|
|
|
return
|
|
|
|
|
|
|
|
for z in 0 ..< numItems:
|
|
|
|
var value = stack.values.pop()
|
|
|
|
case value.kind:
|
|
|
|
of VInt:
|
|
|
|
result.add(toType(value.i, T))
|
|
|
|
of VBinary:
|
|
|
|
result.add(toType(value.b, T))
|
|
|
|
|
|
|
|
template ensurePop(elements: untyped, a: untyped): untyped =
|
|
|
|
if len(`elements`) < `a`:
|
|
|
|
raise newException(InsufficientStack, "No stack items")
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc pop*(stack: var Stack): Value =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Pop an item off the stack
|
|
|
|
var elements = stack.internalPop(1)
|
|
|
|
ensurePop(elements, 1)
|
|
|
|
result = elements[0]
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc pop*(stack: var Stack; numItems: int): seq[Value] =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Pop many items off the stack
|
|
|
|
result = stack.internalPop(numItems)
|
|
|
|
ensurePop(result, numItems)
|
|
|
|
|
2018-01-16 18:42:38 +00:00
|
|
|
proc popInt*(stack: var Stack): Int256 =
|
|
|
|
var elements = stack.internalPop(1, Int256)
|
|
|
|
ensurePop(elements, 1)
|
|
|
|
result = elements[0]
|
2018-01-15 18:42:40 +00:00
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
macro internalPopTuple(numItems: static[int]): untyped =
|
2018-01-22 22:23:07 +00:00
|
|
|
var name = ident(&"internalPopTuple{numItems}")
|
2018-01-16 17:05:20 +00:00
|
|
|
var typ = nnkPar.newTree()
|
|
|
|
var t = ident("T")
|
2018-01-16 18:42:38 +00:00
|
|
|
var resultNode = ident("result")
|
|
|
|
var stackNode = ident("stack")
|
2018-01-16 17:05:20 +00:00
|
|
|
for z in 0 ..< numItems:
|
|
|
|
typ.add(t)
|
|
|
|
result = quote:
|
2018-01-16 18:42:38 +00:00
|
|
|
proc `name`*(`stackNode`: var Stack, `t`: typedesc): `typ`
|
|
|
|
result[^1] = nnkStmtList.newTree()
|
|
|
|
for z in 0 ..< numItems:
|
|
|
|
var zNode = newLit(z)
|
|
|
|
var element = quote:
|
|
|
|
var value = `stackNode`.values.pop()
|
|
|
|
case value.kind:
|
|
|
|
of VInt:
|
|
|
|
`resultNode`[`zNode`] = toType(value.i, `t`)
|
|
|
|
of VBinary:
|
|
|
|
`resultNode`[`zNode`] = toType(value.b, `t`)
|
|
|
|
result[^1].add(element)
|
2018-01-16 17:05:20 +00:00
|
|
|
|
|
|
|
# define pop<T> for tuples
|
|
|
|
internalPopTuple(2)
|
|
|
|
internalPopTuple(3)
|
|
|
|
internalPopTuple(4)
|
|
|
|
internalPopTuple(5)
|
|
|
|
internalPopTuple(6)
|
|
|
|
internalPopTuple(7)
|
|
|
|
|
|
|
|
macro popInt*(stack: typed; numItems: static[int]): untyped =
|
|
|
|
var resultNode = ident("result")
|
|
|
|
if numItems >= 8:
|
|
|
|
result = quote:
|
2018-01-16 18:42:38 +00:00
|
|
|
`stack`.internalPop(`numItems`, Int256)
|
2018-01-16 17:05:20 +00:00
|
|
|
else:
|
2018-01-22 22:23:07 +00:00
|
|
|
var name = ident(&"internalPopTuple{numItems}")
|
2018-01-16 17:05:20 +00:00
|
|
|
result = quote:
|
2018-01-16 18:42:38 +00:00
|
|
|
`name`(`stack`, Int256)
|
2018-01-16 17:05:20 +00:00
|
|
|
|
|
|
|
# proc popInt*(stack: var Stack, numItems: int): seq[Int256] =
|
|
|
|
# result = stack.internalPop(numItems, Int256)
|
|
|
|
# ensurePop(result, numItems)
|
2018-01-15 18:42:40 +00:00
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
proc popBinary*(stack: var Stack): string =
|
|
|
|
var elements = stack.internalPop(1, string)
|
2018-01-15 18:42:40 +00:00
|
|
|
ensurePop(elements, 1)
|
|
|
|
result = elements[0]
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
proc popBinary*(stack: var Stack; numItems: int): seq[string] =
|
|
|
|
result = stack.internalPop(numItems, string)
|
2018-01-15 18:42:40 +00:00
|
|
|
ensurePop(result, numItems)
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc newStack*(): Stack =
|
|
|
|
new(result)
|
|
|
|
result.logger = logging.getLogger("evm.vm.stack.Stack")
|
2018-01-15 18:42:40 +00:00
|
|
|
result.values = @[]
|
|
|
|
|
2018-01-16 17:05:20 +00:00
|
|
|
proc swap*(stack: var Stack; position: int) =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Perform a SWAP operation on the stack
|
|
|
|
var idx = position + 1
|
|
|
|
if idx < len(stack) + 1:
|
|
|
|
(stack.values[^1], stack.values[^idx]) = (stack.values[^idx], stack.values[^1])
|
|
|
|
else:
|
|
|
|
raise newException(InsufficientStack,
|
2018-01-22 22:23:07 +00:00
|
|
|
&"Insufficient stack items for SWAP{position}")
|
2018-01-15 18:42:40 +00:00
|
|
|
|
2018-02-06 19:20:06 +00:00
|
|
|
proc dup*(stack: var Stack; position: int | Int256) =
|
2018-01-15 18:42:40 +00:00
|
|
|
## Perform a DUP operation on the stack
|
2018-02-06 19:20:06 +00:00
|
|
|
if (position != 0 and position.getInt < stack.len + 1) or (position == 0 and position.getInt < stack.len):
|
|
|
|
stack.push(stack.values[^position.getInt])
|
2018-01-15 18:42:40 +00:00
|
|
|
else:
|
|
|
|
raise newException(InsufficientStack,
|
2018-01-22 22:23:07 +00:00
|
|
|
&"Insufficient stack items for DUP{position}")
|
2018-01-15 18:42:40 +00:00
|
|
|
|
2018-01-24 13:31:24 +00:00
|
|
|
|
2018-01-29 13:56:51 +00:00
|
|
|
proc getInt*(stack: Stack, position: int): Int256 =
|
|
|
|
let element = stack.values[position]
|
|
|
|
case element.kind:
|
|
|
|
of VInt:
|
|
|
|
result = element.i
|
|
|
|
else:
|
|
|
|
raise newException(TypeError, "Expected int")
|
|
|
|
|
2018-01-31 12:57:05 +00:00
|
|
|
proc getBinary*(stack: Stack, position: int): string =
|
2018-01-29 13:56:51 +00:00
|
|
|
let element = stack.values[position]
|
|
|
|
case element.kind:
|
|
|
|
of VBinary:
|
|
|
|
result = element.b
|
|
|
|
else:
|
|
|
|
raise newException(TypeError, "Expected binary")
|
|
|
|
|
2018-01-24 13:31:24 +00:00
|
|
|
proc `$`*(stack: Stack): string =
|
|
|
|
let values = stack.values.mapIt(&" {$it}").join("\n")
|
|
|
|
&"Stack:\n{values}"
|