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