diff --git a/packages/web/spec/pointer/expression.mdx b/packages/web/spec/pointer/expression.mdx
index 4c8548a5..971f0099 100644
--- a/packages/web/spec/pointer/expression.mdx
+++ b/packages/web/spec/pointer/expression.mdx
@@ -6,12 +6,28 @@ import SchemaViewer from "@site/src/components/SchemaViewer";
# Expression syntax
+Pointer expressions operate on the domain of bytes representing unsigned
+integers.
+
## Literal values
+An expression can be a literal value.
+
+Literal values **must** be represented either as JSON numbers or as
+`0x`-prefixed hexadecimal strings. Hexadecimal strings always represent a
+literal string of bytes.
+
+For convenience, this schema does not restrict hexadecimal string
+representations to those that specify an even-number of digits (i.e., those
+that specify complete byte pairs); odd numbers of hexadecimal digits are fine.
+
+Hexadecimal string representations **may** omit leading zeroes; values are
+assumed to be left-padded to the bytes width appropriate for the context.
+
: [...] }`, where ``
+denotes an arithmetic operation.
+
+## Lookup region definition
+
+An expression can reference properties defined for a particular region, such as
+another region's `"offset"` or `"length"`. Such expressions resolve to the
+same value as the expression specified for that corresponding property.
+
+
+
## Reading from the EVM
+An expression can be an object of the form `{ "$read": "" }`, where
+`` references a particular region defined in some root pointer.
+
+The value of such an expression is the concatenation of bytes present in the
+running machine state that correspond to the bytes addressed by the referenced
+region.
+
+
+## Region references
+
+Regions can be referenced either by name (which **must** be a defined region),
+or by use of the literal string value `"$this"` (which indicates that the
+referenced region is the region containing the expression itself).
+
+In cases where an expression is used outside the context of a particular
+region definition, the use of `"$this"` is **prohibited**.
+
+Individual properties **may not** be defined with any reference to themselves.
+Properties also **may not** be defined in terms of mutual reference to each
+other. (Don't make this harder than it has to be.)
+
+
diff --git a/packages/web/src/schemas.ts b/packages/web/src/schemas.ts
index 01e9128e..0216b3fc 100644
--- a/packages/web/src/schemas.ts
+++ b/packages/web/src/schemas.ts
@@ -62,6 +62,10 @@ export const schemaIndex: SchemaIndex = {
href: "/spec/type/complex/function#parameters-schema"
},
+ "schema:ethdebug/format/pointer": {
+ href: "/spec/pointer"
+ },
+
"schema:ethdebug/format/pointer/region": {
href: "/spec/pointer/region"
},
@@ -127,6 +131,11 @@ export const schemaIndex: SchemaIndex = {
href: "/spec/pointer/expression#arithmetic-operations"
},
+ "schema:ethdebug/format/pointer/expression#/$defs/Lookup": {
+ title: "Lookup expression schema",
+ href: "/spec/pointer/expression#lookup-region-definition"
+ },
+
"schema:ethdebug/format/pointer/expression#/$defs/Read": {
title: "Read expression schema",
href: "/spec/pointer/expression#reading-from-the-evm"
@@ -136,4 +145,9 @@ export const schemaIndex: SchemaIndex = {
title: "Keccak256 hash expression schema",
href: "/spec/pointer/expression#keccak256-hashes"
},
+
+ "schema:ethdebug/format/pointer/expression#/$defs/Reference": {
+ title: "Region reference",
+ href: "/spec/pointer/expression#region-references"
+ },
};
diff --git a/packages/web/src/theme/JSONSchemaViewer/components/UnnecessaryComposition.tsx b/packages/web/src/theme/JSONSchemaViewer/components/UnnecessaryComposition.tsx
index ac1cdd2f..dd20f6fd 100644
--- a/packages/web/src/theme/JSONSchemaViewer/components/UnnecessaryComposition.tsx
+++ b/packages/web/src/theme/JSONSchemaViewer/components/UnnecessaryComposition.tsx
@@ -1,9 +1,17 @@
import React from "react";
import type { JSONSchema } from "json-schema-typed/draft-2020-12";
-import CreateNodes from "@theme-original/JSONSchemaViewer/components/CreateNodes";
+import CreateNodes from "@theme/JSONSchemaViewer/components/CreateNodes";
+import CreateEdge from "@theme-original/JSONSchemaViewer/components/CreateEdge";
+import { SchemaHierarchyComponent } from "@theme-original/JSONSchemaViewer/contexts"
+import { Collapsible } from "@theme/JSONSchemaViewer/components";
+import { GenerateFriendlyName, QualifierMessages } from "@theme/JSONSchemaViewer/utils";
+import { internalIdKey } from "@site/src/contexts/SchemaContext";
+import { CreateDescription } from "@theme/JSONSchemaViewer/JSONSchemaElements";
+import { useJSVOptionsContext } from "@theme/JSONSchemaViewer/contexts"
export interface UnnecessaryComposition {
- schemaWithoutUnnecessaryComposition: JSONSchema;
+ schemaWithoutUnnecessaryComposition: Exclude;
+ unnecessaryCompositionKeyword: "allOf" | "oneOf" | "anyOf";
unnecessarilyComposedSchema: JSONSchema;
}
@@ -14,17 +22,17 @@ export function detectUnnecessaryComposition(
return;
}
- const redundantKeywords = (["allOf", "oneOf", "anyOf"] as const)
+ const unnecessaryCompositionKeywords = (["allOf", "oneOf", "anyOf"] as const)
.filter(keyword => keyword in schema && (schema[keyword] || []).length === 1);
- if (redundantKeywords.length !== 1) {
+ if (unnecessaryCompositionKeywords.length !== 1) {
return;
}
- const [redundantKeyword] = redundantKeywords;
+ const [unnecessaryCompositionKeyword] = unnecessaryCompositionKeywords;
const {
- [redundantKeyword]: composition,
+ [unnecessaryCompositionKeyword]: composition,
...schemaWithoutUnnecessaryComposition
} = schema;
@@ -33,6 +41,7 @@ export function detectUnnecessaryComposition(
return {
unnecessarilyComposedSchema,
+ unnecessaryCompositionKeyword,
schemaWithoutUnnecessaryComposition
};
}
@@ -42,29 +51,94 @@ export interface UnnecessaryCompositionSchemaProps extends UnnecessaryCompositio
export default function UnnecessaryCompositionSchema({
schemaWithoutUnnecessaryComposition,
+ unnecessaryCompositionKeyword,
unnecessarilyComposedSchema
}: UnnecessaryCompositionSchemaProps): JSX.Element {
- const unnecessarilyComposedSchemaNecessities = removeDocumentingFields(
- unnecessarilyComposedSchema
- );
+ const jsvOptions = useJSVOptionsContext();
+
+ // treat the unnecessary composition to represent the extension of a base
+ // schema, where the unnecessarily composed schema is the base
+ const baseSchema = unnecessarilyComposedSchema;
+ const extensionSchema = schemaWithoutUnnecessaryComposition;
+ const {
+ documentation,
+ semantics
+ } = separateDocumentationFromSemantics(extensionSchema);
+
+ if (Object.keys(semantics).length === 0) {
+ const { description } = documentation;
+
+ return <>
+
+ {description && }
+
+
+
+
+
+ >;
+ }
return (
<>
+ extensions
+ These extensions apply to the base schema below:
+
+
+
+
+
+
+ base schema
+ >
+ }
+ detailsProps={{
+ open: true
+ }}
+ >
+
+
+
+
+
>
);
}
-function removeDocumentingFields(schema: JSONSchema): JSONSchema {
+function separateDocumentationFromSemantics(schema: JSONSchema): {
+ documentation: Exclude,
+ semantics: JSONSchema
+} {
if (typeof schema === "boolean") {
- return schema;
+ return {
+ documentation: {},
+ semantics: schema
+ };
}
const {
title,
description,
examples,
- ...schemaNecessities
+ default: default_,
+ // @ts-ignore
+ [internalIdKey]: _id,
+ ...semantics
} = schema;
- return schemaNecessities;
+ return {
+ documentation: {
+ title,
+ description,
+ examples,
+ default: default_
+ },
+ semantics
+ };
}
diff --git a/schemas/pointer/collection/group.schema.yaml b/schemas/pointer/collection/group.schema.yaml
index 8ac03ae5..5be69724 100644
--- a/schemas/pointer/collection/group.schema.yaml
+++ b/schemas/pointer/collection/group.schema.yaml
@@ -3,7 +3,7 @@ $id: "schema:ethdebug/format/pointer/collection/group"
title: ethdebug/format/pointer/collection/group
description: |
- A schema for ...
+ A composite collection of pointers
type: object
properties:
group:
diff --git a/schemas/pointer/collection/list.schema.yaml b/schemas/pointer/collection/list.schema.yaml
index 84764e16..32af2a33 100644
--- a/schemas/pointer/collection/list.schema.yaml
+++ b/schemas/pointer/collection/list.schema.yaml
@@ -3,7 +3,7 @@ $id: "schema:ethdebug/format/pointer/collection/list"
title: ethdebug/format/pointer/collection/list
description: |
- A schema for ...
+ An ordered list of pointers, indexed starting at zero.
type: object
properties:
@@ -11,10 +11,17 @@ properties:
type: object
properties:
count:
+ description: |
+ The size of the list that this collection represents.
$ref: "schema:ethdebug/format/pointer/expression"
each:
- type: string
+ description: |
+ An identifier name whose value as an expression resolves to the index
+ in the list
+ $ref: "schema:ethdebug/format/pointer/identifier"
is:
+ description: |
+ The dynamically-generated pointer repeated as a list
$ref: "schema:ethdebug/format/pointer"
required:
- count
diff --git a/schemas/pointer/expression.schema.yaml b/schemas/pointer/expression.schema.yaml
index 971ae596..55fbdbb3 100644
--- a/schemas/pointer/expression.schema.yaml
+++ b/schemas/pointer/expression.schema.yaml
@@ -53,18 +53,39 @@ $defs:
type: object
properties:
"$sum":
+ description: |
+ A list of expressions to be added together.
$ref: "#/$defs/Operands"
"$difference":
+ description: |
+ A tuple of two expressions where the second is to be subtracted from
+ the first.
+
+ (i.e., `{ "$difference": [a, b] }` equals `a` minus `b`.)
$ref: "#/$defs/Operands"
minItems: 2
maxItems: 2
"$product":
+ description: |
+ A list of expressions to be multipled.
$ref: "#/$defs/Operands"
"$quotient":
+ description: |
+ A tuple of two expressions where the first corresponds to the
+ dividend and the second corresponds to the divisor, for the purposes
+ of doing integer division.
+
+ (i.e., `{ "$quotient": [a, b] }` equals `a` divided by `b`.)
$ref: "#/$defs/Operands"
minItems: 2
maxItems: 2
"$remainder":
+ description: |
+ A tuple of two expressions where the first corresponds to the
+ dividend and the second corresponds to the divisor, for the purposes
+ of computing the modular-arithmetic remainder.
+
+ (i.e., `{ "$remainder": [a, b] }` equals `a` mod `b`.)
$ref: "#/$defs/Operands"
minItems: 2
maxItems: 2
@@ -90,28 +111,20 @@ $defs:
$ref: "schema:ethdebug/format/pointer/expression"
Lookup:
- title: Region definition reference
+ title: Lookup region definition
description: |
- An object of the form \`{ ".": "" }\`, to
+ An object of the form `{ ".": "" }`, to
denote that this expression is equivalent to the defined value for
- the property named \`\` inside the region named
- \`\`.
-
- \`\` **must** correspond to at least one region declared
- with \`{ "name": "" }\` previously in some root pointer
- representation.
-
- If more than one region is defined with the same name, resolution is
- defined as resolving, first, to the latest earlier sibling that declares
- the matching name, then second, to parent then parent's earlier siblings,
- and so on.
+ the property named `` inside the region referenced as
+ ``.
- \`\` **must** be a valid and present property on the
- corresponding region.
+ `` **must** be a valid and present property on the
+ corresponding region, or it **must** correspond to an optional property
+ whose schema specifies a default value for that property.
type: object
patternProperties:
"^\\.(offset|length|slot)$":
- $ref: "schema:ethdebug/format/pointer/identifier"
+ $ref: "#/$defs/Reference"
additionalProperties: false
minProperties: 1
maxProperties: 1
@@ -119,23 +132,45 @@ $defs:
examples:
- .offset: "array-count"
- .length: "array-item"
+ - .offset: $this
+
Read:
- title: Read machine state
+ title: Read region bytes
description: |
- An object of the form `{ "read": "" }`, where ``
- indicates the identifier for the region whose bytes to read in order to
- find the value indicated by this expression.
+ An object of the form `{ "$read": "" }`. The value of this
+ expression equals the raw bytes present in the running machine state
+ in the referenced region.
type: object
properties:
$read:
- $ref: "schema:ethdebug/format/pointer/identifier"
+ $ref: "#/$defs/Reference"
required:
- $read
additionalProperties: false
examples:
- $read: "struct-start"
+ Reference:
+ title: Region reference
+ description: |
+ A string value that **must** either be the `"name"` of at least one
+ region declared with `{ "name": "" }` previously in some root
+ pointer representation, or it **must** be the literal value `"$this"`,
+ which indicates a reference to the region containing this expression.
+
+ If more than one region is defined with the same name, resolution is
+ defined as firstly resolving to the latest earlier sibling that declares
+ the matching name, then secondly resolving to the parent if it matches,
+ then to parent's earlier siblings, and so on.
+ type: string
+
+ oneOf:
+ - $ref: "schema:ethdebug/format/pointer/identifier"
+ - const: "$this"
+ description: |
+ Indicates a reference to the region containing this expression.
+
Keccak256:
title: Keccak256 hash
description: |
diff --git a/schemas/pointer/scheme/segment.schema.yaml b/schemas/pointer/scheme/segment.schema.yaml
index 03ddab87..447356ed 100644
--- a/schemas/pointer/scheme/segment.schema.yaml
+++ b/schemas/pointer/scheme/segment.schema.yaml
@@ -21,15 +21,38 @@ properties:
slot:
$ref: "schema:ethdebug/format/pointer/expression"
offset:
+ description: |
+ The starting byte index within the slot.
+
+ This field is **optional**. If unspecified, it has the default value of
+ `0`, indicating that the segment begins at the start of the specified
+ slot.
+
+ This field's expression must resolve to a value _n_ such that
+ 0 ≤ _n_ \< `$wordsize` (i.e., the offset **must**
+ begin inside the slot).
$ref: "schema:ethdebug/format/pointer/expression"
default: 0
length:
+ description: |
+ The length of the bytes range this segment represents.
+
+ This field is **optional**. If unspecified, its default value indicates
+ that the segment ends at the end of the slot.
+
+ If this field has value larger than the default value, i.e., if the
+ segment extends beyond the last byte in the slot, then this segment is
+ defined to be the concatenation of the sequentially-addressed slot(s)
+ following following the slot specified.
$ref: "schema:ethdebug/format/pointer/expression"
- default: $wordsize
+ default:
+ $difference:
+ - $wordsize
+ - .offset: $this
+
required:
- slot
-
examples:
- slot: 0
- slot: 1