Skip to content

Commit f966ef5

Browse files
authored
format: add TypeScript types for function call contexts (#186)
Add Type.Reference, Type.Specifier, and type guards for the new function call context schemas (invoke, return, revert). - Type.Reference: { id: string | number } for type references - Type.Specifier: Type | Reference union (matches type/specifier schema) - Context.Function.Identity: shared function identity fields - Context.Invoke: internal calls, external calls, contract creation - Context.Return: function return with data and optional success - Context.Revert: function revert with optional reason/panic - Update Variable.type to accept Type.Specifier (was Type only) - Update Type.Wrapper to use Type.Specifier
1 parent b42da9f commit f966ef5

4 files changed

Lines changed: 216 additions & 7 deletions

File tree

‎packages/format/src/types/program/context.test.ts‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,20 @@ testSchemaGuards("ethdebug/format/program/context", [
3030
schema: "schema:ethdebug/format/program/context/frame",
3131
guard: Context.isFrame,
3232
},
33+
{
34+
schema: "schema:ethdebug/format/program/context/function",
35+
guard: Context.Function.isIdentity,
36+
},
37+
{
38+
schema: "schema:ethdebug/format/program/context/function/invoke",
39+
guard: Context.isInvoke,
40+
},
41+
{
42+
schema: "schema:ethdebug/format/program/context/function/return",
43+
guard: Context.isReturn,
44+
},
45+
{
46+
schema: "schema:ethdebug/format/program/context/function/revert",
47+
guard: Context.isRevert,
48+
},
3349
] as const);

‎packages/format/src/types/program/context.ts‎

Lines changed: 168 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Materials } from "#types/materials";
2-
import { Type, isType } from "#types/type";
2+
import { Type } from "#types/type";
33
import { Pointer, isPointer } from "#types/pointer";
44

55
export type Context =
@@ -8,7 +8,10 @@ export type Context =
88
| Context.Remark
99
| Context.Pick
1010
| Context.Gather
11-
| Context.Frame;
11+
| Context.Frame
12+
| Context.Invoke
13+
| Context.Return
14+
| Context.Revert;
1215

1316
export const isContext = (value: unknown): value is Context =>
1417
[
@@ -18,6 +21,9 @@ export const isContext = (value: unknown): value is Context =>
1821
Context.isPick,
1922
Context.isFrame,
2023
Context.isGather,
24+
Context.isInvoke,
25+
Context.isReturn,
26+
Context.isRevert,
2127
].some((guard) => guard(value));
2228

