/src/solidity/libsolidity/codegen/LValue.cpp
Line | Count | Source |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | /** |
19 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2015 |
21 | | * LValues for use in the expression compiler. |
22 | | */ |
23 | | |
24 | | #include <libsolidity/codegen/LValue.h> |
25 | | |
26 | | #include <libsolidity/ast/AST.h> |
27 | | #include <libsolidity/ast/Types.h> |
28 | | #include <libsolidity/codegen/CompilerUtils.h> |
29 | | #include <libevmasm/Instruction.h> |
30 | | |
31 | | #include <libsolutil/StackTooDeepString.h> |
32 | | |
33 | | using namespace solidity; |
34 | | using namespace solidity::evmasm; |
35 | | using namespace solidity::frontend; |
36 | | using namespace solidity::langutil; |
37 | | using namespace solidity::util; |
38 | | |
39 | | StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): |
40 | 138k | LValue(_compilerContext, _declaration.annotation().type), |
41 | 138k | m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)), |
42 | 138k | m_size(m_dataType->sizeOnStack()) |
43 | 138k | { |
44 | 138k | } |
45 | | |
46 | | void StackVariable::retrieveValue(SourceLocation const& _location, bool) const |
47 | 138k | { |
48 | 138k | unsigned stackPos = m_context.baseToCurrentStackOffset(m_baseStackOffset); |
49 | 138k | if (stackPos + 1 > m_context.reachableStackDepth()) //@todo correct this by fetching earlier or moving to memory |
50 | 138k | BOOST_THROW_EXCEPTION( |
51 | 138k | StackTooDeepError() << |
52 | 138k | errinfo_sourceLocation(_location) << |
53 | 138k | util::errinfo_comment(util::stackTooDeepString) |
54 | 138k | ); |
55 | 138k | solAssert(stackPos + 1 >= m_size, "Size and stack pos mismatch."); |
56 | 282k | for (unsigned i = 0; i < m_size; ++i) |
57 | 144k | m_context << dupInstruction(stackPos + 1); |
58 | 138k | } |
59 | | |
60 | | void StackVariable::storeValue(Type const&, SourceLocation const& _location, bool _move) const |
61 | 8.37k | { |
62 | 8.37k | unsigned stackDiff = m_context.baseToCurrentStackOffset(m_baseStackOffset) - m_size + 1; |
63 | 8.37k | if (stackDiff > m_context.reachableStackDepth()) |
64 | 8.37k | BOOST_THROW_EXCEPTION( |
65 | 8.37k | StackTooDeepError() << |
66 | 8.37k | errinfo_sourceLocation(_location) << |
67 | 8.37k | util::errinfo_comment(util::stackTooDeepString) |
68 | 8.37k | ); |
69 | 8.37k | else if (stackDiff > 0) |
70 | 16.8k | for (unsigned i = 0; i < m_size; ++i) |
71 | 8.43k | m_context << swapInstruction(stackDiff) << Instruction::POP; |
72 | 8.37k | if (!_move) |
73 | 3.58k | retrieveValue(_location); |
74 | 8.37k | } |
75 | | |
76 | | void StackVariable::setToZero(SourceLocation const& _location, bool) const |
77 | 35 | { |
78 | 35 | CompilerUtils(m_context).pushZeroValue(*m_dataType); |
79 | 35 | storeValue(*m_dataType, _location, true); |
80 | 35 | } |
81 | | |
82 | | MemoryItem::MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded): |
83 | 357k | LValue(_compilerContext, &_type), |
84 | 357k | m_padded(_padded) |
85 | 357k | { |
86 | 357k | } |
87 | | |
88 | | void MemoryItem::retrieveValue(SourceLocation const&, bool _remove) const |
89 | 338k | { |
90 | 338k | if (m_dataType->isValueType()) |
91 | 45.8k | { |
92 | 45.8k | if (!_remove) |
93 | 13 | m_context << Instruction::DUP1; |
94 | 45.8k | CompilerUtils(m_context).loadFromMemoryDynamic(*m_dataType, false, m_padded, false); |
95 | 45.8k | } |
96 | 292k | else |
97 | 292k | m_context << Instruction::MLOAD; |
98 | 338k | } |
99 | | |
100 | | void MemoryItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const |
101 | 19.5k | { |
102 | 19.5k | CompilerUtils utils(m_context); |
103 | 19.5k | if (m_dataType->isValueType()) |
104 | 15.1k | { |
105 | 15.1k | solAssert(_sourceType.isValueType(), ""); |
106 | 15.1k | utils.moveIntoStack(_sourceType.sizeOnStack()); |
107 | 15.1k | utils.convertType(_sourceType, *m_dataType, true); |
108 | 15.1k | if (!_move) |
109 | 15.1k | { |
110 | 15.1k | utils.moveToStackTop(m_dataType->sizeOnStack()); |
111 | 15.1k | utils.copyToStackTop(1 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack()); |
112 | 15.1k | } |
113 | 15.1k | if (!m_padded) |
114 | 1.54k | { |
115 | 1.54k | solAssert(m_dataType->calldataEncodedSize(false) == 1, "Invalid non-padded type."); |
116 | 1.54k | solAssert(m_dataType->category() != Type::Category::UserDefinedValueType, ""); |
117 | 1.54k | if (m_dataType->category() == Type::Category::FixedBytes) |
118 | 1.54k | m_context << u256(0) << Instruction::BYTE; |
119 | 1.54k | m_context << Instruction::SWAP1 << Instruction::MSTORE8; |
120 | 1.54k | } |
121 | 13.5k | else |
122 | 13.5k | { |
123 | 13.5k | utils.storeInMemoryDynamic(*m_dataType, m_padded); |
124 | 13.5k | m_context << Instruction::POP; |
125 | 13.5k | } |
126 | 15.1k | } |
127 | 4.43k | else |
128 | 4.43k | { |
129 | 4.43k | solUnimplementedAssert(_sourceType == *m_dataType, "Conversion not implemented for assignment to memory."); |
130 | | |
131 | 4.43k | solAssert(m_dataType->sizeOnStack() == 1, ""); |
132 | 4.43k | if (!_move) |
133 | 4.43k | m_context << Instruction::DUP2 << Instruction::SWAP1; |
134 | | // stack: [value] value lvalue |
135 | | // only store the reference |
136 | 4.43k | m_context << Instruction::MSTORE; |
137 | 4.43k | } |
138 | 19.5k | } |
139 | | |
140 | | void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const |
141 | 30 | { |
142 | 30 | CompilerUtils utils(m_context); |
143 | 30 | solAssert(_removeReference, ""); |
144 | 30 | utils.pushZeroValue(*m_dataType); |
145 | | // stack pre: reference 0 |
146 | 30 | if (!m_padded) |
147 | 0 | { |
148 | 0 | solUnimplementedAssert( |
149 | 0 | m_dataType->calldataEncodedSize(/* _padded */ false) == 1, |
150 | 0 | "Zeroing of multi-byte non-padded types is not implemented." |
151 | 0 | ); |
152 | 0 | solAssert(m_dataType->category() != Type::Category::UserDefinedValueType); |
153 | 0 | m_context << Instruction::SWAP1 << Instruction::MSTORE8; |
154 | 0 | } |
155 | 30 | else |
156 | 30 | { |
157 | 30 | utils.storeInMemoryDynamic(*m_dataType, m_padded); |
158 | 30 | m_context << Instruction::POP; |
159 | 30 | } |
160 | 30 | } |
161 | | |
162 | | |
163 | | ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable): |
164 | 648 | LValue(_compilerContext, _variable.annotation().type), m_variable(_variable) |
165 | 648 | { |
166 | 648 | solAssert(_variable.immutable(), ""); |
167 | 648 | } |
168 | | |
169 | | void ImmutableItem::retrieveValue(SourceLocation const&, bool) const |
170 | 324 | { |
171 | 324 | solUnimplementedAssert(m_dataType->isValueType()); |
172 | | |
173 | 324 | if (m_context.runtimeContext()) |
174 | 176 | CompilerUtils(m_context).loadFromMemory( |
175 | 176 | static_cast<unsigned>(m_context.immutableMemoryOffset(m_variable)), |
176 | 176 | *m_dataType, |
177 | 176 | false, |
178 | 176 | true |
179 | 176 | ); |
180 | 148 | else |
181 | 148 | for (auto&& slotName: m_context.immutableVariableSlotNames(m_variable)) |
182 | 148 | m_context.appendImmutable(slotName); |
183 | 324 | } |
184 | | |
185 | | void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const |
186 | 419 | { |
187 | 419 | CompilerUtils utils(m_context); |
188 | 419 | solUnimplementedAssert(m_dataType->isValueType()); |
189 | 419 | solAssert(_sourceType.isValueType(), ""); |
190 | | |
191 | 419 | utils.convertType(_sourceType, *m_dataType, true); |
192 | 419 | m_context << m_context.immutableMemoryOffset(m_variable); |
193 | 419 | if (_move) |
194 | 140 | utils.moveIntoStack(m_dataType->sizeOnStack()); |
195 | 279 | else |
196 | 279 | utils.copyToStackTop(m_dataType->sizeOnStack() + 1, m_dataType->sizeOnStack()); |
197 | 419 | utils.storeInMemoryDynamic(*m_dataType); |
198 | 419 | m_context << Instruction::POP; |
199 | 419 | } |
200 | | |
201 | | void ImmutableItem::setToZero(SourceLocation const&, bool _removeReference) const |
202 | 5 | { |
203 | 5 | CompilerUtils utils(m_context); |
204 | 5 | solUnimplementedAssert(m_dataType->isValueType()); |
205 | 5 | solAssert(_removeReference); |
206 | | |
207 | 5 | m_context << m_context.immutableMemoryOffset(m_variable); |
208 | 5 | utils.pushZeroValue(*m_dataType); |
209 | 5 | utils.storeInMemoryDynamic(*m_dataType); |
210 | 5 | m_context << Instruction::POP; |
211 | 5 | } |
212 | | |
213 | | template<bool IsTransient> |
214 | | GenericStorageItem<IsTransient>::GenericStorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): |
215 | 40.8k | GenericStorageItem<IsTransient>(_compilerContext, *_declaration.annotation().type) |
216 | 40.8k | { |
217 | 40.8k | solAssert(!_declaration.immutable(), ""); |
218 | 40.8k | auto const& location = m_context.storageLocationOfVariable(_declaration); |
219 | 40.8k | m_context << location.first << u256(location.second); |
220 | 40.8k | } solidity::frontend::GenericStorageItem<false>::GenericStorageItem(solidity::frontend::CompilerContext&, solidity::frontend::VariableDeclaration const&) Line | Count | Source | 215 | 40.8k | GenericStorageItem<IsTransient>(_compilerContext, *_declaration.annotation().type) | 216 | 40.8k | { | 217 | | solAssert(!_declaration.immutable(), ""); | 218 | 40.8k | auto const& location = m_context.storageLocationOfVariable(_declaration); | 219 | 40.8k | m_context << location.first << u256(location.second); | 220 | 40.8k | } |
Unexecuted instantiation: solidity::frontend::GenericStorageItem<true>::GenericStorageItem(solidity::frontend::CompilerContext&, solidity::frontend::VariableDeclaration const&) |
221 | | |
222 | | template<bool IsTransient> |
223 | | GenericStorageItem<IsTransient>::GenericStorageItem(CompilerContext& _compilerContext, Type const& _type): |
224 | 151k | LValue(_compilerContext, &_type) |
225 | 151k | { |
226 | 151k | if (m_dataType->isValueType()) |
227 | 27.0k | { |
228 | 27.0k | if (m_dataType->category() != Type::Category::Function) |
229 | 27.0k | solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), ""); |
230 | 27.0k | solAssert(m_dataType->storageSize() == 1, "Invalid storage size."); |
231 | 27.0k | } |
232 | 151k | } solidity::frontend::GenericStorageItem<false>::GenericStorageItem(solidity::frontend::CompilerContext&, solidity::frontend::Type const&) Line | Count | Source | 224 | 151k | LValue(_compilerContext, &_type) | 225 | 151k | { | 226 | 151k | if (m_dataType->isValueType()) | 227 | 27.0k | { | 228 | 27.0k | if (m_dataType->category() != Type::Category::Function) | 229 | 27.0k | solAssert(m_dataType->storageSize() == m_dataType->sizeOnStack(), ""); | 230 | | solAssert(m_dataType->storageSize() == 1, "Invalid storage size."); | 231 | 27.0k | } | 232 | 151k | } |
Unexecuted instantiation: solidity::frontend::GenericStorageItem<true>::GenericStorageItem(solidity::frontend::CompilerContext&, solidity::frontend::Type const&) |
233 | | |
234 | | template<bool IsTransient> |
235 | | void GenericStorageItem<IsTransient>::retrieveValue(langutil::SourceLocation const&, bool _remove) const |
236 | 120k | { |
237 | | // stack: storage_key storage_offset |
238 | 120k | if (!m_dataType->isValueType()) |
239 | 113k | { |
240 | 113k | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); |
241 | 113k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size."); |
242 | 113k | if (_remove) |
243 | 113k | m_context << Instruction::POP; // remove byte offset |
244 | 0 | else |
245 | 0 | m_context << Instruction::DUP2; |
246 | 113k | return; |
247 | 113k | } |
248 | 6.88k | if (!_remove) |
249 | 278 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); |
250 | 6.88k | if (m_dataType->storageBytes() == 32) |
251 | 4.44k | m_context << Instruction::POP << s_loadInstruction; |
252 | 2.44k | else |
253 | 2.44k | { |
254 | 2.44k | Type const* type = m_dataType; |
255 | 2.44k | if (type->category() == Type::Category::UserDefinedValueType) |
256 | 2 | type = type->encodingType(); |
257 | 2.44k | bool cleaned = false; |
258 | | // Load the slot value at storage_key and shift it to the right using storage_offset. |
259 | | // Note that storage_offset is in bytes, so we need to shift by 8 * storage_offset bits. |
260 | 2.44k | m_context |
261 | 2.44k | << Instruction::SWAP1 << s_loadInstruction << Instruction::SWAP1 |
262 | 2.44k | << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; |
263 | 2.44k | if (type->category() == Type::Category::FixedPoint) |
264 | | // implementation should be very similar to the integer case. |
265 | 2.44k | solUnimplemented("Not yet implemented - FixedPointType."); |
266 | 2.44k | else if (auto const* fun = dynamic_cast<FunctionType const*>(type)) |
267 | 129 | { |
268 | 129 | if (fun->kind() == FunctionType::Kind::External) |
269 | 40 | { |
270 | 40 | CompilerUtils(m_context).splitExternalFunctionType(false); |
271 | 40 | cleaned = true; |
272 | 40 | } |
273 | 89 | else if (fun->kind() == FunctionType::Kind::Internal) |
274 | 89 | { |
275 | | // Mask out the upper bits not belonging to the value of the function pointer. |
276 | 89 | m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; |
277 | 89 | cleaned = true; |
278 | 89 | m_context << Instruction::DUP1 << Instruction::ISZERO; |
279 | 89 | CompilerUtils(m_context).pushZeroValue(*fun); |
280 | 89 | m_context << Instruction::MUL << Instruction::OR; |
281 | 89 | } |
282 | 129 | } |
283 | 2.31k | else if (type->leftAligned()) |
284 | 262 | { |
285 | 262 | CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * type->storageBytes()); |
286 | 262 | cleaned = true; |
287 | 262 | } |
288 | 2.05k | else if ( |
289 | 2.05k | type->category() == Type::Category::Integer && |
290 | 933 | dynamic_cast<IntegerType const&>(*type).isSigned() |
291 | 2.05k | ) |
292 | 251 | { |
293 | 251 | m_context << u256(type->storageBytes() - 1) << Instruction::SIGNEXTEND; |
294 | 251 | cleaned = true; |
295 | 251 | } |
296 | | |
297 | 2.44k | if (!cleaned) |
298 | 1.80k | { |
299 | 1.80k | solAssert(type->sizeOnStack() == 1, ""); |
300 | 1.80k | m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; |
301 | 1.80k | } |
302 | 2.44k | } |
303 | 6.88k | } solidity::frontend::GenericStorageItem<false>::retrieveValue(solidity::langutil::SourceLocation const&, bool) const Line | Count | Source | 236 | 120k | { | 237 | | // stack: storage_key storage_offset | 238 | 120k | if (!m_dataType->isValueType()) | 239 | 113k | { | 240 | 113k | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); | 241 | 113k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid storage ref size."); | 242 | 113k | if (_remove) | 243 | 113k | m_context << Instruction::POP; // remove byte offset | 244 | 0 | else | 245 | 0 | m_context << Instruction::DUP2; | 246 | 113k | return; | 247 | 113k | } | 248 | 6.88k | if (!_remove) | 249 | 278 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); | 250 | 6.88k | if (m_dataType->storageBytes() == 32) | 251 | 4.44k | m_context << Instruction::POP << s_loadInstruction; | 252 | 2.44k | else | 253 | 2.44k | { | 254 | 2.44k | Type const* type = m_dataType; | 255 | 2.44k | if (type->category() == Type::Category::UserDefinedValueType) | 256 | 2 | type = type->encodingType(); | 257 | 2.44k | bool cleaned = false; | 258 | | // Load the slot value at storage_key and shift it to the right using storage_offset. | 259 | | // Note that storage_offset is in bytes, so we need to shift by 8 * storage_offset bits. | 260 | 2.44k | m_context | 261 | 2.44k | << Instruction::SWAP1 << s_loadInstruction << Instruction::SWAP1 | 262 | 2.44k | << u256(0x100) << Instruction::EXP << Instruction::SWAP1 << Instruction::DIV; | 263 | 2.44k | if (type->category() == Type::Category::FixedPoint) | 264 | | // implementation should be very similar to the integer case. | 265 | 2.44k | solUnimplemented("Not yet implemented - FixedPointType."); | 266 | 2.44k | else if (auto const* fun = dynamic_cast<FunctionType const*>(type)) | 267 | 129 | { | 268 | 129 | if (fun->kind() == FunctionType::Kind::External) | 269 | 40 | { | 270 | 40 | CompilerUtils(m_context).splitExternalFunctionType(false); | 271 | 40 | cleaned = true; | 272 | 40 | } | 273 | 89 | else if (fun->kind() == FunctionType::Kind::Internal) | 274 | 89 | { | 275 | | // Mask out the upper bits not belonging to the value of the function pointer. | 276 | 89 | m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; | 277 | 89 | cleaned = true; | 278 | 89 | m_context << Instruction::DUP1 << Instruction::ISZERO; | 279 | 89 | CompilerUtils(m_context).pushZeroValue(*fun); | 280 | 89 | m_context << Instruction::MUL << Instruction::OR; | 281 | 89 | } | 282 | 129 | } | 283 | 2.31k | else if (type->leftAligned()) | 284 | 262 | { | 285 | 262 | CompilerUtils(m_context).leftShiftNumberOnStack(256 - 8 * type->storageBytes()); | 286 | 262 | cleaned = true; | 287 | 262 | } | 288 | 2.05k | else if ( | 289 | 2.05k | type->category() == Type::Category::Integer && | 290 | 933 | dynamic_cast<IntegerType const&>(*type).isSigned() | 291 | 2.05k | ) | 292 | 251 | { | 293 | 251 | m_context << u256(type->storageBytes() - 1) << Instruction::SIGNEXTEND; | 294 | 251 | cleaned = true; | 295 | 251 | } | 296 | | | 297 | 2.44k | if (!cleaned) | 298 | 1.80k | { | 299 | | solAssert(type->sizeOnStack() == 1, ""); | 300 | 1.80k | m_context << ((u256(0x1) << (8 * type->storageBytes())) - 1) << Instruction::AND; | 301 | 1.80k | } | 302 | 2.44k | } | 303 | 6.88k | } |
Unexecuted instantiation: solidity::frontend::GenericStorageItem<true>::retrieveValue(solidity::langutil::SourceLocation const&, bool) const |
304 | | |
305 | | template<bool IsTransient> |
306 | | void GenericStorageItem<IsTransient>::storeValue(Type const& _sourceType, langutil::SourceLocation const& _location, bool _move) const |
307 | 31.8k | { |
308 | 31.8k | CompilerUtils utils(m_context); |
309 | 31.8k | solAssert(m_dataType, ""); |
310 | | |
311 | | // stack: value storage_key storage_offset |
312 | 31.8k | if (m_dataType->isValueType()) |
313 | 20.5k | { |
314 | 20.5k | solAssert(m_dataType->storageBytes() <= 32, "Invalid storage bytes size."); |
315 | 20.5k | solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size."); |
316 | 20.5k | if (m_dataType->storageBytes() == 32) |
317 | 3.33k | { |
318 | 3.33k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size."); |
319 | | // offset should be zero |
320 | 3.33k | m_context << Instruction::POP; |
321 | 3.33k | if (!_move) |
322 | 2.82k | m_context << Instruction::DUP2 << Instruction::SWAP1; |
323 | | |
324 | 3.33k | m_context << Instruction::SWAP1; |
325 | 3.33k | utils.convertType(_sourceType, *m_dataType, true); |
326 | 3.33k | m_context << Instruction::SWAP1; |
327 | | |
328 | 3.33k | m_context << s_storeInstruction; |
329 | 3.33k | } |
330 | 17.2k | else |
331 | 17.2k | { |
332 | | // OR the value into the other values in the storage slot |
333 | 17.2k | m_context << u256(0x100) << Instruction::EXP; |
334 | | // stack: value storage_ref multiplier |
335 | | // fetch old value |
336 | 17.2k | m_context << Instruction::DUP2 << s_loadInstruction; |
337 | | // stack: value storage_ref multiplier old_full_value |
338 | | // clear bytes in old value |
339 | 17.2k | m_context |
340 | 17.2k | << Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) |
341 | 17.2k | << Instruction::MUL; |
342 | 17.2k | m_context << Instruction::NOT << Instruction::AND << Instruction::SWAP1; |
343 | | // stack: value storage_ref cleared_value multiplier |
344 | 17.2k | utils.copyToStackTop(3 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack()); |
345 | | // stack: value storage_ref cleared_value multiplier value |
346 | 17.2k | if (auto const* fun = dynamic_cast<FunctionType const*>(m_dataType)) |
347 | 185 | { |
348 | 185 | solAssert( |
349 | 185 | _sourceType.isImplicitlyConvertibleTo(*m_dataType), |
350 | 185 | "function item stored but target is not implicitly convertible to source" |
351 | 185 | ); |
352 | 185 | solAssert(!fun->hasBoundFirstArgument(), ""); |
353 | 185 | if (fun->kind() == FunctionType::Kind::External) |
354 | 20 | { |
355 | 20 | solAssert(fun->sizeOnStack() == 2, ""); |
356 | | // Combine the two-item function type into a single stack slot. |
357 | 20 | utils.combineExternalFunctionType(false); |
358 | 20 | } |
359 | 165 | else |
360 | 165 | { |
361 | 165 | solAssert(fun->sizeOnStack() == 1, ""); |
362 | 165 | m_context << |
363 | 165 | ((u256(1) << (8 * m_dataType->storageBytes())) - 1) << |
364 | 165 | Instruction::AND; |
365 | 165 | } |
366 | 185 | } |
367 | 17.0k | else if (m_dataType->leftAligned()) |
368 | 2.58k | { |
369 | 2.58k | solAssert(_sourceType.category() == Type::Category::FixedBytes || ( |
370 | 2.58k | _sourceType.encodingType() && |
371 | 2.58k | _sourceType.encodingType()->category() == Type::Category::FixedBytes |
372 | 2.58k | ), "source not fixed bytes"); |
373 | 2.58k | CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * m_dataType->storageBytes()); |
374 | 2.58k | } |
375 | 14.4k | else |
376 | 14.4k | { |
377 | 14.4k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size for opaque type."); |
378 | | // remove the higher order bits |
379 | 14.4k | utils.convertType(_sourceType, *m_dataType, true, true); |
380 | 14.4k | } |
381 | 17.2k | m_context << Instruction::MUL << Instruction::OR; |
382 | | // stack: value storage_ref updated_value |
383 | 17.2k | m_context << Instruction::SWAP1 << s_storeInstruction; |
384 | 17.2k | if (_move) |
385 | 515 | utils.popStackElement(*m_dataType); |
386 | 17.2k | } |
387 | 20.5k | } |
388 | 11.2k | else |
389 | 11.2k | { |
390 | 11.2k | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); |
391 | 11.2k | solAssert( |
392 | 11.2k | _sourceType.category() == m_dataType->category(), |
393 | 11.2k | "Wrong type conversation for assignment." |
394 | 11.2k | ); |
395 | 11.2k | if (m_dataType->category() == Type::Category::Array) |
396 | 11.1k | { |
397 | 11.1k | m_context << Instruction::POP; // remove byte offset |
398 | 11.1k | ArrayUtils(m_context).copyArrayToStorage( |
399 | 11.1k | dynamic_cast<ArrayType const&>(*m_dataType), |
400 | 11.1k | dynamic_cast<ArrayType const&>(_sourceType) |
401 | 11.1k | ); |
402 | 11.1k | if (_move) |
403 | 1.29k | m_context << Instruction::POP; |
404 | 11.1k | } |
405 | 67 | else if (m_dataType->category() == Type::Category::Struct) |
406 | 67 | { |
407 | | // stack layout: source_ref target_ref target_offset |
408 | | // note that we have structs, so offset should be zero and are ignored |
409 | 67 | m_context << Instruction::POP; |
410 | 67 | auto const& structType = dynamic_cast<StructType const&>(*m_dataType); |
411 | 67 | auto const& sourceType = dynamic_cast<StructType const&>(_sourceType); |
412 | 67 | solAssert( |
413 | 67 | structType.structDefinition() == sourceType.structDefinition(), |
414 | 67 | "Struct assignment with conversion." |
415 | 67 | ); |
416 | 67 | solAssert(!structType.containsNestedMapping(), ""); |
417 | 67 | if (sourceType.location() == DataLocation::CallData) |
418 | 11 | { |
419 | 11 | solAssert(sourceType.sizeOnStack() == 1, ""); |
420 | 11 | solAssert(structType.sizeOnStack() == 1, ""); |
421 | 11 | m_context << Instruction::DUP2 << Instruction::DUP2; |
422 | 11 | m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(sourceType, structType, VariableDeclaration::Location::Unspecified, 0), 2, 0); |
423 | 11 | } |
424 | 56 | else |
425 | 56 | { |
426 | 56 | for (auto const& member: structType.members(nullptr)) |
427 | 119 | { |
428 | | // assign each member that can live outside of storage |
429 | 119 | Type const* memberType = member.type; |
430 | 119 | solAssert(memberType->nameable(), ""); |
431 | 119 | Type const* sourceMemberType = sourceType.memberType(member.name); |
432 | 119 | if (sourceType.location() == DataLocation::Storage) |
433 | 14 | { |
434 | | // stack layout: source_ref target_ref |
435 | 14 | std::pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name); |
436 | 14 | m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; |
437 | 14 | m_context << u256(offsets.second); |
438 | | // stack: source_ref target_ref source_member_ref source_member_off |
439 | 14 | StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); |
440 | | // stack: source_ref target_ref source_value... |
441 | 14 | } |
442 | 105 | else |
443 | 105 | { |
444 | 105 | solAssert(sourceType.location() == DataLocation::Memory, ""); |
445 | | // stack layout: source_ref target_ref |
446 | 105 | m_context << sourceType.memoryOffsetOfMember(member.name); |
447 | 105 | m_context << Instruction::DUP3 << Instruction::ADD; |
448 | 105 | MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); |
449 | | // stack layout: source_ref target_ref source_value... |
450 | 105 | } |
451 | 119 | unsigned stackSize = sourceMemberType->sizeOnStack(); |
452 | 119 | std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name); |
453 | 119 | m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; |
454 | 119 | m_context << u256(offsets.second); |
455 | | // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off |
456 | 119 | StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); |
457 | 119 | } |
458 | 56 | } |
459 | | // stack layout: source_ref target_ref |
460 | 67 | solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size."); |
461 | 67 | if (_move) |
462 | 30 | utils.popStackSlots(2); |
463 | 37 | else |
464 | 37 | m_context << Instruction::SWAP1 << Instruction::POP; |
465 | 67 | } |
466 | 0 | else |
467 | 67 | solAssert(false, "Invalid non-value type for assignment."); |
468 | 11.2k | } |
469 | 31.8k | } solidity::frontend::GenericStorageItem<false>::storeValue(solidity::frontend::Type const&, solidity::langutil::SourceLocation const&, bool) const Line | Count | Source | 307 | 31.8k | { | 308 | 31.8k | CompilerUtils utils(m_context); | 309 | 31.8k | solAssert(m_dataType, ""); | 310 | | | 311 | | // stack: value storage_key storage_offset | 312 | 31.8k | if (m_dataType->isValueType()) | 313 | 20.5k | { | 314 | 20.5k | solAssert(m_dataType->storageBytes() <= 32, "Invalid storage bytes size."); | 315 | 20.5k | solAssert(m_dataType->storageBytes() > 0, "Invalid storage bytes size."); | 316 | 20.5k | if (m_dataType->storageBytes() == 32) | 317 | 3.33k | { | 318 | 3.33k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size."); | 319 | | // offset should be zero | 320 | 3.33k | m_context << Instruction::POP; | 321 | 3.33k | if (!_move) | 322 | 2.82k | m_context << Instruction::DUP2 << Instruction::SWAP1; | 323 | | | 324 | 3.33k | m_context << Instruction::SWAP1; | 325 | 3.33k | utils.convertType(_sourceType, *m_dataType, true); | 326 | 3.33k | m_context << Instruction::SWAP1; | 327 | | | 328 | 3.33k | m_context << s_storeInstruction; | 329 | 3.33k | } | 330 | 17.2k | else | 331 | 17.2k | { | 332 | | // OR the value into the other values in the storage slot | 333 | 17.2k | m_context << u256(0x100) << Instruction::EXP; | 334 | | // stack: value storage_ref multiplier | 335 | | // fetch old value | 336 | 17.2k | m_context << Instruction::DUP2 << s_loadInstruction; | 337 | | // stack: value storage_ref multiplier old_full_value | 338 | | // clear bytes in old value | 339 | 17.2k | m_context | 340 | 17.2k | << Instruction::DUP2 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) | 341 | 17.2k | << Instruction::MUL; | 342 | 17.2k | m_context << Instruction::NOT << Instruction::AND << Instruction::SWAP1; | 343 | | // stack: value storage_ref cleared_value multiplier | 344 | 17.2k | utils.copyToStackTop(3 + m_dataType->sizeOnStack(), m_dataType->sizeOnStack()); | 345 | | // stack: value storage_ref cleared_value multiplier value | 346 | 17.2k | if (auto const* fun = dynamic_cast<FunctionType const*>(m_dataType)) | 347 | 185 | { | 348 | 185 | solAssert( | 349 | 185 | _sourceType.isImplicitlyConvertibleTo(*m_dataType), | 350 | 185 | "function item stored but target is not implicitly convertible to source" | 351 | 185 | ); | 352 | 185 | solAssert(!fun->hasBoundFirstArgument(), ""); | 353 | 185 | if (fun->kind() == FunctionType::Kind::External) | 354 | 20 | { | 355 | 20 | solAssert(fun->sizeOnStack() == 2, ""); | 356 | | // Combine the two-item function type into a single stack slot. | 357 | 20 | utils.combineExternalFunctionType(false); | 358 | 20 | } | 359 | 165 | else | 360 | 165 | { | 361 | 165 | solAssert(fun->sizeOnStack() == 1, ""); | 362 | 165 | m_context << | 363 | 165 | ((u256(1) << (8 * m_dataType->storageBytes())) - 1) << | 364 | 165 | Instruction::AND; | 365 | 165 | } | 366 | 185 | } | 367 | 17.0k | else if (m_dataType->leftAligned()) | 368 | 2.58k | { | 369 | 2.58k | solAssert(_sourceType.category() == Type::Category::FixedBytes || ( | 370 | 2.58k | _sourceType.encodingType() && | 371 | 2.58k | _sourceType.encodingType()->category() == Type::Category::FixedBytes | 372 | 2.58k | ), "source not fixed bytes"); | 373 | 2.58k | CompilerUtils(m_context).rightShiftNumberOnStack(256 - 8 * m_dataType->storageBytes()); | 374 | 2.58k | } | 375 | 14.4k | else | 376 | 14.4k | { | 377 | 14.4k | solAssert(m_dataType->sizeOnStack() == 1, "Invalid stack size for opaque type."); | 378 | | // remove the higher order bits | 379 | 14.4k | utils.convertType(_sourceType, *m_dataType, true, true); | 380 | 14.4k | } | 381 | 17.2k | m_context << Instruction::MUL << Instruction::OR; | 382 | | // stack: value storage_ref updated_value | 383 | 17.2k | m_context << Instruction::SWAP1 << s_storeInstruction; | 384 | 17.2k | if (_move) | 385 | 515 | utils.popStackElement(*m_dataType); | 386 | 17.2k | } | 387 | 20.5k | } | 388 | 11.2k | else | 389 | 11.2k | { | 390 | 11.2k | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); | 391 | 11.2k | solAssert( | 392 | 11.2k | _sourceType.category() == m_dataType->category(), | 393 | 11.2k | "Wrong type conversation for assignment." | 394 | 11.2k | ); | 395 | 11.2k | if (m_dataType->category() == Type::Category::Array) | 396 | 11.1k | { | 397 | 11.1k | m_context << Instruction::POP; // remove byte offset | 398 | 11.1k | ArrayUtils(m_context).copyArrayToStorage( | 399 | 11.1k | dynamic_cast<ArrayType const&>(*m_dataType), | 400 | 11.1k | dynamic_cast<ArrayType const&>(_sourceType) | 401 | 11.1k | ); | 402 | 11.1k | if (_move) | 403 | 1.29k | m_context << Instruction::POP; | 404 | 11.1k | } | 405 | 67 | else if (m_dataType->category() == Type::Category::Struct) | 406 | 67 | { | 407 | | // stack layout: source_ref target_ref target_offset | 408 | | // note that we have structs, so offset should be zero and are ignored | 409 | 67 | m_context << Instruction::POP; | 410 | 67 | auto const& structType = dynamic_cast<StructType const&>(*m_dataType); | 411 | 67 | auto const& sourceType = dynamic_cast<StructType const&>(_sourceType); | 412 | 67 | solAssert( | 413 | 67 | structType.structDefinition() == sourceType.structDefinition(), | 414 | 67 | "Struct assignment with conversion." | 415 | 67 | ); | 416 | 67 | solAssert(!structType.containsNestedMapping(), ""); | 417 | 67 | if (sourceType.location() == DataLocation::CallData) | 418 | 11 | { | 419 | 11 | solAssert(sourceType.sizeOnStack() == 1, ""); | 420 | 11 | solAssert(structType.sizeOnStack() == 1, ""); | 421 | 11 | m_context << Instruction::DUP2 << Instruction::DUP2; | 422 | 11 | m_context.callYulFunction(m_context.utilFunctions().updateStorageValueFunction(sourceType, structType, VariableDeclaration::Location::Unspecified, 0), 2, 0); | 423 | 11 | } | 424 | 56 | else | 425 | 56 | { | 426 | 56 | for (auto const& member: structType.members(nullptr)) | 427 | 119 | { | 428 | | // assign each member that can live outside of storage | 429 | 119 | Type const* memberType = member.type; | 430 | 119 | solAssert(memberType->nameable(), ""); | 431 | 119 | Type const* sourceMemberType = sourceType.memberType(member.name); | 432 | 119 | if (sourceType.location() == DataLocation::Storage) | 433 | 14 | { | 434 | | // stack layout: source_ref target_ref | 435 | 14 | std::pair<u256, unsigned> const& offsets = sourceType.storageOffsetsOfMember(member.name); | 436 | 14 | m_context << offsets.first << Instruction::DUP3 << Instruction::ADD; | 437 | 14 | m_context << u256(offsets.second); | 438 | | // stack: source_ref target_ref source_member_ref source_member_off | 439 | 14 | StorageItem(m_context, *sourceMemberType).retrieveValue(_location, true); | 440 | | // stack: source_ref target_ref source_value... | 441 | 14 | } | 442 | 105 | else | 443 | 105 | { | 444 | 105 | solAssert(sourceType.location() == DataLocation::Memory, ""); | 445 | | // stack layout: source_ref target_ref | 446 | 105 | m_context << sourceType.memoryOffsetOfMember(member.name); | 447 | 105 | m_context << Instruction::DUP3 << Instruction::ADD; | 448 | 105 | MemoryItem(m_context, *sourceMemberType).retrieveValue(_location, true); | 449 | | // stack layout: source_ref target_ref source_value... | 450 | 105 | } | 451 | 119 | unsigned stackSize = sourceMemberType->sizeOnStack(); | 452 | 119 | std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name); | 453 | 119 | m_context << dupInstruction(1 + stackSize) << offsets.first << Instruction::ADD; | 454 | 119 | m_context << u256(offsets.second); | 455 | | // stack: source_ref target_ref target_off source_value... target_member_ref target_member_byte_off | 456 | 119 | StorageItem(m_context, *memberType).storeValue(*sourceMemberType, _location, true); | 457 | 119 | } | 458 | 56 | } | 459 | | // stack layout: source_ref target_ref | 460 | 67 | solAssert(sourceType.sizeOnStack() == 1, "Unexpected source size."); | 461 | 67 | if (_move) | 462 | 30 | utils.popStackSlots(2); | 463 | 37 | else | 464 | 37 | m_context << Instruction::SWAP1 << Instruction::POP; | 465 | 67 | } | 466 | 0 | else | 467 | | solAssert(false, "Invalid non-value type for assignment."); | 468 | 11.2k | } | 469 | 31.8k | } |
Unexecuted instantiation: solidity::frontend::GenericStorageItem<true>::storeValue(solidity::frontend::Type const&, solidity::langutil::SourceLocation const&, bool) const |
470 | | |
471 | | template<bool IsTransient> |
472 | | void GenericStorageItem<IsTransient>::setToZero(langutil::SourceLocation const&, bool _removeReference) const |
473 | 331 | { |
474 | 331 | if (m_dataType->category() == Type::Category::Array) |
475 | 54 | { |
476 | 54 | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); |
477 | 54 | if (!_removeReference) |
478 | 0 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); |
479 | 54 | ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(*m_dataType)); |
480 | 54 | } |
481 | 277 | else if (m_dataType->category() == Type::Category::Struct) |
482 | 22 | { |
483 | 22 | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); |
484 | | // stack layout: storage_key storage_offset |
485 | | // @todo this can be improved: use StorageItem for non-value types, and just store 0 in |
486 | | // all slots that contain value types later. |
487 | 22 | auto const& structType = dynamic_cast<StructType const&>(*m_dataType); |
488 | 22 | for (auto const& member: structType.members(nullptr)) |
489 | 40 | { |
490 | | // zero each member that is not a mapping |
491 | 40 | Type const* memberType = member.type; |
492 | 40 | if (memberType->category() == Type::Category::Mapping) |
493 | 0 | continue; |
494 | 40 | std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name); |
495 | 40 | m_context |
496 | 40 | << offsets.first << Instruction::DUP3 << Instruction::ADD |
497 | 40 | << u256(offsets.second); |
498 | 40 | StorageItem(m_context, *memberType).setToZero(); |
499 | 40 | } |
500 | 22 | if (_removeReference) |
501 | 22 | m_context << Instruction::POP << Instruction::POP; |
502 | 22 | } |
503 | 255 | else |
504 | 255 | { |
505 | 255 | solAssert(m_dataType->isValueType(), "Clearing of unsupported type requested: " + m_dataType->toString()); |
506 | 255 | if (!_removeReference) |
507 | 0 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); |
508 | 255 | if (m_dataType->storageBytes() == 32) |
509 | 204 | { |
510 | | // offset should be zero |
511 | 204 | m_context |
512 | 204 | << Instruction::POP << u256(0) |
513 | 204 | << Instruction::SWAP1 << s_storeInstruction; |
514 | 204 | } |
515 | 51 | else |
516 | 51 | { |
517 | 51 | m_context << u256(0x100) << Instruction::EXP; |
518 | | // stack: storage_ref multiplier |
519 | | // fetch old value |
520 | 51 | m_context << Instruction::DUP2 << s_loadInstruction; |
521 | | // stack: storage_ref multiplier old_full_value |
522 | | // clear bytes in old value |
523 | 51 | m_context |
524 | 51 | << Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) |
525 | 51 | << Instruction::MUL; |
526 | 51 | m_context << Instruction::NOT << Instruction::AND; |
527 | | // stack: storage_ref cleared_value |
528 | 51 | m_context << Instruction::SWAP1 << s_storeInstruction; |
529 | 51 | } |
530 | 255 | } |
531 | 331 | } solidity::frontend::GenericStorageItem<false>::setToZero(solidity::langutil::SourceLocation const&, bool) const Line | Count | Source | 473 | 331 | { | 474 | 331 | if (m_dataType->category() == Type::Category::Array) | 475 | 54 | { | 476 | 54 | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); | 477 | 54 | if (!_removeReference) | 478 | 0 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); | 479 | 54 | ArrayUtils(m_context).clearArray(dynamic_cast<ArrayType const&>(*m_dataType)); | 480 | 54 | } | 481 | 277 | else if (m_dataType->category() == Type::Category::Struct) | 482 | 22 | { | 483 | 22 | solUnimplementedAssert(!IsTransient, "Transient storage reference types are not supported yet."); | 484 | | // stack layout: storage_key storage_offset | 485 | | // @todo this can be improved: use StorageItem for non-value types, and just store 0 in | 486 | | // all slots that contain value types later. | 487 | 22 | auto const& structType = dynamic_cast<StructType const&>(*m_dataType); | 488 | 22 | for (auto const& member: structType.members(nullptr)) | 489 | 40 | { | 490 | | // zero each member that is not a mapping | 491 | 40 | Type const* memberType = member.type; | 492 | 40 | if (memberType->category() == Type::Category::Mapping) | 493 | 0 | continue; | 494 | 40 | std::pair<u256, unsigned> const& offsets = structType.storageOffsetsOfMember(member.name); | 495 | 40 | m_context | 496 | 40 | << offsets.first << Instruction::DUP3 << Instruction::ADD | 497 | 40 | << u256(offsets.second); | 498 | 40 | StorageItem(m_context, *memberType).setToZero(); | 499 | 40 | } | 500 | 22 | if (_removeReference) | 501 | 22 | m_context << Instruction::POP << Instruction::POP; | 502 | 22 | } | 503 | 255 | else | 504 | 255 | { | 505 | 255 | solAssert(m_dataType->isValueType(), "Clearing of unsupported type requested: " + m_dataType->toString()); | 506 | 255 | if (!_removeReference) | 507 | 0 | CompilerUtils(m_context).copyToStackTop(sizeOnStack(), sizeOnStack()); | 508 | 255 | if (m_dataType->storageBytes() == 32) | 509 | 204 | { | 510 | | // offset should be zero | 511 | 204 | m_context | 512 | 204 | << Instruction::POP << u256(0) | 513 | 204 | << Instruction::SWAP1 << s_storeInstruction; | 514 | 204 | } | 515 | 51 | else | 516 | 51 | { | 517 | 51 | m_context << u256(0x100) << Instruction::EXP; | 518 | | // stack: storage_ref multiplier | 519 | | // fetch old value | 520 | 51 | m_context << Instruction::DUP2 << s_loadInstruction; | 521 | | // stack: storage_ref multiplier old_full_value | 522 | | // clear bytes in old value | 523 | 51 | m_context | 524 | 51 | << Instruction::SWAP1 << ((u256(1) << (8 * m_dataType->storageBytes())) - 1) | 525 | 51 | << Instruction::MUL; | 526 | 51 | m_context << Instruction::NOT << Instruction::AND; | 527 | | // stack: storage_ref cleared_value | 528 | 51 | m_context << Instruction::SWAP1 << s_storeInstruction; | 529 | 51 | } | 530 | 255 | } | 531 | 331 | } |
Unexecuted instantiation: solidity::frontend::GenericStorageItem<true>::setToZero(solidity::langutil::SourceLocation const&, bool) const |
532 | | |
533 | | StorageByteArrayElement::StorageByteArrayElement(CompilerContext& _compilerContext): |
534 | 782 | LValue(_compilerContext, TypeProvider::byte()) |
535 | 782 | { |
536 | 782 | } |
537 | | |
538 | | void StorageByteArrayElement::retrieveValue(SourceLocation const&, bool _remove) const |
539 | 445 | { |
540 | | // stack: ref byte_number |
541 | 445 | if (_remove) |
542 | 445 | m_context << Instruction::SWAP1 << Instruction::SLOAD |
543 | 445 | << Instruction::SWAP1 << Instruction::BYTE; |
544 | 0 | else |
545 | 0 | m_context << Instruction::DUP2 << Instruction::SLOAD |
546 | 0 | << Instruction::DUP2 << Instruction::BYTE; |
547 | 445 | m_context << (u256(1) << (256 - 8)) << Instruction::MUL; |
548 | 445 | } |
549 | | |
550 | | void StorageByteArrayElement::storeValue(Type const&, SourceLocation const&, bool _move) const |
551 | 328 | { |
552 | | // stack: value ref byte_number |
553 | 328 | m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; |
554 | | // stack: value ref (1<<(8*(31-byte_number))) |
555 | 328 | m_context << Instruction::DUP2 << Instruction::SLOAD; |
556 | | // stack: value ref (1<<(8*(31-byte_number))) old_full_value |
557 | | // clear byte in old value |
558 | 328 | m_context << Instruction::DUP2 << u256(0xff) << Instruction::MUL |
559 | 328 | << Instruction::NOT << Instruction::AND; |
560 | | // stack: value ref (1<<(32-byte_number)) old_full_value_with_cleared_byte |
561 | 328 | m_context << Instruction::SWAP1; |
562 | 328 | m_context << (u256(1) << (256 - 8)) << Instruction::DUP5 << Instruction::DIV |
563 | 328 | << Instruction::MUL << Instruction::OR; |
564 | | // stack: value ref new_full_value |
565 | 328 | m_context << Instruction::SWAP1 << Instruction::SSTORE; |
566 | 328 | if (_move) |
567 | 296 | m_context << Instruction::POP; |
568 | 328 | } |
569 | | |
570 | | void StorageByteArrayElement::setToZero(SourceLocation const&, bool _removeReference) const |
571 | 9 | { |
572 | | // stack: ref byte_number |
573 | 9 | solAssert(_removeReference, ""); |
574 | 9 | m_context << u256(31) << Instruction::SUB << u256(0x100) << Instruction::EXP; |
575 | | // stack: ref (1<<(8*(31-byte_number))) |
576 | 9 | m_context << Instruction::DUP2 << Instruction::SLOAD; |
577 | | // stack: ref (1<<(8*(31-byte_number))) old_full_value |
578 | | // clear byte in old value |
579 | 9 | m_context << Instruction::SWAP1 << u256(0xff) << Instruction::MUL; |
580 | 9 | m_context << Instruction::NOT << Instruction::AND; |
581 | | // stack: ref old_full_value_with_cleared_byte |
582 | 9 | m_context << Instruction::SWAP1 << Instruction::SSTORE; |
583 | 9 | } |
584 | | |
585 | | TupleObject::TupleObject( |
586 | | CompilerContext& _compilerContext, |
587 | | std::vector<std::unique_ptr<LValue>>&& _lvalues |
588 | | ): |
589 | 637 | LValue(_compilerContext), m_lvalues(std::move(_lvalues)) |
590 | 637 | { |
591 | 637 | } |
592 | | |
593 | | unsigned TupleObject::sizeOnStack() const |
594 | 691 | { |
595 | 691 | unsigned size = 0; |
596 | 691 | for (auto const& lv: m_lvalues) |
597 | 1.45k | if (lv) |
598 | 903 | size += lv->sizeOnStack(); |
599 | 691 | return size; |
600 | 691 | } |
601 | | |
602 | | void TupleObject::retrieveValue(SourceLocation const&, bool) const |
603 | 0 | { |
604 | 0 | solAssert(false, "Tried to retrieve value of tuple."); |
605 | 0 | } |
606 | | |
607 | | void TupleObject::storeValue(Type const& _sourceType, SourceLocation const& _location, bool) const |
608 | 637 | { |
609 | | // values are below the lvalue references |
610 | 637 | unsigned valuePos = sizeOnStack(); |
611 | 637 | TypePointers const& valueTypes = dynamic_cast<TupleType const&>(_sourceType).components(); |
612 | 637 | solAssert(valueTypes.size() == m_lvalues.size(), ""); |
613 | | // valuePos .... refPos ... |
614 | | // We will assign from right to left to optimize stack layout. |
615 | 1.98k | for (size_t i = 0; i < m_lvalues.size(); ++i) |
616 | 1.35k | { |
617 | 1.35k | std::unique_ptr<LValue> const& lvalue = m_lvalues[m_lvalues.size() - i - 1]; |
618 | 1.35k | Type const* valType = valueTypes[valueTypes.size() - i - 1]; |
619 | 1.35k | unsigned stackHeight = m_context.stackHeight(); |
620 | 1.35k | solAssert(!valType == !lvalue, ""); |
621 | 1.35k | if (!lvalue) |
622 | 510 | continue; |
623 | 841 | valuePos += valType->sizeOnStack(); |
624 | | // copy value to top |
625 | 841 | CompilerUtils(m_context).copyToStackTop(valuePos, valType->sizeOnStack()); |
626 | | // move lvalue ref above value |
627 | 841 | CompilerUtils(m_context).moveToStackTop(valType->sizeOnStack(), lvalue->sizeOnStack()); |
628 | 841 | lvalue->storeValue(*valType, _location, true); |
629 | 841 | valuePos += m_context.stackHeight() - stackHeight; |
630 | 841 | } |
631 | | // As the type of an assignment to a tuple type is the empty tuple, we always move. |
632 | 637 | CompilerUtils(m_context).popStackElement(_sourceType); |
633 | 637 | } |
634 | | |
635 | | void TupleObject::setToZero(SourceLocation const&, bool) const |
636 | 0 | { |
637 | | solAssert(false, "Tried to delete tuple."); |
638 | 0 | } |
639 | | |
640 | | template class solidity::frontend::GenericStorageItem<false>; |
641 | | template class solidity::frontend::GenericStorageItem<true>; |