Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
bddfa6f
feat(inspector): configure stepped opcodes
DaniPopes May 29, 2026
c97982c
perf(inspector): run full steps in dispatch loop
DaniPopes May 29, 2026
a03c018
fix(inspector): specialize full-step dispatch
DaniPopes May 29, 2026
02d0ec5
refactor(inspector): use normal table for full steps
DaniPopes May 29, 2026
39b5ed9
perf(inspector): skip steps for empty opcode set
DaniPopes May 29, 2026
e95cebd
chore(config): implement execution debug manually
DaniPopes May 29, 2026
0bfea7c
refactor(inspector): add config opcode-set builder
DaniPopes May 30, 2026
6d1b432
feat(inspector): request config reconfigure
DaniPopes May 30, 2026
9604e73
Merge remote-tracking branch 'origin/main' into dani/inspector-opcode…
DaniPopes May 30, 2026
c1161bf
chore: format merged inspector imports
DaniPopes May 30, 2026
81e6a12
test(inspector): cover nested reconfigure
DaniPopes May 30, 2026
7f3edbb
clean
DaniPopes May 30, 2026
050a242
test(inspector): model cheatcode reconfigure
DaniPopes May 30, 2026
c3ed774
test(inspector): accept slices in push helper
DaniPopes May 30, 2026
b1382ef
chore: re-register only when None
DaniPopes May 30, 2026
62c3764
refactor(inspector): pass interpreter to call hooks
DaniPopes May 30, 2026
435368e
refactor(inspector): pass interpreter to call hooks
DaniPopes May 30, 2026
2269ac1
Merge branch 'dani/inspector-call-interpreter' into dani/inspector-op…
DaniPopes May 30, 2026
50baac1
Merge remote-tracking branch 'origin/main' into dani/inspector-opcode…
DaniPopes Jun 27, 2026
0c7291b
test: update inspector opcode config tests
DaniPopes Jun 27, 2026
9c0b153
Merge remote-tracking branch 'origin/main' into dani/inspector-opcode…
DaniPopes Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ This is a work-in-progress repo with no public API stability guarantees. Do not
backwards-compatibility aliases, deprecated wrappers, compatibility shims, or similar
transitional API layers unless explicitly requested.

## Code Style

- for public structs that should be non-exhaustive, prefer the repo pattern:
a hidden public `_non_exhaustive: ()` field initialized by constructors, not
`#[non_exhaustive]`, unless the surrounding code already uses the attribute.

For all work under `crates/jit`, follow `crates/jit/AGENTS.md` in addition to
this root file.

Expand Down
123 changes: 91 additions & 32 deletions crates/evm2/src/evm/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
use crate::{
OpcodeConfig, SpecId,
ethereum::RecoveredTxEnvelope,
evm::inspector::{InspectorConfig, OpcodeSet},
interpreter::{
Host,
dispatch::{ConfigInstrTables, InstrTable, SelectorInstrTables},
dispatch::{self, ConfigInstrTables, InstrTable, SelectorInstrTables},
},
version::Version,
};
use derive_where::derive_where;
use alloc::boxed::Box;
use core::fmt;

/// Runtime EVM type family.
///
Expand Down Expand Up @@ -115,87 +117,144 @@ where
///
/// Bundles the active runtime `Version` with the finalized instruction dispatch table selected for
/// an EVM instance. This is the data passed to the interpreter when it runs.
#[derive_where(Debug)]
pub struct ExecutionConfig<T: EvmTypes> {
inner: Box<ExecutionConfigInner<T>>,
}

struct ExecutionConfigInner<T: EvmTypes> {
base_spec_id: SpecId,
pub(crate) version: Version,
#[derive_where(skip)]
pub(crate) instructions: &'static InstrTable<T>,
#[derive_where(skip)]
pub(crate) inspect_instructions: &'static InstrTable<T>,
version: Version,
instructions: &'static InstrTable<T>,
inspect_instructions: InstrTable<T>,
inspect_instruction_source: &'static InstrTable<T>,
}

impl<T: EvmTypes> Clone for ExecutionConfig<T> {
#[inline]
fn clone(&self) -> Self {
*self
Self { inner: self.inner.clone() }
}
}

impl<T: EvmTypes> Copy for ExecutionConfig<T> {}
impl<T: EvmTypes> fmt::Debug for ExecutionConfig<T> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExecutionConfig")
.field("version", &self.inner.version)
.finish_non_exhaustive()
}
}

impl<T: EvmTypes> Clone for ExecutionConfigInner<T> {
#[inline]
fn clone(&self) -> Self {
Self {
base_spec_id: self.base_spec_id,
version: self.version,
instructions: self.instructions,
inspect_instructions: self.inspect_instructions,
inspect_instruction_source: self.inspect_instruction_source,
}
}
}

