Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Define pointer templates #103

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
3 changes: 3 additions & 0 deletions packages/pointers/src/dereference/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { processPointer, type ProcessOptions } from "./process.js";
* for a particular pointer at runtime.
*/
export interface GenerateRegionsOptions {
templates: Pointer.Templates;
state: Machine.State;
initialStackLength: bigint;
}
Expand Down Expand Up @@ -62,6 +63,7 @@ export async function* generateRegions(
}

async function initializeProcessOptions({
templates,
state,
initialStackLength
}: GenerateRegionsOptions): Promise<ProcessOptions> {
Expand All @@ -72,6 +74,7 @@ async function initializeProcessOptions({
const variables: Record<string, Data> = {};

return {
templates,
state,
stackLengthChange,
regions,
Expand Down
31 changes: 31 additions & 0 deletions packages/pointers/src/dereference/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,35 @@ describe("dereference", () => {
expect(regions[0].offset).toEqual(Data.fromNumber(0));
expect(regions[0].length).toEqual(Data.fromNumber(32));
});

it("works for templates", async () => {
const templates: Pointer.Templates = {
"memory-range": {
expect: ["offset", "length"],
for: {
location: "memory",
offset: "offset",
length: "length"
}
}
};

const pointer: Pointer = {
define: {
"offset": 0,
"length": 32
},
in: {
template: "memory-range"
}
};

const cursor = await dereference(pointer, { templates });

const { regions } = await cursor.view(state);

expect(regions).toHaveLength(1);
expect(regions[0].offset).toEqual(Data.fromNumber(0));
expect(regions[0].length).toEqual(Data.fromNumber(32));
});
});
7 changes: 6 additions & 1 deletion packages/pointers/src/dereference/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface DereferenceOptions {
* Required for any pointers that reference the stack.
*/
state?: Machine.State;
templates?: Pointer.Templates
}

/**
Expand Down Expand Up @@ -43,11 +44,15 @@ export async function dereference(
* `generateRegions()` will potentially need.
*/
async function initializeGenerateRegionsOptions({
templates = {},
state: initialState
}: DereferenceOptions): Promise<Omit<GenerateRegionsOptions, "state">> {
const initialStackLength = initialState
? await initialState.stack.length
: 0n;

return { initialStackLength };
return {
templates,
initialStackLength
};
}
43 changes: 43 additions & 0 deletions packages/pointers/src/dereference/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { adjustStackLength, evaluateRegion } from "./region.js";
* Contextual information for use within a pointer dereference process
*/
export interface ProcessOptions {
templates: Pointer.Templates;
state: Machine.State;
stackLengthChange: bigint;
regions: Record<string, Cursor.Region>;
Expand Down Expand Up @@ -56,6 +57,10 @@ export async function* processPointer(
return yield* processScope(collection, options);
}

if (Pointer.Collection.isReference(collection)) {
return yield* processReference(collection, options);
}

console.error("%s", JSON.stringify(pointer, undefined, 2));
throw new Error("Unexpected unknown kind of pointer");
}
Expand Down Expand Up @@ -150,3 +155,41 @@ async function* processScope(
Memo.dereferencePointer(in_)
];
}

async function* processReference(
collection: Pointer.Collection.Reference,
options: ProcessOptions
): Process {
const { template: templateName } = collection;

const { templates, variables } = options;

const template = templates[templateName];

if (!template) {
throw new Error(
`Unknown pointer template named ${templateName}`
);
}

const {
expect: expectedVariables,
for: pointer
} = template;

const definedVariables = new Set(Object.keys(variables));
const missingVariables = expectedVariables
.filter(identifier => !definedVariables.has(identifier));

if (missingVariables.length > 0) {
throw new Error([
`Invalid reference to template named ${templateName}; missing expected `,
`variables with identifiers: ${missingVariables.join(", ")}. `,
`Please ensure these variables are defined prior to this reference.`
].join(""));
}

return [
Memo.dereferencePointer(pointer)
];
}
6 changes: 6 additions & 0 deletions packages/pointers/src/pointer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ describe("type guards", () => {
},
guard: isPointer
},
{
schema: {
id: "schema:ethdebug/format/pointer/template"
},
guard: Pointer.isTemplate
},
] as const;

