nimbus-eth1/src/vm/stack.nim

124 lines
3.3 KiB
Nim

import
strformat,
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")
method len*(stack: Stack): int =
len(stack.values)
method push*(stack: var Stack; value: Value) =
## Push an item onto the stack
ensureStackLimit()
stack.values.add(value)
method push*(stack: var Stack; value: int) =
## Push an integer onto the stack
ensureStackLimit()
stack.values.add(Value(kind: VInt, i: value))
method push*(stack: var Stack; value: cstring) =
## Push a binary onto the stack
ensureStackLimit()
stack.values.add(Value(kind: VBinary, b: value))
method internalPop(stack: var Stack; numItems: int): seq[Value] =
if len(stack) < numItems:
result = @[]
else:
result = stack.values[^numItems .. ^1]
stack.values = stack.values[0 ..< ^numItems]
template toType(i: int, _: typedesc[int]): int =
i
template toType(i: int, _: typedesc[cstring]): cstring =
intToBigEndian(i)
template toType(b: cstring, _: typedesc[int]): int =
bigEndianToInt(b)
template toType(b: cstring, _: typedesc[cstring]): cstring =
b
method internalPop(stack: var Stack; numItems: int, T: typedesc): seq[T] =
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")
method pop*(stack: var Stack): Value =
## Pop an item off the stack
var elements = stack.internalPop(1)
ensurePop(elements, 1)
result = elements[0]
method pop*(stack: var Stack; numItems: int): seq[Value] =
## Pop many items off the stack
result = stack.internalPop(numItems)
ensurePop(result, numItems)
method popInt*(stack: var Stack): int =
var elements = stack.internalPop(1, int)
ensurePop(elements, 1)
result = elements[0]
method popInt*(stack: var Stack; numItems: int): seq[int] =
result = stack.internalPop(numItems, int)
ensurePop(result, numItems)
method popBinary*(stack: var Stack): cstring =
var elements = stack.internalPop(1, cstring)
ensurePop(elements, 1)
result = elements[0]
method popBinary*(stack: var Stack; numItems: int): seq[cstring] =
result = stack.internalPop(numItems, cstring)
ensurePop(result, numItems)
proc makeStack*(): Stack =
# result.logger = logging.getLogger("evm.vm.stack.Stack")
result.values = @[]
method swap*(stack: var Stack; position: int) =
## 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,
%"Insufficient stack items for SWAP{position}")
method dup*(stack: var Stack; position: int) =
## Perform a DUP operation on the stack
if position < len(stack) + 1:
stack.push(stack.values[^position])
else:
raise newException(InsufficientStack,
%"Insufficient stack items for DUP{position}")