Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c4ac4cc
Implement partial transpilation
Amxx Sep 19, 2023
06f00f1
missing newlines
Amxx Sep 19, 2023
8064ec1
use getData to mark object that are not transpiled
Amxx Sep 19, 2023
890d8de
remove non-transpiled objects from files that are transpiled
Amxx Sep 19, 2023
48d3f61
fix
Amxx Sep 20, 2023
71e16a9
lint
Amxx Sep 20, 2023
f6cd41a
remove unecessary eslint-disable
Amxx Sep 20, 2023
4b19e44
declare TransformData.importPath in the fix-import-directives file
Amxx Sep 20, 2023
bfcb1e1
fix bug when a.local === undefined
Amxx Sep 20, 2023
9b831ea
refactor import for Initializable
Amxx Sep 20, 2023
46c62ab
update test cache
Amxx Sep 20, 2023
d216e47
change linting of the produced solidity code
Amxx Sep 20, 2023
904500b
Apply suggestions from code review
Amxx Sep 21, 2023
d4faaf8
Update src/transformations/rename-identifiers.ts
Amxx Sep 21, 2023
7f53af6
Rename importPath to importFromPeer
Amxx Sep 21, 2023
94c1892
move partial transpilation form class Transform to function transpile
Amxx Sep 21, 2023
8d5a339
add helper function excludeAndImportPathsForPeer
Amxx Sep 21, 2023
1bc7930
Update index.ts
Amxx Sep 21, 2023
2b95625
change exclude return
Amxx Sep 23, 2023
b12b69d
renames
frangio Sep 25, 2023
7d7cb3f
use for..of
frangio Sep 25, 2023
fd1302d
move variable definition inside
frangio Sep 25, 2023
0a3b6cf
Apply suggestions from code review
Amxx Sep 25, 2023
30ab602
resolve any imported node
Amxx Sep 25, 2023
be412df
lint
Amxx Sep 25, 2023
b806261
Switch node type
Amxx Sep 25, 2023
13ddd81
peer import constant variable declarations
Amxx Sep 25, 2023
e2456ab
Update peer-import.ts
Amxx Sep 25, 2023
3037c78
explicit comparaison against undefined
Amxx Sep 25, 2023
eb9a435
minimize change
Amxx Sep 25, 2023
d4a70dd
Update src/transformations/peer-import.ts
Amxx Sep 25, 2023
1f7c353
add assertion
frangio Sep 25, 2023
ea04008
add struct to test
frangio Sep 25, 2023
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
7 changes: 7 additions & 0 deletions contracts/project/ISomeInterface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6;

interface ISomeInterface {
function someFunction() external returns (bool);
function someOtherFunction() external returns (bool);
}
21 changes: 21 additions & 0 deletions contracts/project/SomeContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6;

import { ISomeInterface } from "./ISomeInterface.sol";
import { SomeLibrary } from "./SomeLibrary.sol";

interface ISomeContract is ISomeInterface {}

contract SomeContract is ISomeContract {
function someFunction() public override returns (bool) {
return false;
}

function someOtherFunction() public override returns (bool) {
return true;
}

function test(ISomeInterface other) public returns (bool) {
return SomeLibrary.bothFunctions(this) && SomeLibrary.bothFunctions(other);
}
}
10 changes: 10 additions & 0 deletions contracts/project/SomeLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6;

import { ISomeInterface } from "./ISomeInterface.sol";

library SomeLibrary {
function bothFunctions(ISomeInterface instance) internal returns (bool) {
return instance.someFunction() && instance.someOtherFunction();
}
}
12 changes: 12 additions & 0 deletions contracts/project/SomeOtherContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.6;

import { ISomeInterface } from "./ISomeInterface.sol";
import { SomeLibrary } from "./SomeLibrary.sol";
import { ISomeContract, SomeContract } from "./SomeContract.sol";

contract SomeOtherContract is SomeContract {
function extraFunction() public returns (uint256) {
return 42;
}
}
3 changes: 3 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface Options {
publicInitializers: string[];
namespaced: boolean;
namespaceExclude: string[];
peerProject?: string;
}

