Skip to content

Commit c03d8fe

Browse files
mw2000claude
andcommitted
refactor: drive opcode dispatch from the Registry
Replace the hand-maintained case-clause dispatch in `EEVM.Interpreter` with a single registry lookup. Each opcode entry in `EEVM.Interpreter.Instructions.Registry` now carries `:module` (the implementing module) and an optional `:state_mutating` flag. `execute_opcode/2` becomes: - if the opcode is state-mutating and the frame is static → halt :reverted - if the registry has a module → delegate `execute(opcode, state)` - otherwise → halt :invalid One source of truth for opcode metadata + dispatch, fewer places to forget when adding a new opcode, and the static-context check is expressed declaratively instead of as five separate guard clauses. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 09d939d commit c03d8fe

2 files changed

Lines changed: 145 additions & 180 deletions

File tree

lib/eevm/interpreter.ex

Lines changed: 23 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -29,35 +29,17 @@ defmodule EEVM.Interpreter do
2929
runs in constant stack space even for long-running contracts.
3030
- The three `run_loop/1` clauses use pattern matching on `status` to cleanly
3131
separate running, halted, and out-of-gas states without any conditionals.
32-
- `execute_opcode/2` is a private dispatch table. Specific opcodes come first,
33-
then ranges. Elixir matches clauses top-to-bottom, so narrower patterns
34-
must precede broader ones.
32+
- Opcode dispatch is data-driven: `EEVM.Interpreter.Instructions.Registry`
33+
maps each opcode to its implementing module, and `execute_opcode/2` is a
34+
single registry lookup rather than a hand-maintained case table.
3535
- Separation of concerns: the executor only orchestrates; opcode modules own
3636
their semantics.
3737
"""
3838

3939
alias EEVM.Database
4040
alias EEVM.Gas.Static
4141
alias EEVM.HardforkConfig
42-
43-
alias EEVM.Interpreter.Instructions.{
44-
Arithmetic,
45-
Bitwise,
46-
Comparison,
47-
ControlFlow,
48-
Crypto,
49-
Environment.Data,
50-
Environment.External,
51-
Environment.Simple,
52-
Logging,
53-
StackMemoryStorage.MemoryOps,
54-
StackMemoryStorage.StackOps,
55-
StackMemoryStorage.StorageOps,
56-
System.Calls,
57-
System.Creation,
58-
System.Termination
59-
}
60-
42+
alias EEVM.Interpreter.Instructions.Registry
6143
alias EEVM.Interpreter.{MachineState, Memory, Stack}
6244
alias EEVM.Tracer
6345
alias EEVM.Tracer.TraceStep
@@ -196,72 +178,26 @@ defmodule EEVM.Interpreter do
196178

197179
defp cleanup_touched_empty_accounts(state), do: state
198180

199-
# Dispatch table for execute_opcode/2.
181+
# Dispatch an opcode byte to its implementing module via the Registry.
200182
#
201-
# Routes opcode bytes to the module that implements them. Specific opcodes
202-
# are listed first; ranges follow. This ordering matters — Elixir matches
203-
# clauses top-to-bottom, so e.g. 0x50 must appear before the 0x51..0x55
204-
# range to ensure it is caught by its dedicated StackMemoryStorage clause.
205-
# The fallback clause treats unknown opcodes as INVALID (halt, no gas refund).
206-
207-
defp execute_opcode(0x55, %{is_static: true} = state),
208-
do: {:ok, MachineState.halt(state, :reverted)}
209-
210-
defp execute_opcode(0x5D, %{is_static: true} = state),
211-
do: {:ok, MachineState.halt(state, :reverted)}
212-
213-
defp execute_opcode(op, %{is_static: true} = state) when op in 0xA0..0xA4,
214-
do: {:ok, MachineState.halt(state, :reverted)}
215-
216-
defp execute_opcode(op, %{is_static: true} = state) when op in [0xF0, 0xF5],
217-
do: {:ok, MachineState.halt(state, :reverted)}
218-
219-
defp execute_opcode(0xFF, %{is_static: true} = state),
220-
do: {:ok, MachineState.halt(state, :reverted)}
221-
222-
defp execute_opcode(0x00, state), do: Termination.execute(0x00, state)
223-
defp execute_opcode(op, state) when op in 0x01..0x0B, do: Arithmetic.execute(op, state)
224-
defp execute_opcode(op, state) when op in 0x10..0x15, do: Comparison.execute(op, state)
225-
defp execute_opcode(op, state) when op in 0x16..0x1D, do: Bitwise.execute(op, state)
226-
defp execute_opcode(0x20, state), do: Crypto.execute(0x20, state)
227-
228-
defp execute_opcode(op, state) when op in [0x30, 0x32, 0x33, 0x34, 0x36, 0x38, 0x3A, 0x3D],
229-
do: Simple.execute(op, state)
230-
231-
defp execute_opcode(op, state) when op in [0x35, 0x37, 0x39, 0x3E], do: Data.execute(op, state)
232-
233-
defp execute_opcode(op, state) when op in [0x31, 0x3B, 0x3C, 0x3F],
234-
do: External.execute(op, state)
235-
236-
defp execute_opcode(op, state)
237-
when op in [0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A],
238-
do: Simple.execute(op, state)
239-
240-
defp execute_opcode(0x47, state), do: External.execute(0x47, state)
241-
defp execute_opcode(0x50, state), do: StackOps.execute(0x50, state)
242-
243-
defp execute_opcode(op, state) when op in [0x51, 0x52, 0x53, 0x59, 0x5E],
244-
do: MemoryOps.execute(op, state)
245-
246-
defp execute_opcode(op, state) when op in [0x54, 0x55, 0x5C, 0x5D],
247-
do: StorageOps.execute(op, state)
248-
249-
defp execute_opcode(0x5A, state), do: Simple.execute(0x5A, state)
250-
defp execute_opcode(op, state) when op in 0x56..0x5B, do: ControlFlow.execute(op, state)
251-
defp execute_opcode(0x5F, state), do: ControlFlow.execute(0x5F, state)
252-
defp execute_opcode(op, state) when op in 0x60..0x9F, do: ControlFlow.execute(op, state)
253-
defp execute_opcode(op, state) when op in 0xA0..0xA4, do: Logging.execute(op, state)
254-
255-
defp execute_opcode(op, state) when op in [0xF0, 0xF5],
256-
do: Creation.execute(op, state)
257-
258-
defp execute_opcode(op, state) when op in [0xF1, 0xF2, 0xF4, 0xFA],
259-
do: Calls.execute(op, state)
260-
261-
defp execute_opcode(op, state) when op in [0xF3, 0xFD, 0xFE, 0xFF],
262-
do: Termination.execute(op, state)
263-
264-
defp execute_opcode(_op, state), do: {:ok, MachineState.halt(state, :invalid)}
183+
# The Registry maps each opcode to a `:module` (the `Instructions.*` module
184+
# whose `execute/2` handles it) and a `:state_mutating` flag. Inside a
185+
# STATICCALL frame (`state.is_static`), state-mutating opcodes (SSTORE,
186+
# TSTORE, LOG0..4, CREATE, CREATE2, SELFDESTRUCT) halt with `:reverted`
187+
# before reaching the module. Unknown opcodes halt as `:invalid`.
188+
189+
defp execute_opcode(opcode, state) do
190+
case Registry.info(opcode) do
191+
{:ok, %{state_mutating: true}} when state.is_static ->
192+
{:ok, MachineState.halt(state, :reverted)}
193+
194+
{:ok, %{module: module}} ->
195+
module.execute(opcode, state)
196+
197+
{:error, :unknown_opcode} ->
198+
{:ok, MachineState.halt(state, :invalid)}
199+
end
200+
end
265201

266202
# Trace bookkeeping is a no-op when no tracer is attached — one pattern match,
267203
# one return. All helpers below keep this fast path.

lib/eevm/interpreter/instructions/registry.ex

Lines changed: 122 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,39 @@
11
defmodule EEVM.Interpreter.Instructions.Registry do
22
@moduledoc """
3-
Opcode metadata registry — maps opcode bytes to names and stack I/O signatures.
3+
Opcode metadata registry — single source of truth for every opcode's name,
4+
stack I/O signature, implementing module, and static-context flag.
45
5-
This module is the single source of truth for opcode metadata. Given an opcode byte
6-
(0x00–0xFF), it returns the human-readable name, the number of stack inputs consumed,
7-
and the number of stack outputs produced. This information is used by the disassembler
8-
and can be used to validate stack depth statically.
6+
Given an opcode byte (0x00–0xFF) `info/1` returns:
7+
8+
- `:name` — the human-readable mnemonic (e.g. `"ADD"`)
9+
- `:inputs` / `:outputs` — stack arity, used by the disassembler and stack-depth checks
10+
- `:module` — the `EEVM.Interpreter.Instructions.*` module that implements the opcode;
11+
`EEVM.Interpreter.execute_opcode/2` dispatches on this field
12+
- `:state_mutating` (when present) — opcode mutates persistent state and must
13+
halt with `:reverted` if executed inside a STATICCALL frame
914
1015
PUSH1–PUSH32, DUP1–DUP16, and SWAP1–SWAP16 are generated dynamically from their
1116
opcode ranges rather than stored in the static map.
1217
"""
1318

19+
alias EEVM.Interpreter.Instructions.{
20+
Arithmetic,
21+
Bitwise,
22+
Comparison,
23+
ControlFlow,
24+
Crypto,
25+
Environment.Data,
26+
Environment.External,
27+
Environment.Simple,
28+
Logging,
29+
StackMemoryStorage.MemoryOps,
30+
StackMemoryStorage.StackOps,
31+
StackMemoryStorage.StorageOps,
32+
System.Calls,
33+
System.Creation,
34+
System.Termination
35+
}
36+
1437
@stop 0x00
1538
@add 0x01
1639
@mul 0x02
@@ -111,107 +134,113 @@ defmodule EEVM.Interpreter.Instructions.Registry do
111134
@invalid 0xFE
112135

113136
@opcodes %{
114-
@stop => %{name: "STOP", inputs: 0, outputs: 0},
115-
@add => %{name: "ADD", inputs: 2, outputs: 1},
116-
@mul => %{name: "MUL", inputs: 2, outputs: 1},
117-
@sub => %{name: "SUB", inputs: 2, outputs: 1},
118-
@div_ => %{name: "DIV", inputs: 2, outputs: 1},
119-
@sdiv => %{name: "SDIV", inputs: 2, outputs: 1},
120-
@mod => %{name: "MOD", inputs: 2, outputs: 1},
121-
@smod => %{name: "SMOD", inputs: 2, outputs: 1},
122-
@addmod => %{name: "ADDMOD", inputs: 3, outputs: 1},
123-
@mulmod => %{name: "MULMOD", inputs: 3, outputs: 1},
124-
@exp => %{name: "EXP", inputs: 2, outputs: 1},
125-
@signextend => %{name: "SIGNEXTEND", inputs: 2, outputs: 1},
126-
@keccak256 => %{name: "KECCAK256", inputs: 2, outputs: 1},
127-
@lt => %{name: "LT", inputs: 2, outputs: 1},
128-
@gt => %{name: "GT", inputs: 2, outputs: 1},
129-
@slt => %{name: "SLT", inputs: 2, outputs: 1},
130-
@sgt => %{name: "SGT", inputs: 2, outputs: 1},
131-
@eq => %{name: "EQ", inputs: 2, outputs: 1},
132-
@iszero => %{name: "ISZERO", inputs: 1, outputs: 1},
133-
@and_ => %{name: "AND", inputs: 2, outputs: 1},
134-
@or_ => %{name: "OR", inputs: 2, outputs: 1},
135-
@xor_ => %{name: "XOR", inputs: 2, outputs: 1},
136-
@not_ => %{name: "NOT", inputs: 1, outputs: 1},
137-
@byte_ => %{name: "BYTE", inputs: 2, outputs: 1},
138-
@shl => %{name: "SHL", inputs: 2, outputs: 1},
139-
@shr => %{name: "SHR", inputs: 2, outputs: 1},
140-
@sar => %{name: "SAR", inputs: 2, outputs: 1},
141-
@address => %{name: "ADDRESS", inputs: 0, outputs: 1},
142-
@balance => %{name: "BALANCE", inputs: 1, outputs: 1},
143-
@origin => %{name: "ORIGIN", inputs: 0, outputs: 1},
144-
@caller => %{name: "CALLER", inputs: 0, outputs: 1},
145-
@callvalue => %{name: "CALLVALUE", inputs: 0, outputs: 1},
146-
@calldataload => %{name: "CALLDATALOAD", inputs: 1, outputs: 1},
147-
@calldatasize => %{name: "CALLDATASIZE", inputs: 0, outputs: 1},
148-
@calldatacopy => %{name: "CALLDATACOPY", inputs: 3, outputs: 0},
149-
@codecopy => %{name: "CODECOPY", inputs: 3, outputs: 0},
150-
@extcodecopy => %{name: "EXTCODECOPY", inputs: 4, outputs: 0},
151-
@returndatacopy => %{name: "RETURNDATACOPY", inputs: 3, outputs: 0},
152-
@codesize => %{name: "CODESIZE", inputs: 0, outputs: 1},
153-
@extcodesize => %{name: "EXTCODESIZE", inputs: 1, outputs: 1},
154-
@gasprice => %{name: "GASPRICE", inputs: 0, outputs: 1},
155-
@returndatasize => %{name: "RETURNDATASIZE", inputs: 0, outputs: 1},
156-
@extcodehash => %{name: "EXTCODEHASH", inputs: 1, outputs: 1},
157-
@blockhash => %{name: "BLOCKHASH", inputs: 1, outputs: 1},
158-
@coinbase => %{name: "COINBASE", inputs: 0, outputs: 1},
159-
@timestamp => %{name: "TIMESTAMP", inputs: 0, outputs: 1},
160-
@number => %{name: "NUMBER", inputs: 0, outputs: 1},
161-
@prevrandao => %{name: "PREVRANDAO", inputs: 0, outputs: 1},
162-
@gaslimit => %{name: "GASLIMIT", inputs: 0, outputs: 1},
163-
@chainid => %{name: "CHAINID", inputs: 0, outputs: 1},
164-
@selfbalance => %{name: "SELFBALANCE", inputs: 0, outputs: 1},
165-
@basefee => %{name: "BASEFEE", inputs: 0, outputs: 1},
166-
@blobhash => %{name: "BLOBHASH", inputs: 1, outputs: 1},
167-
@blobbasefee => %{name: "BLOBBASEFEE", inputs: 0, outputs: 1},
168-
@gas_ => %{name: "GAS", inputs: 0, outputs: 1},
169-
@push0 => %{name: "PUSH0", inputs: 0, outputs: 1},
170-
@pop => %{name: "POP", inputs: 1, outputs: 0},
171-
@mload => %{name: "MLOAD", inputs: 1, outputs: 1},
172-
@mstore => %{name: "MSTORE", inputs: 2, outputs: 0},
173-
@mstore8 => %{name: "MSTORE8", inputs: 2, outputs: 0},
174-
@sload => %{name: "SLOAD", inputs: 1, outputs: 1},
175-
@sstore => %{name: "SSTORE", inputs: 2, outputs: 0},
176-
@tload => %{name: "TLOAD", inputs: 1, outputs: 1},
177-
@tstore => %{name: "TSTORE", inputs: 2, outputs: 0},
178-
@msize => %{name: "MSIZE", inputs: 0, outputs: 1},
179-
@mcopy => %{name: "MCOPY", inputs: 3, outputs: 0},
180-
@jump => %{name: "JUMP", inputs: 1, outputs: 0},
181-
@jumpi => %{name: "JUMPI", inputs: 2, outputs: 0},
182-
@pc => %{name: "PC", inputs: 0, outputs: 1},
183-
@jumpdest => %{name: "JUMPDEST", inputs: 0, outputs: 0},
184-
@log0 => %{name: "LOG0", inputs: 2, outputs: 0},
185-
@log1 => %{name: "LOG1", inputs: 3, outputs: 0},
186-
@log2 => %{name: "LOG2", inputs: 4, outputs: 0},
187-
@log3 => %{name: "LOG3", inputs: 5, outputs: 0},
188-
@log4 => %{name: "LOG4", inputs: 6, outputs: 0},
189-
@create => %{name: "CREATE", inputs: 3, outputs: 1},
190-
@call => %{name: "CALL", inputs: 7, outputs: 1},
191-
@callcode => %{name: "CALLCODE", inputs: 7, outputs: 1},
192-
@delegatecall => %{name: "DELEGATECALL", inputs: 6, outputs: 1},
193-
@create2 => %{name: "CREATE2", inputs: 4, outputs: 1},
194-
@staticcall => %{name: "STATICCALL", inputs: 6, outputs: 1},
195-
@return_ => %{name: "RETURN", inputs: 2, outputs: 0},
196-
@revert => %{name: "REVERT", inputs: 2, outputs: 0},
197-
@invalid => %{name: "INVALID", inputs: 0, outputs: 0},
198-
@selfdestruct => %{name: "SELFDESTRUCT", inputs: 1, outputs: 0}
137+
@stop => %{name: "STOP", inputs: 0, outputs: 0, module: Termination},
138+
@add => %{name: "ADD", inputs: 2, outputs: 1, module: Arithmetic},
139+
@mul => %{name: "MUL", inputs: 2, outputs: 1, module: Arithmetic},
140+
@sub => %{name: "SUB", inputs: 2, outputs: 1, module: Arithmetic},
141+
@div_ => %{name: "DIV", inputs: 2, outputs: 1, module: Arithmetic},
142+
@sdiv => %{name: "SDIV", inputs: 2, outputs: 1, module: Arithmetic},
143+
@mod => %{name: "MOD", inputs: 2, outputs: 1, module: Arithmetic},
144+
@smod => %{name: "SMOD", inputs: 2, outputs: 1, module: Arithmetic},
145+
@addmod => %{name: "ADDMOD", inputs: 3, outputs: 1, module: Arithmetic},
146+
@mulmod => %{name: "MULMOD", inputs: 3, outputs: 1, module: Arithmetic},
147+
@exp => %{name: "EXP", inputs: 2, outputs: 1, module: Arithmetic},
148+
@signextend => %{name: "SIGNEXTEND", inputs: 2, outputs: 1, module: Arithmetic},
149+
@keccak256 => %{name: "KECCAK256", inputs: 2, outputs: 1, module: Crypto},
150+
@lt => %{name: "LT", inputs: 2, outputs: 1, module: Comparison},
151+
@gt => %{name: "GT", inputs: 2, outputs: 1, module: Comparison},
152+
@slt => %{name: "SLT", inputs: 2, outputs: 1, module: Comparison},
153+
@sgt => %{name: "SGT", inputs: 2, outputs: 1, module: Comparison},
154+
@eq => %{name: "EQ", inputs: 2, outputs: 1, module: Comparison},
155+
@iszero => %{name: "ISZERO", inputs: 1, outputs: 1, module: Comparison},
156+
@and_ => %{name: "AND", inputs: 2, outputs: 1, module: Bitwise},
157+
@or_ => %{name: "OR", inputs: 2, outputs: 1, module: Bitwise},
158+
@xor_ => %{name: "XOR", inputs: 2, outputs: 1, module: Bitwise},
159+
@not_ => %{name: "NOT", inputs: 1, outputs: 1, module: Bitwise},
160+
@byte_ => %{name: "BYTE", inputs: 2, outputs: 1, module: Bitwise},
161+
@shl => %{name: "SHL", inputs: 2, outputs: 1, module: Bitwise},
162+
@shr => %{name: "SHR", inputs: 2, outputs: 1, module: Bitwise},
163+
@sar => %{name: "SAR", inputs: 2, outputs: 1, module: Bitwise},
164+
@address => %{name: "ADDRESS", inputs: 0, outputs: 1, module: Simple},
165+
@balance => %{name: "BALANCE", inputs: 1, outputs: 1, module: External},
166+
@origin => %{name: "ORIGIN", inputs: 0, outputs: 1, module: Simple},
167+
@caller => %{name: "CALLER", inputs: 0, outputs: 1, module: Simple},
168+
@callvalue => %{name: "CALLVALUE", inputs: 0, outputs: 1, module: Simple},
169+
@calldataload => %{name: "CALLDATALOAD", inputs: 1, outputs: 1, module: Data},
170+
@calldatasize => %{name: "CALLDATASIZE", inputs: 0, outputs: 1, module: Simple},
171+
@calldatacopy => %{name: "CALLDATACOPY", inputs: 3, outputs: 0, module: Data},
172+
@codecopy => %{name: "CODECOPY", inputs: 3, outputs: 0, module: Data},
173+
@extcodecopy => %{name: "EXTCODECOPY", inputs: 4, outputs: 0, module: External},
174+
@returndatacopy => %{name: "RETURNDATACOPY", inputs: 3, outputs: 0, module: Data},
175+
@codesize => %{name: "CODESIZE", inputs: 0, outputs: 1, module: Simple},
176+
@extcodesize => %{name: "EXTCODESIZE", inputs: 1, outputs: 1, module: External},
177+
@gasprice => %{name: "GASPRICE", inputs: 0, outputs: 1, module: Simple},
178+
@returndatasize => %{name: "RETURNDATASIZE", inputs: 0, outputs: 1, module: Simple},
179+
@extcodehash => %{name: "EXTCODEHASH", inputs: 1, outputs: 1, module: External},
180+
@blockhash => %{name: "BLOCKHASH", inputs: 1, outputs: 1, module: Simple},
181+
@coinbase => %{name: "COINBASE", inputs: 0, outputs: 1, module: Simple},
182+
@timestamp => %{name: "TIMESTAMP", inputs: 0, outputs: 1, module: Simple},
183+
@number => %{name: "NUMBER", inputs: 0, outputs: 1, module: Simple},
184+
@prevrandao => %{name: "PREVRANDAO", inputs: 0, outputs: 1, module: Simple},
185+
@gaslimit => %{name: "GASLIMIT", inputs: 0, outputs: 1, module: Simple},
186+
@chainid => %{name: "CHAINID", inputs: 0, outputs: 1, module: Simple},
187+
@selfbalance => %{name: "SELFBALANCE", inputs: 0, outputs: 1, module: External},
188+
@basefee => %{name: "BASEFEE", inputs: 0, outputs: 1, module: Simple},
189+
@blobhash => %{name: "BLOBHASH", inputs: 1, outputs: 1, module: Simple},
190+
@blobbasefee => %{name: "BLOBBASEFEE", inputs: 0, outputs: 1, module: Simple},
191+
@gas_ => %{name: "GAS", inputs: 0, outputs: 1, module: Simple},
192+
@push0 => %{name: "PUSH0", inputs: 0, outputs: 1, module: ControlFlow},
193+
@pop => %{name: "POP", inputs: 1, outputs: 0, module: StackOps},
194+
@mload => %{name: "MLOAD", inputs: 1, outputs: 1, module: MemoryOps},
195+
@mstore => %{name: "MSTORE", inputs: 2, outputs: 0, module: MemoryOps},
196+
@mstore8 => %{name: "MSTORE8", inputs: 2, outputs: 0, module: MemoryOps},
197+
@sload => %{name: "SLOAD", inputs: 1, outputs: 1, module: StorageOps},
198+
@sstore => %{name: "SSTORE", inputs: 2, outputs: 0, module: StorageOps, state_mutating: true},
199+
@tload => %{name: "TLOAD", inputs: 1, outputs: 1, module: StorageOps},
200+
@tstore => %{name: "TSTORE", inputs: 2, outputs: 0, module: StorageOps, state_mutating: true},
201+
@msize => %{name: "MSIZE", inputs: 0, outputs: 1, module: MemoryOps},
202+
@mcopy => %{name: "MCOPY", inputs: 3, outputs: 0, module: MemoryOps},
203+
@jump => %{name: "JUMP", inputs: 1, outputs: 0, module: ControlFlow},
204+
@jumpi => %{name: "JUMPI", inputs: 2, outputs: 0, module: ControlFlow},
205+
@pc => %{name: "PC", inputs: 0, outputs: 1, module: ControlFlow},
206+
@jumpdest => %{name: "JUMPDEST", inputs: 0, outputs: 0, module: ControlFlow},
207+
@log0 => %{name: "LOG0", inputs: 2, outputs: 0, module: Logging, state_mutating: true},
208+
@log1 => %{name: "LOG1", inputs: 3, outputs: 0, module: Logging, state_mutating: true},
209+
@log2 => %{name: "LOG2", inputs: 4, outputs: 0, module: Logging, state_mutating: true},
210+
@log3 => %{name: "LOG3", inputs: 5, outputs: 0, module: Logging, state_mutating: true},
211+
@log4 => %{name: "LOG4", inputs: 6, outputs: 0, module: Logging, state_mutating: true},
212+
@create => %{name: "CREATE", inputs: 3, outputs: 1, module: Creation, state_mutating: true},
213+
@call => %{name: "CALL", inputs: 7, outputs: 1, module: Calls},
214+
@callcode => %{name: "CALLCODE", inputs: 7, outputs: 1, module: Calls},
215+
@delegatecall => %{name: "DELEGATECALL", inputs: 6, outputs: 1, module: Calls},
216+
@create2 => %{name: "CREATE2", inputs: 4, outputs: 1, module: Creation, state_mutating: true},
217+
@staticcall => %{name: "STATICCALL", inputs: 6, outputs: 1, module: Calls},
218+
@return_ => %{name: "RETURN", inputs: 2, outputs: 0, module: Termination},
219+
@revert => %{name: "REVERT", inputs: 2, outputs: 0, module: Termination},
220+
@invalid => %{name: "INVALID", inputs: 0, outputs: 0, module: Termination},
221+
@selfdestruct => %{
222+
name: "SELFDESTRUCT",
223+
inputs: 1,
224+
outputs: 0,
225+
module: Termination,
226+
state_mutating: true
227+
}
199228
}
200229

201230
@spec info(non_neg_integer()) :: {:ok, map()} | {:error, :unknown_opcode}
202231
def info(op) when op >= @push1 and op <= @push32 do
203232
n = op - @push1 + 1
204-
{:ok, %{name: "PUSH#{n}", inputs: 0, outputs: 1, push_bytes: n}}
233+
{:ok, %{name: "PUSH#{n}", inputs: 0, outputs: 1, push_bytes: n, module: ControlFlow}}
205234
end
206235

207236
def info(op) when op >= @dup1 and op <= @dup16 do
208237
n = op - @dup1 + 1
209-
{:ok, %{name: "DUP#{n}", inputs: n, outputs: n + 1, dup_depth: n - 1}}
238+
{:ok, %{name: "DUP#{n}", inputs: n, outputs: n + 1, dup_depth: n - 1, module: ControlFlow}}
210239
end
211240

212241
def info(op) when op >= @swap1 and op <= @swap16 do
213242
n = op - @swap1 + 1
214-
{:ok, %{name: "SWAP#{n}", inputs: n + 1, outputs: n + 1, swap_depth: n}}
243+
{:ok, %{name: "SWAP#{n}", inputs: n + 1, outputs: n + 1, swap_depth: n, module: ControlFlow}}
215244
end
216245

217246
def info(op) do

0 commit comments

Comments
 (0)