/src/solidity/libsolidity/ast/ASTUtils.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | |
19 | | #include <libsolidity/analysis/ConstantEvaluator.h> |
20 | | #include <libsolidity/ast/AST.h> |
21 | | #include <libsolidity/ast/ASTUtils.h> |
22 | | #include <libsolidity/ast/ASTVisitor.h> |
23 | | |
24 | | #include <libsolutil/Algorithms.h> |
25 | | |
26 | | namespace solidity::frontend |
27 | | { |
28 | | |
29 | | ASTNode const* locateInnermostASTNode(int _offsetInFile, SourceUnit const& _sourceUnit) |
30 | 0 | { |
31 | 0 | ASTNode const* innermostMatch = nullptr; |
32 | 0 | auto locator = SimpleASTVisitor( |
33 | 0 | [&](ASTNode const& _node) -> bool |
34 | 0 | { |
35 | | // In the AST parent location always covers the whole child location. |
36 | | // The parent is visited first so to get the innermost node we simply |
37 | | // take the last one that still contains the offset. |
38 | |
|
39 | 0 | if (!_node.location().containsOffset(_offsetInFile)) |
40 | 0 | return false; |
41 | | |
42 | 0 | innermostMatch = &_node; |
43 | 0 | return true; |
44 | 0 | }, |
45 | 0 | [](ASTNode const&) {} |
46 | 0 | ); |
47 | 0 | _sourceUnit.accept(locator); |
48 | 0 | return innermostMatch; |
49 | 0 | } |
50 | | |
51 | | bool isConstantVariableRecursive(VariableDeclaration const& _varDecl) |
52 | 285 | { |
53 | 285 | solAssert(_varDecl.isConstant(), "Constant variable expected"); |
54 | | |
55 | 285 | auto visitor = [](VariableDeclaration const& _variable, util::CycleDetector<VariableDeclaration>& _cycleDetector, size_t _depth) |
56 | 303 | { |
57 | 303 | solAssert(_depth < 256, "Recursion depth limit reached"); |
58 | 303 | if (!_variable.value()) |
59 | | // This should result in an error later on. |
60 | 4 | return; |
61 | | |
62 | 299 | if (auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>( |
63 | 299 | ASTNode::referencedDeclaration(*_variable.value())) |
64 | 299 | ) |
65 | 26 | if (referencedVarDecl->isConstant()) |
66 | 22 | _cycleDetector.run(*referencedVarDecl); |
67 | 299 | }; |
68 | | |
69 | 285 | return util::CycleDetector<VariableDeclaration>(visitor).run(_varDecl) != nullptr; |
70 | 285 | } |
71 | | |
72 | | VariableDeclaration const* rootConstVariableDeclaration(VariableDeclaration const& _varDecl) |
73 | 169 | { |
74 | 169 | solAssert(_varDecl.isConstant(), "Constant variable expected"); |
75 | 169 | solAssert(!isConstantVariableRecursive(_varDecl), "Recursive declaration"); |
76 | | |
77 | 169 | VariableDeclaration const* rootDecl = &_varDecl; |
78 | 169 | Identifier const* identifier; |
79 | 176 | while ((identifier = dynamic_cast<Identifier const*>(rootDecl->value().get()))) |
80 | 11 | { |
81 | 11 | auto referencedVarDecl = dynamic_cast<VariableDeclaration const*>(identifier->annotation().referencedDeclaration); |
82 | 11 | if (!referencedVarDecl || !referencedVarDecl->isConstant()) |
83 | 4 | return nullptr; |
84 | 7 | rootDecl = referencedVarDecl; |
85 | 7 | } |
86 | 165 | return rootDecl; |
87 | 169 | } |
88 | | |
89 | | Expression const* resolveOuterUnaryTuples(Expression const* _expr) |
90 | 8.40k | { |
91 | 8.49k | while (auto const* tupleExpression = dynamic_cast<TupleExpression const*>(_expr)) |
92 | 532 | if (tupleExpression->components().size() == 1) |
93 | 89 | _expr = tupleExpression->components().front().get(); |
94 | 443 | else |
95 | 443 | break; |
96 | 8.40k | return _expr; |
97 | 8.40k | } |
98 | | |
99 | | Type const* type(Expression const& _expression) |
100 | 1.54M | { |
101 | 1.54M | solAssert(!!_expression.annotation().type, "Type requested but not present."); |
102 | 1.54M | return _expression.annotation().type; |
103 | 1.54M | } |
104 | | |
105 | | Type const* type(VariableDeclaration const& _variable) |
106 | 154k | { |
107 | 154k | solAssert(!!_variable.annotation().type, "Type requested but not present."); |
108 | 154k | return _variable.annotation().type; |
109 | 154k | } |
110 | | |
111 | | bigint contractStorageSizeUpperBound(ContractDefinition const& _contract, VariableDeclaration::Location _location) |
112 | 69.1k | { |
113 | 69.1k | solAssert(_location == VariableDeclaration::Location::Unspecified || _location == VariableDeclaration::Location::Transient); |
114 | | |
115 | 69.1k | bigint size = 0; |
116 | 69.1k | for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts) |
117 | 83.0k | for (VariableDeclaration const* variable: contract->stateVariables()) |
118 | 36.2k | if ( |
119 | 36.2k | !(variable->isConstant() || variable->immutable()) && |
120 | 32.3k | variable->referenceLocation() == _location |
121 | 36.2k | ) |
122 | 20.3k | size += variable->annotation().type->storageSizeUpperBound(); |
123 | | |
124 | 69.1k | return size; |
125 | 69.1k | } |
126 | | |
127 | | u256 layoutBaseForInheritanceHierarchy(ContractDefinition const& _topLevelContract, DataLocation _location) |
128 | 82.7k | { |
129 | 82.7k | if (_location != DataLocation::Storage) |
130 | 32.7k | { |
131 | 32.7k | solAssert(_location == DataLocation::Transient); |
132 | 32.7k | return 0; |
133 | 32.7k | } |
134 | | |
135 | 50.0k | if (auto const* storageLayoutSpecifier = _topLevelContract.storageLayoutSpecifier()) |
136 | 0 | return *storageLayoutSpecifier->annotation().baseSlot; |
137 | | |
138 | 50.0k | return 0; |
139 | 50.0k | } |
140 | | |
141 | | std::optional<u256> erc7201CompileTimeValue(FunctionCall const& _erc7201Call) |
142 | 0 | { |
143 | 0 | auto const* erc7201Function = dynamic_cast<MagicVariableDeclaration const*>(ASTNode::referencedDeclaration(_erc7201Call.expression())); |
144 | 0 | solAssert(erc7201Function); |
145 | 0 | solAssert(erc7201Function->functionType(true /* internal */)->kind() == FunctionType::Kind::ERC7201); |
146 | |
|
147 | 0 | auto evaluatedResult = ConstantEvaluator::tryEvaluate(_erc7201Call); |
148 | |
|
149 | 0 | if (std::holds_alternative<std::monostate>(evaluatedResult.value)) |
150 | 0 | return std::nullopt; |
151 | | |
152 | 0 | solAssert(std::holds_alternative<rational>(evaluatedResult.value)); |
153 | 0 | auto rationalValue = std::get<rational>(evaluatedResult.value); |
154 | 0 | solAssert(rationalValue.denominator() == 1); |
155 | 0 | bigint baseSlot = rationalValue.numerator(); |
156 | | solAssert(baseSlot <= std::numeric_limits<u256>::max()); |
157 | 0 | return u256(baseSlot); |
158 | 0 | } |
159 | | } |