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
20 changes: 20 additions & 0 deletions packages/bugc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@ support. Changes to the specification itself are tracked in the root
Before, the pointer named the whole word `x`, and `x-data` took its
offset and length as `~quotient` and `~remainder` of that word by
2^128. The bytes the pointer reads are the same ([#398]).
- A pointer shape that repeats is now a pointer template, defined
once, and a variable's pointer refers to it by name:
`{ "define": { ... }, "in": { "template": ..., "yields": { ... } } }`.
The bytecode output has a new field, `resources`: an
`ethdebug/format/info/resources` object whose `pointers` holds the
templates. Dereference a pointer with them:
`dereference(pointer, { state, templates: resources.pointers })`.
The templates are `frame-local` (a local in the call frame);
`frame-calldata-local`, `memory-calldata-local` and
`stack-calldata-local` (bytes or a string in calldata, by where the
local's word is); `memory-bytes` and `memory-<type>` (the data of a
string, bytes, array or struct in memory); `storage-bytes` (a string
or bytes in storage); and `storage-<type>` (an array, struct or
mapping in storage, with a mapping's entry templates in it). Region
names do not change, and each pointer reads the same bytes as
before. A storage variable of a value type, and a local in a stack
slot or at a static memory offset, keep their inline pointers. The
programs are about half as large. The new example
`intermediate/pointer-shapes.bug` has these shapes ([#399]).

### Fixed

Expand Down Expand Up @@ -606,4 +625,5 @@ First publication.
[#395]: https://github.com/ethdebug/format/pull/395
[#397]: https://github.com/ethdebug/format/pull/397
[#398]: https://github.com/ethdebug/format/pull/398
[#399]: https://github.com/ethdebug/format/pull/399
[#400]: https://github.com/ethdebug/format/pull/400
11 changes: 2 additions & 9 deletions packages/bugc/examples/intermediate/mappings.bug
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,8 @@ code {
variables:
balances:
pointer:
templates:
entry:
expect: [slot, key]
for:
define:
slot:
~keccak256: [{ ~wordsized: key }, { ~wordsized: slot }]
in: { location: storage, slot: slot, name: value }
in: { location: storage, slot: 0 }
define: { slot: 0 }
in: { template: storage-mapping-address-uint256 }
*/

// Read from mapping
Expand Down
61 changes: 61 additions & 0 deletions packages/bugc/examples/intermediate/pointer-shapes.bug
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
name PointerShapes;

// Pointer shapes that refer to shared templates: a dynamic storage
// array of structs, a mapping inside a struct, struct fields named
// like a template's own regions (`length`, `data`), and locals in a
// function's frame that are a string in memory and bytes in calldata

define {
struct Blob {
length: uint64;
data: bytes;
};

struct Entry {
length: uint256;
data: string;
tags: mapping<uint256, uint8>;
};

function record(note: string calldata, raw: bytes calldata) -> uint256 {
let copy: string = note;
entries.push();
blobs.push();
entries[0].length = note.length;
entries[0].data = copy;
entries[0].tags[7] = 3;
blobs[0].length = raw.length as uint64;
blobs[0].data = raw;
let blob: Blob = blobs[0];
return blob.length as uint256;
};
}

storage {
[0] entries: array<Entry>;
[1] blobs: array<Blob>;
[2] byOwner: mapping<address, Entry>;
[3] total: uint256;
}

code {
let len = msg.data[0:32] as bytes32 as uint256;
let note: string calldata = msg.data[32:32 + len] as string calldata;
let raw: bytes calldata = msg.data[32:32 + len] as bytes calldata;
total = record(note, raw);
/*@test recorded
call-data: "000000000000000000000000000000000000000000000000000000000000002d61206e6f7465206c6f6e676572207468616e207468697274792d74776f2062797465732c20746f207370696c6c"
variables:
total:
value: 45
entries:
values:
array-length: 1
length: 45
data-data: "0x61206e6f7465206c6f6e676572207468616e207468697274792d74776f2062797465732c20746f207370696c6c"
blobs:
values:
length: 45
data-data: "0x61206e6f7465206c6f6e676572207468616e207468697274792d74776f2062797465732c20746f207370696c6c"
*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ exports[`the code does not depend on the markers > is the same for every example
"intermediate/phi-nodes.bug at O1": "6d4c6ea207ad3a5c967c8ca80d9a7a594c17ab9fd582856f0f977e766d316b58",
"intermediate/phi-nodes.bug at O2": "6d4c6ea207ad3a5c967c8ca80d9a7a594c17ab9fd582856f0f977e766d316b58",
"intermediate/phi-nodes.bug at O3": "6d4c6ea207ad3a5c967c8ca80d9a7a594c17ab9fd582856f0f977e766d316b58",
"intermediate/pointer-shapes.bug at O0": "0ea8fcf89707f2868284d965ddd743ad96df4091b78642e5cd328fcfac42b6a9",
"intermediate/pointer-shapes.bug at O1": "c1a7b25b6c663855114141be67df907a165c269099f1e2af7666372c62e3830b",
"intermediate/pointer-shapes.bug at O2": "369bc9d3794d4400613f92b5d614080a844c4e22968d149475142354e5df3861",
"intermediate/pointer-shapes.bug at O3": "024072c4c363eee5bd0488cf46d168691234fb6926f2771bfcf483f68e0b5c37",
"intermediate/scopes.bug at O0": "6f7fab968d0fdf458d2536c83abbb3e36a2291d4d3c32b6c4874831a7d28e9bc",
"intermediate/scopes.bug at O1": "9e9e0d1d5e3b39e7ddb512bb5de2ca8d3d6d32fd12a211a68ed0c27ac273b0dc",
"intermediate/scopes.bug at O2": "990ea851a89f3cf194506deb42a9f0640fda9b4da8f6984219622b2f6f7538e3",
Expand Down
20 changes: 15 additions & 5 deletions packages/bugc/src/evmgen/debug/local-variables.soundness.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1684,13 +1684,23 @@ describe.each(levels)("a calldata reference's word at O%i", (level) => {
const context = run.instructionAt(run.steps[k])?.context;
for (const entry of localsOf(context)) {
const pointer = entry.pointer as Format.Pointer | undefined;
const json = JSON.stringify(pointer);
if (!pointer || !json.includes('"calldata"')) continue;
expect(json).not.toMatch(/~quotient|~remainder/);

const name = entry.identifier as string;
if (!pointer) continue;
const state = run.stateAt(k + 1);
const view = await (await dereference(pointer, { state })).view(state);
const { templates } = run;
const view = await (
await dereference(pointer, { state, templates })
).view(state);
const data = view.regions.named(`${name}-data`);
if (data[0]?.location !== "calldata") continue;
// (the pointer, and the templates it refers to)
const referred = [
...JSON.stringify(pointer).matchAll(/"template":"([^"]+)"/g),
].map(([, template]) => templates[template]);
expect(JSON.stringify([pointer, referred])).not.toMatch(
/~quotient|~remainder/,
);

const [offset, length] = ["offset", "length"].map((part) => {
const regions = view.regions.named(`${name}-${part}`);
expect(regions).toHaveLength(1);
Expand Down
19 changes: 11 additions & 8 deletions packages/bugc/src/evmgen/debug/local-variables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,19 @@ function localEntries(
return byId;
}

/** True if a pointer is a frame-relative group (name "-frame" + ~read). */
/**
* True if a pointer is frame-relative: a reference to a frame template
* (whose regions read the frame pointer, "-frame"), at an offset.
*/
function isFrameRelative(pointer: unknown): boolean {
if (!pointer || typeof pointer !== "object") return false;
const group = (pointer as { group?: unknown[] }).group;
if (!Array.isArray(group) || group.length < 2) return false;
const frame = group[0] as Record<string, unknown>;
const data = group[1] as Record<string, unknown>;
if (frame.name !== "-frame" || frame.location !== "memory") return false;
const offset = data.offset as Record<string, unknown> | undefined;
return !!offset && Array.isArray(offset["~sum"]);
const { define, in: in_ } = pointer as {
define?: { offset?: unknown };
in?: { template?: string };
};
return (
typeof define?.offset === "number" && !!in_?.template?.startsWith("frame-")
);
}

describe("local-variable debug emission (memory-homed, O0)", () => {
Expand Down
Loading
Loading