UNPKG

@openzeppelin/upgrade-safe-transpiler

Version:

Solidity preprocessor used to generate OpenZeppelin Contracts Upgrade Safe.

156 lines 8.14 kB
"use strict"; 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"); const add_namespace_struct_1 = require("./add-namespace-struct"); const is_storage_variable_1 = require("./utils/is-storage-variable"); 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?.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(isNamespaced) { return function* (sourceUnit, tools) { const { resolver, getData } = tools; const useNamespaces = isNamespaced?.(sourceUnit.absolutePath) ?? false; 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, resolver); const namespace = (0, add_namespace_struct_1.getNamespaceStructName)(name); const constructorUsesStorage = constructorNode !== undefined && usesStorageVariables(constructorNode, resolver); 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(' '), // To correctly place the namespace variable before variable initilaization // nodes, for the constructor it must be emitted here rather than in addNamespaceStruct useNamespaces && (varInitNodes.length > 0 || constructorUsesStorage) ? [`${namespace} storage $ = _get${namespace}();`] : [], varInitNodes.flatMap(v => { const prefix = useNamespaces ? '$.' : ''; const newExpr = (0, new_expression_1.parseNewExpression)(v.value); if (!newExpr) { return [`${prefix}${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 [ `${prefix}${v.name} = ${newCall(helper)};`, `${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; function usesStorageVariables(fnDef, resolver) { if (fnDef.body) { for (const ref of (0, utils_1.findAll)('Identifier', fnDef.body)) { const varDecl = resolver.tryResolveNode('VariableDeclaration', ref.referencedDeclaration); if (varDecl && (0, is_storage_variable_1.isStorageVariable)(varDecl, resolver)) { return true; } } } return false; } //# sourceMappingURL=transform-constructor.js.map