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
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React from 'react';
import type { JSONSchema } from "json-schema-typed/draft-2020-12"
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";

import { CreateNodes } from "@theme-original/JSONSchemaViewer/components"
import {
SchemaHierarchyContextProvider,
useSchemaHierarchyContext,
} from "@theme-original/JSONSchemaViewer/contexts"

export interface Inclusions {
propertyNames: string[];
schemasByPropertyName: {
[propertyName: string]: {
schema: Exclude<JSONSchema, boolean>;
index: number;
}
}
}

export interface InclusiveRequiredPropertiesSchemaProps extends Inclusions {
}

export default function InclusiveRequiredPropertiesSchema({
propertyNames,
schemasByPropertyName
}: InclusiveRequiredPropertiesSchemaProps): JSX.Element {
const { jsonPointer: currentJsonPointer, level: currentLevel } =
useSchemaHierarchyContext()

return (
<div>
<hr />
<span className="badge badge--info">independently-inclusive required properties</span>&nbsp;
This object may specify any of the following:
<ul>
{propertyNames.map((propertyName, index) =>
<li key={index}><code>{propertyName}</code></li>
)}
</ul>

Depending on which required properties are used, the following
corresponding sub-schemas may apply:

<Tabs>{
Object.entries(schemasByPropertyName)
.map(([propertyName, { schema, index }]) => (
<TabItem
key={propertyName}
label={("title" in schema && typeof schema.title === "string" && schema.title) || propertyName}
value={propertyName}
>
<SchemaHierarchyContextProvider
value={{
level: currentLevel + 1,
jsonPointer: `${currentJsonPointer}/allOf/${index + 1}/then`
}}
>
<CreateNodes schema={schema} />
</SchemaHierarchyContextProvider>
</TabItem>
))
}</Tabs>

</div>
);
}

const isIfThen = (
schema: JSONSchema
): schema is JSONSchema & {
if: JSONSchema;
then: JSONSchema;
} =>
typeof schema !== "boolean" &&
"if" in schema && "then" in schema;

const isSingleRequiredProperty = (
schema: JSONSchema
): schema is JSONSchema & {
required: [string]
} =>
typeof schema !== "boolean" &&
"required" in schema && schema.required?.length === 1;

export function detectInclusiveRequiredProperties(schema: {
allOf: JSONSchema[]
}): Inclusions | undefined {
const { allOf } = schema;

// every schema in the `allOf` should be structured as an if/then/else,
// where every `if` schema checks only for a single, unique required property

if (!allOf.every(isIfThen)) {
return;
}

const ifs = allOf.map(({ if: if_ }) => if_);

if (!ifs.every(isSingleRequiredProperty)) {
return;
}

const propertyNames = [...new Set(
ifs.map(({ required: [propertyName] }) => propertyName)
)];

// check that property names are unique
if (propertyNames.length !== ifs.length) {
console.debug("property names are not unique");
return;
}


const schemasByPropertyName = (
allOf as (JSONSchema & { if: { required: [string] }; then: Exclude<JSONSchema, boolean>; })[]
)
.map(
(
{
if: { required: [propertyName] },
then
},
index
) => ({
[propertyName]: {
schema: then,
index
}
})
)
.reduce((a, b) => ({ ...a, ...b }), {});

return {
propertyNames,
schemasByPropertyName
};
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import ExclusiveRequiredPropertiesSchema, {
detectExclusiveRequiredProperties
} from "./ExclusiveRequiredPropertiesSchema";

import InclusiveRequiredPropertiesSchema, {
detectInclusiveRequiredProperties
} from "./InclusiveRequiredPropertiesSchema";

export default function allOfSchemaWrapper(props: {
schema: Exclude<JSONSchema, boolean> & { allOf: JSONSchema[]; }
}) {
Expand All @@ -25,6 +29,11 @@ export default function allOfSchemaWrapper(props: {
return <ExclusiveRequiredPropertiesSchema {...exclusions} />
}

const inclusions = detectInclusiveRequiredProperties(schema);
if (inclusions) {
return <InclusiveRequiredPropertiesSchema {...inclusions} />
}

return (
<>
<AllOfSchema {...props} />
Expand Down
34 changes: 28 additions & 6 deletions schemas/program/context.schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,37 @@ $id: "schema:ethdebug/format/program/context"

title: ethdebug/format/program/context
description: |
A schema for representing the information known at compile-time about the
high-level language concerns at a particular point in code execution.
An **ethdebug/format/program/context** object represents compile-time
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.

type: object

anyOf:
- $ref: "schema:ethdebug/format/program/context/code"
- $ref: "schema:ethdebug/format/program/context/variables"
- $ref: "schema:ethdebug/format/program/context/remark"
allOf:
- if:
required: ["code"]
then:
$ref: "schema:ethdebug/format/program/context/code"
- if:
required: ["variables"]
then:
$ref: "schema:ethdebug/format/program/context/variables"
- if:
required: ["remark"]
then:
$ref: "schema:ethdebug/format/program/context/remark"

unevaluatedProperties: false

Expand Down