impl<T: EvmTypes> ExecutionConfig<T> {
/// Creates an execution config for a base `SpecId` through selector `F`.
///
/// This uses the selector's base inherited tables by passing `u32::MAX` as the custom-spec
/// sentinel.
#[inline]
pub(crate) const fn for_base_spec<F: EvmConfigSelector<T>>(base_spec_id: SpecId) -> Self {
pub(crate) fn for_base_spec<F: EvmConfigSelector<T>>(base_spec_id: SpecId) -> Self {
Self::for_custom_spec::<F, { u32::MAX }>(base_spec_id)
}

/// Creates an execution config for selector custom spec `CUSTOM_SPEC_ID` and base `SpecId`.
#[inline]
pub(crate) const fn for_custom_spec<F: EvmConfigSelector<T>, const CUSTOM_SPEC_ID: u32>(
pub(crate) fn for_custom_spec<F: EvmConfigSelector<T>, const CUSTOM_SPEC_ID: u32>(
base_spec_id: SpecId,
) -> Self {
let i = base_spec_id as usize;
let inspect_instruction_source =
&SelectorInstrTables::<T, F, CUSTOM_SPEC_ID>::INSPECT_INSTRUCTIONS[i];
Self {
base_spec_id,
version: Version::new(base_spec_id),
instructions: &SelectorInstrTables::<T, F, CUSTOM_SPEC_ID>::INSTRUCTIONS[i],
inspect_instructions:
&SelectorInstrTables::<T, F, CUSTOM_SPEC_ID>::INSPECT_INSTRUCTIONS[i],
inner: Box::new(ExecutionConfigInner {
base_spec_id,
version: Version::new(base_spec_id),
instructions: &SelectorInstrTables::<T, F, CUSTOM_SPEC_ID>::INSTRUCTIONS[i],
inspect_instructions: *inspect_instruction_source,
inspect_instruction_source,
}),
}
}

/// Creates an execution config for concrete EVM configuration `C`.
#[inline]
pub const fn for_config<C: EvmConfig<T>>() -> Self {
pub fn for_config<C: EvmConfig<T>>() -> Self {
let base_spec_id = C::BASE_SPEC_ID;
let inspect_instruction_source = ConfigInstrTables::<T, C>::INSPECT_INSTRUCTIONS;
Self {
base_spec_id,
version: Version::new(base_spec_id),
instructions: ConfigInstrTables::<T, C>::INSTRUCTIONS,
inspect_instructions: ConfigInstrTables::<T, C>::INSPECT_INSTRUCTIONS,
inner: Box::new(ExecutionConfigInner {
base_spec_id,
version: Version::new(base_spec_id),
instructions: ConfigInstrTables::<T, C>::INSTRUCTIONS,
inspect_instructions: *inspect_instruction_source,
inspect_instruction_source,
}),
}
}

/// Creates an execution config for `spec_id` with dynamic runtime version data.
#[inline]
pub fn for_spec_and_version(spec_id: T::SpecId, version: Version) -> Self {
let config = <T::ConfigSelector as EvmConfigSelector<T>>::execution_config(spec_id);
assert_eq!(spec_id.into(), config.base_spec_id, "execution config spec mismatch");
assert_eq!(spec_id.into(), config.base_spec_id(), "execution config spec mismatch");
config.with_version(version)
}

/// Replaces the runtime version data while keeping the same dispatch table.
#[inline]
pub const fn with_version(mut self, version: Version) -> Self {
self.version = version;
pub fn with_version(mut self, version: Version) -> Self {
self.inner.version = version;
self
}

/// Returns the active base specification ID.
#[inline]
pub const fn base_spec_id(&self) -> SpecId {
self.base_spec_id
self.inner.base_spec_id
}

/// Returns the active EVM version.
#[inline]
pub const fn version(&self) -> &Version {
&self.version
pub fn version(&self) -> &Version {
&self.inner.version
}

#[inline]
pub(crate) fn instructions(&self) -> &InstrTable<T> {
self.inner.instructions
}

#[inline]
pub(crate) fn inspect_instructions(&self) -> &InstrTable<T> {
&self.inner.inspect_instructions
}

#[inline]
pub(crate) fn register_inspector(&mut self, inspector_config: &InspectorConfig) {
if inspector_config.set.is_empty() {
self.inner.inspect_instructions = *self.inner.instructions;
return;
}
if inspector_config.set == OpcodeSet::ALL {
self.inner.inspect_instructions = if cfg!(tco) {
*self.inner.inspect_instruction_source
} else {
*self.inner.instructions
};
return;
}
self.inner.inspect_instructions = dispatch::make_inspect_table(
self.inner.instructions,
self.inner.inspect_instruction_source,
&inspector_config.set,
);
}
}

Expand Down
Loading
Loading