Skip to content

Commit 75e9ca7

Browse files
committed
refactor: Separate data collection and rendering in console-library-generator.ts
1 parent 948fa85 commit 75e9ca7

File tree

2 files changed

+91
-101
lines changed

2 files changed

+91
-101
lines changed

packages/hardhat-core/console.sol

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ library console {
3737
function log() internal pure {
3838
_sendLogPayload(abi.encodeWithSignature("log()"));
3939
}
40+
4041
function logInt(int256 p0) internal pure {
4142
_sendLogPayload(abi.encodeWithSignature("log(int256)", p0));
4243
}
@@ -1548,5 +1549,4 @@ library console {
15481549
function log(address p0, address p1, address p2, address p3) internal pure {
15491550
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
15501551
}
1551-
15521552
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import fs from "fs";
1+
import fs from "node:fs";
22

33
import { keccak256 } from "../src/internal/util/keccak";
44

@@ -17,7 +17,6 @@ function* genPermutations(elemCount: number, len: number) {
1717
// from 0 to max number of permutations (elemCount ** len) and convert
1818
// each number to a list of digits as per the base `elemCount`, see above.
1919
const numberOfPermutations = elemCount ** len;
20-
// Pre-compute the base `elemCount` dividers ([1, elemCount, elemCount ** 2, ...])
2120
const dividers = Array(elemCount)
2221
.fill(0)
2322
.map((_, i) => elemCount ** i);
@@ -38,9 +37,9 @@ type TypeName = { type: string; modifier?: "memory" };
3837
type FnParam = TypeName & { name: string };
3938

4039
/** Computes the function selector for the given function with simple arguments. */
41-
function selector({ name = "log", params = [] as TypeName[] }) {
42-
const sig = params.map((p) => p.type).join(",");
43-
return keccak256(Buffer.from(`${name}(${sig})`)).slice(0, 4);
40+
function selector({ name = "", params = [] as TypeName[] }) {
41+
const sigParams = params.map((p) => p.type).join(",");
42+
return keccak256(Buffer.from(`${name}(${sigParams})`)).slice(0, 4);
4443
}
4544

4645
function toHex(value: Uint8Array) {
@@ -67,7 +66,66 @@ const TYPES = [
6766
{ type: "address" },
6867
] as const;
6968

70-
let consoleSolFile = `// SPDX-License-Identifier: MIT
69+
/** A list of `console.log*` functions that we want to generate. */
70+
const CONSOLE_LOG_FUNCTIONS =
71+
// Basic `log()` function
72+
[{ name: "log", params: [] as FnParam[] }]
73+
// Generate single parameter functions that are type-suffixed for
74+
// backwards-compatibility, e.g. logInt, logUint, logString, etc.
75+
.concat(
76+
SINGLE_TYPES.map((single) => {
77+
const param = { ...single, name: "p0" } as const;
78+
const nameSuffix = capitalize(param.type.replace("int256", "int"));
79+
80+
return {
81+
name: `log${nameSuffix}`,
82+
params: [param],
83+
} as const;
84+
})
85+
)
86+
// Also generate the function definitions for `log` for permutations of
87+
// up to 4 parameters using the `types` (uint256, string, bool, address).
88+
.concat(
89+
[...Array(4)].flatMap((_, paramCount) => {
90+
return Array.from(
91+
genPermutations(TYPES.length, paramCount + 1),
92+
(permutation) => ({
93+
name: "log",
94+
params: permutation.map((typeIndex, i) => ({
95+
...TYPES[typeIndex],
96+
name: `p${i}`,
97+
})),
98+
})
99+
);
100+
})
101+
);
102+
103+
/** Maps from a 4-byte function selector to a signature (argument types) */
104+
const CONSOLE_LOG_SIGNATURES: Map<string, string[]> =
105+
CONSOLE_LOG_FUNCTIONS.reduce((acc, { params }) => {
106+
// We always use `log` for the selector, even if it's logUint, for example.
107+
const signature = toHex(selector({ name: "log", params }));
108+
const types = params.map((p) => p.type);
109+
acc.set(signature, types);
110+
111+
// For backwards compatibility, we additionally support the (invalid)
112+
// selectors that contain the `int`/`uint` aliases in the selector calculation.
113+
if (params.some((p) => ["uint256", "int256"].includes(p.type))) {
114+
const aliased = params.map((p) => ({
115+
...p,
116+
type: p.type.replace("int256", "int"),
117+
}));
118+
119+
const signature = toHex(selector({ name: "log", params: aliased }));
120+
acc.set(signature, types);
121+
}
122+
123+
return acc;
124+
}, new Map());
125+
126+
// Finally, render and save the console.sol and logger.ts files
127+
const consoleSolFile = `\
128+
// SPDX-License-Identifier: MIT
71129
pragma solidity >=0.4.22 <0.9.0;
72130
73131
library console {
@@ -103,114 +161,46 @@ library console {
103161
_castToPure(_sendLogPayloadImplementation)(payload);
104162
}
105163
106-
function log() internal pure {
107-
_sendLogPayload(abi.encodeWithSignature("log()"));
108-
}
109-
`;
110-
111-
function renderLogFunction({ name = "log", params = [] as FnParam[] }): string {
164+
${CONSOLE_LOG_FUNCTIONS.map(({ name, params }) => {
112165
let fnParams = params
113166
.map((p) => `${p.type}${p.modifier ? ` ${p.modifier}` : ""} ${p.name}`)
114167
.join(", ");
115168
let sig = params.map((p) => p.type).join(",");
116169
let passed = params.map((p) => p.name).join(", ");
170+
let passedArgs = passed.length > 0 ? `, ${passed}` : "";
117171
118-
return ` function ${name}(${fnParams}) internal pure {
119-
_sendLogPayload(abi.encodeWithSignature("log(${sig})", ${passed}));
172+
return `\
173+
function ${name}(${fnParams}) internal pure {
174+
_sendLogPayload(abi.encodeWithSignature("log(${sig})"${passedArgs}));
120175
}
121-
122176
`;
177+
}).join("\n")}\
123178
}
179+
`;
124180

125-
let logger =
126-
"// ------------------------------------\n" +
127-
"// This code was autogenerated using\n" +
128-
"// scripts/console-library-generator.ts\n" +
129-
"// ------------------------------------\n\n";
130-
131-
for (let i = 0; i < SINGLE_TYPES.length; i++) {
132-
const type = capitalize(SINGLE_TYPES[i].type);
133-
134-
logger += `export const ${type}Ty = "${type}";\n`;
135-
}
181+
const loggerFile = `\
182+
// ------------------------------------
183+
// This code was autogenerated using
184+
// scripts/console-library-generator.ts
185+
// ------------------------------------
136186
137-
logger +=
138-
"\n/** Maps from a 4-byte function selector to a signature (argument types) */\n" +
139-
"export const CONSOLE_LOG_SIGNATURES: Record<number, string[]> = {\n";
187+
${Array.from(SINGLE_TYPES.map((t) => capitalize(t.type)))
188+
.map((t) => `export const ${t}Ty = "${t}";`)
189+
.join("\n")}
140190
141191
/** Maps from a 4-byte function selector to a signature (argument types) */
142-
const CONSOLE_LOG_SIGNATURES: Map<string, string[]> = new Map();
143-
144-
// Add the empty log() first
145-
const sigInt = toHex(selector({ name: "log", params: [] }));
146-
CONSOLE_LOG_SIGNATURES.set(sigInt, []);
147-
148-
// Generate single parameter functions that are type-suffixed for
149-
// backwards-compatibility, e.g. logInt, logUint, logString, etc.
150-
for (let i = 0; i < SINGLE_TYPES.length; i++) {
151-
const param = { ...SINGLE_TYPES[i], name: "p0" } as const;
152-
153-
const typeAliased = param.type.replace("int256", "int");
154-
const nameSuffix = capitalize(typeAliased);
155-
156-
const signature = toHex(selector({ name: "log", params: [param] }));
157-
CONSOLE_LOG_SIGNATURES.set(signature, [param.type]);
158-
159-
// For full backwards compatibility, also support the (invalid) selectors of
160-
// `log(int)` and `log(uint)`. The selector encoding specifies that one has to
161-
// use the canonical type name but it seems that we supported it in the past.
162-
if (["uint256", "int256"].includes(param.type)) {
163-
const signature = toHex(
164-
selector({ name: "log", params: [{ type: typeAliased }] })
165-
);
166-
CONSOLE_LOG_SIGNATURES.set(signature, [param.type]);
167-
}
168-
169-
consoleSolFile += renderLogFunction({
170-
name: `log${nameSuffix}`,
171-
params: [param],
172-
});
173-
}
174-
175-
// Now generate the function definitions for `log` for permutations of
176-
// up to 4 parameters using the `types` (uint256, string, bool, address).
177-
const MAX_NUMBER_OF_PARAMETERS = 4;
178-
for (let paramCount = 0; paramCount < MAX_NUMBER_OF_PARAMETERS; paramCount++) {
179-
for (const permutation of genPermutations(TYPES.length, paramCount + 1)) {
180-
const params = permutation.map(
181-
(typeIndex, i) => ({ ...TYPES[typeIndex], name: `p${i}` } as const)
182-
);
183-
184-
consoleSolFile += renderLogFunction({ params });
185-
186-
const types = params.map((p) => p.type);
187-
const signature = toHex(selector({ name: "log", params }));
188-
CONSOLE_LOG_SIGNATURES.set(signature, types);
189-
190-
// Again, for full backwards compatibility, also support the (invalid)
191-
// selectors that contain the `int`/`uint` aliases in the selector calculation.
192-
if (params.some((p) => ["uint256", "int256"].includes(p.type))) {
193-
const aliased = params.map((p) => ({
194-
...p,
195-
type: p.type.replace("int256", "int"),
196-
}));
197-
198-
const signature = toHex(selector({ name: "log", params: aliased }));
199-
CONSOLE_LOG_SIGNATURES.set(signature, types);
200-
}
201-
}
202-
}
203-
204-
for (const [sig, types] of CONSOLE_LOG_SIGNATURES) {
205-
const typeNames = types.map((t) => `${capitalize(t)}Ty`).join(", ");
206-
logger += ` ${sig}: [${typeNames}],\n`;
207-
}
208-
209-
consoleSolFile += "}\n";
210-
logger += "};\n";
192+
export const CONSOLE_LOG_SIGNATURES: Record<number, string[]> = {
193+
${Array.from(CONSOLE_LOG_SIGNATURES)
194+
.map(([sig, types]) => {
195+
const typeNames = types.map((t) => `${capitalize(t)}Ty`).join(", ");
196+
return ` ${sig}: [${typeNames}],`;
197+
})
198+
.join("\n")}
199+
};
200+
`;
211201

202+
fs.writeFileSync(__dirname + "/../console.sol", consoleSolFile);
212203
fs.writeFileSync(
213204
__dirname + "/../src/internal/hardhat-network/stack-traces/logger.ts",
214-
logger
205+
loggerFile
215206
);
216-
fs.writeFileSync(__dirname + "/../console.sol", consoleSolFile);

0 commit comments

Comments
 (0)