@solvprotocol/upgrade-safe-transpiler
Version:
Solidity preprocessor used to generate OpenZeppelin Contracts Upgrade Safe.
129 lines • 6.51 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformConstructor = exports.removeLeftoverConstructorHead = void 0;
const ast_utils_1 = require("../solc/ast-utils");
const build_super_calls_for_chain_1 = require("./utils/build-super-calls-for-chain");
const utils_1 = require("solidity-ast/utils");
const new_function_position_1 = require("./utils/new-function-position");
const format_lines_1 = require("./utils/format-lines");
const upgrades_overrides_1 = require("../utils/upgrades-overrides");
const get_initializer_items_1 = require("./utils/get-initializer-items");
const new_expression_1 = require("../utils/new-expression");
function getArgsList(constructor, helper) {
return helper.read(constructor.parameters).replace(/^\((.*)\)$/s, '$1');
}
// Removes parameters unused by the constructor's body
function getUnchainedArguments(constructor, helper, modifiers) {
// Get declared parameters information
const parameters = constructor.parameters.parameters;
// Gets all arguments arrays and concat them into one array
const usedOnModifiers = modifiers.flatMap((m) => [
...(0, utils_1.findAll)('Identifier', m),
]);
if (!(parameters === null || parameters === void 0 ? void 0 : parameters.length)) {
return '';
}
else {
let result = getArgsList(constructor, helper);
const usedIds = new Set([...(0, utils_1.findAll)('Identifier', constructor.body)].map(i => i.referencedDeclaration));
for (const p of parameters) {
// Check if parameter is used on the body or the modifiers
if (!usedIds.has(p.id) &&
!usedOnModifiers.some((m) => m.referencedDeclaration === p.id)) {
// Remove unused parameter
result = result.replace(/\s+[a-z0-9$_]+/gi, m => (m.trim() === p.name ? '' : m));
}
}
return result;
}
}
// Runs after transformConstructor to remove the constructor keyword and parameters until the first `{`. For example
// This: constructor(uint a) /* modifiers */ public { function __Name_init(uint a) /* modifiers */
// Results in: function __Name_init(uint a) /* modifiers */
function* removeLeftoverConstructorHead(sourceUnit) {
for (const contractNode of (0, utils_1.findAll)('ContractDefinition', sourceUnit)) {
if ((0, upgrades_overrides_1.hasConstructorOverride)(contractNode)) {
continue;
}
const constructorNode = (0, ast_utils_1.getConstructor)(contractNode);
if (constructorNode) {
const { start: ctorStart } = (0, ast_utils_1.getNodeBounds)(constructorNode);
const { start: bodyStart } = (0, ast_utils_1.getNodeBounds)(constructorNode.body);
yield {
start: ctorStart,
length: bodyStart + 1 - ctorStart,
kind: 'remove-leftover-constructor',
text: '',
};
}
}
}
exports.removeLeftoverConstructorHead = removeLeftoverConstructorHead;
// Inserts the init and unchained function declarations before the constructor first`{`,
// and must run removeLeftoverConstructorHead after. For example
// This: constructor(uint a) /* modifiers */ public
// Results in: constructor(uint a) /* modifiers */ public { function __Name_init(uint a) /* modifiers */
function* transformConstructor(sourceUnit, tools) {
const { resolver, getData } = tools;
for (const contractNode of (0, utils_1.findAll)('ContractDefinition', sourceUnit)) {
if (contractNode.contractKind !== 'contract' || (0, upgrades_overrides_1.hasConstructorOverride)(contractNode)) {
continue;
}
const { name } = contractNode;
const { constructorNode, varInitNodes, modifiers, emptyUnchained: emptyConstructor, } = (0, get_initializer_items_1.getInitializerItems)(contractNode);
const initializer = (helper, argsList = '', unchainedArgsList = '', argNames = []) => [
`function __${name}_init(${argsList}) internal onlyInitializing {`,
(0, build_super_calls_for_chain_1.buildSuperCallsForChain)(contractNode, tools, helper),
emptyConstructor ? [] : [`__${name}_init_unchained(${argNames.join(', ')});`],
`}`,
``,
[
`function`,
`__${name}_init_unchained(${unchainedArgsList})`,
`internal onlyInitializing`,
...modifiers.map(m => helper.read(m)),
`{`,
].join(' '),
varInitNodes.map(v => {
const newExpr = (0, new_expression_1.parseNewExpression)(v.value);
if (!newExpr) {
return `${v.name} = ${helper.read(v.value)};`;
}
else {
const { typeName, newCall, initializeCall } = newExpr;
const createdContract = resolver.resolveContract(typeName.referencedDeclaration);
if (createdContract) {
getData(createdContract).isUsedInNewStatement = true;
}
return `${v.name} = ${newCall(helper)};\n ${initializeCall(v.name, helper)};`;
}
}),
`}`,
];
if (constructorNode) {
const { start: bodyStart } = (0, ast_utils_1.getNodeBounds)(constructorNode.body);
const argNames = constructorNode.parameters.parameters.map(p => p.name);
yield {
start: bodyStart + 1,
length: 0,
kind: 'transform-constructor',
transform: (_, helper) => {
const argsList = getArgsList(constructorNode, helper);
const unchainedArgsList = getUnchainedArguments(constructorNode, helper, modifiers);
return (0, format_lines_1.formatLines)(1, initializer(helper, argsList, unchainedArgsList, argNames).slice(0, -1)).trim();
},
};
}
else {
const start = (0, new_function_position_1.newFunctionPosition)(contractNode, tools);
yield {
start,
length: 0,
kind: 'transform-constructor',
transform: (source, helper) => (0, format_lines_1.formatLines)(1, initializer(helper)),
};
}
}
}
exports.transformConstructor = transformConstructor;
//# sourceMappingURL=transform-constructor.js.map