nimbus-eth1/execution_chain/evm/code_stream.nim
pmmiranda 411a3cadfa
Renamed 'nimbus' directory and its references to 'execution_chain' (#3052)
* renamed nimbus folder to execution_chain

* Renamed "nimbus" references to "execution_chain"

* fixed wrongly changed http reference

* delete snap types file given that it was deleted before this PR merge

* missing 'execution_chain' replacement

---------

Co-authored-by: pmmiranda <pedro.miranda@nimbus.team>
2025-02-11 22:28:42 +00:00

100 lines
2.8 KiB
Nim

# Nimbus
# Copyright (c) 2018-2024 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed except according to those terms.
import chronicles, stint, stew/byteutils, ./interpreter/op_codes, ./code_bytes
export code_bytes
type CodeStream* = object
code: CodeBytesRef
pc*: int
func init*(T: type CodeStream, code: CodeBytesRef): T =
T(code: code)
func init*(T: type CodeStream, code: sink seq[byte]): T =
T(code: CodeBytesRef.init(move(code)))
func init*(T: type CodeStream, code: openArray[byte]): T =
T(code: CodeBytesRef.init(code))
func init*(T: type CodeStream, code: openArray[char]): T =
T(code: CodeBytesRef.init(code))
template read*(c: var CodeStream, size: int): openArray[byte] =
let
pos = c.pc
last = pos + size
if last <= c.bytes.len:
c.pc = last
c.code.bytes.toOpenArray(pos, last - 1)
else:
c.pc = c.bytes.len
c.code.bytes.toOpenArray(pos, c.bytes.high)
func readVmWord*(c: var CodeStream, n: static int): UInt256 {.inline, noinit.} =
## Reads `n` bytes from the code stream and pads
## the remaining bytes with zeros.
UInt256.fromBytesBE(c.read(n))
func len*(c: CodeStream): int =
len(c.code)
template next*(c: var CodeStream): Op =
# Retrieve the next opcode (or stop) - this is a hot spot in the interpreter
# and must be kept small for performance
let
# uint: range checked manually -> benefit from smaller codegen
pc = uint(c.pc)
bytes {.cursor.} = c.code.bytes
if pc < uint(bytes.len):
let op = Op(bytes[pc])
c.pc = cast[int](pc + 1)
op
else:
Op.Stop
iterator items*(c: var CodeStream): Op =
var nextOpcode = c.next()
while nextOpcode != Op.Stop:
yield nextOpcode
nextOpcode = c.next()
func `[]`*(c: CodeStream, offset: int): Op =
Op(c.code.bytes[offset])
func peek*(c: var CodeStream): Op =
if c.pc < c.code.bytes.len:
Op(c.code.bytes[c.pc])
else:
Op.Stop
func updatePc*(c: var CodeStream, value: int) =
c.pc = min(value, len(c))
func isValidOpcode*(c: CodeStream, position: int): bool =
c.code.isValidOpcode(position)
func bytes*(c: CodeStream): lent seq[byte] =
c.code.bytes()
func atEnd*(c: CodeStream): bool =
c.pc >= c.code.bytes.len
proc decompile*(original: CodeStream): seq[(int, Op, string)] =
# behave as https://etherscan.io/opcode-tool
var c = CodeStream.init(original.bytes)
while not c.atEnd:
var op = c.next
if op >= Push1 and op <= Push32:
result.add((c.pc - 1, op, "0x" & c.read(op.int - 95).toHex))
elif op != Op.Stop:
result.add((c.pc - 1, op, ""))
else:
result.add((-1, Op.Stop, ""))