Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace solc dependency with a thin solidity_compile wrapper #6073

Open
wants to merge 2 commits into
base: robust-compiler-downloader
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
52 changes: 18 additions & 34 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion v-next/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
"micro-eth-signer": "^0.13.0",
"p-map": "^7.0.2",
"semver": "^7.6.3",
"solc": "^0.8.27",
"tsx": "^4.11.0",
"ws": "^8.18.0",
"zod": "^3.23.8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ async function readStream(
}

async function getSolcJs(solcJsPath: string) {
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions --
We cast to string because it doesn't have types, and otherwise TS complains */
const { default: solcWrapper } = await import("solc/wrapper.js" as string);
const { default: solcWrapper } = await import("./solcjs-wrapper.js");
const { default: solc } = await import(solcJsPath);

return solcWrapper(solc);
Expand All @@ -28,7 +26,8 @@ async function main() {
const solcjsPath = process.argv[2];
const solc = await getSolcJs(solcjsPath);

const output = solc.compile(input);
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- the input read from the stdin should be a string
const output = solc.compile(input as string);

console.log(output);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// This wrapper was created by extracting the parts of the solc-js package
// (https://github.com/ethereum/solc-js) that we need to perform compilation.

import * as semver from "semver";

interface Solc {
cwrap<T>(ident: string, returnType: string | null, argTypes: string[]): T;

// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_reset?: Reset | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_version?: Version | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_version?: Version | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_compileStandard?: Compile | null;
// eslint-disable-next-line @typescript-eslint/naming-convention -- this is a C function
_solidity_compile?: Compile | null;
}

type Reset = () => string;
type Version = () => string;
type Compile = (
input: string,
callbackPtr: number | null,
callbackContextPtr?: null,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an odd type, but we either want to skip this argument or provide an explicit null. That depends on the version of solc that we're going to be using.

) => string;

export interface SolcWrapper {
compile: CompileWrapper;
}

export type CompileWrapper = (input: string) => string;

export default function wrapper(solc: Solc): SolcWrapper {
const version = bindVersion(solc);
const isVersion6OrNewer = semver.gte(versionToSemver(version()), "0.6.0");
galargh marked this conversation as resolved.
Show resolved Hide resolved
const reset = bindReset(solc);
const compile = bindCompile(solc, isVersion6OrNewer);

if (compile === undefined) {
// eslint-disable-next-line no-restricted-syntax -- should we use a HardhatError here?
throw new Error(
'Could not find the "compile" function in the solc library',
);
Comment on lines +43 to +46
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should throw a HardhatError here?

}

return {
compile: compileWrapper(isVersion6OrNewer, compile, reset),
};
}

function compileWrapper(
isVersion6OrNewer: boolean,
compile: Compile,
reset?: Reset,
): CompileWrapper {
return (input: string): string => {
const output = isVersion6OrNewer
? compile(input, null, null)
: compile(input, null);
Comment on lines +60 to +62
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The underlying compile functions expect explicit values as far as I understand. Also, their arguments differ depending on the version being used.


if (reset !== undefined) {
// Explicitly free memory.
//
// NOTE: cwrap() of "compile" will copy the returned pointer into a
// Javascript string and it is not possible to call free() on it.
// reset() however will clear up all allocations.
reset();
}

return output;
};
}

function bindVersion(solc: Solc): Version {
if (solc._solidity_version === null || solc._solidity_version === undefined) {
return solc.cwrap("version", "string", []);
}

return solc.cwrap("solidity_version", "string", []);
}

function bindReset(solc: Solc): Reset | undefined {
if (solc._solidity_reset === null || solc._solidity_reset === undefined) {
return undefined;
}

return solc.cwrap("solidity_reset", null, []);
}

function bindCompile(
solc: Solc,
isVersion6OrNewer: boolean,
): CompileWrapper | undefined {
if (isVersion6OrNewer) {
if (
solc._solidity_compile !== null &&
solc._solidity_compile !== undefined
) {
return solc.cwrap("solidity_compile", "string", [
"string",
"number",
"number",
]);
}
} else {
if (
solc._solidity_compile !== null &&
solc._solidity_compile !== undefined
) {
return solc.cwrap("solidity_compile", "string", ["string", "number"]);
}
if (solc._compileStandard !== null && solc._compileStandard !== undefined) {
return solc.cwrap("compileStandard", "string", ["string", "number"]);
}
}

return undefined;
}

function versionToSemver(version: string): string {
// FIXME: parse more detail, but this is a good start
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The implementation of this function is a direct copy from solc-js.

const parsed = version.match(
/^([0-9]+\.[0-9]+\.[0-9]+)-([0-9a-f]{8})[/*].*$/,
);
if (parsed !== null) {
return parsed[1] + "+commit." + parsed[2];
}
if (version.indexOf("0.1.3-0") !== -1) {
return "0.1.3";
}
if (version.indexOf("0.3.5-0") !== -1) {
return "0.3.5";
}
// assume it is already semver compatible
return version;
}
Loading