it.each(schemaGuards)("matches its examples", ({
Expand Down
43 changes: 40 additions & 3 deletions packages/pointers/src/pointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,16 @@ export namespace Pointer {
| Collection.Group
| Collection.List
| Collection.Conditional
| Collection.Scope;
| Collection.Scope
| Collection.Reference;

export const isCollection = (value: unknown): value is Collection =>
[
Collection.isGroup,
Collection.isList,
Collection.isConditional,
Collection.isScope
Collection.isScope,
Collection.isReference
].some(guard => guard(value));

export namespace Collection {
Expand Down Expand Up @@ -202,6 +205,16 @@ export namespace Pointer {
Object.keys(value.define).every(key => isIdentifier(key)) &&
"in" in value &&
isPointer(value.in);

export interface Reference {
template: string;
}

export const isReference = (value: unknown): value is Reference =>
!!value &&
typeof value === "object" &&
"template" in value &&
typeof value.template === "string" && !!value.template
}

export type Expression =
Expand Down Expand Up @@ -421,8 +434,32 @@ export namespace Pointer {
"$wordsized" in value &&
typeof value.$wordsized !== "undefined" &&
isExpression(value.$wordsized);
}
}

export interface Templates {
[identifier: string]: Pointer.Template;
}

}
export const isTemplates = (value: unknown): value is Templates =>
!!value &&
typeof value === "object" &&
Object.keys(value).every(isIdentifier) &&
Object.values(value).every(isTemplate);

export interface Template {
expect: string[];
for: Pointer;
}

export const isTemplate = (value: unknown): value is Template =>
!!value &&
typeof value === "object" &&
Object.keys(value).length === 2 &&
"expect" in value &&
value.expect instanceof Array &&
value.expect.every(isIdentifier) &&
"for" in value &&
isPointer(value.for);

}
8 changes: 7 additions & 1 deletion packages/pointers/test/observe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ export interface ObserveTraceOptions<V> {
*/
pointer: Pointer;

/**
* Pointer templates that may be referenced by the given pointer
*/
templates?: Pointer.Templates;

/**
* The necessary metadata and the Solidity source code for a contract whose
* `constructor()` manages the lifecycle of the variable that the specified
Expand Down Expand Up @@ -58,6 +63,7 @@ export interface ObserveTraceOptions<V> {
*/
export async function observeTrace<V>({
pointer,
templates = {},
compileOptions,
observe,
equals = (a, b) => a === b,
Expand Down Expand Up @@ -89,7 +95,7 @@ export async function observeTrace<V>({
}

if (!cursor) {
cursor = await dereference(pointer, { state });
cursor = await dereference(pointer, { state, templates });
}

const { regions, read } = await cursor.view(state);
Expand Down
18 changes: 18 additions & 0 deletions packages/web/spec/pointer/templates.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
sidebar_position: 7
---

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

# Pointer templates

This format provides the concept of a **pointer template** to allow
deduplicating representations. Pointer templates are defined to specify the
variables they expect in scope and the pointer definition that uses those
variables.

<SchemaViewer
schema={{
id: "schema:ethdebug/format/pointer/template"
}}
/>
4 changes: 4 additions & 0 deletions packages/web/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,10 @@ export const schemaIndex: SchemaIndex = {
href: "/spec/pointer/expression"
},

"schema:ethdebug/format/pointer/template": {
href: "/spec/pointer/template"
},

...Object.entries({
Literal: {
title: "Literal values schema",
Expand Down
2 changes: 1 addition & 1 deletion schemas/pointer.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ examples:
- .length: "struct-member-0"
length: $wordsize

- # example `string storage` allocation
- # example template reference
define:
"string-storage-contract-variable-slot": 0
in:
Expand Down
31 changes: 15 additions & 16 deletions schemas/pointer/collection.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,33 @@ type: object

allOf:
- oneOf:
- required:
- group
- required:
- list
- required:
- if
- required:
- define
- required: [group]
- required: [list]
- required: [if]
- required: [define]
- required: [template]

- if:
required:
- group
required: [group]
then:
$ref: "schema:ethdebug/format/pointer/collection/group"

- if:
required:
- list
required: [list]
then:
$ref: "schema:ethdebug/format/pointer/collection/list"

- if:
required:
- if
required: [if]
then:
$ref: "schema:ethdebug/format/pointer/collection/conditional"

- if:
required:
- define
required: [define]
then:
$ref: "schema:ethdebug/format/pointer/collection/scope"

- if:
required: [template]
then:
$ref: "schema:ethdebug/format/pointer/collection/reference"
21 changes: 21 additions & 0 deletions schemas/pointer/collection/reference.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "schema:ethdebug/format/pointer/collection/reference"

title: ethdebug/format/pointer/collection/reference
description: |
A pointer by named reference to a pointer template (defined elsewhere).

type: object

properties:
template:
title: Template identifier
$ref: "schema:ethdebug/format/pointer/identifier"

required:
- template

additionalProperties: false

examples:
- template: "string-storage-pointer"
Loading
Loading