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
12 changes: 12 additions & 0 deletions packages/format/src/types/program/context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,16 @@ testSchemaGuards("ethdebug/format/program/context", [
schema: "schema:ethdebug/format/program/context/remark",
guard: Context.isRemark
},
{
schema: "schema:ethdebug/format/program/context/pick",
guard: Context.isPick
},
{
schema: "schema:ethdebug/format/program/context/gather",
guard: Context.isGather
},
{
schema: "schema:ethdebug/format/program/context/frame",
guard: Context.isFrame
},
] as const);
34 changes: 33 additions & 1 deletion packages/format/src/types/program/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import { Pointer, isPointer } from "../pointer";
export type Context =
| Context.Code
| Context.Variables
| Context.Remark;
| Context.Remark
| Context.Pick
| Context.Gather
| Context.Frame;

export const isContext = (value: unknown): value is Context => [
Context.isCode,
Context.isVariables,
Context.isRemark,
Context.isPick,
Context.isFrame,
Context.isGather,
].some(guard => guard(value));

export namespace Context {
Expand Down Expand Up @@ -76,4 +82,30 @@ export namespace Context {
export const isRemark = (value: unknown): value is Remark =>
typeof value === "object" && !!value &&
"remark" in value && typeof value.remark === "string";

export interface Pick {
pick: Context[];
}

export const isPick = (value: unknown): value is Pick =>
typeof value === "object" && !!value &&
"pick" in value && Array.isArray(value.pick) &&
value.pick.every(isContext);

export interface Gather {
gather: Context[];
}

export const isGather = (value: unknown): value is Gather =>
typeof value === "object" && !!value &&
"gather" in value && Array.isArray(value.gather) &&
value.gather.every(isContext);

export interface Frame {
frame: string;
}

export const isFrame = (value: unknown): value is Frame =>
typeof value === "object" && !!value &&
"frame" in value && typeof value.frame === "string";
}
11 changes: 11 additions & 0 deletions packages/web/spec/program/context/frame.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
sidebar_position: 7
---

import SchemaViewer from "@site/src/components/SchemaViewer";

# Frame contexts

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/frame" }}
/>
11 changes: 11 additions & 0 deletions packages/web/spec/program/context/gather.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
sidebar_position: 7
---

import SchemaViewer from "@site/src/components/SchemaViewer";

# Gather multiple contexts

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/gather" }}
/>
11 changes: 11 additions & 0 deletions packages/web/spec/program/context/pick.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
sidebar_position: 7
---

import SchemaViewer from "@site/src/components/SchemaViewer";

# Pick one of several contexts

<SchemaViewer
schema={{ id: "schema:ethdebug/format/program/context/pick" }}
/>
7 changes: 5 additions & 2 deletions packages/web/spec/program/example.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ code {

let localValue = storedValue + 1;
storedValue = localValue;
return;
}
`}]}
instructions={[
Expand Down Expand Up @@ -285,8 +286,10 @@ code {
mnemonic: "JUMPDEST"
},
context: ({ findSourceRange }) => ({
code: findSourceRange("return;"),
remark: "skip to here if not enough paid"
pick: [
{ code: findSourceRange("return;") },
{ code: findSourceRange("return;", { after: "return;" }) },
]
})
}
]}
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ const programSchemaIndex: SchemaIndex = {
},

...(
["code", "variables", "remark"].map(name => ({
["code", "variables", "remark", "pick", "gather", "frame"].map(name => ({
[`schema:ethdebug/format/program/context/${name}`]: {
href: `/spec/program/context/${name}`
}
Expand Down
67 changes: 67 additions & 0 deletions packages/web/src/theme/ProgramExample/Details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Admonition from "@theme/Admonition";
import Link from "@docusaurus/Link";
import { Program } from "@ethdebug/format";
import { useProgramExampleContext } from "./ProgramExampleContext";
import { HighlightedInstruction } from "./HighlightedInstruction";
import { Variables } from "./Variables";

// imported for style legend
import "./SourceContents.css";

export interface Props {
}

export function Details(props: Props): JSX.Element {
const { highlightedInstruction, highlightMode } = useProgramExampleContext();

if (highlightMode === "simple" || !highlightedInstruction) {
return <>
<h3>Details</h3>
<BasicAdmonition />
</>;
}


return <>
<h3>Details</h3>
<InstructionAdmonition instruction={highlightedInstruction} />
<details>
<summary>See full <strong>ethdebug/format/program/instruction</strong> object</summary>
<HighlightedInstruction />
</details>
</>;
}

interface InstructionAdmonitionProps {
instruction: Program.Instruction;
}
function InstructionAdmonition({
instruction
}: InstructionAdmonitionProps): JSX.Element {
return <Admonition type="info">
<p>
The selected instruction provides the following <Link to="/spec/program/context">
<strong>ethdebug/format</strong> Program contexts
</Link>:
</p>
<ul>
<li>
<strong>Code context</strong> is highlighted <span className="highlighted-code">in this
style</span> above.
</li>
<li>
<strong>Variables context</strong> is indicated by variable declarations
highlighted <span className="highlighted-variable-declaration">in this
style</span> above.
</li>
</ul>
</Admonition>;
}

function BasicAdmonition(props: {}): JSX.Element {
return <Admonition type="tip">
Select an instruction offset to see associated <strong>
ethdebug/format
</strong> debugging information.
</Admonition>;
}
5 changes: 5 additions & 0 deletions packages/web/src/theme/ProgramExample/SourceContents.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
background-color: var(--ifm-color-primary-lightest);
}

.highlighted-ambiguous-code {
font-weight: bold;
background-color: var(--ifm-color-warning-lightest);
}

.highlighted-variable-declaration {
text-decoration: underline;
text-decoration-style: wavy;
Expand Down
26 changes: 23 additions & 3 deletions packages/web/src/theme/ProgramExample/SourceContents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ export function SourceContents(

const simpleDecorations = Program.Context.isCode(context)
? decorateCodeContext(context, source)
: [];
: Program.Context.isPick(context)
? decoratePickContext(context, source)
: [];

const detailedDecorations = [
...simpleDecorations,
Expand All @@ -56,7 +58,8 @@ export function SourceContents(

function decorateCodeContext(
{ code }: Program.Context.Code,
source: Materials.Source
source: Materials.Source,
className: string = "highlighted-code"
): Shiki.DecorationItem[] {
const { offset, length } = normalizeRange(code.range, source);

Expand All @@ -65,12 +68,29 @@ function decorateCodeContext(
start: offset,
end: offset + length,
properties: {
class: "highlighted-code"
class: className
}
}
];
}

function decoratePickContext(
{ pick }: Program.Context.Pick,
source: Materials.Source
): Shiki.DecorationItem[] {
// HACK this only supports picking from a choice of several different code
// contexts
if (!pick.every(Program.Context.isCode)) {
console.warn("decoratePickContext encountered non-code contexts in pick array. These will be ignored.");
return [];
}

return pick.flatMap(
(choice) => decorateCodeContext(choice, source, "highlighted-ambiguous-code")
);
}


function decorateVariablesContext(
{ variables }: Program.Context.Variables,
source: Materials.Source
Expand Down
48 changes: 2 additions & 46 deletions packages/web/src/theme/ProgramExample/Viewer.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,16 @@
import Admonition from "@theme/Admonition";
import Link from "@docusaurus/Link";
import { useProgramExampleContext } from "./ProgramExampleContext";
import { SourceContents } from "./SourceContents";
import { Opcodes } from "./Opcodes";
import { HighlightedInstruction } from "./HighlightedInstruction";
import { Details } from "./Details";
import { Variables } from "./Variables";

import "./Viewer.css";
import "./SourceContents.css";

export interface Props {
}

export function Viewer(props: Props): JSX.Element {
const { highlightedInstruction, highlightMode } = useProgramExampleContext();

const basicAdmonition = <Admonition type="tip">
Select an instruction offset to see associated <strong>
ethdebug/format
</strong> debugging information.
</Admonition>;

const detailedAdmonition = <Admonition type="info">
<p>
The selected instruction provides the following <Link to="/spec/program/context">
<strong>ethdebug/format</strong> Program contexts
</Link>:
</p>
<ul>
<li>
<strong>Code context</strong> is highlighted <span className="highlighted-code">in this
style</span> above.
</li>
<li>
<strong>Variables context</strong> is indicated by variable declarations
highlighted <span className="highlighted-variable-declaration">in this
style</span> above.
</li>
</ul>
</Admonition>;

const details = highlightedInstruction && highlightMode === "detailed"
? <>
<h3>Details</h3>
{detailedAdmonition}
<details>
<summary>See full <strong>ethdebug/format/program/instruction</strong> object</summary>
<HighlightedInstruction />
</details>
</>
: <>
<h3>Details</h3>
{basicAdmonition}
</>;


return <>
<h2>Interactive example</h2>
<div className="viewer-row">
Expand All @@ -67,6 +23,6 @@ export function Viewer(props: Props): JSX.Element {
<Opcodes />
</div>
</div>
{details}
<Details />
</>;
}
38 changes: 27 additions & 11 deletions schemas/program/context.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,21 @@ description: |
information about the high-level runtime execution state at a specific point
in a program's bytecode.

This schema defines the structure for encoding compile-time knowledge about
the high-level runtime state. Contexts are treated as units of information
that may encompass multiple related or unrelated facts about the program's
state. This may include, e.g., source mapping information (via the `"code"`
property) or information about known variable allocations, etc.

The interpretation of a context depends on its properties and its
relationship to program elements such as instructions or control flow
structures. For example, a context associated with an instruction may
indicate that the specified conditions hold true following the execution of
that instruction.
This schema provides a formal specification for this format's model of what
information can be known at compile-time about the high-level runtime. This
includes data such as a particular machine instruction's source mapping or
what variables exist in runtime state following some instruction.

The context object supports dynamic context combination and selection through
the use of `gather`, and `pick` properties. This allows for flexible
composition and extraction of context information.

Contexts serve as a bridge between low-level EVM execution and high-level
language constructs. Debuggers can use these compile-time guarantees to
maintain a coherent view of the high-level language runtime throughout
program execution. This enables debugging tools to map execution points to
source code, reconstruct variable states, provide meaningful stack traces,
and offer insights into control flow and data structures.

type: object

Expand All @@ -34,6 +38,18 @@ allOf:
required: ["remark"]
then:
$ref: "schema:ethdebug/format/program/context/remark"
- if:
required: ["pick"]
then:
$ref: "schema:ethdebug/format/program/context/pick"
- if:
required: ["gather"]
then:
$ref: "schema:ethdebug/format/program/context/gather"
- if:
required: ["frame"]
then:
$ref: "schema:ethdebug/format/program/context/frame"

unevaluatedProperties: false

Expand Down
Loading