/src/solidity/libsolidity/codegen/ExpressionCompiler.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2014 |
21 | | * Solidity AST to EVM bytecode compiler for expressions. |
22 | | */ |
23 | | |
24 | | #include <libsolidity/codegen/ExpressionCompiler.h> |
25 | | |
26 | | #include <libsolidity/codegen/ReturnInfo.h> |
27 | | #include <libsolidity/codegen/CompilerContext.h> |
28 | | #include <libsolidity/codegen/CompilerUtils.h> |
29 | | #include <libsolidity/codegen/LValue.h> |
30 | | |
31 | | #include <libsolidity/ast/AST.h> |
32 | | #include <libsolidity/ast/ASTUtils.h> |
33 | | #include <libsolidity/ast/TypeProvider.h> |
34 | | |
35 | | #include <libevmasm/GasMeter.h> |
36 | | #include <libsolutil/Common.h> |
37 | | #include <libsolutil/FunctionSelector.h> |
38 | | #include <libsolutil/Keccak256.h> |
39 | | #include <libsolutil/Whiskers.h> |
40 | | #include <libsolutil/StackTooDeepString.h> |
41 | | |
42 | | #include <boost/algorithm/string/replace.hpp> |
43 | | #include <numeric> |
44 | | #include <utility> |
45 | | |
46 | | using namespace std; |
47 | | using namespace solidity; |
48 | | using namespace solidity::evmasm; |
49 | | using namespace solidity::frontend; |
50 | | using namespace solidity::langutil; |
51 | | using namespace solidity::util; |
52 | | |
53 | | namespace |
54 | | { |
55 | | |
56 | | Type const* closestType(Type const* _type, Type const* _targetType, bool _isShiftOp) |
57 | 355k | { |
58 | 355k | if (_isShiftOp) |
59 | 0 | return _type->mobileType(); |
60 | 355k | else if (auto const* tupleType = dynamic_cast<TupleType const*>(_type)) |
61 | 3.48k | { |
62 | 3.48k | solAssert(_targetType, ""); |
63 | 3.48k | TypePointers const& targetComponents = dynamic_cast<TupleType const&>(*_targetType).components(); |
64 | 3.48k | solAssert(tupleType->components().size() == targetComponents.size(), ""); |
65 | 3.48k | TypePointers tempComponents(targetComponents.size()); |
66 | 10.4k | for (size_t i = 0; i < targetComponents.size(); ++i) |
67 | 6.96k | { |
68 | 6.96k | if (tupleType->components()[i] && targetComponents[i]) |
69 | 3.48k | { |
70 | 3.48k | tempComponents[i] = closestType(tupleType->components()[i], targetComponents[i], _isShiftOp); |
71 | 3.48k | solAssert(tempComponents[i], ""); |
72 | 3.48k | } |
73 | 6.96k | } |
74 | 3.48k | return TypeProvider::tuple(move(tempComponents)); |
75 | 3.48k | } |
76 | 351k | else |
77 | 351k | return _targetType->dataStoredIn(DataLocation::Storage) ? _type->mobileType() : _targetType; |
78 | 355k | } |
79 | | |
80 | | } |
81 | | |
82 | | void ExpressionCompiler::compile(Expression const& _expression) |
83 | 1.97M | { |
84 | 1.97M | _expression.accept(*this); |
85 | 1.97M | } |
86 | | |
87 | | void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration const& _varDecl) |
88 | 0 | { |
89 | 0 | if (!_varDecl.value()) |
90 | 0 | return; |
91 | 0 | Type const* type = _varDecl.value()->annotation().type; |
92 | 0 | solAssert(!!type, "Type information not available."); |
93 | 0 | CompilerContext::LocationSetter locationSetter(m_context, _varDecl); |
94 | 0 | _varDecl.value()->accept(*this); |
95 | |
|
96 | 0 | if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage)) |
97 | 0 | { |
98 | | // reference type, only convert value to mobile type and do final conversion in storeValue. |
99 | 0 | auto mt = type->mobileType(); |
100 | 0 | solAssert(mt, ""); |
101 | 0 | utils().convertType(*type, *mt); |
102 | 0 | type = mt; |
103 | 0 | } |
104 | 0 | else |
105 | 0 | { |
106 | 0 | utils().convertType(*type, *_varDecl.annotation().type); |
107 | 0 | type = _varDecl.annotation().type; |
108 | 0 | } |
109 | 0 | if (_varDecl.immutable()) |
110 | 0 | ImmutableItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); |
111 | 0 | else |
112 | 0 | StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); |
113 | 0 | } |
114 | | |
115 | | void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl) |
116 | 0 | { |
117 | 0 | solAssert(_varDecl.isConstant(), ""); |
118 | 0 | acceptAndConvert(*_varDecl.value(), *_varDecl.annotation().type); |
119 | | |
120 | | // append return |
121 | 0 | m_context << dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1); |
122 | 0 | m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction); |
123 | 0 | } |
124 | | |
125 | | void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) |
126 | 0 | { |
127 | 0 | solAssert(!_varDecl.isConstant(), ""); |
128 | 0 | CompilerContext::LocationSetter locationSetter(m_context, _varDecl); |
129 | 0 | FunctionType accessorType(_varDecl); |
130 | |
|
131 | 0 | TypePointers paramTypes = accessorType.parameterTypes(); |
132 | 0 | if (_varDecl.immutable()) |
133 | 0 | solAssert(paramTypes.empty(), ""); |
134 | | |
135 | 0 | m_context.adjustStackOffset(static_cast<int>(1 + CompilerUtils::sizeOnStack(paramTypes))); |
136 | |
|
137 | 0 | if (!_varDecl.immutable()) |
138 | 0 | { |
139 | | // retrieve the position of the variable |
140 | 0 | auto const& location = m_context.storageLocationOfVariable(_varDecl); |
141 | 0 | m_context << location.first << u256(location.second); |
142 | 0 | } |
143 | |
|
144 | 0 | Type const* returnType = _varDecl.annotation().type; |
145 | |
|
146 | 0 | for (size_t i = 0; i < paramTypes.size(); ++i) |
147 | 0 | { |
148 | 0 | if (auto mappingType = dynamic_cast<MappingType const*>(returnType)) |
149 | 0 | { |
150 | 0 | solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); |
151 | | |
152 | | // pop offset |
153 | 0 | m_context << Instruction::POP; |
154 | 0 | if (paramTypes[i]->isDynamicallySized()) |
155 | 0 | { |
156 | 0 | solAssert( |
157 | 0 | dynamic_cast<ArrayType const&>(*paramTypes[i]).isByteArrayOrString(), |
158 | 0 | "Expected string or byte array for mapping key type" |
159 | 0 | ); |
160 | | |
161 | | // stack: <keys..> <slot position> |
162 | | |
163 | | // copy key[i] to top. |
164 | 0 | utils().copyToStackTop(static_cast<unsigned>(paramTypes.size() - i + 1), 1); |
165 | |
|
166 | 0 | m_context.appendInlineAssembly(R"({ |
167 | 0 | let key_len := mload(key_ptr) |
168 | 0 | // Temp. use the memory after the array data for the slot |
169 | 0 | // position |
170 | 0 | let post_data_ptr := add(key_ptr, add(key_len, 0x20)) |
171 | 0 | let orig_data := mload(post_data_ptr) |
172 | 0 | mstore(post_data_ptr, slot_pos) |
173 | 0 | let hash := keccak256(add(key_ptr, 0x20), add(key_len, 0x20)) |
174 | 0 | mstore(post_data_ptr, orig_data) |
175 | 0 | slot_pos := hash |
176 | 0 | })", {"slot_pos", "key_ptr"}); |
177 | |
|
178 | 0 | m_context << Instruction::POP; |
179 | 0 | } |
180 | 0 | else |
181 | 0 | { |
182 | 0 | solAssert(paramTypes[i]->isValueType(), "Expected value type for mapping key"); |
183 | | |
184 | | // move storage offset to memory. |
185 | 0 | utils().storeInMemory(32); |
186 | | |
187 | | // move key to memory. |
188 | 0 | utils().copyToStackTop(static_cast<unsigned>(paramTypes.size() - i), 1); |
189 | 0 | utils().storeInMemory(0); |
190 | 0 | m_context << u256(64) << u256(0); |
191 | 0 | m_context << Instruction::KECCAK256; |
192 | 0 | } |
193 | | |
194 | | // push offset |
195 | 0 | m_context << u256(0); |
196 | 0 | returnType = mappingType->valueType(); |
197 | 0 | } |
198 | 0 | else if (auto arrayType = dynamic_cast<ArrayType const*>(returnType)) |
199 | 0 | { |
200 | | // pop offset |
201 | 0 | m_context << Instruction::POP; |
202 | 0 | utils().copyToStackTop(static_cast<unsigned>(paramTypes.size() - i + 1), 1); |
203 | |
|
204 | 0 | ArrayUtils(m_context).retrieveLength(*arrayType, 1); |
205 | | // Stack: ref [length] index length |
206 | | // check out-of-bounds access |
207 | 0 | m_context << Instruction::DUP2 << Instruction::LT; |
208 | 0 | auto tag = m_context.appendConditionalJump(); |
209 | 0 | m_context << u256(0) << Instruction::DUP1 << Instruction::REVERT; |
210 | 0 | m_context << tag; |
211 | |
|
212 | 0 | ArrayUtils(m_context).accessIndex(*arrayType, false); |
213 | 0 | returnType = arrayType->baseType(); |
214 | 0 | } |
215 | 0 | else |
216 | 0 | solAssert(false, "Index access is allowed only for \"mapping\" and \"array\" types."); |
217 | 0 | } |
218 | | // remove index arguments. |
219 | 0 | if (paramTypes.size() == 1) |
220 | 0 | m_context << Instruction::SWAP2 << Instruction::POP << Instruction::SWAP1; |
221 | 0 | else if (paramTypes.size() >= 2) |
222 | 0 | { |
223 | 0 | m_context << swapInstruction(static_cast<unsigned>(paramTypes.size())); |
224 | 0 | m_context << Instruction::POP; |
225 | 0 | m_context << swapInstruction(static_cast<unsigned>(paramTypes.size())); |
226 | 0 | utils().popStackSlots(paramTypes.size() - 1); |
227 | 0 | } |
228 | 0 | unsigned retSizeOnStack = 0; |
229 | 0 | auto returnTypes = accessorType.returnParameterTypes(); |
230 | 0 | solAssert(returnTypes.size() >= 1, ""); |
231 | 0 | if (StructType const* structType = dynamic_cast<StructType const*>(returnType)) |
232 | 0 | { |
233 | 0 | solAssert(!_varDecl.immutable(), ""); |
234 | | // remove offset |
235 | 0 | m_context << Instruction::POP; |
236 | 0 | auto const& names = accessorType.returnParameterNames(); |
237 | | // struct |
238 | 0 | for (size_t i = 0; i < names.size(); ++i) |
239 | 0 | { |
240 | 0 | if (returnTypes[i]->category() == Type::Category::Mapping) |
241 | 0 | continue; |
242 | 0 | if (auto arrayType = dynamic_cast<ArrayType const*>(returnTypes[i])) |
243 | 0 | if (!arrayType->isByteArrayOrString()) |
244 | 0 | continue; |
245 | 0 | pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]); |
246 | 0 | m_context << Instruction::DUP1 << u256(offsets.first) << Instruction::ADD << u256(offsets.second); |
247 | 0 | Type const* memberType = structType->memberType(names[i]); |
248 | 0 | StorageItem(m_context, *memberType).retrieveValue(SourceLocation(), true); |
249 | 0 | utils().convertType(*memberType, *returnTypes[i]); |
250 | 0 | utils().moveToStackTop(returnTypes[i]->sizeOnStack()); |
251 | 0 | retSizeOnStack += returnTypes[i]->sizeOnStack(); |
252 | 0 | } |
253 | | // remove slot |
254 | 0 | m_context << Instruction::POP; |
255 | 0 | } |
256 | 0 | else |
257 | 0 | { |
258 | | // simple value or array |
259 | 0 | solAssert(returnTypes.size() == 1, ""); |
260 | 0 | if (_varDecl.immutable()) |
261 | 0 | ImmutableItem(m_context, _varDecl).retrieveValue(SourceLocation()); |
262 | 0 | else |
263 | 0 | StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); |
264 | 0 | utils().convertType(*returnType, *returnTypes.front()); |
265 | 0 | retSizeOnStack = returnTypes.front()->sizeOnStack(); |
266 | 0 | } |
267 | 0 | solAssert(retSizeOnStack == utils().sizeOnStack(returnTypes), ""); |
268 | 0 | if (retSizeOnStack > 15) |
269 | 0 | BOOST_THROW_EXCEPTION( |
270 | 0 | StackTooDeepError() << |
271 | 0 | errinfo_sourceLocation(_varDecl.location()) << |
272 | 0 | util::errinfo_comment(util::stackTooDeepString) |
273 | 0 | ); |
274 | 0 | m_context << dupInstruction(retSizeOnStack + 1); |
275 | 0 | m_context.appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction); |
276 | 0 | } |
277 | | |
278 | | bool ExpressionCompiler::visit(Conditional const& _condition) |
279 | 0 | { |
280 | 0 | CompilerContext::LocationSetter locationSetter(m_context, _condition); |
281 | 0 | _condition.condition().accept(*this); |
282 | 0 | evmasm::AssemblyItem trueTag = m_context.appendConditionalJump(); |
283 | 0 | acceptAndConvert(_condition.falseExpression(), *_condition.annotation().type); |
284 | 0 | evmasm::AssemblyItem endTag = m_context.appendJumpToNew(); |
285 | 0 | m_context << trueTag; |
286 | 0 | int offset = static_cast<int>(_condition.annotation().type->sizeOnStack()); |
287 | 0 | m_context.adjustStackOffset(-offset); |
288 | 0 | acceptAndConvert(_condition.trueExpression(), *_condition.annotation().type); |
289 | 0 | m_context << endTag; |
290 | 0 | return false; |
291 | 0 | } |
292 | | |
293 | | bool ExpressionCompiler::visit(Assignment const& _assignment) |
294 | 351k | { |
295 | 351k | CompilerContext::LocationSetter locationSetter(m_context, _assignment); |
296 | 351k | Token op = _assignment.assignmentOperator(); |
297 | 351k | Token binOp = op == Token::Assign ? op : TokenTraits::AssignmentToBinaryOp(op); |
298 | 351k | Type const& leftType = *_assignment.leftHandSide().annotation().type; |
299 | 351k | if (leftType.category() == Type::Category::Tuple) |
300 | 3.48k | { |
301 | 3.48k | solAssert(*_assignment.annotation().type == TupleType(), ""); |
302 | 3.48k | solAssert(op == Token::Assign, ""); |
303 | 3.48k | } |
304 | 348k | else |
305 | 351k | solAssert(*_assignment.annotation().type == leftType, ""); |
306 | 351k | bool cleanupNeeded = false; |
307 | 351k | if (op != Token::Assign) |
308 | 0 | cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp, m_context.arithmetic()); |
309 | 351k | _assignment.rightHandSide().accept(*this); |
310 | | // Perform some conversion already. This will convert storage types to memory and literals |
311 | | // to their actual type, but will not convert e.g. memory to storage. |
312 | 351k | Type const* rightIntermediateType = closestType( |
313 | 351k | _assignment.rightHandSide().annotation().type, |
314 | 351k | _assignment.leftHandSide().annotation().type, |
315 | 351k | op != Token::Assign && TokenTraits::isShiftOp(binOp) |
316 | 351k | ); |
317 | | |
318 | 351k | solAssert(rightIntermediateType, ""); |
319 | 351k | utils().convertType(*_assignment.rightHandSide().annotation().type, *rightIntermediateType, cleanupNeeded); |
320 | | |
321 | 351k | _assignment.leftHandSide().accept(*this); |
322 | 351k | solAssert(!!m_currentLValue, "LValue not retrieved."); |
323 | | |
324 | 351k | if (op == Token::Assign) |
325 | 351k | m_currentLValue->storeValue(*rightIntermediateType, _assignment.location()); |
326 | 0 | else // compound assignment |
327 | 0 | { |
328 | 0 | solAssert(binOp != Token::Exp, "Compound exp is not possible."); |
329 | 0 | solAssert(leftType.isValueType(), "Compound operators only available for value types."); |
330 | 0 | unsigned lvalueSize = m_currentLValue->sizeOnStack(); |
331 | 0 | unsigned itemSize = _assignment.annotation().type->sizeOnStack(); |
332 | 0 | if (lvalueSize > 0) |
333 | 0 | { |
334 | 0 | utils().copyToStackTop(lvalueSize + itemSize, itemSize); |
335 | 0 | utils().copyToStackTop(itemSize + lvalueSize, lvalueSize); |
336 | | // value lvalue_ref value lvalue_ref |
337 | 0 | } |
338 | 0 | m_currentLValue->retrieveValue(_assignment.location(), true); |
339 | 0 | utils().convertType(leftType, leftType, cleanupNeeded); |
340 | |
|
341 | 0 | if (TokenTraits::isShiftOp(binOp)) |
342 | 0 | appendShiftOperatorCode(binOp, leftType, *rightIntermediateType); |
343 | 0 | else |
344 | 0 | { |
345 | 0 | solAssert(leftType == *rightIntermediateType, ""); |
346 | 0 | appendOrdinaryBinaryOperatorCode(binOp, leftType); |
347 | 0 | } |
348 | 0 | if (lvalueSize > 0) |
349 | 0 | { |
350 | 0 | if (itemSize + lvalueSize > 16) |
351 | 0 | BOOST_THROW_EXCEPTION( |
352 | 0 | StackTooDeepError() << |
353 | 0 | errinfo_sourceLocation(_assignment.location()) << |
354 | 0 | util::errinfo_comment(util::stackTooDeepString) |
355 | 0 | ); |
356 | | // value [lvalue_ref] updated_value |
357 | 0 | for (unsigned i = 0; i < itemSize; ++i) |
358 | 0 | m_context << swapInstruction(itemSize + lvalueSize) << Instruction::POP; |
359 | 0 | } |
360 | 0 | m_currentLValue->storeValue(*_assignment.annotation().type, _assignment.location()); |
361 | 0 | } |
362 | 351k | m_currentLValue.reset(); |
363 | 351k | return false; |
364 | 351k | } |
365 | | |
366 | | bool ExpressionCompiler::visit(TupleExpression const& _tuple) |
367 | 11.5k | { |
368 | 11.5k | if (_tuple.isInlineArray()) |
369 | 0 | { |
370 | 0 | ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); |
371 | |
|
372 | 0 | solAssert(!arrayType.isDynamicallySized(), "Cannot create dynamically sized inline array."); |
373 | 0 | utils().allocateMemory(max(u256(32u), arrayType.memoryDataSize())); |
374 | 0 | m_context << Instruction::DUP1; |
375 | |
|
376 | 0 | for (auto const& component: _tuple.components()) |
377 | 0 | { |
378 | 0 | acceptAndConvert(*component, *arrayType.baseType(), true); |
379 | 0 | utils().storeInMemoryDynamic(*arrayType.baseType(), true); |
380 | 0 | } |
381 | |
|
382 | 0 | m_context << Instruction::POP; |
383 | 0 | } |
384 | 11.5k | else |
385 | 11.5k | { |
386 | 11.5k | vector<unique_ptr<LValue>> lvalues; |
387 | 11.5k | for (auto const& component: _tuple.components()) |
388 | 18.9k | if (component) |
389 | 15.4k | { |
390 | 15.4k | component->accept(*this); |
391 | 15.4k | if (_tuple.annotation().willBeWrittenTo) |
392 | 3.48k | { |
393 | 3.48k | solAssert(!!m_currentLValue, ""); |
394 | 3.48k | lvalues.push_back(move(m_currentLValue)); |
395 | 3.48k | } |
396 | 15.4k | } |
397 | 3.48k | else if (_tuple.annotation().willBeWrittenTo) |
398 | 3.48k | lvalues.push_back(unique_ptr<LValue>()); |
399 | 11.5k | if (_tuple.annotation().willBeWrittenTo) |
400 | 3.48k | { |
401 | 3.48k | if (_tuple.components().size() == 1) |
402 | 0 | m_currentLValue = move(lvalues[0]); |
403 | 3.48k | else |
404 | 3.48k | m_currentLValue = make_unique<TupleObject>(m_context, move(lvalues)); |
405 | 3.48k | } |
406 | 11.5k | } |
407 | 11.5k | return false; |
408 | 11.5k | } |
409 | | |
410 | | bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation) |
411 | 217k | { |
412 | 217k | CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation); |
413 | 217k | Type const& type = *_unaryOperation.annotation().type; |
414 | 217k | if (type.category() == Type::Category::RationalNumber) |
415 | 58.4k | { |
416 | 58.4k | m_context << type.literalValue(nullptr); |
417 | 58.4k | return false; |
418 | 58.4k | } |
419 | | |
420 | 158k | _unaryOperation.subExpression().accept(*this); |
421 | | |
422 | 158k | switch (_unaryOperation.getOperator()) |
423 | 158k | { |
424 | 96.2k | case Token::Not: // ! |
425 | 96.2k | m_context << Instruction::ISZERO; |
426 | 96.2k | break; |
427 | 0 | case Token::BitNot: // ~ |
428 | 0 | m_context << Instruction::NOT; |
429 | 0 | break; |
430 | 0 | case Token::Delete: // delete |
431 | 0 | solAssert(!!m_currentLValue, "LValue not retrieved."); |
432 | 0 | m_currentLValue->setToZero(_unaryOperation.location()); |
433 | 0 | m_currentLValue.reset(); |
434 | 0 | break; |
435 | 62.6k | case Token::Inc: // ++ (pre- or postfix) |
436 | 62.6k | case Token::Dec: // -- (pre- or postfix) |
437 | 62.6k | solAssert(!!m_currentLValue, "LValue not retrieved."); |
438 | 62.6k | solUnimplementedAssert( |
439 | 62.6k | type.category() != Type::Category::FixedPoint, |
440 | 62.6k | "Not yet implemented - FixedPointType." |
441 | 62.6k | ); |
442 | 62.6k | m_currentLValue->retrieveValue(_unaryOperation.location()); |
443 | 62.6k | if (!_unaryOperation.isPrefixOperation()) |
444 | 62.6k | { |
445 | | // store value for later |
446 | 62.6k | solUnimplementedAssert(type.sizeOnStack() == 1, "Stack size != 1 not implemented."); |
447 | 62.6k | m_context << Instruction::DUP1; |
448 | 62.6k | if (m_currentLValue->sizeOnStack() > 0) |
449 | 0 | for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i) |
450 | 0 | m_context << swapInstruction(i); |
451 | 62.6k | } |
452 | 62.6k | if (_unaryOperation.getOperator() == Token::Inc) |
453 | 62.6k | { |
454 | 62.6k | if (m_context.arithmetic() == Arithmetic::Checked) |
455 | 62.6k | m_context.callYulFunction(m_context.utilFunctions().incrementCheckedFunction(type), 1, 1); |
456 | 0 | else |
457 | 0 | { |
458 | 0 | m_context << u256(1); |
459 | 0 | m_context << Instruction::ADD; |
460 | 0 | } |
461 | 62.6k | } |
462 | 0 | else |
463 | 0 | { |
464 | 0 | if (m_context.arithmetic() == Arithmetic::Checked) |
465 | 0 | m_context.callYulFunction(m_context.utilFunctions().decrementCheckedFunction(type), 1, 1); |
466 | 0 | else |
467 | 0 | { |
468 | 0 | m_context << u256(1); |
469 | 0 | m_context << Instruction::SWAP1 << Instruction::SUB; |
470 | 0 | } |
471 | 0 | } |
472 | | // Stack for prefix: [ref...] (*ref)+-1 |
473 | | // Stack for postfix: *ref [ref...] (*ref)+-1 |
474 | 62.6k | for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i) |
475 | 0 | m_context << swapInstruction(i); |
476 | 62.6k | m_currentLValue->storeValue( |
477 | 62.6k | *_unaryOperation.annotation().type, _unaryOperation.location(), |
478 | 62.6k | !_unaryOperation.isPrefixOperation()); |
479 | 62.6k | m_currentLValue.reset(); |
480 | 62.6k | break; |
481 | 0 | case Token::Add: // + |
482 | | // unary add, so basically no-op |
483 | 0 | break; |
484 | 0 | case Token::Sub: // - |
485 | 0 | solUnimplementedAssert( |
486 | 0 | type.category() != Type::Category::FixedPoint, |
487 | 0 | "Not yet implemented - FixedPointType." |
488 | 0 | ); |
489 | 0 | if (m_context.arithmetic() == Arithmetic::Checked) |
490 | 0 | m_context.callYulFunction(m_context.utilFunctions().negateNumberCheckedFunction(type), 1, 1); |
491 | 0 | else |
492 | 0 | m_context << u256(0) << Instruction::SUB; |
493 | 0 | break; |
494 | 0 | default: |
495 | 0 | solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator()))); |
496 | 158k | } |
497 | 158k | return false; |
498 | 158k | } |
499 | | |
500 | | bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation) |
501 | 662k | { |
502 | 662k | CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation); |
503 | 662k | Expression const& leftExpression = _binaryOperation.leftExpression(); |
504 | 662k | Expression const& rightExpression = _binaryOperation.rightExpression(); |
505 | 662k | solAssert(!!_binaryOperation.annotation().commonType, ""); |
506 | 662k | Type const* commonType = _binaryOperation.annotation().commonType; |
507 | 662k | Token const c_op = _binaryOperation.getOperator(); |
508 | | |
509 | 662k | if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting |
510 | 3.48k | appendAndOrOperatorCode(_binaryOperation); |
511 | 659k | else if (commonType->category() == Type::Category::RationalNumber) |
512 | 0 | m_context << commonType->literalValue(nullptr); |
513 | 659k | else |
514 | 659k | { |
515 | 659k | bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op, m_context.arithmetic()); |
516 | | |
517 | 659k | Type const* leftTargetType = commonType; |
518 | 659k | Type const* rightTargetType = |
519 | 659k | TokenTraits::isShiftOp(c_op) || c_op == Token::Exp ? |
520 | 0 | rightExpression.annotation().type->mobileType() : |
521 | 659k | commonType; |
522 | 659k | solAssert(rightTargetType, ""); |
523 | | |
524 | | // for commutative operators, push the literal as late as possible to allow improved optimization |
525 | 659k | auto isLiteral = [](Expression const& _e) |
526 | 659k | { |
527 | 0 | return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::RationalNumber; |
528 | 0 | }; |
529 | 659k | bool swap = m_optimiseOrderLiterals && TokenTraits::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression); |
530 | 659k | if (swap) |
531 | 0 | { |
532 | 0 | acceptAndConvert(leftExpression, *leftTargetType, cleanupNeeded); |
533 | 0 | acceptAndConvert(rightExpression, *rightTargetType, cleanupNeeded); |
534 | 0 | } |
535 | 659k | else |
536 | 659k | { |
537 | 659k | acceptAndConvert(rightExpression, *rightTargetType, cleanupNeeded); |
538 | 659k | acceptAndConvert(leftExpression, *leftTargetType, cleanupNeeded); |
539 | 659k | } |
540 | 659k | if (TokenTraits::isShiftOp(c_op)) |
541 | | // shift only cares about the signedness of both sides |
542 | 0 | appendShiftOperatorCode(c_op, *leftTargetType, *rightTargetType); |
543 | 659k | else if (c_op == Token::Exp) |
544 | 0 | appendExpOperatorCode(*leftTargetType, *rightTargetType); |
545 | 659k | else if (TokenTraits::isCompareOp(c_op)) |
546 | 628k | appendCompareOperatorCode(c_op, *commonType); |
547 | 31.2k | else |
548 | 31.2k | appendOrdinaryBinaryOperatorCode(c_op, *commonType); |
549 | 659k | } |
550 | | |
551 | | // do not visit the child nodes, we already did that explicitly |
552 | 662k | return false; |
553 | 662k | } |
554 | | |
555 | | bool ExpressionCompiler::visit(FunctionCall const& _functionCall) |
556 | 358k | { |
557 | 358k | auto functionCallKind = *_functionCall.annotation().kind; |
558 | | |
559 | 358k | CompilerContext::LocationSetter locationSetter(m_context, _functionCall); |
560 | 358k | if (functionCallKind == FunctionCallKind::TypeConversion) |
561 | 121k | { |
562 | 121k | solAssert(_functionCall.arguments().size() == 1, ""); |
563 | 121k | solAssert(_functionCall.names().empty(), ""); |
564 | 121k | auto const& expression = *_functionCall.arguments().front(); |
565 | 121k | auto const& targetType = *_functionCall.annotation().type; |
566 | 121k | if (auto const* typeType = dynamic_cast<TypeType const*>(expression.annotation().type)) |
567 | 0 | if (auto const* addressType = dynamic_cast<AddressType const*>(&targetType)) |
568 | 0 | { |
569 | 0 | auto const* contractType = dynamic_cast<ContractType const*>(typeType->actualType()); |
570 | 0 | solAssert( |
571 | 0 | contractType && |
572 | 0 | contractType->contractDefinition().isLibrary() && |
573 | 0 | addressType->stateMutability() == StateMutability::NonPayable, |
574 | 0 | "" |
575 | 0 | ); |
576 | 0 | m_context.appendLibraryAddress(contractType->contractDefinition().fullyQualifiedName()); |
577 | 0 | return false; |
578 | 0 | } |
579 | 121k | acceptAndConvert(expression, targetType); |
580 | 121k | return false; |
581 | 121k | } |
582 | | |
583 | 236k | FunctionTypePointer functionType; |
584 | 236k | if (functionCallKind == FunctionCallKind::StructConstructorCall) |
585 | 0 | { |
586 | 0 | auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); |
587 | 0 | auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); |
588 | 0 | functionType = structType.constructorType(); |
589 | 0 | } |
590 | 236k | else |
591 | 236k | functionType = dynamic_cast<FunctionType const*>(_functionCall.expression().annotation().type); |
592 | | |
593 | 236k | TypePointers parameterTypes = functionType->parameterTypes(); |
594 | | |
595 | 236k | vector<ASTPointer<Expression const>> const& arguments = _functionCall.sortedArguments(); |
596 | | |
597 | 236k | if (functionCallKind == FunctionCallKind::StructConstructorCall) |
598 | 0 | { |
599 | 0 | TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type); |
600 | 0 | auto const& structType = dynamic_cast<StructType const&>(*type.actualType()); |
601 | |
|
602 | 0 | utils().allocateMemory(max(u256(32u), structType.memoryDataSize())); |
603 | 0 | m_context << Instruction::DUP1; |
604 | |
|
605 | 0 | for (unsigned i = 0; i < arguments.size(); ++i) |
606 | 0 | { |
607 | 0 | acceptAndConvert(*arguments[i], *functionType->parameterTypes()[i]); |
608 | 0 | utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]); |
609 | 0 | } |
610 | 0 | m_context << Instruction::POP; |
611 | 0 | } |
612 | 236k | else |
613 | 236k | { |
614 | 236k | FunctionType const& function = *functionType; |
615 | 236k | if (function.bound()) |
616 | 236k | solAssert( |
617 | 236k | function.kind() == FunctionType::Kind::DelegateCall || |
618 | 236k | function.kind() == FunctionType::Kind::Internal || |
619 | 236k | function.kind() == FunctionType::Kind::ArrayPush || |
620 | 236k | function.kind() == FunctionType::Kind::ArrayPop, |
621 | 236k | ""); |
622 | 236k | switch (function.kind()) |
623 | 236k | { |
624 | 0 | case FunctionType::Kind::Declaration: |
625 | 0 | solAssert(false, "Attempted to generate code for calling a function definition."); |
626 | 0 | break; |
627 | 111k | case FunctionType::Kind::Internal: |
628 | 111k | { |
629 | | // Calling convention: Caller pushes return address and arguments |
630 | | // Callee removes them and pushes return values |
631 | | |
632 | 111k | evmasm::AssemblyItem returnLabel = m_context.pushNewTag(); |
633 | 344k | for (unsigned i = 0; i < arguments.size(); ++i) |
634 | 233k | acceptAndConvert(*arguments[i], *function.parameterTypes()[i]); |
635 | | |
636 | 111k | { |
637 | 111k | bool shortcutTaken = false; |
638 | 111k | if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression())) |
639 | 111k | { |
640 | 111k | solAssert(!function.bound(), ""); |
641 | 111k | if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration)) |
642 | 111k | { |
643 | | // Do not directly visit the identifier, because this way, we can avoid |
644 | | // the runtime entry label to be created at the creation time context. |
645 | 111k | CompilerContext::LocationSetter locationSetter2(m_context, *identifier); |
646 | 111k | solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, ""); |
647 | 111k | utils().pushCombinedFunctionEntryLabel( |
648 | 111k | functionDef->resolveVirtual(m_context.mostDerivedContract()), |
649 | 111k | false |
650 | 111k | ); |
651 | 111k | shortcutTaken = true; |
652 | 111k | } |
653 | 111k | } |
654 | | |
655 | 111k | if (!shortcutTaken) |
656 | 0 | _functionCall.expression().accept(*this); |
657 | 111k | } |
658 | | |
659 | 0 | unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes()); |
660 | 111k | if (function.bound()) |
661 | 0 | { |
662 | | // stack: arg2, ..., argn, label, arg1 |
663 | 0 | unsigned depth = parameterSize + 1; |
664 | 0 | utils().moveIntoStack(depth, function.selfType()->sizeOnStack()); |
665 | 0 | parameterSize += function.selfType()->sizeOnStack(); |
666 | 0 | } |
667 | | |
668 | 111k | if (m_context.runtimeContext()) |
669 | | // We have a runtime context, so we need the creation part. |
670 | 0 | utils().rightShiftNumberOnStack(32); |
671 | 111k | else |
672 | | // Extract the runtime part. |
673 | 111k | m_context << ((u256(1) << 32) - 1) << Instruction::AND; |
674 | | |
675 | 111k | m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction); |
676 | 111k | m_context << returnLabel; |
677 | | |
678 | 111k | unsigned returnParametersSize = CompilerUtils::sizeOnStack(function.returnParameterTypes()); |
679 | | // callee adds return parameters, but removes arguments and return label |
680 | 111k | m_context.adjustStackOffset(static_cast<int>(returnParametersSize - parameterSize) - 1); |
681 | 111k | break; |
682 | 111k | } |
683 | 6.96k | case FunctionType::Kind::BareCall: |
684 | 6.96k | case FunctionType::Kind::BareDelegateCall: |
685 | 6.96k | case FunctionType::Kind::BareStaticCall: |
686 | 6.96k | solAssert(!_functionCall.annotation().tryCall, ""); |
687 | 6.96k | [[fallthrough]]; |
688 | 28.9k | case FunctionType::Kind::External: |
689 | 28.9k | case FunctionType::Kind::DelegateCall: |
690 | 28.9k | _functionCall.expression().accept(*this); |
691 | 28.9k | appendExternalFunctionCall(function, arguments, _functionCall.annotation().tryCall); |
692 | 28.9k | break; |
693 | 0 | case FunctionType::Kind::BareCallCode: |
694 | 0 | solAssert(false, "Callcode has been removed."); |
695 | 0 | case FunctionType::Kind::Creation: |
696 | 0 | { |
697 | 0 | _functionCall.expression().accept(*this); |
698 | | // Stack: [salt], [value] |
699 | |
|
700 | 0 | solAssert(!function.gasSet(), "Gas limit set for contract creation."); |
701 | 0 | solAssert(function.returnParameterTypes().size() == 1, ""); |
702 | 0 | TypePointers argumentTypes; |
703 | 0 | for (auto const& arg: arguments) |
704 | 0 | { |
705 | 0 | arg->accept(*this); |
706 | 0 | argumentTypes.push_back(arg->annotation().type); |
707 | 0 | } |
708 | 0 | ContractDefinition const* contract = |
709 | 0 | &dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition(); |
710 | 0 | utils().fetchFreeMemoryPointer(); |
711 | 0 | utils().copyContractCodeToMemory(*contract, true); |
712 | 0 | utils().abiEncode(argumentTypes, function.parameterTypes()); |
713 | | // now on stack: [salt], [value], memory_end_ptr |
714 | | // need: [salt], size, offset, value |
715 | |
|
716 | 0 | if (function.saltSet()) |
717 | 0 | { |
718 | 0 | m_context << dupInstruction(2 + (function.valueSet() ? 1 : 0)); |
719 | 0 | m_context << Instruction::SWAP1; |
720 | 0 | } |
721 | | |
722 | | // now: [salt], [value], [salt], memory_end_ptr |
723 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
724 | | |
725 | | // now: [salt], [value], [salt], size, offset |
726 | 0 | if (function.valueSet()) |
727 | 0 | m_context << dupInstruction(3 + (function.saltSet() ? 1 : 0)); |
728 | 0 | else |
729 | 0 | m_context << u256(0); |
730 | | |
731 | | // now: [salt], [value], [salt], size, offset, value |
732 | 0 | if (function.saltSet()) |
733 | 0 | m_context << Instruction::CREATE2; |
734 | 0 | else |
735 | 0 | m_context << Instruction::CREATE; |
736 | | |
737 | | // now: [salt], [value], address |
738 | |
|
739 | 0 | if (function.valueSet()) |
740 | 0 | m_context << swapInstruction(1) << Instruction::POP; |
741 | 0 | if (function.saltSet()) |
742 | 0 | m_context << swapInstruction(1) << Instruction::POP; |
743 | | |
744 | | // Check if zero (reverted) |
745 | 0 | m_context << Instruction::DUP1 << Instruction::ISZERO; |
746 | 0 | if (_functionCall.annotation().tryCall) |
747 | 0 | { |
748 | | // If this is a try call, return "<address> 1" in the success case and |
749 | | // "0" in the error case. |
750 | 0 | AssemblyItem errorCase = m_context.appendConditionalJump(); |
751 | 0 | m_context << u256(1); |
752 | 0 | m_context << errorCase; |
753 | 0 | } |
754 | 0 | else |
755 | 0 | m_context.appendConditionalRevert(true); |
756 | 0 | break; |
757 | 0 | } |
758 | 0 | case FunctionType::Kind::SetGas: |
759 | 0 | { |
760 | | // stack layout: contract_address function_id [gas] [value] |
761 | 0 | _functionCall.expression().accept(*this); |
762 | |
|
763 | 0 | acceptAndConvert(*arguments.front(), *TypeProvider::uint256(), true); |
764 | | // Note that function is not the original function, but the ".gas" function. |
765 | | // Its values of gasSet and valueSet is equal to the original function's though. |
766 | 0 | unsigned stackDepth = (function.gasSet() ? 1u : 0u) + (function.valueSet() ? 1u : 0u); |
767 | 0 | if (stackDepth > 0) |
768 | 0 | m_context << swapInstruction(stackDepth); |
769 | 0 | if (function.gasSet()) |
770 | 0 | m_context << Instruction::POP; |
771 | 0 | break; |
772 | 0 | } |
773 | 0 | case FunctionType::Kind::SetValue: |
774 | | // stack layout: contract_address function_id [gas] [value] |
775 | 0 | _functionCall.expression().accept(*this); |
776 | | // Note that function is not the original function, but the ".value" function. |
777 | | // Its values of gasSet and valueSet is equal to the original function's though. |
778 | 0 | if (function.valueSet()) |
779 | 0 | m_context << Instruction::POP; |
780 | 0 | arguments.front()->accept(*this); |
781 | 0 | break; |
782 | 0 | case FunctionType::Kind::Send: |
783 | 0 | case FunctionType::Kind::Transfer: |
784 | 0 | { |
785 | 0 | _functionCall.expression().accept(*this); |
786 | | // Provide the gas stipend manually at first because we may send zero ether. |
787 | | // Will be zeroed if we send more than zero ether. |
788 | 0 | m_context << u256(evmasm::GasCosts::callStipend); |
789 | 0 | acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), true); |
790 | | // gas <- gas * !value |
791 | 0 | m_context << Instruction::SWAP1 << Instruction::DUP2; |
792 | 0 | m_context << Instruction::ISZERO << Instruction::MUL << Instruction::SWAP1; |
793 | 0 | FunctionType::Options callOptions; |
794 | 0 | callOptions.valueSet = true; |
795 | 0 | callOptions.gasSet = true; |
796 | 0 | appendExternalFunctionCall( |
797 | 0 | FunctionType( |
798 | 0 | TypePointers{}, |
799 | 0 | TypePointers{}, |
800 | 0 | strings(), |
801 | 0 | strings(), |
802 | 0 | FunctionType::Kind::BareCall, |
803 | 0 | StateMutability::NonPayable, |
804 | 0 | nullptr, |
805 | 0 | callOptions |
806 | 0 | ), |
807 | 0 | {}, |
808 | 0 | false |
809 | 0 | ); |
810 | 0 | if (function.kind() == FunctionType::Kind::Transfer) |
811 | 0 | { |
812 | | // Check if zero (out of stack or not enough balance). |
813 | 0 | m_context << Instruction::ISZERO; |
814 | | // Revert message bubbles up. |
815 | 0 | m_context.appendConditionalRevert(true); |
816 | 0 | } |
817 | 0 | break; |
818 | 0 | } |
819 | 0 | case FunctionType::Kind::Selfdestruct: |
820 | 0 | acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), true); |
821 | 0 | m_context << Instruction::SELFDESTRUCT; |
822 | 0 | break; |
823 | 0 | case FunctionType::Kind::Revert: |
824 | 0 | { |
825 | 0 | if (arguments.empty()) |
826 | 0 | m_context.appendRevert(); |
827 | 0 | else |
828 | 0 | { |
829 | | // function-sel(Error(string)) + encoding |
830 | 0 | solAssert(arguments.size() == 1, ""); |
831 | 0 | solAssert(function.parameterTypes().size() == 1, ""); |
832 | 0 | if (m_context.revertStrings() == RevertStrings::Strip) |
833 | 0 | { |
834 | 0 | if (!*arguments.front()->annotation().isPure) |
835 | 0 | { |
836 | 0 | arguments.front()->accept(*this); |
837 | 0 | utils().popStackElement(*arguments.front()->annotation().type); |
838 | 0 | } |
839 | 0 | m_context.appendRevert(); |
840 | 0 | } |
841 | 0 | else |
842 | 0 | { |
843 | 0 | arguments.front()->accept(*this); |
844 | 0 | utils().revertWithStringData(*arguments.front()->annotation().type); |
845 | 0 | } |
846 | 0 | } |
847 | 0 | break; |
848 | 0 | } |
849 | 0 | case FunctionType::Kind::KECCAK256: |
850 | 0 | { |
851 | 0 | solAssert(arguments.size() == 1, ""); |
852 | 0 | solAssert(!function.padArguments(), ""); |
853 | 0 | Type const* argType = arguments.front()->annotation().type; |
854 | 0 | solAssert(argType, ""); |
855 | 0 | arguments.front()->accept(*this); |
856 | 0 | if (auto const* stringLiteral = dynamic_cast<StringLiteralType const*>(argType)) |
857 | | // Optimization: Compute keccak256 on string literals at compile-time. |
858 | 0 | m_context << u256(keccak256(stringLiteral->value())); |
859 | 0 | else if (*argType == *TypeProvider::bytesMemory() || *argType == *TypeProvider::stringMemory()) |
860 | 0 | { |
861 | | // Optimization: If type is bytes or string, then do not encode, |
862 | | // but directly compute keccak256 on memory. |
863 | 0 | ArrayUtils(m_context).retrieveLength(*TypeProvider::bytesMemory()); |
864 | 0 | m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD; |
865 | 0 | m_context << Instruction::KECCAK256; |
866 | 0 | } |
867 | 0 | else |
868 | 0 | { |
869 | 0 | utils().fetchFreeMemoryPointer(); |
870 | 0 | utils().packedEncode({argType}, TypePointers()); |
871 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
872 | 0 | m_context << Instruction::KECCAK256; |
873 | 0 | } |
874 | 0 | break; |
875 | 0 | } |
876 | 0 | case FunctionType::Kind::Event: |
877 | 0 | { |
878 | 0 | _functionCall.expression().accept(*this); |
879 | 0 | auto const& event = dynamic_cast<EventDefinition const&>(function.declaration()); |
880 | 0 | unsigned numIndexed = 0; |
881 | 0 | TypePointers paramTypes = function.parameterTypes(); |
882 | | // All indexed arguments go to the stack |
883 | 0 | for (size_t arg = arguments.size(); arg > 0; --arg) |
884 | 0 | if (event.parameters()[arg - 1]->isIndexed()) |
885 | 0 | { |
886 | 0 | ++numIndexed; |
887 | 0 | arguments[arg - 1]->accept(*this); |
888 | 0 | if (auto const& referenceType = dynamic_cast<ReferenceType const*>(paramTypes[arg - 1])) |
889 | 0 | { |
890 | 0 | utils().fetchFreeMemoryPointer(); |
891 | 0 | utils().packedEncode( |
892 | 0 | {arguments[arg - 1]->annotation().type}, |
893 | 0 | {referenceType} |
894 | 0 | ); |
895 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
896 | 0 | m_context << Instruction::KECCAK256; |
897 | 0 | } |
898 | 0 | else |
899 | 0 | { |
900 | 0 | solAssert(paramTypes[arg - 1]->isValueType(), ""); |
901 | 0 | if (auto functionType = dynamic_cast<FunctionType const*>(paramTypes[arg - 1])) |
902 | 0 | { |
903 | 0 | auto argumentType = |
904 | 0 | dynamic_cast<FunctionType const*>(arguments[arg-1]->annotation().type); |
905 | 0 | solAssert( |
906 | 0 | argumentType && |
907 | 0 | functionType->kind() == FunctionType::Kind::External && |
908 | 0 | argumentType->kind() == FunctionType::Kind::External && |
909 | 0 | !argumentType->bound(), |
910 | 0 | "" |
911 | 0 | ); |
912 | | |
913 | 0 | utils().combineExternalFunctionType(true); |
914 | 0 | } |
915 | 0 | else |
916 | 0 | utils().convertType( |
917 | 0 | *arguments[arg - 1]->annotation().type, |
918 | 0 | *paramTypes[arg - 1], |
919 | 0 | true |
920 | 0 | ); |
921 | 0 | } |
922 | 0 | } |
923 | 0 | if (!event.isAnonymous()) |
924 | 0 | { |
925 | 0 | m_context << u256(h256::Arith(keccak256(function.externalSignature()))); |
926 | 0 | ++numIndexed; |
927 | 0 | } |
928 | 0 | solAssert(numIndexed <= 4, "Too many indexed arguments."); |
929 | | // Copy all non-indexed arguments to memory (data) |
930 | | // Memory position is only a hack and should be removed once we have free memory pointer. |
931 | 0 | TypePointers nonIndexedArgTypes; |
932 | 0 | TypePointers nonIndexedParamTypes; |
933 | 0 | for (unsigned arg = 0; arg < arguments.size(); ++arg) |
934 | 0 | if (!event.parameters()[arg]->isIndexed()) |
935 | 0 | { |
936 | 0 | arguments[arg]->accept(*this); |
937 | 0 | nonIndexedArgTypes.push_back(arguments[arg]->annotation().type); |
938 | 0 | nonIndexedParamTypes.push_back(paramTypes[arg]); |
939 | 0 | } |
940 | 0 | utils().fetchFreeMemoryPointer(); |
941 | 0 | utils().abiEncode(nonIndexedArgTypes, nonIndexedParamTypes); |
942 | | // need: topic1 ... topicn memsize memstart |
943 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
944 | 0 | m_context << logInstruction(numIndexed); |
945 | 0 | break; |
946 | 0 | } |
947 | 0 | case FunctionType::Kind::Error: |
948 | 0 | { |
949 | 0 | _functionCall.expression().accept(*this); |
950 | 0 | vector<Type const*> argumentTypes; |
951 | 0 | for (ASTPointer<Expression const> const& arg: _functionCall.sortedArguments()) |
952 | 0 | { |
953 | 0 | arg->accept(*this); |
954 | 0 | argumentTypes.push_back(arg->annotation().type); |
955 | 0 | } |
956 | 0 | solAssert(dynamic_cast<ErrorDefinition const*>(&function.declaration()), ""); |
957 | 0 | utils().revertWithError( |
958 | 0 | function.externalSignature(), |
959 | 0 | function.parameterTypes(), |
960 | 0 | argumentTypes |
961 | 0 | ); |
962 | 0 | break; |
963 | 0 | } |
964 | 0 | case FunctionType::Kind::Wrap: |
965 | 0 | case FunctionType::Kind::Unwrap: |
966 | 0 | { |
967 | 0 | solAssert(arguments.size() == 1, ""); |
968 | 0 | Type const* argumentType = arguments.at(0)->annotation().type; |
969 | 0 | Type const* functionCallType = _functionCall.annotation().type; |
970 | 0 | solAssert(argumentType, ""); |
971 | 0 | solAssert(functionCallType, ""); |
972 | 0 | FunctionType::Kind kind = functionType->kind(); |
973 | 0 | if (kind == FunctionType::Kind::Wrap) |
974 | 0 | { |
975 | 0 | solAssert( |
976 | 0 | argumentType->isImplicitlyConvertibleTo( |
977 | 0 | dynamic_cast<UserDefinedValueType const&>(*functionCallType).underlyingType() |
978 | 0 | ), |
979 | 0 | "" |
980 | 0 | ); |
981 | 0 | solAssert(argumentType->isImplicitlyConvertibleTo(*function.parameterTypes()[0]), ""); |
982 | 0 | } |
983 | 0 | else |
984 | 0 | solAssert( |
985 | 0 | dynamic_cast<UserDefinedValueType const&>(*argumentType) == |
986 | 0 | dynamic_cast<UserDefinedValueType const&>(*function.parameterTypes()[0]), |
987 | 0 | "" |
988 | 0 | ); |
989 | | |
990 | 0 | acceptAndConvert(*arguments[0], *function.parameterTypes()[0]); |
991 | 0 | break; |
992 | 0 | } |
993 | 0 | case FunctionType::Kind::BlockHash: |
994 | 0 | { |
995 | 0 | acceptAndConvert(*arguments[0], *function.parameterTypes()[0], true); |
996 | 0 | m_context << Instruction::BLOCKHASH; |
997 | 0 | break; |
998 | 0 | } |
999 | 0 | case FunctionType::Kind::AddMod: |
1000 | 0 | case FunctionType::Kind::MulMod: |
1001 | 0 | { |
1002 | 0 | acceptAndConvert(*arguments[2], *TypeProvider::uint256()); |
1003 | 0 | m_context << Instruction::DUP1 << Instruction::ISZERO; |
1004 | 0 | m_context.appendConditionalPanic(util::PanicCode::DivisionByZero); |
1005 | 0 | for (unsigned i = 1; i < 3; i ++) |
1006 | 0 | acceptAndConvert(*arguments[2 - i], *TypeProvider::uint256()); |
1007 | 0 | if (function.kind() == FunctionType::Kind::AddMod) |
1008 | 0 | m_context << Instruction::ADDMOD; |
1009 | 0 | else |
1010 | 0 | m_context << Instruction::MULMOD; |
1011 | 0 | break; |
1012 | 0 | } |
1013 | 0 | case FunctionType::Kind::ECRecover: |
1014 | 0 | case FunctionType::Kind::SHA256: |
1015 | 0 | case FunctionType::Kind::RIPEMD160: |
1016 | 0 | { |
1017 | 0 | _functionCall.expression().accept(*this); |
1018 | 0 | static map<FunctionType::Kind, u256> const contractAddresses{ |
1019 | 0 | {FunctionType::Kind::ECRecover, 1}, |
1020 | 0 | {FunctionType::Kind::SHA256, 2}, |
1021 | 0 | {FunctionType::Kind::RIPEMD160, 3} |
1022 | 0 | }; |
1023 | 0 | m_context << contractAddresses.at(function.kind()); |
1024 | 0 | for (unsigned i = function.sizeOnStack(); i > 0; --i) |
1025 | 0 | m_context << swapInstruction(i); |
1026 | 0 | solAssert(!_functionCall.annotation().tryCall, ""); |
1027 | 0 | appendExternalFunctionCall(function, arguments, false); |
1028 | 0 | break; |
1029 | 0 | } |
1030 | 50.5k | case FunctionType::Kind::ArrayPush: |
1031 | 50.5k | { |
1032 | 50.5k | solAssert(function.bound(), ""); |
1033 | 50.5k | _functionCall.expression().accept(*this); |
1034 | | |
1035 | 50.5k | if (function.parameterTypes().size() == 0) |
1036 | 50.5k | { |
1037 | 50.5k | auto paramType = function.returnParameterTypes().at(0); |
1038 | 50.5k | solAssert(paramType, ""); |
1039 | | |
1040 | 50.5k | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType()); |
1041 | 50.5k | solAssert(arrayType, ""); |
1042 | | |
1043 | | // stack: ArrayReference |
1044 | 50.5k | m_context << u256(1) << Instruction::DUP2; |
1045 | 50.5k | ArrayUtils(m_context).incrementDynamicArraySize(*arrayType); |
1046 | | // stack: ArrayReference 1 newLength |
1047 | 50.5k | m_context << Instruction::SUB; |
1048 | | // stack: ArrayReference (newLength-1) |
1049 | 50.5k | ArrayUtils(m_context).accessIndex(*arrayType, false); |
1050 | | |
1051 | 50.5k | if (arrayType->isByteArrayOrString()) |
1052 | 0 | setLValue<StorageByteArrayElement>(_functionCall); |
1053 | 50.5k | else |
1054 | 50.5k | setLValueToStorageItem(_functionCall); |
1055 | 50.5k | } |
1056 | 0 | else |
1057 | 0 | { |
1058 | 0 | solAssert(function.parameterTypes().size() == 1, ""); |
1059 | 0 | solAssert(!!function.parameterTypes()[0], ""); |
1060 | 0 | Type const* paramType = function.parameterTypes()[0]; |
1061 | 0 | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType()); |
1062 | 0 | solAssert(arrayType, ""); |
1063 | | |
1064 | | // stack: ArrayReference |
1065 | 0 | arguments[0]->accept(*this); |
1066 | 0 | Type const* argType = arguments[0]->annotation().type; |
1067 | | // stack: ArrayReference argValue |
1068 | 0 | utils().moveToStackTop(argType->sizeOnStack(), 1); |
1069 | | // stack: argValue ArrayReference |
1070 | 0 | m_context << Instruction::DUP1; |
1071 | 0 | ArrayUtils(m_context).incrementDynamicArraySize(*arrayType); |
1072 | | // stack: argValue ArrayReference newLength |
1073 | 0 | m_context << u256(1) << Instruction::SWAP1 << Instruction::SUB; |
1074 | | // stack: argValue ArrayReference (newLength-1) |
1075 | 0 | ArrayUtils(m_context).accessIndex(*arrayType, false); |
1076 | | // stack: argValue storageSlot slotOffset |
1077 | 0 | utils().moveToStackTop(2, argType->sizeOnStack()); |
1078 | | // stack: storageSlot slotOffset argValue |
1079 | 0 | Type const* type = |
1080 | 0 | arrayType->baseType()->dataStoredIn(DataLocation::Storage) ? |
1081 | 0 | arguments[0]->annotation().type->mobileType() : |
1082 | 0 | arrayType->baseType(); |
1083 | 0 | solAssert(type, ""); |
1084 | 0 | utils().convertType(*argType, *type); |
1085 | 0 | utils().moveToStackTop(1 + type->sizeOnStack()); |
1086 | 0 | utils().moveToStackTop(1 + type->sizeOnStack()); |
1087 | | // stack: argValue storageSlot slotOffset |
1088 | 0 | if (!arrayType->isByteArrayOrString()) |
1089 | 0 | StorageItem(m_context, *paramType).storeValue(*type, _functionCall.location(), true); |
1090 | 0 | else |
1091 | 0 | StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true); |
1092 | 0 | } |
1093 | 50.5k | break; |
1094 | 50.5k | } |
1095 | 50.5k | case FunctionType::Kind::ArrayPop: |
1096 | 0 | { |
1097 | 0 | _functionCall.expression().accept(*this); |
1098 | 0 | solAssert(function.bound(), ""); |
1099 | 0 | solAssert(function.parameterTypes().empty(), ""); |
1100 | 0 | ArrayType const* arrayType = dynamic_cast<ArrayType const*>(function.selfType()); |
1101 | 0 | solAssert(arrayType && arrayType->dataStoredIn(DataLocation::Storage), ""); |
1102 | 0 | ArrayUtils(m_context).popStorageArrayElement(*arrayType); |
1103 | 0 | break; |
1104 | 0 | } |
1105 | 0 | case FunctionType::Kind::StringConcat: |
1106 | 0 | case FunctionType::Kind::BytesConcat: |
1107 | 0 | { |
1108 | 0 | _functionCall.expression().accept(*this); |
1109 | 0 | vector<Type const*> argumentTypes; |
1110 | 0 | vector<Type const*> targetTypes; |
1111 | 0 | for (auto const& argument: arguments) |
1112 | 0 | { |
1113 | 0 | argument->accept(*this); |
1114 | 0 | solAssert(argument->annotation().type, ""); |
1115 | 0 | argumentTypes.emplace_back(argument->annotation().type); |
1116 | 0 | if (argument->annotation().type->category() == Type::Category::FixedBytes) |
1117 | 0 | targetTypes.emplace_back(argument->annotation().type); |
1118 | 0 | else if ( |
1119 | 0 | auto const* literalType = dynamic_cast<StringLiteralType const*>(argument->annotation().type); |
1120 | 0 | literalType && !literalType->value().empty() && literalType->value().size() <= 32 |
1121 | 0 | ) |
1122 | 0 | targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size()))); |
1123 | 0 | else |
1124 | 0 | { |
1125 | 0 | solAssert(!dynamic_cast<RationalNumberType const*>(argument->annotation().type), ""); |
1126 | 0 | if (function.kind() == FunctionType::Kind::StringConcat) |
1127 | 0 | { |
1128 | 0 | solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()), ""); |
1129 | 0 | targetTypes.emplace_back(TypeProvider::stringMemory()); |
1130 | 0 | } |
1131 | 0 | else if (function.kind() == FunctionType::Kind::BytesConcat) |
1132 | 0 | { |
1133 | 0 | solAssert(argument->annotation().type->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()), ""); |
1134 | 0 | targetTypes.emplace_back(TypeProvider::bytesMemory()); |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 | } |
1138 | 0 | utils().fetchFreeMemoryPointer(); |
1139 | | // stack: <arg1> <arg2> ... <argn> <free mem> |
1140 | 0 | m_context << u256(32) << Instruction::ADD; |
1141 | 0 | utils().packedEncode(argumentTypes, targetTypes); |
1142 | 0 | utils().fetchFreeMemoryPointer(); |
1143 | 0 | m_context.appendInlineAssembly(R"({ |
1144 | 0 | mstore(mem_ptr, sub(sub(mem_end, mem_ptr), 0x20)) |
1145 | 0 | })", {"mem_end", "mem_ptr"}); |
1146 | 0 | m_context << Instruction::SWAP1; |
1147 | 0 | utils().storeFreeMemoryPointer(); |
1148 | |
|
1149 | 0 | break; |
1150 | 0 | } |
1151 | 39.0k | case FunctionType::Kind::ObjectCreation: |
1152 | 39.0k | { |
1153 | 39.0k | ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type); |
1154 | 39.0k | _functionCall.expression().accept(*this); |
1155 | 39.0k | solAssert(arguments.size() == 1, ""); |
1156 | | |
1157 | | // Fetch requested length. |
1158 | 39.0k | acceptAndConvert(*arguments[0], *TypeProvider::uint256()); |
1159 | | |
1160 | | // Make sure we can allocate memory without overflow |
1161 | 39.0k | m_context << u256(0xffffffffffffffff); |
1162 | 39.0k | m_context << Instruction::DUP2; |
1163 | 39.0k | m_context << Instruction::GT; |
1164 | 39.0k | m_context.appendConditionalPanic(PanicCode::ResourceError); |
1165 | | |
1166 | | // Stack: requested_length |
1167 | 39.0k | utils().fetchFreeMemoryPointer(); |
1168 | | |
1169 | | // Stack: requested_length memptr |
1170 | 39.0k | m_context << Instruction::SWAP1; |
1171 | | // Stack: memptr requested_length |
1172 | | // store length |
1173 | 39.0k | m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::MSTORE; |
1174 | | // Stack: memptr requested_length |
1175 | | // update free memory pointer |
1176 | 39.0k | m_context << Instruction::DUP1; |
1177 | | // Stack: memptr requested_length requested_length |
1178 | 39.0k | if (arrayType.isByteArrayOrString()) |
1179 | | // Round up to multiple of 32 |
1180 | 6.96k | m_context << u256(31) << Instruction::ADD << u256(31) << Instruction::NOT << Instruction::AND; |
1181 | 32.0k | else |
1182 | 32.0k | m_context << arrayType.baseType()->memoryHeadSize() << Instruction::MUL; |
1183 | | // stacK: memptr requested_length data_size |
1184 | 39.0k | m_context << u256(32) << Instruction::ADD; |
1185 | 39.0k | m_context << Instruction::DUP3 << Instruction::ADD; |
1186 | 39.0k | utils().storeFreeMemoryPointer(); |
1187 | | // Stack: memptr requested_length |
1188 | | |
1189 | | // Check if length is zero |
1190 | 39.0k | m_context << Instruction::DUP1 << Instruction::ISZERO; |
1191 | 39.0k | auto skipInit = m_context.appendConditionalJump(); |
1192 | | // Always initialize because the free memory pointer might point at |
1193 | | // a dirty memory area. |
1194 | 39.0k | m_context << Instruction::DUP2 << u256(32) << Instruction::ADD; |
1195 | 39.0k | utils().zeroInitialiseMemoryArray(arrayType); |
1196 | 39.0k | m_context << skipInit; |
1197 | 39.0k | m_context << Instruction::POP; |
1198 | 39.0k | break; |
1199 | 39.0k | } |
1200 | 0 | case FunctionType::Kind::Assert: |
1201 | 0 | case FunctionType::Kind::Require: |
1202 | 0 | { |
1203 | 0 | acceptAndConvert(*arguments.front(), *function.parameterTypes().front(), false); |
1204 | |
|
1205 | 0 | bool haveReasonString = arguments.size() > 1 && m_context.revertStrings() != RevertStrings::Strip; |
1206 | |
|
1207 | 0 | if (arguments.size() > 1) |
1208 | 0 | { |
1209 | | // Users probably expect the second argument to be evaluated |
1210 | | // even if the condition is false, as would be the case for an actual |
1211 | | // function call. |
1212 | 0 | solAssert(arguments.size() == 2, ""); |
1213 | 0 | solAssert(function.kind() == FunctionType::Kind::Require, ""); |
1214 | 0 | if (m_context.revertStrings() == RevertStrings::Strip) |
1215 | 0 | { |
1216 | 0 | if (!*arguments.at(1)->annotation().isPure) |
1217 | 0 | { |
1218 | 0 | arguments.at(1)->accept(*this); |
1219 | 0 | utils().popStackElement(*arguments.at(1)->annotation().type); |
1220 | 0 | } |
1221 | 0 | } |
1222 | 0 | else |
1223 | 0 | { |
1224 | 0 | arguments.at(1)->accept(*this); |
1225 | 0 | utils().moveIntoStack(1, arguments.at(1)->annotation().type->sizeOnStack()); |
1226 | 0 | } |
1227 | 0 | } |
1228 | | // Stack: <error string (unconverted)> <condition> |
1229 | | // jump if condition was met |
1230 | 0 | m_context << Instruction::ISZERO << Instruction::ISZERO; |
1231 | 0 | auto success = m_context.appendConditionalJump(); |
1232 | 0 | if (function.kind() == FunctionType::Kind::Assert) |
1233 | | // condition was not met, flag an error |
1234 | 0 | m_context.appendPanic(util::PanicCode::Assert); |
1235 | 0 | else if (haveReasonString) |
1236 | 0 | { |
1237 | 0 | utils().revertWithStringData(*arguments.at(1)->annotation().type); |
1238 | | // Here, the argument is consumed, but in the other branch, it is still there. |
1239 | 0 | m_context.adjustStackOffset(static_cast<int>(arguments.at(1)->annotation().type->sizeOnStack())); |
1240 | 0 | } |
1241 | 0 | else |
1242 | 0 | m_context.appendRevert(); |
1243 | | // the success branch |
1244 | 0 | m_context << success; |
1245 | 0 | if (haveReasonString) |
1246 | 0 | utils().popStackElement(*arguments.at(1)->annotation().type); |
1247 | 0 | break; |
1248 | 0 | } |
1249 | 3.41k | case FunctionType::Kind::ABIEncode: |
1250 | 3.41k | case FunctionType::Kind::ABIEncodePacked: |
1251 | 3.41k | case FunctionType::Kind::ABIEncodeWithSelector: |
1252 | 3.41k | case FunctionType::Kind::ABIEncodeCall: |
1253 | 3.41k | case FunctionType::Kind::ABIEncodeWithSignature: |
1254 | 3.41k | { |
1255 | 3.41k | bool const isPacked = function.kind() == FunctionType::Kind::ABIEncodePacked; |
1256 | 3.41k | bool const hasSelectorOrSignature = |
1257 | 3.41k | function.kind() == FunctionType::Kind::ABIEncodeWithSelector || |
1258 | 3.41k | function.kind() == FunctionType::Kind::ABIEncodeCall || |
1259 | 3.41k | function.kind() == FunctionType::Kind::ABIEncodeWithSignature; |
1260 | | |
1261 | 3.41k | TypePointers argumentTypes; |
1262 | 3.41k | TypePointers targetTypes; |
1263 | | |
1264 | 3.41k | ASTNode::listAccept(arguments, *this); |
1265 | | |
1266 | 3.41k | if (function.kind() == FunctionType::Kind::ABIEncodeCall) |
1267 | 0 | { |
1268 | 0 | solAssert(arguments.size() == 2); |
1269 | | |
1270 | | // Account for tuples with one component which become that component |
1271 | 0 | if (auto const tupleType = dynamic_cast<TupleType const*>(arguments[1]->annotation().type)) |
1272 | 0 | argumentTypes = tupleType->components(); |
1273 | 0 | else |
1274 | 0 | argumentTypes.emplace_back(arguments[1]->annotation().type); |
1275 | |
|
1276 | 0 | auto functionPtr = dynamic_cast<FunctionTypePointer>(arguments[0]->annotation().type); |
1277 | 0 | solAssert(functionPtr); |
1278 | 0 | functionPtr = functionPtr->asExternallyCallableFunction(false); |
1279 | 0 | solAssert(functionPtr); |
1280 | 0 | targetTypes = functionPtr->parameterTypes(); |
1281 | 0 | } |
1282 | 3.41k | else |
1283 | 8.51k | for (unsigned i = 0; i < arguments.size(); ++i) |
1284 | 5.09k | { |
1285 | | // Do not keep the selector as part of the ABI encoded args |
1286 | 5.09k | if (!hasSelectorOrSignature || i > 0) |
1287 | 5.09k | argumentTypes.push_back(arguments[i]->annotation().type); |
1288 | 5.09k | } |
1289 | | |
1290 | 3.41k | utils().fetchFreeMemoryPointer(); |
1291 | | // stack now: [<selector/functionPointer/signature>] <arg1> .. <argN> <free_mem> |
1292 | | |
1293 | | // adjust by 32(+4) bytes to accommodate the length(+selector) |
1294 | 3.41k | m_context << u256(32 + (hasSelectorOrSignature ? 4 : 0)) << Instruction::ADD; |
1295 | | // stack now: [<selector/functionPointer/signature>] <arg1> .. <argN> <data_encoding_area_start> |
1296 | | |
1297 | 3.41k | if (isPacked) |
1298 | 0 | { |
1299 | 0 | solAssert(!function.padArguments(), ""); |
1300 | 0 | utils().packedEncode(argumentTypes, targetTypes); |
1301 | 0 | } |
1302 | 3.41k | else |
1303 | 3.41k | { |
1304 | 3.41k | solAssert(function.padArguments(), ""); |
1305 | 3.41k | utils().abiEncode(argumentTypes, targetTypes); |
1306 | 3.41k | } |
1307 | 3.41k | utils().fetchFreeMemoryPointer(); |
1308 | | // stack: [<selector/functionPointer/signature>] <data_encoding_area_end> <bytes_memory_ptr> |
1309 | | |
1310 | | // size is end minus start minus length slot |
1311 | 3.41k | m_context.appendInlineAssembly(R"({ |
1312 | 3.41k | mstore(mem_ptr, sub(sub(mem_end, mem_ptr), 0x20)) |
1313 | 3.41k | })", {"mem_end", "mem_ptr"}); |
1314 | 3.41k | m_context << Instruction::SWAP1; |
1315 | 3.41k | utils().storeFreeMemoryPointer(); |
1316 | | // stack: [<selector/functionPointer/signature>] <memory ptr> |
1317 | | |
1318 | 3.41k | if (hasSelectorOrSignature) |
1319 | 0 | { |
1320 | | // stack: <selector/functionPointer/signature> <memory pointer> |
1321 | 0 | solAssert(arguments.size() >= 1, ""); |
1322 | 0 | Type const* selectorType = arguments[0]->annotation().type; |
1323 | 0 | utils().moveIntoStack(selectorType->sizeOnStack()); |
1324 | 0 | Type const* dataOnStack = selectorType; |
1325 | | |
1326 | | // stack: <memory pointer> <selector/functionPointer/signature> |
1327 | 0 | if (function.kind() == FunctionType::Kind::ABIEncodeWithSignature) |
1328 | 0 | { |
1329 | | // hash the signature |
1330 | 0 | if (auto const* stringType = dynamic_cast<StringLiteralType const*>(selectorType)) |
1331 | 0 | { |
1332 | 0 | m_context << util::selectorFromSignature(stringType->value()); |
1333 | 0 | dataOnStack = TypeProvider::fixedBytes(4); |
1334 | 0 | } |
1335 | 0 | else |
1336 | 0 | { |
1337 | 0 | utils().fetchFreeMemoryPointer(); |
1338 | | // stack: <memory pointer> <signature> <free mem ptr> |
1339 | 0 | utils().packedEncode(TypePointers{selectorType}, TypePointers()); |
1340 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
1341 | 0 | m_context << Instruction::KECCAK256; |
1342 | | // stack: <memory pointer> <hash> |
1343 | |
|
1344 | 0 | dataOnStack = TypeProvider::fixedBytes(32); |
1345 | 0 | } |
1346 | 0 | } |
1347 | 0 | else if (function.kind() == FunctionType::Kind::ABIEncodeCall) |
1348 | 0 | { |
1349 | 0 | auto const& funType = dynamic_cast<FunctionType const&>(*selectorType); |
1350 | 0 | if (funType.kind() == FunctionType::Kind::Declaration) |
1351 | 0 | { |
1352 | 0 | solAssert(funType.hasDeclaration()); |
1353 | 0 | solAssert(selectorType->sizeOnStack() == 0); |
1354 | 0 | m_context << funType.externalIdentifier(); |
1355 | 0 | } |
1356 | 0 | else |
1357 | 0 | { |
1358 | 0 | solAssert(selectorType->sizeOnStack() == 2); |
1359 | | // stack: <memory pointer> <functionPointer> |
1360 | | // Extract selector from the stack |
1361 | 0 | m_context << Instruction::SWAP1 << Instruction::POP; |
1362 | 0 | } |
1363 | | // Conversion will be done below |
1364 | 0 | dataOnStack = TypeProvider::uint(32); |
1365 | 0 | } |
1366 | 0 | else |
1367 | 0 | solAssert(function.kind() == FunctionType::Kind::ABIEncodeWithSelector, ""); |
1368 | | |
1369 | 0 | utils().convertType(*dataOnStack, FixedBytesType(4), true); |
1370 | | |
1371 | | // stack: <memory pointer> <selector> |
1372 | | |
1373 | | // load current memory, mask and combine the selector |
1374 | 0 | string mask = formatNumber((u256(-1) >> 32)); |
1375 | 0 | m_context.appendInlineAssembly(R"({ |
1376 | 0 | let data_start := add(mem_ptr, 0x20) |
1377 | 0 | let data := mload(data_start) |
1378 | 0 | let mask := )" + mask + R"( |
1379 | 0 | mstore(data_start, or(and(data, mask), selector)) |
1380 | 0 | })", {"mem_ptr", "selector"}); |
1381 | 0 | m_context << Instruction::POP; |
1382 | 0 | } |
1383 | | |
1384 | | // stack now: <memory pointer> |
1385 | 3.41k | break; |
1386 | 3.41k | } |
1387 | 3.48k | case FunctionType::Kind::ABIDecode: |
1388 | 3.48k | { |
1389 | 3.48k | arguments.front()->accept(*this); |
1390 | 3.48k | Type const* firstArgType = arguments.front()->annotation().type; |
1391 | 3.48k | TypePointers targetTypes; |
1392 | 3.48k | if (TupleType const* targetTupleType = dynamic_cast<TupleType const*>(_functionCall.annotation().type)) |
1393 | 0 | targetTypes = targetTupleType->components(); |
1394 | 3.48k | else |
1395 | 3.48k | targetTypes = TypePointers{_functionCall.annotation().type}; |
1396 | 3.48k | if ( |
1397 | 3.48k | auto referenceType = dynamic_cast<ReferenceType const*>(firstArgType); |
1398 | 3.48k | referenceType && referenceType->dataStoredIn(DataLocation::CallData) |
1399 | 3.48k | ) |
1400 | 0 | { |
1401 | 0 | solAssert(referenceType->isImplicitlyConvertibleTo(*TypeProvider::bytesCalldata()), ""); |
1402 | 0 | utils().convertType(*referenceType, *TypeProvider::bytesCalldata()); |
1403 | 0 | utils().abiDecode(targetTypes, false); |
1404 | 0 | } |
1405 | 3.48k | else |
1406 | 3.48k | { |
1407 | 3.48k | utils().convertType(*firstArgType, *TypeProvider::bytesMemory()); |
1408 | 3.48k | m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; |
1409 | 3.48k | m_context << Instruction::SWAP1 << Instruction::MLOAD; |
1410 | | // stack now: <mem_pos> <length> |
1411 | | |
1412 | 3.48k | utils().abiDecode(targetTypes, true); |
1413 | 3.48k | } |
1414 | 3.48k | break; |
1415 | 3.48k | } |
1416 | 3.48k | case FunctionType::Kind::GasLeft: |
1417 | 0 | m_context << Instruction::GAS; |
1418 | 0 | break; |
1419 | 0 | case FunctionType::Kind::MetaType: |
1420 | | // No code to generate. |
1421 | 0 | break; |
1422 | 236k | } |
1423 | 236k | } |
1424 | 236k | return false; |
1425 | 236k | } |
1426 | | |
1427 | | bool ExpressionCompiler::visit(FunctionCallOptions const& _functionCallOptions) |
1428 | 0 | { |
1429 | 0 | _functionCallOptions.expression().accept(*this); |
1430 | | |
1431 | | // Desired Stack: [salt], [gas], [value] |
1432 | 0 | enum Option { Salt, Gas, Value }; |
1433 | |
|
1434 | 0 | vector<Option> presentOptions; |
1435 | 0 | FunctionType const& funType = dynamic_cast<FunctionType const&>( |
1436 | 0 | *_functionCallOptions.expression().annotation().type |
1437 | 0 | ); |
1438 | 0 | if (funType.saltSet()) presentOptions.emplace_back(Salt); |
1439 | 0 | if (funType.gasSet()) presentOptions.emplace_back(Gas); |
1440 | 0 | if (funType.valueSet()) presentOptions.emplace_back(Value); |
1441 | |
|
1442 | 0 | for (size_t i = 0; i < _functionCallOptions.options().size(); ++i) |
1443 | 0 | { |
1444 | 0 | string const& name = *_functionCallOptions.names()[i]; |
1445 | 0 | Type const* requiredType = TypeProvider::uint256(); |
1446 | 0 | Option newOption; |
1447 | 0 | if (name == "salt") |
1448 | 0 | { |
1449 | 0 | newOption = Salt; |
1450 | 0 | requiredType = TypeProvider::fixedBytes(32); |
1451 | 0 | } |
1452 | 0 | else if (name == "gas") |
1453 | 0 | newOption = Gas; |
1454 | 0 | else if (name == "value") |
1455 | 0 | newOption = Value; |
1456 | 0 | else |
1457 | 0 | solAssert(false, "Unexpected option name!"); |
1458 | 0 | acceptAndConvert(*_functionCallOptions.options()[i], *requiredType); |
1459 | |
|
1460 | 0 | solAssert(!util::contains(presentOptions, newOption), ""); |
1461 | 0 | ptrdiff_t insertPos = presentOptions.end() - lower_bound(presentOptions.begin(), presentOptions.end(), newOption); |
1462 | |
|
1463 | 0 | utils().moveIntoStack(static_cast<unsigned>(insertPos), 1); |
1464 | 0 | presentOptions.insert(presentOptions.end() - insertPos, newOption); |
1465 | 0 | } |
1466 | | |
1467 | 0 | return false; |
1468 | 0 | } |
1469 | | |
1470 | | bool ExpressionCompiler::visit(NewExpression const&) |
1471 | 39.0k | { |
1472 | | // code is created for the function call (CREATION) only |
1473 | 39.0k | return false; |
1474 | 39.0k | } |
1475 | | |
1476 | | bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) |
1477 | 1.63M | { |
1478 | 1.63M | CompilerContext::LocationSetter locationSetter(m_context, _memberAccess); |
1479 | | // Check whether the member is a bound function. |
1480 | 1.63M | ASTString const& member = _memberAccess.memberName(); |
1481 | 1.63M | if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type)) |
1482 | 79.4k | if (funType->bound()) |
1483 | 50.5k | { |
1484 | 50.5k | acceptAndConvert(_memberAccess.expression(), *funType->selfType(), true); |
1485 | 50.5k | if (funType->kind() == FunctionType::Kind::Internal) |
1486 | 0 | { |
1487 | 0 | FunctionDefinition const& funDef = dynamic_cast<decltype(funDef)>(funType->declaration()); |
1488 | 0 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); |
1489 | 0 | utils().pushCombinedFunctionEntryLabel(funDef); |
1490 | 0 | utils().moveIntoStack(funType->selfType()->sizeOnStack(), 1); |
1491 | 0 | } |
1492 | 50.5k | else if ( |
1493 | 50.5k | funType->kind() == FunctionType::Kind::ArrayPop || |
1494 | 50.5k | funType->kind() == FunctionType::Kind::ArrayPush |
1495 | 50.5k | ) |
1496 | 50.5k | { |
1497 | 50.5k | } |
1498 | 0 | else |
1499 | 0 | { |
1500 | 0 | solAssert(funType->kind() == FunctionType::Kind::DelegateCall, ""); |
1501 | 0 | auto contract = dynamic_cast<ContractDefinition const*>(funType->declaration().scope()); |
1502 | 0 | solAssert(contract && contract->isLibrary(), ""); |
1503 | 0 | m_context.appendLibraryAddress(contract->fullyQualifiedName()); |
1504 | 0 | m_context << funType->externalIdentifier(); |
1505 | 0 | utils().moveIntoStack(funType->selfType()->sizeOnStack(), 2); |
1506 | 0 | } |
1507 | 50.5k | return false; |
1508 | 50.5k | } |
1509 | | |
1510 | | // Special processing for TypeType because we do not want to visit the library itself |
1511 | | // for internal functions, or enum/struct definitions. |
1512 | 1.58M | if (TypeType const* type = dynamic_cast<TypeType const*>(_memberAccess.expression().annotation().type)) |
1513 | 0 | { |
1514 | 0 | if (auto contractType = dynamic_cast<ContractType const*>(type->actualType())) |
1515 | 0 | { |
1516 | 0 | solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); |
1517 | 0 | if (contractType->isSuper()) |
1518 | 0 | { |
1519 | 0 | _memberAccess.expression().accept(*this); |
1520 | 0 | solAssert(_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); |
1521 | 0 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Super, ""); |
1522 | 0 | utils().pushCombinedFunctionEntryLabel(m_context.superFunction( |
1523 | 0 | dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration), |
1524 | 0 | contractType->contractDefinition() |
1525 | 0 | )); |
1526 | 0 | } |
1527 | 0 | else |
1528 | 0 | { |
1529 | 0 | if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
1530 | 0 | appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); |
1531 | 0 | else if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type)) |
1532 | 0 | { |
1533 | 0 | switch (funType->kind()) |
1534 | 0 | { |
1535 | 0 | case FunctionType::Kind::Declaration: |
1536 | 0 | break; |
1537 | 0 | case FunctionType::Kind::Internal: |
1538 | | // We do not visit the expression here on purpose, because in the case of an |
1539 | | // internal library function call, this would push the library address forcing |
1540 | | // us to link against it although we actually do not need it. |
1541 | 0 | if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
1542 | 0 | { |
1543 | 0 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); |
1544 | 0 | utils().pushCombinedFunctionEntryLabel(*function); |
1545 | 0 | } |
1546 | 0 | else |
1547 | 0 | solAssert(false, "Function not found in member access"); |
1548 | 0 | break; |
1549 | 0 | case FunctionType::Kind::Event: |
1550 | 0 | if (!dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
1551 | 0 | solAssert(false, "event not found"); |
1552 | | // no-op, because the parent node will do the job |
1553 | 0 | break; |
1554 | 0 | case FunctionType::Kind::Error: |
1555 | 0 | if (!dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
1556 | 0 | solAssert(false, "error not found"); |
1557 | | // no-op, because the parent node will do the job |
1558 | 0 | break; |
1559 | 0 | case FunctionType::Kind::DelegateCall: |
1560 | 0 | _memberAccess.expression().accept(*this); |
1561 | 0 | m_context << funType->externalIdentifier(); |
1562 | 0 | break; |
1563 | 0 | case FunctionType::Kind::External: |
1564 | 0 | case FunctionType::Kind::Creation: |
1565 | 0 | case FunctionType::Kind::Send: |
1566 | 0 | case FunctionType::Kind::BareCall: |
1567 | 0 | case FunctionType::Kind::BareCallCode: |
1568 | 0 | case FunctionType::Kind::BareDelegateCall: |
1569 | 0 | case FunctionType::Kind::BareStaticCall: |
1570 | 0 | case FunctionType::Kind::Transfer: |
1571 | 0 | case FunctionType::Kind::ECRecover: |
1572 | 0 | case FunctionType::Kind::SHA256: |
1573 | 0 | case FunctionType::Kind::RIPEMD160: |
1574 | 0 | default: |
1575 | 0 | solAssert(false, "unsupported member function"); |
1576 | 0 | } |
1577 | 0 | } |
1578 | 0 | else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type)) |
1579 | 0 | { |
1580 | | // no-op |
1581 | 0 | } |
1582 | 0 | else |
1583 | 0 | _memberAccess.expression().accept(*this); |
1584 | 0 | } |
1585 | 0 | } |
1586 | 0 | else if (auto enumType = dynamic_cast<EnumType const*>(type->actualType())) |
1587 | 0 | { |
1588 | 0 | _memberAccess.expression().accept(*this); |
1589 | 0 | m_context << enumType->memberValue(_memberAccess.memberName()); |
1590 | 0 | } |
1591 | 0 | else |
1592 | 0 | _memberAccess.expression().accept(*this); |
1593 | 0 | return false; |
1594 | 0 | } |
1595 | | // Another special case for `this.f.selector` and for ``C.f.selector`` which do not need the address. |
1596 | | // There are other uses of `.selector` which do need the address, but we want these |
1597 | | // specific uses to be pure expressions. |
1598 | 1.58M | if ( |
1599 | 1.58M | auto const* functionType = dynamic_cast<FunctionType const*>(_memberAccess.expression().annotation().type); |
1600 | 1.58M | functionType && member == "selector" |
1601 | 1.58M | ) |
1602 | 6.82k | { |
1603 | 6.82k | if (functionType->hasDeclaration()) |
1604 | 6.82k | { |
1605 | 6.82k | if (functionType->kind() == FunctionType::Kind::Event) |
1606 | 0 | m_context << u256(h256::Arith(util::keccak256(functionType->externalSignature()))); |
1607 | 6.82k | else |
1608 | 6.82k | { |
1609 | 6.82k | m_context << functionType->externalIdentifier(); |
1610 | | /// need to store it as bytes4 |
1611 | 6.82k | utils().leftShiftNumberOnStack(224); |
1612 | 6.82k | } |
1613 | 6.82k | return false; |
1614 | 6.82k | } |
1615 | 0 | else if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression())) |
1616 | 0 | if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression())) |
1617 | 0 | if (exprInt->name() == "this") |
1618 | 0 | if (Declaration const* declaration = expr->annotation().referencedDeclaration) |
1619 | 0 | { |
1620 | 0 | u256 identifier; |
1621 | 0 | if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) |
1622 | 0 | identifier = FunctionType(*variable).externalIdentifier(); |
1623 | 0 | else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) |
1624 | 0 | identifier = FunctionType(*function).externalIdentifier(); |
1625 | 0 | else |
1626 | 0 | solAssert(false, "Contract member is neither variable nor function."); |
1627 | 0 | m_context << identifier; |
1628 | | /// need to store it as bytes4 |
1629 | 0 | utils().leftShiftNumberOnStack(224); |
1630 | 0 | return false; |
1631 | 0 | } |
1632 | 6.82k | } |
1633 | | // Another special case for `address(this).balance`. Post-Istanbul, we can use the selfbalance |
1634 | | // opcode. |
1635 | 1.58M | if ( |
1636 | 1.58M | m_context.evmVersion().hasSelfBalance() && |
1637 | 1.58M | member == "balance" && |
1638 | 1.58M | _memberAccess.expression().annotation().type->category() == Type::Category::Address |
1639 | 1.58M | ) |
1640 | 0 | if (FunctionCall const* funCall = dynamic_cast<FunctionCall const*>(&_memberAccess.expression())) |
1641 | 0 | if (auto const* addr = dynamic_cast<ElementaryTypeNameExpression const*>(&funCall->expression())) |
1642 | 0 | if ( |
1643 | 0 | addr->type().typeName().token() == Token::Address && |
1644 | 0 | funCall->arguments().size() == 1 |
1645 | 0 | ) |
1646 | 0 | if (auto arg = dynamic_cast<Identifier const*>( funCall->arguments().front().get())) |
1647 | 0 | if ( |
1648 | 0 | arg->name() == "this" && |
1649 | 0 | dynamic_cast<MagicVariableDeclaration const*>(arg->annotation().referencedDeclaration) |
1650 | 0 | ) |
1651 | 0 | { |
1652 | 0 | m_context << Instruction::SELFBALANCE; |
1653 | 0 | return false; |
1654 | 0 | } |
1655 | | |
1656 | | // Another special case for `address.code.length`, which should simply call extcodesize |
1657 | 1.58M | if ( |
1658 | 1.58M | auto innerExpression = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()); |
1659 | 1.58M | member == "length" && |
1660 | 1.58M | innerExpression && |
1661 | 1.58M | innerExpression->memberName() == "code" && |
1662 | 1.58M | innerExpression->expression().annotation().type->category() == Type::Category::Address |
1663 | 1.58M | ) |
1664 | 0 | { |
1665 | 0 | solAssert(innerExpression->annotation().type->category() == Type::Category::Array, ""); |
1666 | | |
1667 | 0 | innerExpression->expression().accept(*this); |
1668 | |
|
1669 | 0 | utils().convertType( |
1670 | 0 | *innerExpression->expression().annotation().type, |
1671 | 0 | *TypeProvider::address(), |
1672 | 0 | true |
1673 | 0 | ); |
1674 | 0 | m_context << Instruction::EXTCODESIZE; |
1675 | |
|
1676 | 0 | return false; |
1677 | 0 | } |
1678 | | |
1679 | 1.58M | _memberAccess.expression().accept(*this); |
1680 | 1.58M | switch (_memberAccess.expression().annotation().type->category()) |
1681 | 1.58M | { |
1682 | 21.9k | case Type::Category::Contract: |
1683 | 21.9k | { |
1684 | 21.9k | ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type); |
1685 | | // ordinary contract type |
1686 | 21.9k | if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) |
1687 | 21.9k | { |
1688 | 21.9k | u256 identifier; |
1689 | 21.9k | if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration)) |
1690 | 0 | identifier = FunctionType(*variable).externalIdentifier(); |
1691 | 21.9k | else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration)) |
1692 | 21.9k | identifier = FunctionType(*function).externalIdentifier(); |
1693 | 0 | else |
1694 | 21.9k | solAssert(false, "Contract member is neither variable nor function."); |
1695 | 21.9k | utils().convertType(type, type.isPayable() ? *TypeProvider::payableAddress() : *TypeProvider::address(), true); |
1696 | 21.9k | m_context << identifier; |
1697 | 21.9k | } |
1698 | 0 | else |
1699 | 21.9k | solAssert(false, "Invalid member access in contract"); |
1700 | 21.9k | break; |
1701 | 21.9k | } |
1702 | 21.9k | case Type::Category::Integer: |
1703 | 0 | { |
1704 | 0 | solAssert(false, "Invalid member access to integer"); |
1705 | 0 | break; |
1706 | 0 | } |
1707 | 6.96k | case Type::Category::Address: |
1708 | 6.96k | { |
1709 | 6.96k | if (member == "balance") |
1710 | 0 | { |
1711 | 0 | utils().convertType( |
1712 | 0 | *_memberAccess.expression().annotation().type, |
1713 | 0 | *TypeProvider::address(), |
1714 | 0 | true |
1715 | 0 | ); |
1716 | 0 | m_context << Instruction::BALANCE; |
1717 | 0 | } |
1718 | 6.96k | else if (member == "code") |
1719 | 0 | { |
1720 | | // Stack: <address> |
1721 | 0 | utils().convertType( |
1722 | 0 | *_memberAccess.expression().annotation().type, |
1723 | 0 | *TypeProvider::address(), |
1724 | 0 | true |
1725 | 0 | ); |
1726 | |
|
1727 | 0 | m_context << Instruction::DUP1 << Instruction::EXTCODESIZE; |
1728 | | // Stack post: <address> <size> |
1729 | |
|
1730 | 0 | m_context << Instruction::DUP1; |
1731 | | // Account for the size field of `bytes memory` |
1732 | 0 | m_context << u256(32) << Instruction::ADD; |
1733 | 0 | utils().allocateMemory(); |
1734 | | // Stack post: <address> <size> <mem_offset> |
1735 | | |
1736 | | // Store size at mem_offset |
1737 | 0 | m_context << Instruction::DUP2 << Instruction::DUP2 << Instruction::MSTORE; |
1738 | |
|
1739 | 0 | m_context << u256(0) << Instruction::SWAP1 << Instruction::DUP1; |
1740 | | // Stack post: <address> <size> 0 <mem_offset> <mem_offset> |
1741 | |
|
1742 | 0 | m_context << u256(32) << Instruction::ADD << Instruction::SWAP1; |
1743 | | // Stack post: <address> <size> 0 <mem_offset_adjusted> <mem_offset> |
1744 | |
|
1745 | 0 | m_context << Instruction::SWAP4; |
1746 | | // Stack post: <mem_offset> <size> 0 <mem_offset_adjusted> <address> |
1747 | |
|
1748 | 0 | m_context << Instruction::EXTCODECOPY; |
1749 | | // Stack post: <mem_offset> |
1750 | 0 | } |
1751 | 6.96k | else if (member == "codehash") |
1752 | 0 | { |
1753 | 0 | utils().convertType( |
1754 | 0 | *_memberAccess.expression().annotation().type, |
1755 | 0 | *TypeProvider::address(), |
1756 | 0 | true |
1757 | 0 | ); |
1758 | 0 | m_context << Instruction::EXTCODEHASH; |
1759 | 0 | } |
1760 | 6.96k | else if ((set<string>{"send", "transfer"}).count(member)) |
1761 | 0 | { |
1762 | 0 | solAssert(dynamic_cast<AddressType const&>(*_memberAccess.expression().annotation().type).stateMutability() == StateMutability::Payable, ""); |
1763 | 0 | utils().convertType( |
1764 | 0 | *_memberAccess.expression().annotation().type, |
1765 | 0 | AddressType(StateMutability::Payable), |
1766 | 0 | true |
1767 | 0 | ); |
1768 | 0 | } |
1769 | 6.96k | else if ((set<string>{"call", "callcode", "delegatecall", "staticcall"}).count(member)) |
1770 | 6.96k | utils().convertType( |
1771 | 6.96k | *_memberAccess.expression().annotation().type, |
1772 | 6.96k | *TypeProvider::address(), |
1773 | 6.96k | true |
1774 | 6.96k | ); |
1775 | 0 | else |
1776 | 6.96k | solAssert(false, "Invalid member access to address"); |
1777 | 6.96k | break; |
1778 | 6.96k | } |
1779 | 6.96k | case Type::Category::Function: |
1780 | 0 | if (member == "selector") |
1781 | 0 | { |
1782 | 0 | auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type); |
1783 | | // all events should have already been caught by this stage |
1784 | 0 | solAssert(!(functionType.kind() == FunctionType::Kind::Event)); |
1785 | | |
1786 | 0 | if (functionType.kind() == FunctionType::Kind::External) |
1787 | 0 | CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 2); |
1788 | 0 | m_context << Instruction::SWAP1 << Instruction::POP; |
1789 | | |
1790 | | /// need to store it as bytes4 |
1791 | 0 | utils().leftShiftNumberOnStack(224); |
1792 | 0 | } |
1793 | 0 | else if (member == "address") |
1794 | 0 | { |
1795 | 0 | auto const& functionType = dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type); |
1796 | 0 | solAssert(functionType.kind() == FunctionType::Kind::External, ""); |
1797 | 0 | CompilerUtils(m_context).popStackSlots(functionType.sizeOnStack() - 1); |
1798 | 0 | } |
1799 | 0 | else |
1800 | 0 | solAssert( |
1801 | 0 | !!_memberAccess.expression().annotation().type->memberType(member), |
1802 | 0 | "Invalid member access to function." |
1803 | 0 | ); |
1804 | 0 | break; |
1805 | 0 | case Type::Category::Magic: |
1806 | | // we can ignore the kind of magic and only look at the name of the member |
1807 | 0 | if (member == "coinbase") |
1808 | 0 | m_context << Instruction::COINBASE; |
1809 | 0 | else if (member == "timestamp") |
1810 | 0 | m_context << Instruction::TIMESTAMP; |
1811 | 0 | else if (member == "difficulty") |
1812 | 0 | m_context << Instruction::DIFFICULTY; |
1813 | 0 | else if (member == "number") |
1814 | 0 | m_context << Instruction::NUMBER; |
1815 | 0 | else if (member == "gaslimit") |
1816 | 0 | m_context << Instruction::GASLIMIT; |
1817 | 0 | else if (member == "sender") |
1818 | 0 | m_context << Instruction::CALLER; |
1819 | 0 | else if (member == "value") |
1820 | 0 | m_context << Instruction::CALLVALUE; |
1821 | 0 | else if (member == "origin") |
1822 | 0 | m_context << Instruction::ORIGIN; |
1823 | 0 | else if (member == "gasprice") |
1824 | 0 | m_context << Instruction::GASPRICE; |
1825 | 0 | else if (member == "chainid") |
1826 | 0 | m_context << Instruction::CHAINID; |
1827 | 0 | else if (member == "basefee") |
1828 | 0 | m_context << Instruction::BASEFEE; |
1829 | 0 | else if (member == "data") |
1830 | 0 | m_context << u256(0) << Instruction::CALLDATASIZE; |
1831 | 0 | else if (member == "sig") |
1832 | 0 | m_context << u256(0) << Instruction::CALLDATALOAD |
1833 | 0 | << (u256(0xffffffff) << (256 - 32)) << Instruction::AND; |
1834 | 0 | else if (member == "gas") |
1835 | 0 | solAssert(false, "Gas has been removed."); |
1836 | 0 | else if (member == "blockhash") |
1837 | 0 | solAssert(false, "Blockhash has been removed."); |
1838 | 0 | else if (member == "creationCode" || member == "runtimeCode") |
1839 | 0 | { |
1840 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
1841 | 0 | auto const& contractType = dynamic_cast<ContractType const&>(*arg); |
1842 | 0 | solAssert(!contractType.isSuper(), ""); |
1843 | 0 | ContractDefinition const& contract = contractType.contractDefinition(); |
1844 | 0 | utils().fetchFreeMemoryPointer(); |
1845 | 0 | m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; |
1846 | 0 | utils().copyContractCodeToMemory(contract, member == "creationCode"); |
1847 | | // Stack: start end |
1848 | 0 | m_context.appendInlineAssembly( |
1849 | 0 | Whiskers(R"({ |
1850 | 0 | mstore(start, sub(end, add(start, 0x20))) |
1851 | 0 | mstore(<free>, and(add(end, 31), not(31))) |
1852 | 0 | })")("free", to_string(CompilerUtils::freeMemoryPointer)).render(), |
1853 | 0 | {"start", "end"} |
1854 | 0 | ); |
1855 | 0 | m_context << Instruction::POP; |
1856 | 0 | } |
1857 | 0 | else if (member == "name") |
1858 | 0 | { |
1859 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
1860 | 0 | auto const& contractType = dynamic_cast<ContractType const&>(*arg); |
1861 | 0 | ContractDefinition const& contract = contractType.isSuper() ? |
1862 | 0 | *contractType.contractDefinition().superContract(m_context.mostDerivedContract()) : |
1863 | 0 | dynamic_cast<ContractType const&>(*arg).contractDefinition(); |
1864 | 0 | utils().allocateMemory(((contract.name().length() + 31) / 32) * 32 + 32); |
1865 | | // store string length |
1866 | 0 | m_context << u256(contract.name().length()) << Instruction::DUP2 << Instruction::MSTORE; |
1867 | | // adjust pointer |
1868 | 0 | m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; |
1869 | 0 | utils().storeStringData(contract.name()); |
1870 | 0 | } |
1871 | 0 | else if (member == "interfaceId") |
1872 | 0 | { |
1873 | 0 | Type const* arg = dynamic_cast<MagicType const&>(*_memberAccess.expression().annotation().type).typeArgument(); |
1874 | 0 | ContractDefinition const& contract = dynamic_cast<ContractType const&>(*arg).contractDefinition(); |
1875 | 0 | m_context << (u256{contract.interfaceId()} << (256 - 32)); |
1876 | 0 | } |
1877 | 0 | else if (member == "min" || member == "max") |
1878 | 0 | { |
1879 | 0 | MagicType const* arg = dynamic_cast<MagicType const*>(_memberAccess.expression().annotation().type); |
1880 | 0 | if (IntegerType const* integerType = dynamic_cast<IntegerType const*>(arg->typeArgument())) |
1881 | 0 | m_context << (member == "min" ? integerType->min() : integerType->max()); |
1882 | 0 | else if (EnumType const* enumType = dynamic_cast<EnumType const*>(arg->typeArgument())) |
1883 | 0 | m_context << (member == "min" ? enumType->minValue() : enumType->maxValue()); |
1884 | 0 | else |
1885 | 0 | solAssert(false, "min/max not available for the given type."); |
1886 | |
|
1887 | 0 | } |
1888 | 0 | else if ((set<string>{"encode", "encodePacked", "encodeWithSelector", "encodeWithSignature", "decode"}).count(member)) |
1889 | 0 | { |
1890 | | // no-op |
1891 | 0 | } |
1892 | 0 | else |
1893 | 0 | solAssert(false, "Unknown magic member."); |
1894 | 0 | break; |
1895 | 1.35M | case Type::Category::Struct: |
1896 | 1.35M | { |
1897 | 1.35M | StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type); |
1898 | 1.35M | Type const* memberType = _memberAccess.annotation().type; |
1899 | 1.35M | switch (type.location()) |
1900 | 1.35M | { |
1901 | 274k | case DataLocation::Storage: |
1902 | 274k | { |
1903 | 274k | pair<u256, unsigned> const& offsets = type.storageOffsetsOfMember(member); |
1904 | 274k | m_context << offsets.first << Instruction::ADD << u256(offsets.second); |
1905 | 274k | setLValueToStorageItem(_memberAccess); |
1906 | 274k | break; |
1907 | 0 | } |
1908 | 932k | case DataLocation::Memory: |
1909 | 932k | { |
1910 | 932k | m_context << type.memoryOffsetOfMember(member) << Instruction::ADD; |
1911 | 932k | setLValue<MemoryItem>(_memberAccess, *memberType); |
1912 | 932k | break; |
1913 | 0 | } |
1914 | 152k | case DataLocation::CallData: |
1915 | 152k | { |
1916 | 152k | if (_memberAccess.annotation().type->isDynamicallyEncoded()) |
1917 | 93.9k | { |
1918 | 93.9k | m_context << Instruction::DUP1; |
1919 | 93.9k | m_context << type.calldataOffsetOfMember(member) << Instruction::ADD; |
1920 | 93.9k | CompilerUtils(m_context).accessCalldataTail(*memberType); |
1921 | 93.9k | } |
1922 | 58.3k | else |
1923 | 58.3k | { |
1924 | 58.3k | m_context << type.calldataOffsetOfMember(member) << Instruction::ADD; |
1925 | | // For non-value types the calldata offset is returned directly. |
1926 | 58.3k | if (memberType->isValueType()) |
1927 | 36.8k | { |
1928 | 36.8k | solAssert(memberType->calldataEncodedSize() > 0, ""); |
1929 | 36.8k | solAssert(memberType->storageBytes() <= 32, ""); |
1930 | 36.8k | if (memberType->storageBytes() < 32 && m_context.useABICoderV2()) |
1931 | 35.6k | { |
1932 | 35.6k | m_context << u256(32); |
1933 | 35.6k | CompilerUtils(m_context).abiDecodeV2({memberType}, false); |
1934 | 35.6k | } |
1935 | 1.10k | else |
1936 | 1.10k | CompilerUtils(m_context).loadFromMemoryDynamic(*memberType, true, true, false); |
1937 | 36.8k | } |
1938 | 21.5k | else |
1939 | 58.3k | solAssert( |
1940 | 58.3k | memberType->category() == Type::Category::Array || |
1941 | 58.3k | memberType->category() == Type::Category::Struct, |
1942 | 58.3k | "" |
1943 | 58.3k | ); |
1944 | 58.3k | } |
1945 | 152k | break; |
1946 | 152k | } |
1947 | 152k | default: |
1948 | 0 | solAssert(false, "Illegal data location for struct."); |
1949 | 1.35M | } |
1950 | 1.35M | break; |
1951 | 1.35M | } |
1952 | 1.35M | case Type::Category::Enum: |
1953 | 0 | { |
1954 | 0 | EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type); |
1955 | 0 | m_context << type.memberValue(_memberAccess.memberName()); |
1956 | 0 | break; |
1957 | 1.35M | } |
1958 | 194k | case Type::Category::Array: |
1959 | 194k | { |
1960 | 194k | auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type); |
1961 | 194k | if (member == "length") |
1962 | 194k | { |
1963 | 194k | if (!type.isDynamicallySized()) |
1964 | 48.8k | { |
1965 | 48.8k | utils().popStackElement(type); |
1966 | 48.8k | m_context << type.length(); |
1967 | 48.8k | } |
1968 | 145k | else |
1969 | 145k | switch (type.location()) |
1970 | 145k | { |
1971 | 17.2k | case DataLocation::CallData: |
1972 | 17.2k | m_context << Instruction::SWAP1 << Instruction::POP; |
1973 | 17.2k | break; |
1974 | 0 | case DataLocation::Storage: |
1975 | 0 | ArrayUtils(m_context).retrieveLength(type); |
1976 | 0 | m_context << Instruction::SWAP1 << Instruction::POP; |
1977 | 0 | break; |
1978 | 128k | case DataLocation::Memory: |
1979 | 128k | m_context << Instruction::MLOAD; |
1980 | 128k | break; |
1981 | 145k | } |
1982 | 194k | } |
1983 | 0 | else if (member == "push" || member == "pop") |
1984 | 0 | { |
1985 | 0 | solAssert( |
1986 | 0 | type.isDynamicallySized() && |
1987 | 0 | type.location() == DataLocation::Storage && |
1988 | 0 | type.category() == Type::Category::Array, |
1989 | 0 | "Tried to use ." + member + "() on a non-dynamically sized array" |
1990 | 0 | ); |
1991 | 0 | } |
1992 | 0 | else |
1993 | 0 | solAssert(false, "Illegal array member."); |
1994 | 194k | break; |
1995 | 194k | } |
1996 | 194k | case Type::Category::FixedBytes: |
1997 | 0 | { |
1998 | 0 | auto const& type = dynamic_cast<FixedBytesType const&>(*_memberAccess.expression().annotation().type); |
1999 | 0 | utils().popStackElement(type); |
2000 | 0 | if (member == "length") |
2001 | 0 | m_context << u256(type.numBytes()); |
2002 | 0 | else |
2003 | 0 | solAssert(false, "Illegal fixed bytes member."); |
2004 | 0 | break; |
2005 | 0 | } |
2006 | 0 | case Type::Category::Module: |
2007 | 0 | { |
2008 | 0 | Type::Category category = _memberAccess.annotation().type->category(); |
2009 | 0 | solAssert( |
2010 | 0 | dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration) || |
2011 | 0 | dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration) || |
2012 | 0 | dynamic_cast<ErrorDefinition const*>(_memberAccess.annotation().referencedDeclaration) || |
2013 | 0 | category == Type::Category::TypeType || |
2014 | 0 | category == Type::Category::Module, |
2015 | 0 | "" |
2016 | 0 | ); |
2017 | 0 | if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration)) |
2018 | 0 | { |
2019 | 0 | solAssert(variable->isConstant(), ""); |
2020 | 0 | appendVariable(*variable, static_cast<Expression const&>(_memberAccess)); |
2021 | 0 | } |
2022 | 0 | else if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
2023 | 0 | { |
2024 | 0 | auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); |
2025 | 0 | solAssert(function && function->isFree(), ""); |
2026 | 0 | solAssert(funType->kind() == FunctionType::Kind::Internal, ""); |
2027 | 0 | solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); |
2028 | 0 | utils().pushCombinedFunctionEntryLabel(*function); |
2029 | 0 | } |
2030 | 0 | else if (auto const* contract = dynamic_cast<ContractDefinition const*>(_memberAccess.annotation().referencedDeclaration)) |
2031 | 0 | { |
2032 | 0 | if (contract->isLibrary()) |
2033 | 0 | m_context.appendLibraryAddress(contract->fullyQualifiedName()); |
2034 | 0 | } |
2035 | 0 | break; |
2036 | 0 | } |
2037 | 0 | default: |
2038 | 0 | solAssert(false, "Member access to unknown type."); |
2039 | 1.58M | } |
2040 | 1.58M | return false; |
2041 | 1.58M | } |
2042 | | |
2043 | | bool ExpressionCompiler::visit(IndexAccess const& _indexAccess) |
2044 | 3.27M | { |
2045 | 3.27M | CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); |
2046 | 3.27M | _indexAccess.baseExpression().accept(*this); |
2047 | | |
2048 | 3.27M | Type const& baseType = *_indexAccess.baseExpression().annotation().type; |
2049 | | |
2050 | 3.27M | switch (baseType.category()) |
2051 | 3.27M | { |
2052 | 0 | case Type::Category::Mapping: |
2053 | 0 | { |
2054 | | // stack: storage_base_ref |
2055 | 0 | Type const* keyType = dynamic_cast<MappingType const&>(baseType).keyType(); |
2056 | 0 | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2057 | 0 | if (keyType->isDynamicallySized()) |
2058 | 0 | { |
2059 | 0 | _indexAccess.indexExpression()->accept(*this); |
2060 | 0 | utils().fetchFreeMemoryPointer(); |
2061 | | // stack: base index mem |
2062 | | // note: the following operations must not allocate memory! |
2063 | 0 | utils().packedEncode( |
2064 | 0 | TypePointers{_indexAccess.indexExpression()->annotation().type}, |
2065 | 0 | TypePointers{keyType} |
2066 | 0 | ); |
2067 | 0 | m_context << Instruction::SWAP1; |
2068 | 0 | utils().storeInMemoryDynamic(*TypeProvider::uint256()); |
2069 | 0 | utils().toSizeAfterFreeMemoryPointer(); |
2070 | 0 | } |
2071 | 0 | else |
2072 | 0 | { |
2073 | 0 | m_context << u256(0); // memory position |
2074 | 0 | appendExpressionCopyToMemory(*keyType, *_indexAccess.indexExpression()); |
2075 | 0 | m_context << Instruction::SWAP1; |
2076 | 0 | solAssert(CompilerUtils::freeMemoryPointer >= 0x40, ""); |
2077 | 0 | utils().storeInMemoryDynamic(*TypeProvider::uint256()); |
2078 | 0 | m_context << u256(0); |
2079 | 0 | } |
2080 | 0 | m_context << Instruction::KECCAK256; |
2081 | 0 | m_context << u256(0); |
2082 | 0 | setLValueToStorageItem(_indexAccess); |
2083 | 0 | break; |
2084 | 0 | } |
2085 | 0 | case Type::Category::ArraySlice: |
2086 | 0 | { |
2087 | 0 | auto const& arrayType = dynamic_cast<ArraySliceType const&>(baseType).arrayType(); |
2088 | 0 | solAssert( |
2089 | 0 | arrayType.location() == DataLocation::CallData && |
2090 | 0 | arrayType.isDynamicallySized() && |
2091 | 0 | !arrayType.baseType()->isDynamicallyEncoded(), |
2092 | 0 | "" |
2093 | 0 | ); |
2094 | 0 | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2095 | | |
2096 | 0 | acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true); |
2097 | 0 | ArrayUtils(m_context).accessCallDataArrayElement(arrayType); |
2098 | 0 | break; |
2099 | |
|
2100 | 0 | } |
2101 | 3.27M | case Type::Category::Array: |
2102 | 3.27M | { |
2103 | 3.27M | ArrayType const& arrayType = dynamic_cast<ArrayType const&>(baseType); |
2104 | 3.27M | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2105 | | |
2106 | 3.27M | acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true); |
2107 | | // stack layout: <base_ref> [<length>] <index> |
2108 | 3.27M | switch (arrayType.location()) |
2109 | 3.27M | { |
2110 | 691k | case DataLocation::Storage: |
2111 | 691k | ArrayUtils(m_context).accessIndex(arrayType); |
2112 | 691k | if (arrayType.isByteArrayOrString()) |
2113 | 0 | { |
2114 | 0 | solAssert(!arrayType.isString(), "Index access to string is not allowed."); |
2115 | 0 | setLValue<StorageByteArrayElement>(_indexAccess); |
2116 | 0 | } |
2117 | 691k | else |
2118 | 691k | setLValueToStorageItem(_indexAccess); |
2119 | 691k | break; |
2120 | 2.28M | case DataLocation::Memory: |
2121 | 2.28M | ArrayUtils(m_context).accessIndex(arrayType); |
2122 | 2.28M | setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArrayOrString()); |
2123 | 2.28M | break; |
2124 | 301k | case DataLocation::CallData: |
2125 | 301k | ArrayUtils(m_context).accessCallDataArrayElement(arrayType); |
2126 | 301k | break; |
2127 | 3.27M | } |
2128 | 3.27M | break; |
2129 | 3.27M | } |
2130 | 3.27M | case Type::Category::FixedBytes: |
2131 | 3.48k | { |
2132 | 3.48k | FixedBytesType const& fixedBytesType = dynamic_cast<FixedBytesType const&>(baseType); |
2133 | 3.48k | solAssert(_indexAccess.indexExpression(), "Index expression expected."); |
2134 | | |
2135 | 3.48k | acceptAndConvert(*_indexAccess.indexExpression(), *TypeProvider::uint256(), true); |
2136 | | // stack layout: <value> <index> |
2137 | | // check out-of-bounds access |
2138 | 3.48k | m_context << u256(fixedBytesType.numBytes()); |
2139 | 3.48k | m_context << Instruction::DUP2 << Instruction::LT << Instruction::ISZERO; |
2140 | | // out-of-bounds access throws exception |
2141 | 3.48k | m_context.appendConditionalPanic(util::PanicCode::ArrayOutOfBounds); |
2142 | | |
2143 | 3.48k | m_context << Instruction::BYTE; |
2144 | 3.48k | utils().leftShiftNumberOnStack(256 - 8); |
2145 | 3.48k | break; |
2146 | 3.48k | } |
2147 | 0 | case Type::Category::TypeType: |
2148 | 0 | { |
2149 | 0 | solAssert(baseType.sizeOnStack() == 0, ""); |
2150 | 0 | solAssert(_indexAccess.annotation().type->sizeOnStack() == 0, ""); |
2151 | | // no-op - this seems to be a lone array type (`structType[];`) |
2152 | 0 | break; |
2153 | 0 | } |
2154 | 0 | default: |
2155 | 0 | solAssert(false, "Index access only allowed for mappings or arrays."); |
2156 | 0 | break; |
2157 | 3.27M | } |
2158 | | |
2159 | 3.27M | return false; |
2160 | 3.27M | } |
2161 | | |
2162 | | bool ExpressionCompiler::visit(IndexRangeAccess const& _indexAccess) |
2163 | 0 | { |
2164 | 0 | CompilerContext::LocationSetter locationSetter(m_context, _indexAccess); |
2165 | 0 | _indexAccess.baseExpression().accept(*this); |
2166 | | // stack: offset length |
2167 | |
|
2168 | 0 | Type const& baseType = *_indexAccess.baseExpression().annotation().type; |
2169 | |
|
2170 | 0 | ArrayType const *arrayType = dynamic_cast<ArrayType const*>(&baseType); |
2171 | 0 | if (!arrayType) |
2172 | 0 | if (ArraySliceType const* sliceType = dynamic_cast<ArraySliceType const*>(&baseType)) |
2173 | 0 | arrayType = &sliceType->arrayType(); |
2174 | |
|
2175 | 0 | solAssert(arrayType, ""); |
2176 | 0 | solUnimplementedAssert( |
2177 | 0 | arrayType->location() == DataLocation::CallData && |
2178 | 0 | arrayType->isDynamicallySized() && |
2179 | 0 | !arrayType->baseType()->isDynamicallyEncoded() |
2180 | 0 | ); |
2181 | | |
2182 | 0 | if (_indexAccess.startExpression()) |
2183 | 0 | acceptAndConvert(*_indexAccess.startExpression(), *TypeProvider::uint256()); |
2184 | 0 | else |
2185 | 0 | m_context << u256(0); |
2186 | | // stack: offset length sliceStart |
2187 | |
|
2188 | 0 | m_context << Instruction::SWAP1; |
2189 | | // stack: offset sliceStart length |
2190 | |
|
2191 | 0 | if (_indexAccess.endExpression()) |
2192 | 0 | acceptAndConvert(*_indexAccess.endExpression(), *TypeProvider::uint256()); |
2193 | 0 | else |
2194 | 0 | m_context << Instruction::DUP1; |
2195 | | // stack: offset sliceStart length sliceEnd |
2196 | |
|
2197 | 0 | m_context << Instruction::SWAP3; |
2198 | | // stack: sliceEnd sliceStart length offset |
2199 | |
|
2200 | 0 | m_context.callYulFunction(m_context.utilFunctions().calldataArrayIndexRangeAccess(*arrayType), 4, 2); |
2201 | |
|
2202 | 0 | return false; |
2203 | 0 | } |
2204 | | |
2205 | | void ExpressionCompiler::endVisit(Identifier const& _identifier) |
2206 | 1.38M | { |
2207 | 1.38M | CompilerContext::LocationSetter locationSetter(m_context, _identifier); |
2208 | 1.38M | Declaration const* declaration = _identifier.annotation().referencedDeclaration; |
2209 | 1.38M | if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) |
2210 | 28.9k | { |
2211 | 28.9k | switch (magicVar->type()->category()) |
2212 | 28.9k | { |
2213 | 28.9k | case Type::Category::Contract: |
2214 | 28.9k | if (dynamic_cast<ContractType const*>(magicVar->type())) |
2215 | 28.9k | { |
2216 | 28.9k | solAssert(_identifier.name() == "this", ""); |
2217 | 28.9k | m_context << Instruction::ADDRESS; |
2218 | 28.9k | } |
2219 | 28.9k | break; |
2220 | 28.9k | default: |
2221 | 0 | break; |
2222 | 28.9k | } |
2223 | 28.9k | } |
2224 | 1.35M | else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) |
2225 | 0 | { |
2226 | | // If the identifier is called right away, this code is executed in visit(FunctionCall...), because |
2227 | | // we want to avoid having a reference to the runtime function entry point in the |
2228 | | // constructor context, since this would force the compiler to include unreferenced |
2229 | | // internal functions in the runtime context. |
2230 | 0 | solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, ""); |
2231 | 0 | utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); |
2232 | 0 | } |
2233 | 1.35M | else if (auto variable = dynamic_cast<VariableDeclaration const*>(declaration)) |
2234 | 1.35M | appendVariable(*variable, static_cast<Expression const&>(_identifier)); |
2235 | 0 | else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) |
2236 | 0 | { |
2237 | 0 | if (contract->isLibrary()) |
2238 | 0 | m_context.appendLibraryAddress(contract->fullyQualifiedName()); |
2239 | 0 | } |
2240 | 0 | else if (dynamic_cast<EventDefinition const*>(declaration)) |
2241 | 0 | { |
2242 | | // no-op |
2243 | 0 | } |
2244 | 0 | else if (dynamic_cast<ErrorDefinition const*>(declaration)) |
2245 | 0 | { |
2246 | | // no-op |
2247 | 0 | } |
2248 | 0 | else if (dynamic_cast<EnumDefinition const*>(declaration)) |
2249 | 0 | { |
2250 | | // no-op |
2251 | 0 | } |
2252 | 0 | else if (dynamic_cast<UserDefinedValueTypeDefinition const*>(declaration)) |
2253 | 0 | { |
2254 | | // no-op |
2255 | 0 | } |
2256 | 0 | else if (dynamic_cast<StructDefinition const*>(declaration)) |
2257 | 0 | { |
2258 | | // no-op |
2259 | 0 | } |
2260 | 0 | else if (dynamic_cast<ImportDirective const*>(declaration)) |
2261 | 0 | { |
2262 | | // no-op |
2263 | 0 | } |
2264 | 0 | else |
2265 | 0 | { |
2266 | 0 | solAssert(false, "Identifier type not expected in expression context."); |
2267 | 0 | } |
2268 | 1.38M | } |
2269 | | |
2270 | | void ExpressionCompiler::endVisit(Literal const& _literal) |
2271 | 4.98M | { |
2272 | 4.98M | CompilerContext::LocationSetter locationSetter(m_context, _literal); |
2273 | 4.98M | Type const* type = _literal.annotation().type; |
2274 | | |
2275 | 4.98M | switch (type->category()) |
2276 | 4.98M | { |
2277 | 4.57M | case Type::Category::RationalNumber: |
2278 | 4.72M | case Type::Category::Bool: |
2279 | 4.72M | case Type::Category::Address: |
2280 | 4.72M | m_context << type->literalValue(&_literal); |
2281 | 4.72M | break; |
2282 | 259k | case Type::Category::StringLiteral: |
2283 | 259k | break; // will be done during conversion |
2284 | 0 | default: |
2285 | 0 | solUnimplemented("Only integer, boolean and string literals implemented for now."); |
2286 | 4.98M | } |
2287 | 4.98M | } |
2288 | | |
2289 | | void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation) |
2290 | 3.48k | { |
2291 | 3.48k | Token const c_op = _binaryOperation.getOperator(); |
2292 | 3.48k | solAssert(c_op == Token::Or || c_op == Token::And, ""); |
2293 | | |
2294 | 3.48k | _binaryOperation.leftExpression().accept(*this); |
2295 | 3.48k | m_context << Instruction::DUP1; |
2296 | 3.48k | if (c_op == Token::And) |
2297 | 0 | m_context << Instruction::ISZERO; |
2298 | 3.48k | evmasm::AssemblyItem endLabel = m_context.appendConditionalJump(); |
2299 | 3.48k | m_context << Instruction::POP; |
2300 | 3.48k | _binaryOperation.rightExpression().accept(*this); |
2301 | 3.48k | m_context << endLabel; |
2302 | 3.48k | } |
2303 | | |
2304 | | void ExpressionCompiler::appendCompareOperatorCode(Token _operator, Type const& _type) |
2305 | 628k | { |
2306 | 628k | if (_operator == Token::Equal || _operator == Token::NotEqual) |
2307 | 565k | { |
2308 | 565k | FunctionType const* functionType = dynamic_cast<decltype(functionType)>(&_type); |
2309 | 565k | if (functionType && functionType->kind() == FunctionType::Kind::External) |
2310 | 0 | { |
2311 | 0 | solUnimplementedAssert(functionType->sizeOnStack() == 2, ""); |
2312 | 0 | m_context << Instruction::SWAP3; |
2313 | |
|
2314 | 0 | m_context << ((u256(1) << 160) - 1) << Instruction::AND; |
2315 | 0 | m_context << Instruction::SWAP1; |
2316 | 0 | m_context << ((u256(1) << 160) - 1) << Instruction::AND; |
2317 | 0 | m_context << Instruction::EQ; |
2318 | 0 | m_context << Instruction::SWAP2; |
2319 | 0 | m_context << ((u256(1) << 32) - 1) << Instruction::AND; |
2320 | 0 | m_context << Instruction::SWAP1; |
2321 | 0 | m_context << ((u256(1) << 32) - 1) << Instruction::AND; |
2322 | 0 | m_context << Instruction::EQ; |
2323 | 0 | m_context << Instruction::AND; |
2324 | 0 | } |
2325 | 565k | else |
2326 | 565k | { |
2327 | 565k | solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types."); |
2328 | 565k | if (functionType && functionType->kind() == FunctionType::Kind::Internal) |
2329 | 0 | { |
2330 | | // We have to remove the upper bits (construction time value) because they might |
2331 | | // be "unknown" in one of the operands and not in the other. |
2332 | 0 | m_context << ((u256(1) << 32) - 1) << Instruction::AND; |
2333 | 0 | m_context << Instruction::SWAP1; |
2334 | 0 | m_context << ((u256(1) << 32) - 1) << Instruction::AND; |
2335 | 0 | } |
2336 | 565k | m_context << Instruction::EQ; |
2337 | 565k | } |
2338 | 565k | if (_operator == Token::NotEqual) |
2339 | 558k | m_context << Instruction::ISZERO; |
2340 | 565k | } |
2341 | 62.6k | else |
2342 | 62.6k | { |
2343 | 62.6k | solAssert(_type.sizeOnStack() == 1, "Comparison of multi-slot types."); |
2344 | 62.6k | bool isSigned = false; |
2345 | 62.6k | if (auto type = dynamic_cast<IntegerType const*>(&_type)) |
2346 | 62.6k | isSigned = type->isSigned(); |
2347 | | |
2348 | 62.6k | switch (_operator) |
2349 | 62.6k | { |
2350 | 0 | case Token::GreaterThanOrEqual: |
2351 | 0 | m_context << |
2352 | 0 | (isSigned ? Instruction::SLT : Instruction::LT) << |
2353 | 0 | Instruction::ISZERO; |
2354 | 0 | break; |
2355 | 0 | case Token::LessThanOrEqual: |
2356 | 0 | m_context << |
2357 | 0 | (isSigned ? Instruction::SGT : Instruction::GT) << |
2358 | 0 | Instruction::ISZERO; |
2359 | 0 | break; |
2360 | 0 | case Token::GreaterThan: |
2361 | 0 | m_context << (isSigned ? Instruction::SGT : Instruction::GT); |
2362 | 0 | break; |
2363 | 62.6k | case Token::LessThan: |
2364 | 62.6k | m_context << (isSigned ? Instruction::SLT : Instruction::LT); |
2365 | 62.6k | break; |
2366 | 0 | default: |
2367 | 0 | solAssert(false, "Unknown comparison operator."); |
2368 | 62.6k | } |
2369 | 62.6k | } |
2370 | 628k | } |
2371 | | |
2372 | | void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token _operator, Type const& _type) |
2373 | 31.2k | { |
2374 | 31.2k | if (TokenTraits::isArithmeticOp(_operator)) |
2375 | 31.2k | appendArithmeticOperatorCode(_operator, _type); |
2376 | 0 | else if (TokenTraits::isBitOp(_operator)) |
2377 | 0 | appendBitOperatorCode(_operator); |
2378 | 0 | else |
2379 | 0 | solAssert(false, "Unknown binary operator."); |
2380 | 31.2k | } |
2381 | | |
2382 | | void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type const& _type) |
2383 | 31.2k | { |
2384 | 31.2k | if (_type.category() == Type::Category::FixedPoint) |
2385 | 31.2k | solUnimplemented("Not yet implemented - FixedPointType."); |
2386 | | |
2387 | 31.2k | IntegerType const& type = dynamic_cast<IntegerType const&>(_type); |
2388 | 31.2k | if (m_context.arithmetic() == Arithmetic::Checked) |
2389 | 31.2k | { |
2390 | 31.2k | string functionName; |
2391 | 31.2k | switch (_operator) |
2392 | 31.2k | { |
2393 | 20.8k | case Token::Add: |
2394 | 20.8k | functionName = m_context.utilFunctions().overflowCheckedIntAddFunction(type); |
2395 | 20.8k | break; |
2396 | 3.48k | case Token::Sub: |
2397 | 3.48k | functionName = m_context.utilFunctions().overflowCheckedIntSubFunction(type); |
2398 | 3.48k | break; |
2399 | 0 | case Token::Mul: |
2400 | 0 | functionName = m_context.utilFunctions().overflowCheckedIntMulFunction(type); |
2401 | 0 | break; |
2402 | 0 | case Token::Div: |
2403 | 0 | functionName = m_context.utilFunctions().overflowCheckedIntDivFunction(type); |
2404 | 0 | break; |
2405 | 6.96k | case Token::Mod: |
2406 | 6.96k | functionName = m_context.utilFunctions().intModFunction(type); |
2407 | 6.96k | break; |
2408 | 0 | case Token::Exp: |
2409 | | // EXP is handled in a different function. |
2410 | 0 | default: |
2411 | 0 | solAssert(false, "Unknown arithmetic operator."); |
2412 | 31.2k | } |
2413 | | // TODO Maybe we want to force-inline this? |
2414 | 31.2k | m_context.callYulFunction(functionName, 2, 1); |
2415 | 31.2k | } |
2416 | 0 | else |
2417 | 0 | { |
2418 | 0 | bool const c_isSigned = type.isSigned(); |
2419 | |
|
2420 | 0 | switch (_operator) |
2421 | 0 | { |
2422 | 0 | case Token::Add: |
2423 | 0 | m_context << Instruction::ADD; |
2424 | 0 | break; |
2425 | 0 | case Token::Sub: |
2426 | 0 | m_context << Instruction::SUB; |
2427 | 0 | break; |
2428 | 0 | case Token::Mul: |
2429 | 0 | m_context << Instruction::MUL; |
2430 | 0 | break; |
2431 | 0 | case Token::Div: |
2432 | 0 | case Token::Mod: |
2433 | 0 | { |
2434 | | // Test for division by zero |
2435 | 0 | m_context << Instruction::DUP2 << Instruction::ISZERO; |
2436 | 0 | m_context.appendConditionalPanic(util::PanicCode::DivisionByZero); |
2437 | |
|
2438 | 0 | if (_operator == Token::Div) |
2439 | 0 | m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV); |
2440 | 0 | else |
2441 | 0 | m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD); |
2442 | 0 | break; |
2443 | 0 | } |
2444 | 0 | default: |
2445 | 0 | solAssert(false, "Unknown arithmetic operator."); |
2446 | 0 | } |
2447 | 0 | } |
2448 | 31.2k | } |
2449 | | |
2450 | | void ExpressionCompiler::appendBitOperatorCode(Token _operator) |
2451 | 0 | { |
2452 | 0 | switch (_operator) |
2453 | 0 | { |
2454 | 0 | case Token::BitOr: |
2455 | 0 | m_context << Instruction::OR; |
2456 | 0 | break; |
2457 | 0 | case Token::BitAnd: |
2458 | 0 | m_context << Instruction::AND; |
2459 | 0 | break; |
2460 | 0 | case Token::BitXor: |
2461 | 0 | m_context << Instruction::XOR; |
2462 | 0 | break; |
2463 | 0 | default: |
2464 | 0 | solAssert(false, "Unknown bit operator."); |
2465 | 0 | } |
2466 | 0 | } |
2467 | | |
2468 | | void ExpressionCompiler::appendShiftOperatorCode(Token _operator, Type const& _valueType, Type const& _shiftAmountType) |
2469 | 0 | { |
2470 | | // stack: shift_amount value_to_shift |
2471 | |
|
2472 | 0 | bool c_valueSigned = false; |
2473 | 0 | if (auto valueType = dynamic_cast<IntegerType const*>(&_valueType)) |
2474 | 0 | c_valueSigned = valueType->isSigned(); |
2475 | 0 | else |
2476 | 0 | solAssert(dynamic_cast<FixedBytesType const*>(&_valueType), "Only integer and fixed bytes type supported for shifts."); |
2477 | | |
2478 | | // The amount can be a RationalNumberType too. |
2479 | 0 | if (auto amountType = dynamic_cast<RationalNumberType const*>(&_shiftAmountType)) |
2480 | 0 | { |
2481 | | // This should be handled by the type checker. |
2482 | 0 | solAssert(amountType->integerType(), ""); |
2483 | 0 | solAssert(!amountType->integerType()->isSigned(), ""); |
2484 | 0 | } |
2485 | 0 | else if (auto amountType = dynamic_cast<IntegerType const*>(&_shiftAmountType)) |
2486 | 0 | solAssert(!amountType->isSigned(), ""); |
2487 | 0 | else |
2488 | 0 | solAssert(false, "Invalid shift amount type."); |
2489 | | |
2490 | 0 | m_context << Instruction::SWAP1; |
2491 | | // stack: value_to_shift shift_amount |
2492 | |
|
2493 | 0 | switch (_operator) |
2494 | 0 | { |
2495 | 0 | case Token::SHL: |
2496 | 0 | if (m_context.evmVersion().hasBitwiseShifting()) |
2497 | 0 | m_context << Instruction::SHL; |
2498 | 0 | else |
2499 | 0 | m_context << u256(2) << Instruction::EXP << Instruction::MUL; |
2500 | 0 | break; |
2501 | 0 | case Token::SAR: |
2502 | 0 | if (m_context.evmVersion().hasBitwiseShifting()) |
2503 | 0 | m_context << (c_valueSigned ? Instruction::SAR : Instruction::SHR); |
2504 | 0 | else |
2505 | 0 | { |
2506 | 0 | if (c_valueSigned) |
2507 | | // In the following assembly snippet, xor_mask will be zero, if value_to_shift is positive. |
2508 | | // Therefore xor'ing with xor_mask is the identity and the computation reduces to |
2509 | | // div(value_to_shift, exp(2, shift_amount)), which is correct, since for positive values |
2510 | | // arithmetic right shift is dividing by a power of two (which, as a bitwise operation, results |
2511 | | // in discarding bits on the right and filling with zeros from the left). |
2512 | | // For negative values arithmetic right shift, viewed as a bitwise operation, discards bits to the |
2513 | | // right and fills in ones from the left. This is achieved as follows: |
2514 | | // If value_to_shift is negative, then xor_mask will have all bits set, so xor'ing with xor_mask |
2515 | | // will flip all bits. First all bits in value_to_shift are flipped. As for the positive case, |
2516 | | // dividing by a power of two using integer arithmetic results in discarding bits to the right |
2517 | | // and filling with zeros from the left. Flipping all bits in the result again, turns all zeros |
2518 | | // on the left to ones and restores the non-discarded, shifted bits to their original value (they |
2519 | | // have now been flipped twice). In summary we now have discarded bits to the right and filled with |
2520 | | // ones from the left, i.e. we have performed an arithmetic right shift. |
2521 | 0 | m_context.appendInlineAssembly(R"({ |
2522 | 0 | let xor_mask := sub(0, slt(value_to_shift, 0)) |
2523 | 0 | value_to_shift := xor(div(xor(value_to_shift, xor_mask), exp(2, shift_amount)), xor_mask) |
2524 | 0 | })", {"value_to_shift", "shift_amount"}); |
2525 | 0 | else |
2526 | 0 | m_context.appendInlineAssembly(R"({ |
2527 | 0 | value_to_shift := div(value_to_shift, exp(2, shift_amount)) |
2528 | 0 | })", {"value_to_shift", "shift_amount"}); |
2529 | 0 | m_context << Instruction::POP; |
2530 | |
|
2531 | 0 | } |
2532 | 0 | break; |
2533 | 0 | case Token::SHR: |
2534 | 0 | default: |
2535 | 0 | solAssert(false, "Unknown shift operator."); |
2536 | 0 | } |
2537 | 0 | } |
2538 | | |
2539 | | void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type const& _exponentType) |
2540 | 0 | { |
2541 | 0 | solAssert(_valueType.category() == Type::Category::Integer, ""); |
2542 | 0 | solAssert(!dynamic_cast<IntegerType const&>(_exponentType).isSigned(), ""); |
2543 | | |
2544 | | |
2545 | 0 | if (m_context.arithmetic() == Arithmetic::Checked) |
2546 | 0 | m_context.callYulFunction(m_context.utilFunctions().overflowCheckedIntExpFunction( |
2547 | 0 | dynamic_cast<IntegerType const&>(_valueType), |
2548 | 0 | dynamic_cast<IntegerType const&>(_exponentType) |
2549 | 0 | ), 2, 1); |
2550 | 0 | else |
2551 | 0 | m_context << Instruction::EXP; |
2552 | 0 | } |
2553 | | |
2554 | | void ExpressionCompiler::appendExternalFunctionCall( |
2555 | | FunctionType const& _functionType, |
2556 | | vector<ASTPointer<Expression const>> const& _arguments, |
2557 | | bool _tryCall |
2558 | | ) |
2559 | 28.9k | { |
2560 | 28.9k | solAssert( |
2561 | 28.9k | _functionType.takesArbitraryParameters() || |
2562 | 28.9k | _arguments.size() == _functionType.parameterTypes().size(), "" |
2563 | 28.9k | ); |
2564 | | |
2565 | | // Assumed stack content here: |
2566 | | // <stack top> |
2567 | | // value [if _functionType.valueSet()] |
2568 | | // gas [if _functionType.gasSet()] |
2569 | | // self object [if bound - moved to top right away] |
2570 | | // function identifier [unless bare] |
2571 | | // contract address |
2572 | | |
2573 | 28.9k | unsigned selfSize = _functionType.bound() ? _functionType.selfType()->sizeOnStack() : 0; |
2574 | 28.9k | unsigned gasValueSize = (_functionType.gasSet() ? 1u : 0u) + (_functionType.valueSet() ? 1u : 0u); |
2575 | 28.9k | unsigned contractStackPos = m_context.currentToBaseStackOffset(1 + gasValueSize + selfSize + (_functionType.isBareCall() ? 0 : 1)); |
2576 | 28.9k | unsigned gasStackPos = m_context.currentToBaseStackOffset(gasValueSize); |
2577 | 28.9k | unsigned valueStackPos = m_context.currentToBaseStackOffset(1); |
2578 | | |
2579 | | // move self object to top |
2580 | 28.9k | if (_functionType.bound()) |
2581 | 0 | utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); |
2582 | | |
2583 | 28.9k | auto funKind = _functionType.kind(); |
2584 | | |
2585 | 28.9k | solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); |
2586 | | |
2587 | 28.9k | solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); |
2588 | | |
2589 | 28.9k | bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; |
2590 | 28.9k | bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; |
2591 | 28.9k | bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall()); |
2592 | | |
2593 | 28.9k | if (_tryCall) |
2594 | 0 | { |
2595 | 0 | solAssert(!returnSuccessConditionAndReturndata, ""); |
2596 | 0 | solAssert(!_functionType.isBareCall(), ""); |
2597 | 0 | } |
2598 | | |
2599 | 28.9k | ReturnInfo const returnInfo{m_context.evmVersion(), _functionType}; |
2600 | 28.9k | bool const haveReturndatacopy = m_context.evmVersion().supportsReturndata(); |
2601 | 28.9k | unsigned const retSize = returnInfo.estimatedReturnSize; |
2602 | 28.9k | bool const dynamicReturnSize = returnInfo.dynamicReturnSize; |
2603 | 28.9k | TypePointers const& returnTypes = returnInfo.returnTypes; |
2604 | | |
2605 | | // Evaluate arguments. |
2606 | 28.9k | TypePointers argumentTypes; |
2607 | 28.9k | TypePointers parameterTypes = _functionType.parameterTypes(); |
2608 | 28.9k | if (_functionType.bound()) |
2609 | 0 | { |
2610 | 0 | argumentTypes.push_back(_functionType.selfType()); |
2611 | 0 | parameterTypes.insert(parameterTypes.begin(), _functionType.selfType()); |
2612 | 0 | } |
2613 | 66.4k | for (size_t i = 0; i < _arguments.size(); ++i) |
2614 | 37.5k | { |
2615 | 37.5k | _arguments[i]->accept(*this); |
2616 | 37.5k | argumentTypes.push_back(_arguments[i]->annotation().type); |
2617 | 37.5k | } |
2618 | | |
2619 | 28.9k | if (funKind == FunctionType::Kind::ECRecover) |
2620 | 0 | { |
2621 | | // Clears 32 bytes of currently free memory and advances free memory pointer. |
2622 | | // Output area will be "start of input area" - 32. |
2623 | | // The reason is that a failing ECRecover cannot be detected, it will just return |
2624 | | // zero bytes (which we cannot detect). |
2625 | 0 | solAssert(0 < retSize && retSize <= 32, ""); |
2626 | 0 | utils().fetchFreeMemoryPointer(); |
2627 | 0 | m_context << u256(0) << Instruction::DUP2 << Instruction::MSTORE; |
2628 | 0 | m_context << u256(32) << Instruction::ADD; |
2629 | 0 | utils().storeFreeMemoryPointer(); |
2630 | 0 | } |
2631 | | |
2632 | 28.9k | if (!m_context.evmVersion().canOverchargeGasForCall()) |
2633 | 0 | { |
2634 | | // Touch the end of the output area so that we do not pay for memory resize during the call |
2635 | | // (which we would have to subtract from the gas left) |
2636 | | // We could also just use MLOAD; POP right before the gas calculation, but the optimizer |
2637 | | // would remove that, so we use MSTORE here. |
2638 | 0 | if (!_functionType.gasSet() && retSize > 0) |
2639 | 0 | { |
2640 | 0 | m_context << u256(0); |
2641 | 0 | utils().fetchFreeMemoryPointer(); |
2642 | | // This touches too much, but that way we save some rounding arithmetic |
2643 | 0 | m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE; |
2644 | 0 | } |
2645 | 0 | } |
2646 | | |
2647 | | // Copy function identifier to memory. |
2648 | 28.9k | utils().fetchFreeMemoryPointer(); |
2649 | 28.9k | if (!_functionType.isBareCall()) |
2650 | 21.9k | { |
2651 | 21.9k | m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes)); |
2652 | 21.9k | utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false); |
2653 | 21.9k | } |
2654 | | |
2655 | | // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. |
2656 | | // Move arguments to memory, will not update the free memory pointer (but will update the memory |
2657 | | // pointer on the stack). |
2658 | 28.9k | bool encodeInPlace = _functionType.takesArbitraryParameters() || _functionType.isBareCall(); |
2659 | 28.9k | if (_functionType.kind() == FunctionType::Kind::ECRecover) |
2660 | | // This would be the only combination of padding and in-place encoding, |
2661 | | // but all parameters of ecrecover are value types anyway. |
2662 | 0 | encodeInPlace = false; |
2663 | 28.9k | bool encodeForLibraryCall = funKind == FunctionType::Kind::DelegateCall; |
2664 | 28.9k | utils().encodeToMemory( |
2665 | 28.9k | argumentTypes, |
2666 | 28.9k | parameterTypes, |
2667 | 28.9k | _functionType.padArguments(), |
2668 | 28.9k | encodeInPlace, |
2669 | 28.9k | encodeForLibraryCall |
2670 | 28.9k | ); |
2671 | | |
2672 | | // Stack now: |
2673 | | // <stack top> |
2674 | | // input_memory_end |
2675 | | // value [if _functionType.valueSet()] |
2676 | | // gas [if _functionType.gasSet()] |
2677 | | // function identifier [unless bare] |
2678 | | // contract address |
2679 | | |
2680 | | // Output data will replace input data, unless we have ECRecover (then, output |
2681 | | // area will be 32 bytes just before input area). |
2682 | | // put on stack: <size of output> <memory pos of output> <size of input> <memory pos of input> |
2683 | 28.9k | m_context << u256(retSize); |
2684 | 28.9k | utils().fetchFreeMemoryPointer(); // This is the start of input |
2685 | 28.9k | if (funKind == FunctionType::Kind::ECRecover) |
2686 | 0 | { |
2687 | | // In this case, output is 32 bytes before input and has already been cleared. |
2688 | 0 | m_context << u256(32) << Instruction::DUP2 << Instruction::SUB << Instruction::SWAP1; |
2689 | | // Here: <input end> <output size> <outpos> <input pos> |
2690 | 0 | m_context << Instruction::DUP1 << Instruction::DUP5 << Instruction::SUB; |
2691 | 0 | m_context << Instruction::SWAP1; |
2692 | 0 | } |
2693 | 28.9k | else |
2694 | 28.9k | { |
2695 | 28.9k | m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::SUB; |
2696 | 28.9k | m_context << Instruction::DUP2; |
2697 | 28.9k | } |
2698 | | |
2699 | | // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) |
2700 | | // [value,] addr, gas (stack top) |
2701 | 28.9k | if (isDelegateCall) |
2702 | 28.9k | solAssert(!_functionType.valueSet(), "Value set for delegatecall"); |
2703 | 28.9k | else if (useStaticCall) |
2704 | 28.9k | solAssert(!_functionType.valueSet(), "Value set for staticcall"); |
2705 | 8.02k | else if (_functionType.valueSet()) |
2706 | 0 | m_context << dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); |
2707 | 8.02k | else |
2708 | 8.02k | m_context << u256(0); |
2709 | 28.9k | m_context << dupInstruction(m_context.baseToCurrentStackOffset(contractStackPos)); |
2710 | | |
2711 | 28.9k | bool existenceChecked = false; |
2712 | | // Check the target contract exists (has code) for non-low-level calls. |
2713 | 28.9k | if (funKind == FunctionType::Kind::External || funKind == FunctionType::Kind::DelegateCall) |
2714 | 21.9k | { |
2715 | 21.9k | size_t encodedHeadSize = 0; |
2716 | 21.9k | for (auto const& t: returnTypes) |
2717 | 22.3k | encodedHeadSize += t->decodingType()->calldataHeadSize(); |
2718 | | // We do not need to check extcodesize if we expect return data, since if there is no |
2719 | | // code, the call will return empty data and the ABI decoder will revert. |
2720 | 21.9k | if ( |
2721 | 21.9k | encodedHeadSize == 0 || |
2722 | 21.9k | !haveReturndatacopy || |
2723 | 21.9k | m_context.revertStrings() >= RevertStrings::Debug |
2724 | 21.9k | ) |
2725 | 0 | { |
2726 | 0 | m_context << Instruction::DUP1 << Instruction::EXTCODESIZE << Instruction::ISZERO; |
2727 | 0 | m_context.appendConditionalRevert(false, "Target contract does not contain code"); |
2728 | 0 | existenceChecked = true; |
2729 | 0 | } |
2730 | 21.9k | } |
2731 | | |
2732 | 28.9k | if (_functionType.gasSet()) |
2733 | 0 | m_context << dupInstruction(m_context.baseToCurrentStackOffset(gasStackPos)); |
2734 | 28.9k | else if (m_context.evmVersion().canOverchargeGasForCall()) |
2735 | | // Send all gas (requires tangerine whistle EVM) |
2736 | 28.9k | m_context << Instruction::GAS; |
2737 | 0 | else |
2738 | 0 | { |
2739 | | // send all gas except the amount needed to execute "SUB" and "CALL" |
2740 | | // @todo this retains too much gas for now, needs to be fine-tuned. |
2741 | 0 | u256 gasNeededByCaller = evmasm::GasCosts::callGas(m_context.evmVersion()) + 10; |
2742 | 0 | if (_functionType.valueSet()) |
2743 | 0 | gasNeededByCaller += evmasm::GasCosts::callValueTransferGas; |
2744 | 0 | if (!existenceChecked) |
2745 | 0 | gasNeededByCaller += evmasm::GasCosts::callNewAccountGas; // we never know |
2746 | 0 | m_context << gasNeededByCaller << Instruction::GAS << Instruction::SUB; |
2747 | 0 | } |
2748 | | // Order is important here, STATICCALL might overlap with DELEGATECALL. |
2749 | 28.9k | if (isDelegateCall) |
2750 | 0 | m_context << Instruction::DELEGATECALL; |
2751 | 28.9k | else if (useStaticCall) |
2752 | 20.8k | m_context << Instruction::STATICCALL; |
2753 | 8.02k | else |
2754 | 8.02k | m_context << Instruction::CALL; |
2755 | | |
2756 | 28.9k | unsigned remainsSize = |
2757 | 28.9k | 2u + // contract address, input_memory_end |
2758 | 28.9k | (_functionType.valueSet() ? 1 : 0) + |
2759 | 28.9k | (_functionType.gasSet() ? 1 : 0) + |
2760 | 28.9k | (!_functionType.isBareCall() ? 1 : 0); |
2761 | | |
2762 | 28.9k | evmasm::AssemblyItem endTag = m_context.newTag(); |
2763 | | |
2764 | 28.9k | if (!returnSuccessConditionAndReturndata && !_tryCall) |
2765 | 21.9k | { |
2766 | | // Propagate error condition (if CALL pushes 0 on stack). |
2767 | 21.9k | m_context << Instruction::ISZERO; |
2768 | 21.9k | m_context.appendConditionalRevert(true); |
2769 | 21.9k | } |
2770 | 6.96k | else |
2771 | 6.96k | m_context << swapInstruction(remainsSize); |
2772 | 28.9k | utils().popStackSlots(remainsSize); |
2773 | | |
2774 | | // Only success flag is remaining on stack. |
2775 | | |
2776 | 28.9k | if (_tryCall) |
2777 | 0 | { |
2778 | 0 | m_context << Instruction::DUP1 << Instruction::ISZERO; |
2779 | 0 | m_context.appendConditionalJumpTo(endTag); |
2780 | 0 | m_context << Instruction::POP; |
2781 | 0 | } |
2782 | | |
2783 | 28.9k | if (returnSuccessConditionAndReturndata) |
2784 | 6.96k | { |
2785 | | // success condition is already there |
2786 | | // The return parameter types can be empty, when this function is used as |
2787 | | // an internal helper function e.g. for ``send`` and ``transfer``. In that |
2788 | | // case we're only interested in the success condition, not the return data. |
2789 | 6.96k | if (!_functionType.returnParameterTypes().empty()) |
2790 | 6.96k | utils().returnDataToArray(); |
2791 | 6.96k | } |
2792 | 21.9k | else if (funKind == FunctionType::Kind::RIPEMD160) |
2793 | 0 | { |
2794 | | // fix: built-in contract returns right-aligned data |
2795 | 0 | utils().fetchFreeMemoryPointer(); |
2796 | 0 | utils().loadFromMemoryDynamic(IntegerType(160), false, true, false); |
2797 | 0 | utils().convertType(IntegerType(160), FixedBytesType(20)); |
2798 | 0 | } |
2799 | 21.9k | else if (funKind == FunctionType::Kind::ECRecover) |
2800 | 0 | { |
2801 | | // Output is 32 bytes before input / free mem pointer. |
2802 | | // Failing ecrecover cannot be detected, so we clear output before the call. |
2803 | 0 | m_context << u256(32); |
2804 | 0 | utils().fetchFreeMemoryPointer(); |
2805 | 0 | m_context << Instruction::SUB << Instruction::MLOAD; |
2806 | 0 | } |
2807 | 21.9k | else if (!returnTypes.empty()) |
2808 | 21.9k | { |
2809 | 21.9k | utils().fetchFreeMemoryPointer(); |
2810 | | // Stack: return_data_start |
2811 | | |
2812 | | // The old decoder did not allocate any memory (i.e. did not touch the free |
2813 | | // memory pointer), but kept references to the return data for |
2814 | | // (statically-sized) arrays |
2815 | 21.9k | bool needToUpdateFreeMemoryPtr = false; |
2816 | 21.9k | if (dynamicReturnSize || m_context.useABICoderV2()) |
2817 | 21.9k | needToUpdateFreeMemoryPtr = true; |
2818 | 0 | else |
2819 | 0 | for (auto const& retType: returnTypes) |
2820 | 0 | if (dynamic_cast<ReferenceType const*>(retType)) |
2821 | 0 | needToUpdateFreeMemoryPtr = true; |
2822 | | |
2823 | | // Stack: return_data_start |
2824 | 21.9k | if (dynamicReturnSize) |
2825 | 802 | { |
2826 | 802 | solAssert(haveReturndatacopy, ""); |
2827 | 802 | m_context.appendInlineAssembly("{ returndatacopy(return_data_start, 0, returndatasize()) }", {"return_data_start"}); |
2828 | 802 | } |
2829 | 21.1k | else |
2830 | 21.9k | solAssert(retSize > 0, ""); |
2831 | | // Always use the actual return length, and not our calculated expected length, if returndatacopy is supported. |
2832 | | // This ensures it can catch badly formatted input from external calls. |
2833 | 21.9k | m_context << (haveReturndatacopy ? evmasm::AssemblyItem(Instruction::RETURNDATASIZE) : u256(retSize)); |
2834 | | // Stack: return_data_start return_data_size |
2835 | 21.9k | if (needToUpdateFreeMemoryPtr) |
2836 | 21.9k | m_context.appendInlineAssembly(R"({ |
2837 | 21.9k | // round size to the next multiple of 32 |
2838 | 21.9k | let newMem := add(start, and(add(size, 0x1f), not(0x1f))) |
2839 | 21.9k | mstore(0x40, newMem) |
2840 | 21.9k | })", {"start", "size"}); |
2841 | | |
2842 | 21.9k | utils().abiDecode(returnTypes, true); |
2843 | 21.9k | } |
2844 | | |
2845 | 28.9k | if (_tryCall) |
2846 | 0 | { |
2847 | | // Success branch will reach this, failure branch will directly jump to endTag. |
2848 | 0 | m_context << u256(1); |
2849 | 0 | m_context << endTag; |
2850 | 0 | } |
2851 | 28.9k | } |
2852 | | |
2853 | | void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType, Expression const& _expression) |
2854 | 0 | { |
2855 | 0 | solUnimplementedAssert(_expectedType.isValueType(), "Not implemented for non-value types."); |
2856 | 0 | acceptAndConvert(_expression, _expectedType, true); |
2857 | 0 | utils().storeInMemoryDynamic(_expectedType); |
2858 | 0 | } |
2859 | | |
2860 | | void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Expression const& _expression) |
2861 | 1.35M | { |
2862 | 1.35M | if (_variable.isConstant()) |
2863 | 0 | acceptAndConvert(*_variable.value(), *_variable.annotation().type); |
2864 | 1.35M | else if (_variable.immutable()) |
2865 | 0 | setLValue<ImmutableItem>(_expression, _variable); |
2866 | 1.35M | else |
2867 | 1.35M | setLValueFromDeclaration(_variable, _expression); |
2868 | 1.35M | } |
2869 | | |
2870 | | void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression) |
2871 | 1.35M | { |
2872 | 1.35M | if (m_context.isLocalVariable(&_declaration)) |
2873 | 1.13M | setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration)); |
2874 | 219k | else if (m_context.isStateVariable(&_declaration)) |
2875 | 219k | setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration)); |
2876 | 0 | else |
2877 | 0 | BOOST_THROW_EXCEPTION(InternalCompilerError() |
2878 | 1.35M | << errinfo_sourceLocation(_expression.location()) |
2879 | 1.35M | << util::errinfo_comment("Identifier type not supported or identifier not found.")); |
2880 | 1.35M | } |
2881 | | |
2882 | | void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression) |
2883 | 1.01M | { |
2884 | 1.01M | setLValue<StorageItem>(_expression, *_expression.annotation().type); |
2885 | 1.01M | } |
2886 | | |
2887 | | bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic) |
2888 | 659k | { |
2889 | 659k | if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op)) |
2890 | 628k | return true; |
2891 | 31.2k | else if ( |
2892 | 31.2k | _arithmetic == Arithmetic::Wrapping && |
2893 | 31.2k | _type == Type::Category::Integer && |
2894 | 31.2k | (_op == Token::Div || _op == Token::Mod || _op == Token::Exp) |
2895 | 31.2k | ) |
2896 | | // We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0 |
2897 | | // It would suffice to clean the exponent, though. |
2898 | 0 | return true; |
2899 | 31.2k | else |
2900 | 31.2k | return false; |
2901 | 659k | } |
2902 | | |
2903 | | void ExpressionCompiler::acceptAndConvert(Expression const& _expression, Type const& _type, bool _cleanupNeeded) |
2904 | 5.04M | { |
2905 | 5.04M | _expression.accept(*this); |
2906 | 5.04M | utils().convertType(*_expression.annotation().type, _type, _cleanupNeeded); |
2907 | 5.04M | } |
2908 | | |
2909 | | CompilerUtils ExpressionCompiler::utils() |
2910 | 5.91M | { |
2911 | 5.91M | return CompilerUtils(m_context); |
2912 | 5.91M | } |