neo_solidity/ir/context/
storage.rs1fn resolve_storage_reference(
2 expression: &Expression,
3 ctx: &LoweringContext,
4) -> Option<StorageReference> {
5 if let Some(mapping) = resolve_mapping_access(expression, ctx) {
6 return Some(mapping.to_storage_reference());
7 }
8
9 match expression {
10 Expression::Variable(identifier) => {
11 if let Some(alias) = ctx.storage_alias(&identifier.name).cloned() {
12 return Some(alias);
13 }
14
15 let state_index = *ctx.state_index_map.get(&identifier.name)?;
20 let state_type = ctx.state_type(state_index)?;
21 if matches!(state_type, ValueType::Struct { .. }) {
22 Some(StorageReference {
23 state_index,
24 key_expressions: Vec::new(),
25 key_types: Vec::new(),
26 value_type: state_type.clone(),
27 field_path: Vec::new(),
28 })
29 } else {
30 None
31 }
32 }
33 Expression::MemberAccess(_, inner, member) => {
34 let mut base = resolve_storage_reference(inner, ctx)?;
35 let field = find_struct_field(&base.value_type, &member.name)?;
36 base.field_path.push(StorageReferenceField {
37 key: field.key,
38 ty: field.ty.clone(),
39 });
40 base.value_type = field.ty.clone();
41 Some(base)
42 }
43 _ => None,
44 }
45}
46
47fn emit_storage_load(
48 reference: &StorageReference,
49 ctx: &mut LoweringContext,
50 instructions: &mut Vec<Instruction>,
51) -> bool {
52 let tmp_id = ctx.next_label();
53
54 let mut key_locals: Vec<usize> = Vec::new();
57 for (index, expr) in reference.key_expressions.iter().enumerate() {
58 let local = ctx.allocate_local(
59 format!("__storage_key_{tmp_id}_{index}"),
60 reference.key_types.get(index).cloned(),
61 );
62 if !lower_expression(expr, ctx, instructions) {
63 return false;
64 }
65 instructions.push(Instruction::StoreLocal(local));
66 key_locals.push(local);
67 }
68
69 let push_keys_for_slot = |instructions: &mut Vec<Instruction>| {
70 for local in key_locals.iter().rev() {
73 instructions.push(Instruction::LoadLocal(*local));
74 }
75 };
76
77 if let Some(field) = reference.field_path.last() {
78 let field_keys: Vec<[u8; 32]> = reference.field_path.iter().map(|field| field.key).collect();
79 push_keys_for_slot(instructions);
80 instructions.push(Instruction::LoadStructField {
81 state_index: reference.state_index,
82 key_types: reference.key_types.clone(),
83 field_keys,
84 field_type: field.ty.clone(),
85 });
86 return true;
87 }
88
89 if let ValueType::Struct { fields, .. } = &reference.value_type {
91 let out_local = ctx.allocate_local(
92 format!("__storage_struct_{tmp_id}"),
93 Some(reference.value_type.clone()),
94 );
95
96 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
97 BigInt::from(fields.len() as u64),
98 )));
99 instructions.push(Instruction::NewArray {
100 element_type: ValueType::Any,
101 });
102 instructions.push(Instruction::StoreLocal(out_local));
103
104 for (index, field) in fields.iter().enumerate() {
105 instructions.push(Instruction::LoadLocal(out_local));
106 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
107 index as u64,
108 ))));
109 push_keys_for_slot(instructions);
110 instructions.push(Instruction::LoadStructField {
111 state_index: reference.state_index,
112 key_types: reference.key_types.clone(),
113 field_keys: vec![field.key],
114 field_type: field.ty.clone(),
115 });
116 instructions.push(Instruction::ArraySet);
117 }
118
119 instructions.push(Instruction::LoadLocal(out_local));
120 return true;
121 }
122
123 push_keys_for_slot(instructions);
124 instructions.push(Instruction::LoadMappingElement {
125 state_index: reference.state_index,
126 key_types: reference.key_types.clone(),
127 });
128 true
129}
130
131fn emit_storage_store(
132 reference: &StorageReference,
133 ctx: &mut LoweringContext,
134 instructions: &mut Vec<Instruction>,
135) -> bool {
136 let tmp_id = ctx.next_label();
137
138 let mut key_locals: Vec<usize> = Vec::new();
141 for (index, expr) in reference.key_expressions.iter().enumerate() {
142 let local = ctx.allocate_local(
143 format!("__storage_key_{tmp_id}_{index}"),
144 reference.key_types.get(index).cloned(),
145 );
146 if !lower_expression(expr, ctx, instructions) {
147 return false;
148 }
149 instructions.push(Instruction::StoreLocal(local));
150 key_locals.push(local);
151 }
152
153 let push_keys_for_slot = |instructions: &mut Vec<Instruction>| {
154 for local in key_locals.iter().rev() {
155 instructions.push(Instruction::LoadLocal(*local));
156 }
157 };
158
159 if let Some(field) = reference.field_path.last() {
160 let field_keys: Vec<[u8; 32]> = reference.field_path.iter().map(|field| field.key).collect();
161 push_keys_for_slot(instructions);
162 instructions.push(Instruction::StoreStructField {
163 state_index: reference.state_index,
164 key_types: reference.key_types.clone(),
165 field_keys,
166 field_type: field.ty.clone(),
167 });
168 return true;
169 }
170
171 if let ValueType::Struct { fields, .. } = &reference.value_type {
173 let value_local = ctx.allocate_local(
174 format!("__storage_struct_value_{tmp_id}"),
175 Some(reference.value_type.clone()),
176 );
177 instructions.push(Instruction::StoreLocal(value_local));
178
179 for (index, field) in fields.iter().enumerate() {
180 instructions.push(Instruction::LoadLocal(value_local));
181 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
182 index as u64,
183 ))));
184 instructions.push(Instruction::ArrayGet);
185 push_keys_for_slot(instructions);
186 instructions.push(Instruction::StoreStructField {
187 state_index: reference.state_index,
188 key_types: reference.key_types.clone(),
189 field_keys: vec![field.key],
190 field_type: field.ty.clone(),
191 });
192 }
193
194 return true;
195 }
196
197 push_keys_for_slot(instructions);
198 instructions.push(Instruction::StoreMappingElement {
199 state_index: reference.state_index,
200 key_types: reference.key_types.clone(),
201 });
202 true
203}
204
205fn resolve_mapping_access<'a>(
206 expression: &'a Expression,
207 ctx: &LoweringContext,
208) -> Option<MappingAccess<'a>> {
209 let mut keys: Vec<&'a Expression> = Vec::new();
210 let mut current = expression;
211
212 loop {
213 match current {
214 Expression::ArraySubscript(_, inner, maybe_index) => {
215 let index_expr = maybe_index.as_ref()?.as_ref();
216 keys.insert(0, index_expr);
217 current = inner;
218 }
219 Expression::Variable(identifier) => {
220 if keys.is_empty() {
224 return None;
225 }
226
227 let state_index = *ctx.state_index_map.get(&identifier.name)?;
228 let mut current_type = ctx.state_type(state_index)?.clone();
229 let mut key_types = Vec::with_capacity(keys.len());
230
231 for _key_expr in &keys {
232 match current_type {
233 ValueType::Mapping { ref key, ref value } => {
234 key_types.push((**key).clone());
235 current_type = (**value).clone();
236 }
237 ValueType::Array(ref element) => {
238 key_types.push(ValueType::Integer {
240 signed: false,
241 bits: 256,
242 });
243 current_type = (**element).clone();
244 }
245 _ => return None,
246 }
247 }
248
249 return Some(MappingAccess {
250 state_index,
251 key_expressions: keys,
252 key_types,
253 value_type: current_type,
254 });
255 }
256 _ => return None,
257 }
258 }
259}