137 lines
4.4 KiB
Nim
137 lines
4.4 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.
|
|
|
|
const
|
|
# debugging flag, dump macro info when asked for
|
|
noisy {.intdefine.}: int = 0
|
|
# isNoisy {.used.} = noisy > 0
|
|
isChatty {.used.} = noisy > 1
|
|
|
|
import
|
|
../computation,
|
|
../evm_errors,
|
|
../../common/evmforks,
|
|
./gas_costs,
|
|
./gas_meter,
|
|
./op_codes,
|
|
./op_handlers,
|
|
./op_handlers/oph_defs,
|
|
macros
|
|
|
|
export EVMFork, Op, oph_defs, gas_meter
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
template handleStopDirective(cpt: VmCpt, tracingEnabled: bool) =
|
|
#trace "op: Stop"
|
|
when tracingEnabled:
|
|
if not cpt.code.atEnd():
|
|
# we only trace `REAL STOP` and ignore `FAKE STOP`
|
|
cpt.opIndex = cpt.traceOpCodeStarted(Stop)
|
|
cpt.traceOpCodeEnded(Stop, cpt.opIndex)
|
|
|
|
template handleFixedGasCostsDirective(
|
|
fork: EVMFork, op: Op, cost: GasInt, cpt: VmCpt, tracingEnabled: bool
|
|
) =
|
|
when tracingEnabled:
|
|
cpt.opIndex = cpt.traceOpCodeStarted(op)
|
|
|
|
?cpt.opcodeGasCost(op, cost, tracingEnabled, reason = $op)
|
|
?vmOpHandlers[fork][op].run(cpt)
|
|
|
|
# If continuation is not nil, traceOpCodeEnded will be called in executeOpcodes.
|
|
when tracingEnabled:
|
|
if cpt.continuation.isNil:
|
|
cpt.traceOpCodeEnded(op, cpt.opIndex)
|
|
|
|
template handleOtherDirective(fork: EVMFork, op: Op, cpt: VmCpt, tracingEnabled: bool) =
|
|
when tracingEnabled:
|
|
cpt.opIndex = cpt.traceOpCodeStarted(op)
|
|
|
|
?vmOpHandlers[fork][op].run(cpt)
|
|
|
|
# If continuation is not nil, traceOpCodeEnded will be called in executeOpcodes.
|
|
when tracingEnabled:
|
|
if cpt.continuation.isNil:
|
|
cpt.traceOpCodeEnded(op, cpt.opIndex)
|
|
|
|
proc makeCaseDispatcher(forkArg: EVMFork, tracingEnabled: bool, opArg, cpt: NimNode): NimNode =
|
|
# Create a case statement for dispatching opcode to handler for the given
|
|
# fork, taking care to record the gas cost
|
|
# TODO there are several forks for which neither opcodes nor gas costs
|
|
# changed - these could use the same dispatcher thus saving some space
|
|
# and compile time
|
|
let gasCosts = forkToSchedule(forkArg)
|
|
|
|
result = nnkCaseStmt.newTree(opArg)
|
|
for op in Op:
|
|
let
|
|
asOp = quote: `op`
|
|
handler =
|
|
if op == Stop:
|
|
quote:
|
|
handleStopDirective(`cpt`, `tracingEnabled`)
|
|
elif gasCosts[op].kind == GckFixed:
|
|
let cost = gasCosts[op].cost
|
|
quote:
|
|
handleFixedGasCostsDirective(
|
|
`forkArg`, `op`, `cost`, `cpt`, `tracingEnabled`
|
|
)
|
|
else:
|
|
quote:
|
|
handleOtherDirective(`forkArg`, `op`, `cpt`, `tracingEnabled`)
|
|
branch =
|
|
case op
|
|
of Create, Create2, Call, CallCode, DelegateCall, StaticCall:
|
|
# These opcodes use `chainTo` to create a continuation call which must
|
|
# be handled separately
|
|
quote:
|
|
`handler`
|
|
if not `cpt`.continuation.isNil:
|
|
break
|
|
|
|
of Stop, Return, Revert, SelfDestruct:
|
|
quote:
|
|
`handler`
|
|
break
|
|
else:
|
|
handler
|
|
|
|
result.add nnkOfBranch.newTree(asOp, branch)
|
|
|
|
when isChatty:
|
|
echo ">>> ", result.repr
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public macros/functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
macro dispatchInstr*(
|
|
fork: static EVMFork, tracingEnabled: static bool, op: Op, cpt: VmCpt
|
|
): untyped =
|
|
makeCaseDispatcher(fork, tracingEnabled, op, cpt)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Debugging ...
|
|
# ------------------------------------------------------------------------------
|
|
|
|
when isMainModule and isChatty:
|
|
import ../types
|
|
|
|
proc optimised(cpt: VmCpt): EvmResultVoid {.compileTime.} =
|
|
while true:
|
|
dispatchInstr(FkFrontier, false, cpt.instr, cpt)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|