diff --git a/packages/format/src/types/program/context.test.ts b/packages/format/src/types/program/context.test.ts index 47185c7ca..4470a322d 100644 --- a/packages/format/src/types/program/context.test.ts +++ b/packages/format/src/types/program/context.test.ts @@ -30,4 +30,20 @@ testSchemaGuards("ethdebug/format/program/context", [ schema: "schema:ethdebug/format/program/context/frame", guard: Context.isFrame, }, + { + schema: "schema:ethdebug/format/program/context/function", + guard: Context.Function.isIdentity, + }, + { + schema: "schema:ethdebug/format/program/context/function/invoke", + guard: Context.isInvoke, + }, + { + schema: "schema:ethdebug/format/program/context/function/return", + guard: Context.isReturn, + }, + { + schema: "schema:ethdebug/format/program/context/function/revert", + guard: Context.isRevert, + }, ] as const); diff --git a/packages/format/src/types/program/context.ts b/packages/format/src/types/program/context.ts index 6a552734a..9388fea7b 100644 --- a/packages/format/src/types/program/context.ts +++ b/packages/format/src/types/program/context.ts @@ -1,5 +1,5 @@ import { Materials } from "#types/materials"; -import { Type, isType } from "#types/type"; +import { Type } from "#types/type"; import { Pointer, isPointer } from "#types/pointer"; export type Context = @@ -8,7 +8,10 @@ export type Context = | Context.Remark | Context.Pick | Context.Gather - | Context.Frame; + | Context.Frame + | Context.Invoke + | Context.Return + | Context.Revert; export const isContext = (value: unknown): value is Context => [ @@ -18,6 +21,9 @@ export const isContext = (value: unknown): value is Context => Context.isPick, Context.isFrame, Context.isGather, + Context.isInvoke, + Context.isReturn, + Context.isRevert, ].some((guard) => guard(value)); export namespace Context { @@ -47,7 +53,7 @@ export namespace Context { export interface Variable { identifier?: string; declaration?: Materials.SourceRange; - type?: Type; + type?: Type.Specifier; pointer?: Pointer; } @@ -66,7 +72,7 @@ export namespace Context { (!("identifier" in value) || typeof value.identifier === "string") && (!("declaration" in value) || Materials.isSourceRange(value.declaration)) && - (!("type" in value) || isType(value.type)) && + (!("type" in value) || Type.isSpecifier(value.type)) && (!("pointer" in value) || isPointer(value.pointer)); } @@ -111,4 +117,162 @@ export namespace Context { !!value && "frame" in value && typeof value.frame === "string"; + + export namespace Function { + export interface Identity { + identifier?: string; + declaration?: Materials.SourceRange; + type?: Type.Specifier; + } + + export const isIdentity = (value: unknown): value is Identity => + typeof value === "object" && + !!value && + (!("identifier" in value) || typeof value.identifier === "string") && + (!("declaration" in value) || + Materials.isSourceRange(value.declaration)) && + (!("type" in value) || Type.isSpecifier(value.type)); + + export interface PointerRef { + pointer: Pointer; + } + + export const isPointerRef = (value: unknown): value is PointerRef => + typeof value === "object" && + !!value && + "pointer" in value && + isPointer(value.pointer); + } + + export interface Invoke { + invoke: Invoke.Invocation; + } + + export const isInvoke = (value: unknown): value is Invoke => + typeof value === "object" && + !!value && + "invoke" in value && + Invoke.isInvocation(value.invoke); + + export namespace Invoke { + export type Invocation = Function.Identity & + ( + | Invocation.InternalCall + | Invocation.ExternalCall + | Invocation.ContractCreation + ); + + export const isInvocation = (value: unknown): value is Invocation => + Function.isIdentity(value) && + (Invocation.isInternalCall(value) || + Invocation.isExternalCall(value) || + Invocation.isContractCreation(value)); + + export namespace Invocation { + export interface InternalCall extends Function.Identity { + jump: true; + target: Function.PointerRef; + arguments?: Function.PointerRef; + } + + export const isInternalCall = (value: unknown): value is InternalCall => + typeof value === "object" && + !!value && + "jump" in value && + value.jump === true && + "target" in value && + Function.isPointerRef(value.target) && + (!("arguments" in value) || Function.isPointerRef(value.arguments)); + + export interface ExternalCall extends Function.Identity { + message: true; + target: Function.PointerRef; + gas?: Function.PointerRef; + value?: Function.PointerRef; + input?: Function.PointerRef; + delegate?: true; + static?: true; + } + + export const isExternalCall = (value: unknown): value is ExternalCall => + typeof value === "object" && + !!value && + "message" in value && + value.message === true && + "target" in value && + Function.isPointerRef(value.target) && + (!("gas" in value) || Function.isPointerRef(value.gas)) && + (!("value" in value) || Function.isPointerRef(value.value)) && + (!("input" in value) || Function.isPointerRef(value.input)) && + (!("delegate" in value) || value.delegate === true) && + (!("static" in value) || value.static === true); + + export interface ContractCreation extends Function.Identity { + create: true; + value?: Function.PointerRef; + salt?: Function.PointerRef; + input?: Function.PointerRef; + } + + export const isContractCreation = ( + value: unknown, + ): value is ContractCreation => + typeof value === "object" && + !!value && + "create" in value && + value.create === true && + (!("value" in value) || Function.isPointerRef(value.value)) && + (!("salt" in value) || Function.isPointerRef(value.salt)) && + (!("input" in value) || Function.isPointerRef(value.input)); + } + } + + export interface Return { + return: Return.Info; + } + + export const isReturn = (value: unknown): value is Return => + typeof value === "object" && + !!value && + "return" in value && + Return.isInfo(value.return); + + export namespace Return { + export interface Info extends Function.Identity { + data: Function.PointerRef; + success?: Function.PointerRef; + } + + export const isInfo = (value: unknown): value is Info => + Function.isIdentity(value) && + typeof value === "object" && + !!value && + "data" in value && + Function.isPointerRef(value.data) && + (!("success" in value) || Function.isPointerRef(value.success)); + } + + export interface Revert { + revert: Revert.Info; + } + + export const isRevert = (value: unknown): value is Revert => + typeof value === "object" && + !!value && + "revert" in value && + Revert.isInfo(value.revert); + + export namespace Revert { + export interface Info extends Function.Identity { + reason?: Function.PointerRef; + panic?: number; + } + + export const isInfo = (value: unknown): value is Info => + Function.isIdentity(value) && + typeof value === "object" && + !!value && + (!("reason" in value) || Function.isPointerRef(value.reason)) && + (!("panic" in value) || typeof value.panic === "number"); + } } diff --git a/packages/format/src/types/type/index.test.ts b/packages/format/src/types/type/index.test.ts index 61a5349c3..cc461540a 100644 --- a/packages/format/src/types/type/index.test.ts +++ b/packages/format/src/types/type/index.test.ts @@ -80,4 +80,19 @@ testSchemaGuards("ethdebug/format/type", [ schema: "schema:ethdebug/format/type/complex/struct", guard: Type.Complex.isStruct, }, + + // type reference and specifier + + { + schema: "schema:ethdebug/format/type/reference", + guard: Type.isReference, + }, + { + schema: "schema:ethdebug/format/type/specifier", + guard: Type.isSpecifier, + }, + { + schema: "schema:ethdebug/format/type/wrapper", + guard: Type.isWrapper, + }, ] as const); diff --git a/packages/format/src/types/type/index.ts b/packages/format/src/types/type/index.ts index c53d0230b..066f3a6d5 100644 --- a/packages/format/src/types/type/index.ts +++ b/packages/format/src/types/type/index.ts @@ -32,16 +32,30 @@ export namespace Type { (typeof value.contains === "object" && Object.values(value.contains).every(Type.isWrapper))); + export interface Reference { + id: string | number; + } + + export const isReference = (value: unknown): value is Reference => + typeof value === "object" && + !!value && + "id" in value && + (typeof value.id === "string" || typeof value.id === "number"); + + export type Specifier = Type | Reference; + + export const isSpecifier = (value: unknown): value is Specifier => + isType(value) || isReference(value); + export interface Wrapper { - type: Type | { id: any }; + type: Specifier; } export const isWrapper = (value: unknown): value is Wrapper => typeof value === "object" && !!value && "type" in value && - (isType(value.type) || - (typeof value.type === "object" && !!value.type && "id" in value.type)); + isSpecifier(value.type); export type Elementary = | Elementary.Uint