Skip to content

Commit cfd0884

Browse files
committed
Be more strict about types in consoleLogger.ts and make it a static class
This makes it clearer what the exact logic is and if additional state is used, which will be helpful when porting this code to Rust as we don't want to rely on an external console.log decoding logic in JS.
1 parent 3e913bd commit cfd0884

File tree

3 files changed

+34
-38
lines changed

3 files changed

+34
-38
lines changed

packages/hardhat-core/src/internal/hardhat-network/provider/provider.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,7 @@ export class EdrProviderWrapper
289289
},
290290
{
291291
enable: loggerConfig.enabled,
292-
decodeConsoleLogInputsCallback: (inputs: Buffer[]) => {
293-
const consoleLogger = new ConsoleLogger();
294-
return consoleLogger.getDecodedLogs(inputs);
295-
},
292+
decodeConsoleLogInputsCallback: ConsoleLogger.getDecodedLogs,
296293
getContractAndFunctionNameCallback: (
297294
code: Buffer,
298295
calldata?: Buffer

packages/hardhat-core/src/internal/hardhat-network/stack-traces/consoleLogger.ts

+28-29
Original file line numberDiff line numberDiff line change
@@ -50,27 +50,41 @@ import {
5050

5151
const REGISTER_SIZE = 32;
5252

53-
export type ConsoleLogEntry = string | ConsoleLogEntry[];
54-
export type ConsoleLogs = ConsoleLogEntry[];
53+
/** The decoded string representation of the arguments supplied to console.log */
54+
export type ConsoleLogArgs = string[];
55+
export type ConsoleLogs = ConsoleLogArgs[];
5556

5657
export class ConsoleLogger {
5758
/**
5859
* Temporary code to print console.sol messages that come from EDR
5960
*/
60-
public getDecodedLogs(messages: Buffer[]): string[] {
61+
public static getDecodedLogs(messages: Buffer[]): string[] {
6162
const logs: string[] = [];
6263

6364
for (const message of messages) {
64-
const log = this._maybeConsoleLog(message);
65+
const log = ConsoleLogger._maybeConsoleLog(message);
6566
if (log !== undefined) {
66-
logs.push(consoleLogToString(log));
67+
logs.push(ConsoleLogger.format(log));
6768
}
6869
}
6970

7071
return logs;
7172
}
7273

73-
private _maybeConsoleLog(calldata: Buffer): ConsoleLogs | undefined {
74+
/**
75+
* Returns a formatted string using the first argument as a `printf`-like
76+
* format string which can contain zero or more format specifiers.
77+
*
78+
* If there are more arguments passed than the number of specifiers, the
79+
* extra arguments are concatenated to the returned string, separated by spaces.
80+
*/
81+
public static format(args: ConsoleLogArgs = []): string {
82+
return util.format(...args);
83+
}
84+
85+
private static _maybeConsoleLog(
86+
calldata: Buffer
87+
): ConsoleLogArgs | undefined {
7488
const selector = bytesToInt(calldata.slice(0, 4));
7589
const parameters = calldata.slice(4);
7690

@@ -79,15 +93,10 @@ export class ConsoleLogger {
7993
return;
8094
}
8195

82-
const consoleLogs = this._decode(parameters, argTypes);
83-
84-
this._replaceNumberFormatSpecifiers(consoleLogs);
85-
86-
return consoleLogs;
87-
}
96+
const decodedArgs = ConsoleLogger._decode(parameters, argTypes);
8897

89-
private _replaceNumberFormatSpecifiers(consoleLogs: ConsoleLogs) {
9098
/**
99+
* The first argument is interpreted as the format string, which may need adjusting.
91100
* Replace the occurrences of %d and %i with %s. This is necessary because if the arguments passed are numbers,
92101
* they could be too large to be formatted as a Number or an Integer, so it is safer to use a String.
93102
* %d and %i are replaced only if there is an odd number of % before the d or i.
@@ -99,15 +108,18 @@ export class ConsoleLogger {
99108
* (?<!%) negative look-behind to make this work.
100109
* The (?:) is just to avoid capturing that inner group.
101110
*/
102-
if (consoleLogs.length > 0 && typeof consoleLogs[0] === "string") {
103-
consoleLogs[0] = consoleLogs[0].replace(
111+
if (decodedArgs.length > 0) {
112+
decodedArgs[0] = decodedArgs[0].replace(
104113
/((?<!%)(?:%%)*)(%[di])/g,
105114
"$1%s"
106115
);
107116
}
117+
118+
return decodedArgs;
108119
}
109120

110-
private _decode(data: Buffer, types: string[]): ConsoleLogs {
121+
/** Decodes parameters from `data` according to `types` into their string representation. */
122+
private static _decode(data: Buffer, types: string[]): string[] {
111123
return types.map((type, i) => {
112124
const position: number = i * 32;
113125
switch (types[i]) {
@@ -221,16 +233,3 @@ export class ConsoleLogger {
221233
});
222234
}
223235
}
224-
225-
export function consoleLogToString(log: ConsoleLogs): string {
226-
if (log === undefined) {
227-
return "";
228-
}
229-
230-
// special case for console.log()
231-
if (log.length === 0) {
232-
return "";
233-
}
234-
235-
return util.format(log[0], ...log.slice(1));
236-
}

packages/hardhat-core/test/internal/hardhat-network/stack-traces/test.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { EdrProviderWrapper } from "../../../../src/internal/hardhat-network/pro
99
import { ReturnData } from "../../../../src/internal/hardhat-network/provider/return-data";
1010
import {
1111
ConsoleLogs,
12-
consoleLogToString,
12+
ConsoleLogger,
1313
} from "../../../../src/internal/hardhat-network/stack-traces/consoleLogger";
1414
import {
1515
printMessageTrace,
@@ -95,7 +95,7 @@ interface DeploymentTransaction {
9595
};
9696
stackTrace?: StackFrameDescription[]; // No stack trace === the tx MUST be successful
9797
imports?: string[]; // Imports needed for successful compilation
98-
consoleLogs?: ConsoleLogs[];
98+
consoleLogs?: ConsoleLogs;
9999
gas?: number;
100100
}
101101

@@ -110,7 +110,7 @@ interface CallTransaction {
110110
// The second one is with function and parms
111111
function?: string; // Default: no data
112112
params?: Array<string | number>; // Default: no param
113-
consoleLogs?: ConsoleLogs[];
113+
consoleLogs?: ConsoleLogs;
114114
gas?: number;
115115
}
116116

@@ -455,7 +455,7 @@ function compareStackTraces(
455455
assert.lengthOf(trace, description.length);
456456
}
457457

458-
function compareConsoleLogs(logs: string[], expectedLogs?: ConsoleLogs[]) {
458+
function compareConsoleLogs(logs: string[], expectedLogs?: ConsoleLogs) {
459459
if (expectedLogs === undefined) {
460460
return;
461461
}
@@ -464,7 +464,7 @@ function compareConsoleLogs(logs: string[], expectedLogs?: ConsoleLogs[]) {
464464

465465
for (let i = 0; i < logs.length; i++) {
466466
const actual = logs[i];
467-
const expected = consoleLogToString(expectedLogs[i]);
467+
const expected = ConsoleLogger.format(expectedLogs[i]);
468468

469469
assert.equal(actual, expected);
470470
}

0 commit comments

Comments
 (0)