# `EEVM.Interpreter.InstructionTable`
[🔗](https://github.com/mw2000/eevm/blob/v0.1.0/lib/eevm/interpreter/instruction_table.ex#L1)

Per-hardfork instruction tables, built at compile time, and the programs
decoded from them.

A table is a 256-element tuple indexed by opcode byte. Each entry is an
instruction `{opcode, static_gas, min_depth, max_depth, arg}`:

- `opcode` — the opcode `EEVM.Interpreter` dispatches on
- `static_gas` — the cost the interpreter charges before dispatch, from
  `EEVM.Gas.Static` for that hardfork
- `min_depth` / `max_depth` — the stack depths the opcode can run at: it
  pops the Registry's `:inputs`, and must not leave more than 1024 items
- `arg` — `nil` in the table; a decoded program puts PUSH data and jump
  targets here

Bytes the hardfork does not define — unassigned bytes, and opcodes whose
activating EIP (the Registry's `:eip` field) is not yet enabled — get the
INVALID (0xFE) entry, opcode included, so they halt exactly like INVALID
even when their module implements them on later forks.

`EEVM.Interpreter.MachineState.Env` resolves the table once per frame from
the active hardfork, and `decode/3` turns a contract's code into a program
of these instructions at most once per transaction, so the per-opcode path
never checks which fork is active or decodes a byte. Until code has run long
enough to repay decoding, `fetch/3` reads its instructions one at a time
and `jumpdests/1` checks its jumps (#210).

# `instruction`

```elixir
@type instruction() ::
  {opcode(), non_neg_integer(), non_neg_integer(), non_neg_integer(), term()}
```

# `opcode`

```elixir
@type opcode() :: non_neg_integer() | :jump_to | :jumpi_to
```

An opcode, or one of the fused PUSH+JUMP(I) instructions of a program.

# `program`

```elixir
@type program() :: tuple()
```

A decoded program: `elem(program, pc)` is the instruction at `pc`,
`:push_data` inside PUSH data, and `:end` past the end of the code.

# `t`

```elixir
@type t() :: tuple()
```

# `decode`

```elixir
@spec decode(binary(), t(), boolean()) :: program()
```

Decodes `code` into a program for `table`, with one entry per code byte.

Opcodes are the table's own instructions, so most entries share them. A
PUSH carries its value in `arg`, and its data bytes are `:push_data`, so a
jump can land only on a real JUMPDEST (Yellow Paper §9.4.3). A PUSH cut
short by the end of the code reads the missing bytes as zeros.

Without `tracing?`, a PUSH directly followed by JUMP or JUMPI becomes one
instruction, `:jump_to` or `:jumpi_to`, which charges both opcodes' gas and
never puts the destination on the stack: Solidity emits that pair for nearly
every branch and internal call. Its `arg` keeps the PUSH, which the
interpreter runs on its own when the pair cannot run whole, so a failure is
reported at the opcode that fails.

With `tracing?`, nothing is fused, so the tracer sees every opcode, and
each instruction is wrapped as `{:trace, byte, instruction}`: the
interpreter records a trace step for the wrapper, then runs the
instruction. Untraced programs carry no tracing cost at all.

    iex> alias EEVM.Interpreter.InstructionTable
    iex> table = InstructionTable.for_spec(:prague)
    iex> code = <<0x60, 0x03, 0x56, 0x5B>>
    iex> program = InstructionTable.decode(code, table, false)
    iex> elem(program, 0)
    {:jump_to, 11, 0, 1023, {{0x60, 3, 0, 1023, 3}, 3}}
    iex> {elem(program, 1), elem(program, 4)}
    {:push_data, :end}
    iex> InstructionTable.decode(code, table, true) |> elem(0)
    {:trace, 0x60, {0x60, 3, 0, 1023, 3}}

# `fetch`

```elixir
@spec fetch(binary(), non_neg_integer(), t()) :: instruction() | :end
```

Reads the instruction at `pc` straight from `code`, as `decode/3` would
without `tracing?` but with nothing fused, or `:end` past the end of the code.

This runs code that has not been decoded yet, one instruction at a time.
`pc` must be an instruction's position, not PUSH data.

    iex> alias EEVM.Interpreter.InstructionTable
    iex> table = InstructionTable.for_spec(:prague)
    iex> InstructionTable.fetch(<<0x61, 0x01>>, 0, table)
    {0x61, 3, 0, 1023, 0x0100}
    iex> InstructionTable.fetch(<<0x61, 0x01>>, 3, table)
    :end

# `for_spec`

```elixir
@spec for_spec(EEVM.HardforkConfig.spec_id()) :: t()
```

Returns the instruction table for `spec_id`.

# `jumpdests`

```elixir
@spec jumpdests(binary()) :: binary()
```

Returns the JUMPDEST map of `code`: one byte per code byte, 1 where a
JUMPDEST opcode starts and 0 elsewhere, including PUSH data (Yellow Paper
§9.4.3).

Jumps in code that has not been decoded yet are checked against it. One
byte per position rather than one bit keeps each check a single
`:binary.at/2`.

    iex> EEVM.Interpreter.InstructionTable.jumpdests(<<0x60, 0x5B, 0x5B>>)
    <<0, 0, 1>>

---

*Consult [api-reference.md](api-reference.md) for complete listing*