2329
export namespace Context {
@@ -47,7 +53,7 @@ export namespace Context {
4753
export interface Variable {
4854
identifier?: string;
4955
declaration?: Materials.SourceRange;
50-
type?: Type;
56+
type?: Type.Specifier;
5157
pointer?: Pointer;
5258
}
5359

@@ -66,7 +72,7 @@ export namespace Context {
6672
(!("identifier" in value) || typeof value.identifier === "string") &&
6773
(!("declaration" in value) ||
6874
Materials.isSourceRange(value.declaration)) &&
69-
(!("type" in value) || isType(value.type)) &&
75+
(!("type" in value) || Type.isSpecifier(value.type)) &&
7076
(!("pointer" in value) || isPointer(value.pointer));
7177
}
7278

@@ -111,4 +117,162 @@ export namespace Context {
111117
!!value &&
112118
"frame" in value &&
113119
typeof value.frame === "string";
120+
121+
export namespace Function {
122+
export interface Identity {
123+
identifier?: string;
124+
declaration?: Materials.SourceRange;
125+
type?: Type.Specifier;
126+
}
127+
128+
export const isIdentity = (value: unknown): value is Identity =>
129+
typeof value === "object" &&
130+
!!value &&
131+
(!("identifier" in value) || typeof value.identifier === "string") &&
132+
(!("declaration" in value) ||
133+
Materials.isSourceRange(value.declaration)) &&
134+
(!("type" in value) || Type.isSpecifier(value.type));
135+
136+
export interface PointerRef {
137+
pointer: Pointer;
138+
}
139+
140+
export const isPointerRef = (value: unknown): value is PointerRef =>
141+
typeof value === "object" &&
142+
!!value &&
143+
"pointer" in value &&
144+
isPointer(value.pointer);
145+
}
146+
147+
export interface Invoke {
148+
invoke: Invoke.Invocation;
149+
}
150+
151+
export const isInvoke = (value: unknown): value is Invoke =>
152+
typeof value === "object" &&
153+
!!value &&
154+
"invoke" in value &&
155+
Invoke.isInvocation(value.invoke);
156+
157+
export namespace Invoke {
158+
export type Invocation = Function.Identity &
159+
(
160+
| Invocation.InternalCall
161+
| Invocation.ExternalCall
162+
| Invocation.ContractCreation
163+
);
164+
165+
export const isInvocation = (value: unknown): value is Invocation =>
166+
Function.isIdentity(value) &&
167+
(Invocation.isInternalCall(value) ||
168+
Invocation.isExternalCall(value) ||
169+
Invocation.isContractCreation(value));
170+
171+
export namespace Invocation {
172+
export interface InternalCall extends Function.Identity {
173+
jump: true;
174+
target: Function.PointerRef;
175+
arguments?: Function.PointerRef;
176+
}
177+
178+
export const isInternalCall = (value: unknown): value is InternalCall =>
179+
typeof value === "object" &&
180+
!!value &&
181+
"jump" in value &&
182+
value.jump === true &&
183+
"target" in value &&
184+
Function.isPointerRef(value.target) &&
185+
(!("arguments" in value) || Function.isPointerRef(value.arguments));
186+
187+
export interface ExternalCall extends Function.Identity {
188+
message: true;
189+
target: Function.PointerRef;
190+
gas?: Function.PointerRef;
191+
value?: Function.PointerRef;
192+
input?: Function.PointerRef;
193+
delegate?: true;
194+
static?: true;
195+
}
196+
197+
export const isExternalCall = (value: unknown): value is ExternalCall =>
198+
typeof value === "object" &&
199+
!!value &&
200+
"message" in value &&
201+
value.message === true &&
202+
"target" in value &&
203+
Function.isPointerRef(value.target) &&
204+
(!("gas" in value) || Function.isPointerRef(value.gas)) &&
205+
(!("value" in value) || Function.isPointerRef(value.value)) &&
206+
(!("input" in value) || Function.isPointerRef(value.input)) &&
207+
(!("delegate" in value) || value.delegate === true) &&
208+
(!("static" in value) || value.static === true);
209+
210+
export interface ContractCreation extends Function.Identity {
211+
create: true;
212+
value?: Function.PointerRef;
213+
salt?: Function.PointerRef;
214+
input?: Function.PointerRef;
215+
}
216+
217+
export const isContractCreation = (
218+
value: unknown,
219+
): value is ContractCreation =>
220+
typeof value === "object" &&
221+
!!value &&
222+
"create" in value &&
223+
value.create === true &&
224+
(!("value" in value) || Function.isPointerRef(value.value)) &&
225+
(!("salt" in value) || Function.isPointerRef(value.salt)) &&
226+
(!("input" in value) || Function.isPointerRef(value.input));
227+
}
228+
}
229+
230+
export interface Return {
231+
return: Return.Info;
232+
}
233+
234+
export const isReturn = (value: unknown): value is Return =>
235+
typeof value === "object" &&
236+
!!value &&
237+
"return" in value &&
238+
Return.isInfo(value.return);
239+
240+
export namespace Return {
241+
export interface Info extends Function.Identity {
242+
data: Function.PointerRef;
243+
success?: Function.PointerRef;
244+
}
245+
246+
export const isInfo = (value: unknown): value is Info =>
247+
Function.isIdentity(value) &&
248+
typeof value === "object" &&
249+
!!value &&
250+
"data" in value &&
251+
Function.isPointerRef(value.data) &&
252+
(!("success" in value) || Function.isPointerRef(value.success));
253+
}
254+
255+
export interface Revert {
256+
revert: Revert.Info;
257+
}
258+
259+
export const isRevert = (value: unknown): value is Revert =>
260+
typeof value === "object" &&
261+
!!value &&
262+
"revert" in value &&
263+
Revert.isInfo(value.revert);
264+
265+
export namespace Revert {
266+
export interface Info extends Function.Identity {
267+
reason?: Function.PointerRef;
268+
panic?: number;
269+
}
270+
271+
export const isInfo = (value: unknown): value is Info =>
272+
Function.isIdentity(value) &&
273+
typeof value === "object" &&
274+
!!value &&
275+
(!("reason" in value) || Function.isPointerRef(value.reason)) &&
276+
(!("panic" in value) || typeof value.panic === "number");
277+
}
114278
}

‎packages/format/src/types/type/index.test.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,19 @@ testSchemaGuards("ethdebug/format/type", [
8080
schema: "schema:ethdebug/format/type/complex/struct",
8181
guard: Type.Complex.isStruct,
8282
},
83+
84+
// type reference and specifier
85+
86+
{
87+
schema: "schema:ethdebug/format/type/reference",
88+
guard: Type.isReference,
89+
},
90+
{
91+
schema: "schema:ethdebug/format/type/specifier",
92+
guard: Type.isSpecifier,
93+
},
94+
{
95+
schema: "schema:ethdebug/format/type/wrapper",
96+
guard: Type.isWrapper,
97+
},
8398
] as const);

‎packages/format/src/types/type/index.ts‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,30 @@ export namespace Type {
3232
(typeof value.contains === "object" &&
3333
Object.values(value.contains).every(Type.isWrapper)));
3434

35+
export interface Reference {
36+
id: string | number;
37+
}
38+
39+
export const isReference = (value: unknown): value is Reference =>
40+
typeof value === "object" &&
41+
!!value &&
42+
"id" in value &&
43+
(typeof value.id === "string" || typeof value.id === "number");
44+
45+
export type Specifier = Type | Reference;
46+
47+
export const isSpecifier = (value: unknown): value is Specifier =>
48+
isType(value) || isReference(value);
49+
3550
export interface Wrapper {
36-
type: Type | { id: any };
51+
type: Specifier;
3752
}
3853

3954
export const isWrapper = (value: unknown): value is Wrapper =>
4055
typeof value === "object" &&
4156
!!value &&
4257
"type" in value &&
43-
(isType(value.type) ||
44-
(typeof value.type === "object" && !!value.type && "id" in value.type));
58+
isSpecifier(value.type);
4559

4660
export type Elementary =
4761
| Elementary.Uint

0 commit comments

Comments
 (0)