function readCommandFlags(resolveRootRelative: (p: string) => string): Options {
Expand All @@ -36,12 +37,14 @@ function readCommandFlags(resolveRootRelative: (p: string) => string): Options {
W: skipWithInit = false,
n: namespaced = false,
N: namespaceExclude = [],
P: peerProject = false,
} = minimist(process.argv.slice(2));
return {
buildInfo,
deleteOriginals,
skipWithInit,
namespaced,
peerProject,
namespaceExclude: ensureArray(namespaceExclude).map(resolveRootRelative),
initializablePath: initializablePath && resolveRootRelative(initializablePath),
publicInitializers: ensureArray(publicInitializers).map(resolveRootRelative),
Expand Down
24 changes: 12 additions & 12 deletions src/index.test.ts.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'ClassInheritance.sol',
path: 'contracts/solc-0.6/ClassInheritanceUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
interface CICUpgradeable {␊
function fooBar(uint a) external;␊
Expand Down Expand Up @@ -89,7 +89,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'Override.sol',
path: 'contracts/solc-0.6/OverrideUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract Parent1Upgradeable is Initializable {␊
function __Parent1_init() internal onlyInitializing {␊
Expand Down Expand Up @@ -158,7 +158,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'DiamondInheritance.sol',
path: 'contracts/solc-0.6/DiamondInheritanceUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract DAUpgradeable is Initializable {␊
event Log(string);␊
Expand Down Expand Up @@ -254,7 +254,7 @@ Generated by [AVA](https://avajs.dev).
source: `pragma solidity ^0.6.0;␊
import "../../../ElementaryTypesUpgradeable.sol";␊
import "../../../../Initializable.sol";␊
import {Initializable} from "../../../../Initializable.sol";␊
contract DeepUpgradeable is Initializable {␊
function __Deep_init() internal onlyInitializing {␊
Expand All @@ -281,7 +281,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'ElementaryTypes.sol',
path: 'contracts/solc-0.6/ElementaryTypesUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract ElementaryTypesUpgradeable is Initializable {␊
function __ElementaryTypes_init() internal onlyInitializing {␊
Expand Down Expand Up @@ -325,7 +325,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'ElementaryTypesWithConstructor.sol',
path: 'contracts/solc-0.6/ElementaryTypesWithConstructorUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract ElementaryTypesWithConstructorUpgradeable is Initializable {␊
address public owner;␊
Expand Down Expand Up @@ -366,7 +366,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'Imported.sol',
path: 'contracts/solc-0.6/ImportedUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract Imported1Upgradeable is Initializable {␊
function __Imported1_init(uint256 x, uint256 y) internal onlyInitializing {␊
Expand Down Expand Up @@ -409,7 +409,7 @@ Generated by [AVA](https://avajs.dev).
source: `pragma solidity ^0.6.0;␊
import "./ImportedUpgradeable.sol";␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract LocalUpgradeable is Initializable, Imported2Upgradeable {␊
function __Local_init(uint x, uint y) internal onlyInitializing {␊
Expand Down Expand Up @@ -437,7 +437,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'SimpleInheritance.sol',
path: 'contracts/solc-0.6/SimpleInheritanceUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract SIAUpgradeable is Initializable {␊
uint256 public foo;␊
Expand Down Expand Up @@ -509,7 +509,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'StringConstructor.sol',
path: 'contracts/solc-0.6/StringConstructorUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
contract StringConstructorUpgradeable is Initializable {␊
Expand Down Expand Up @@ -557,7 +557,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'AbstractContract.sol',
path: 'contracts/solc-0.6/AbstractContractUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
abstract contract AbstractContractUpgradeable is Initializable {␊
function __AbstractContract_init() internal onlyInitializing {␊
Expand Down Expand Up @@ -598,7 +598,7 @@ Generated by [AVA](https://avajs.dev).
fileName: 'Rename.sol',
path: 'contracts/solc-0.6/RenameUpgradeable.sol',
source: `pragma solidity ^0.6.0;␊
import "../Initializable.sol";␊
import {Initializable} from "../Initializable.sol";␊
library RenameLibraryUpgradeable {␊
function test() external {␊
Expand Down
Binary file modified src/index.test.ts.snap
Binary file not shown.
45 changes: 26 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { renameIdentifiers } from './transformations/rename-identifiers';
import { prependInitializableBase } from './transformations/prepend-initializable-base';
import { removeStateVarInits } from './transformations/purge-var-inits';
import { removeImmutable } from './transformations/remove-immutable';
import { removePartial } from './transformations/remove-partial';
import { removeInheritanceListArguments } from './transformations/remove-inheritance-list-args';
import { renameContractDefinition } from './transformations/rename-contract-definition';
import { appendInitializableImport } from './transformations/append-initializable-import';
Expand Down Expand Up @@ -47,11 +48,12 @@ interface TranspileOptions {
skipWithInit?: boolean;
namespaced?: boolean;
namespaceExclude?: string[];
peerProject?: string;
}

function getExtraOutputPaths(
paths: Paths,
options?: TranspileOptions,
options: TranspileOptions = {},
): Record<'initializable' | 'withInit', string> {
const outputPaths = mapValues(
{
Expand All @@ -61,8 +63,8 @@ function getExtraOutputPaths(
s => path.relative(paths.root, path.join(paths.sources, s)),
);

if (options?.initializablePath) {
outputPaths.initializable = options?.initializablePath;
if (options.initializablePath) {
outputPaths.initializable = options.initializablePath;
}

return outputPaths;
Expand All @@ -72,39 +74,41 @@ export async function transpile(
solcInput: SolcInput,
solcOutput: SolcOutput,
paths: Paths,
options?: TranspileOptions,
options: TranspileOptions = {},
): Promise<OutputFile[]> {
const outputPaths = getExtraOutputPaths(paths, options);
const alreadyInitializable = findAlreadyInitializable(solcOutput, options?.initializablePath);
const alreadyInitializable = findAlreadyInitializable(solcOutput, options.initializablePath);

const excludeSet = new Set([...alreadyInitializable, ...Object.values(outputPaths)]);
const excludeMatch = matcher(options?.exclude ?? []);
const excludeMatch = matcher(options.exclude ?? []);

const namespaceInclude = (source: string) => {
const namespaced = options?.namespaced ?? false;
const namespaceExclude = options?.namespaceExclude ?? [];
const namespaced = options.namespaced ?? false;
const namespaceExclude = options.namespaceExclude ?? [];
return namespaced && !namespaceExclude.some(p => minimatch(source, p));
};

const transform = new Transform(solcInput, solcOutput, {
exclude: source => excludeSet.has(source) || (excludeMatch(source) ?? isRenamed(source)),
peerProject: options.peerProject,
});

transform.apply(renameIdentifiers);
transform.apply(renameContractDefinition);
transform.apply(renameInheritdoc);
transform.apply(prependInitializableBase);
transform.apply(fixImportDirectives);
transform.apply(fixImportDirectives(!!options.peerProject));
transform.apply(appendInitializableImport(outputPaths.initializable));
transform.apply(fixNewStatement);
transform.apply(transformConstructor(namespaceInclude));
transform.apply(removeLeftoverConstructorHead);
transform.apply(addRequiredPublicInitializer(options?.publicInitializers));
transform.apply(addRequiredPublicInitializer(options.publicInitializers));
transform.apply(removeInheritanceListArguments);
transform.apply(removeStateVarInits);
transform.apply(removeImmutable);
transform.apply(removePartial);

if (options?.namespaced) {
if (options.namespaced) {
transform.apply(addNamespaceStruct(namespaceInclude));
} else {
transform.apply(addStorageGaps);
Expand All @@ -126,8 +130,11 @@ export async function transpile(
}

const initializableSource =
options?.initializablePath !== undefined
? transpileInitializable(solcInput, solcOutput, paths, options?.initializablePath)
options.initializablePath !== undefined
? transpileInitializable(solcInput, solcOutput, paths, {
...options,
initializablePath: options.initializablePath,
})
: fs.readFileSync(require.resolve('../Initializable.sol'), 'utf8');

outputFiles.push({
Expand All @@ -136,9 +143,9 @@ export async function transpile(
fileName: path.basename(outputPaths.initializable),
});

if (!options?.skipWithInit) {
if (!options.skipWithInit) {
outputFiles.push({
source: generateWithInit(transform, outputPaths.withInit, options?.solcVersion),
source: generateWithInit(transform, outputPaths.withInit, options.solcVersion),
path: outputPaths.withInit,
fileName: path.basename(outputPaths.withInit),
});
Expand All @@ -151,16 +158,16 @@ function transpileInitializable(
solcInput: SolcInput,
solcOutput: SolcOutput,
paths: Paths,
initializablePath: string,
options: TranspileOptions & Required<Pick<TranspileOptions, 'initializablePath'>>,
): string {
const transform = new Transform(solcInput, solcOutput);

transform.apply(function* (ast, tools) {
if (ast.absolutePath === initializablePath) {
if (ast.absolutePath === options.initializablePath) {
yield* renameIdentifiers(ast, tools);
yield* fixImportDirectives(ast, tools);
yield* fixImportDirectives(!!options.peerProject)(ast, tools);
}
});

return transform.results()[initializablePath];
return transform.results()[options.initializablePath];
}
42 changes: 42 additions & 0 deletions src/partial-transform.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import _test, { TestFn } from 'ava';
import hre from 'hardhat';

import { getBuildInfo } from './test-utils/get-build-info';
import { OutputFile, transpile } from '.';
import { SolcOutput } from './solc/input-output';

const test = _test as TestFn<Context>;

interface Context {
files: OutputFile[];
}

const projectDir = 'contracts/project';
const peerProject = '@openzeppelin/test/..';

test.serial.before('compile', async t => {
const buildInfo = await getBuildInfo('0.6');
const solcInput = buildInfo.input;
const solcOutput = buildInfo.output as SolcOutput;
const exclude = Object.keys(solcOutput.sources).filter(path => !path.startsWith(projectDir));

t.context.files = await transpile(solcInput, solcOutput, hre.config.paths, {
exclude,
peerProject,
});
});

for (const fileName of ['ISomeInterface.sol', 'SomeLibrary.sol']) {
test(`do not transpile ${fileName}`, t => {
const file = t.context.files.find(f => f.fileName === fileName);
t.is(file, undefined, 'file should not be transpiled');
});
}

for (const fileName of ['SomeContract.sol', 'SomeOtherContract.sol']) {
test(`transpile ${fileName}`, t => {
const file = t.context.files.find(f => f.fileName === fileName);
t.not(file, undefined, 'file not found');
t.snapshot(file);
});
}
Loading