Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
49 changes: 49 additions & 0 deletions packages/web/docs/concepts/programs.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,55 @@ Contexts can be composed using:
This composition enables describing complex scenarios like conditional variable
assignments or function inlining.

## Function call contexts

Programs answer "what function are we in?" through three context types
that track function boundaries during execution:

- **invoke** — marks an instruction that enters a function. Indicates
the invocation kind (internal jump, external message call, or
contract creation) and provides pointers to call arguments, target
address, gas, and value as appropriate.
- **return** — marks an instruction associated with a successful
return from a function. Provides a pointer to the return data.
- **revert** — marks an instruction associated with a failed call.
May include a pointer to revert reason data or a numeric panic
code.

All three extend a common **function identity** schema with optional
fields for the function's name, declaration source range, and type.
This lets compilers provide as much or as little attribution as
available — from a fully identified `transfer` call down to an
anonymous indirect invocation through a function pointer.

<SchemaExample
schema="program/context/function/invoke"
href="/spec/program/context/function/invoke"
title="Internal function call"
>
{`{
"invoke": {
"identifier": "transfer",
"jump": true,
"target": {
"pointer": { "location": "stack", "slot": 0 }
},
"arguments": {
"pointer": {
"group": [
{ "name": "to", "location": "stack", "slot": 2 },
{ "name": "amount", "location": "stack", "slot": 3 }
]
}
}
}
}`}
</SchemaExample>

A debugger uses these contexts to reconstruct call stacks, show
function names in stepping UI, and display argument/return values
alongside source code.

## What tracing enables

By following contexts through execution, debuggers can provide:
Expand Down
172 changes: 172 additions & 0 deletions packages/web/docs/core-schemas/programs/tracing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,178 @@ b = b + 1;
}`}
/>

## Tracing through a function call

The examples above trace simple straight-line code. Real programs
make function calls. **invoke** and **return** contexts let a
debugger follow execution across function boundaries.

Click **"Try it"** on the example below, then step through the
trace. Watch for **invoke** contexts on the JUMP into `add` and
**return** contexts on the JUMP back to the caller:

<TraceExample
title="Function call and return"
description="Calls an internal add function and stores the result"
source={`name Adder;

define {
function add(a: uint256, b: uint256) -> uint256 {
return a + b;
};
}

storage {
[0] result: uint256;
}

create {
result = 0;
}

code {
result = add(3, 4);
}`}
/>

As you step through, three phases are visible:

### Before the call — setting up arguments

At the call site, the compiler pushes arguments onto the stack and
prepares the jump. The JUMP instruction carries an **invoke**
context identifying the function, its target, and the argument
locations:

<SchemaExample
schema="program/context/function/invoke"
href="/spec/program/context/function/invoke"
title="Invoke context on the JUMP"
>
{`{
"invoke": {
"identifier": "add",
"jump": true,
"target": {
"pointer": { "location": "stack", "slot": 0 }
},
"arguments": {
"pointer": {
"group": [
{ "name": "a", "location": "stack", "slot": 2 },
{ "name": "b", "location": "stack", "slot": 3 }
]
}
}
}
}`}
</SchemaExample>

The debugger now knows it's entering `add` with arguments at stack
slots 2 and 3. A trace viewer can show `add(3, 4)` in the call
stack.

### Inside the function — normal tracing

Inside `add`, instructions carry their own `code` and `variables`
contexts as usual. The debugger shows the source range within the
function body, and parameters `a` and `b` appear as in-scope
variables.

### Returning — the result

When `add` finishes, the JUMP back to the caller carries a
**return** context with a pointer to the result:

<SchemaExample
schema="program/context/function/return"
href="/spec/program/context/function/return"
title="Return context on the JUMP back"
>
{`{
"return": {
"identifier": "add",
"data": {
"pointer": { "location": "stack", "slot": 0 }
}
}
}`}
</SchemaExample>

The debugger pops `add` from the call stack and can display the
return value (7).

### External calls and reverts

The same pattern applies to external message calls, but with
additional fields. An external CALL instruction carries gas, value,
and input data pointers:

<SchemaExample
schema="program/context/function/invoke"
href="/spec/program/context/function/invoke"
title="External call (CALL)"
>
{`{
"invoke": {
"identifier": "balanceOf",
"message": true,
"target": {
"pointer": { "location": "stack", "slot": 1 }
},
"gas": {
"pointer": { "location": "stack", "slot": 0 }
},
"input": {
"pointer": {
"group": [
{ "name": "selector", "location": "memory",
"offset": "0x80", "length": 4 },
{ "name": "arguments", "location": "memory",
"offset": "0x84", "length": "0x20" }
]
}
}
}
}`}
</SchemaExample>

If the call reverts, a **revert** context captures the reason:

<SchemaExample
schema="program/context/function/revert"
href="/spec/program/context/function/revert"
title="Revert with reason"
>
{`{
"revert": {
"identifier": "transfer",
"reason": {
"pointer": {
"location": "memory",
"offset": "0x80",
"length": "0x64"
}
}
}
}`}
</SchemaExample>

For built-in assertion failures, the compiler can provide a panic
code instead of (or alongside) a reason pointer:

<SchemaExample
schema="program/context/function/revert"
href="/spec/program/context/function/revert"
title="Arithmetic overflow panic"
>
{`{
"revert": {
"panic": 17
}
}`}
</SchemaExample>

## Trace data structure

A trace step captures the EVM state at a single point:
Expand Down
6 changes: 5 additions & 1 deletion packages/web/spec/program/context/function/function.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import SchemaViewer from "@site/src/components/SchemaViewer";
# Function identity

Function contexts (invoke, return, revert) share a common set of
identity fields for the function being called.
identity fields for the function being called. All fields are
optional, allowing compilers to provide as much or as little
detail as available — from a fully named function with source
location and type, down to an empty object for an anonymous
indirect call.

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/function" }}
Expand Down
5 changes: 5 additions & 0 deletions packages/web/spec/program/context/function/return.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import SchemaViewer from "@site/src/components/SchemaViewer";

# Return contexts

A return context marks an instruction associated with a successful
function return. It extends the function identity schema with a
pointer to the return data and, for external calls, the success
status.

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/function/return" }}
/>
5 changes: 5 additions & 0 deletions packages/web/spec/program/context/function/revert.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import SchemaViewer from "@site/src/components/SchemaViewer";

# Revert contexts

A revert context marks an instruction associated with a function
revert. It extends the function identity schema with an optional
pointer to revert reason data and/or a numeric panic code for
built-in assertion failures.

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/function/revert" }}
/>
Loading