-
Notifications
You must be signed in to change notification settings - Fork 15
Implement partial transpilation #130
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
Changes from 22 commits
c4ac4cc
06f00f1
8064ec1
890d8de
48d3f61
71e16a9
f6cd41a
4b19e44
bfcb1e1
9b831ea
46c62ab
d216e47
904500b
d4faaf8
7f53af6
94c1892
8d5a339
1bc7930
2b95625
b12b69d
7d7cb3f
fd1302d
0a3b6cf
30ab602
be412df
b806261
13ddd81
e2456ab
3037c78
eb9a435
d4a70dd
1f7c353
ea04008
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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); | ||
| } |
| 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); | ||
| } | ||
| } |
| 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(); | ||
| } | ||
| } |
| 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; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,10 +3,13 @@ import fs from 'fs'; | |
| import { mapValues } from 'lodash'; | ||
| import { minimatch } from 'minimatch'; | ||
|
|
||
| import { findAll } from 'solidity-ast/utils'; | ||
| import { Node } from 'solidity-ast/node'; | ||
|
|
||
| import { matcher } from './utils/matcher'; | ||
| import { renamePath, isRenamed } from './rename'; | ||
| import { SolcOutput, SolcInput } from './solc/input-output'; | ||
| import { Transform } from './transform'; | ||
| import { Transform, TransformData } from './transform'; | ||
| import { generateWithInit } from './generate-with-init'; | ||
| import { findAlreadyInitializable } from './find-already-initializable'; | ||
|
|
||
|
|
@@ -15,6 +18,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'; | ||
|
|
@@ -47,11 +51,14 @@ interface TranspileOptions { | |
| skipWithInit?: boolean; | ||
| namespaced?: boolean; | ||
| namespaceExclude?: string[]; | ||
| peerProject?: string; | ||
| } | ||
|
|
||
| type NodeTransformData = { node: Node; data: Partial<TransformData> }; | ||
|
|
||
| function getExtraOutputPaths( | ||
| paths: Paths, | ||
| options?: TranspileOptions, | ||
| options: TranspileOptions = {}, | ||
| ): Record<'initializable' | 'withInit', string> { | ||
| const outputPaths = mapValues( | ||
| { | ||
|
|
@@ -61,50 +68,98 @@ 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; | ||
| } | ||
|
|
||
| function getExcludeAndImportPathsForPeer( | ||
| solcOutput: SolcOutput, | ||
| peerProject: string, | ||
| ): [Set<string>, NodeTransformData[]] { | ||
| const data: NodeTransformData[] = []; | ||
| const exclude: Set<string> = new Set(); | ||
|
|
||
| for (const [source, { ast }] of Object.entries(solcOutput.sources)) { | ||
| let shouldExclude = true; | ||
| for (const node of findAll('ContractDefinition', ast)) { | ||
| if (node.contractKind === 'contract') { | ||
|
||
| shouldExclude = false; | ||
| } else { | ||
| const importFromPeer = path.join(peerProject, source); | ||
| data.push({ node, data: { importFromPeer } }); | ||
| } | ||
| } | ||
| if (shouldExclude) { | ||
| exclude.add(source); | ||
| } | ||
| } | ||
|
|
||
| return [exclude, data]; | ||
| } | ||
|
|
||
| export async function transpile( | ||
| solcInput: SolcInput, | ||
| solcOutput: SolcOutput, | ||
| paths: Paths, | ||
| options?: TranspileOptions, | ||
| options: TranspileOptions = {}, | ||
| ): Promise<OutputFile[]> { | ||
| const nodeData: NodeTransformData[] = []; | ||
|
|
||
| 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 softExcludeSet = new Set(); | ||
| 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)); | ||
| }; | ||
|
|
||
| // if partial transpilation, extract the list of soft exclude, and the peer import paths. | ||
| if (options.peerProject !== undefined) { | ||
| const [peerSoftExcludeSet, importFromPeerData] = getExcludeAndImportPathsForPeer( | ||
| solcOutput, | ||
| options.peerProject, | ||
| ); | ||
| peerSoftExcludeSet.forEach(source => softExcludeSet.add(source)); | ||
| nodeData.push(...importFromPeerData); | ||
| } | ||
|
|
||
| const transform = new Transform(solcInput, solcOutput, { | ||
| exclude: source => excludeSet.has(source) || (excludeMatch(source) ?? isRenamed(source)), | ||
| exclude: source => | ||
| excludeSet.has(source) || (excludeMatch(source) ?? isRenamed(source)) | ||
| ? 'hard' | ||
| : softExcludeSet.has(source) | ||
| ? 'soft' | ||
| : false, | ||
| }); | ||
|
|
||
| for (const { node, data } of nodeData) { | ||
| Object.assign(transform.getData(node), data); | ||
| } | ||
|
|
||
| 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); | ||
|
|
@@ -126,8 +181,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({ | ||
|
|
@@ -136,9 +194,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), | ||
| }); | ||
|
|
@@ -151,16 +209,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]; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.