neo_solidity/ir/expressions/
arrays.rs

1fn lower_array_subscript_expression(
2    expr: &Expression,
3    array: &Expression,
4    index: &Expression,
5    ctx: &mut LoweringContext,
6    instructions: &mut Vec<Instruction>,
7) -> bool {
8    if let Some(mapping) = resolve_mapping_access(expr, ctx) {
9        let reference = mapping.to_storage_reference();
10        emit_storage_load(&reference, ctx, instructions)
11    } else if lower_expression(array, ctx, instructions) && lower_expression(index, ctx, instructions) {
12        instructions.push(Instruction::ArrayGet);
13        true
14    } else {
15        false
16    }
17}
18
19fn lower_array_slice_expression(
20    array: &Expression,
21    start: Option<&Expression>,
22    end: Option<&Expression>,
23    ctx: &mut LoweringContext,
24    instructions: &mut Vec<Instruction>,
25) -> bool {
26    let array_local = ctx.allocate_local("__slice_array".to_string(), None);
27    if !lower_expression(array, ctx, instructions) {
28        return false;
29    }
30    instructions.push(Instruction::StoreLocal(array_local));
31
32    let start_local = ctx.allocate_local("__slice_start".to_string(), None);
33    if let Some(start_expr) = start {
34        if !lower_expression(start_expr, ctx, instructions) {
35            return false;
36        }
37    } else {
38        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
39    }
40    instructions.push(Instruction::StoreLocal(start_local));
41
42    let end_local = ctx.allocate_local("__slice_end".to_string(), None);
43    if let Some(end_expr) = end {
44        if !lower_expression(end_expr, ctx, instructions) {
45            return false;
46        }
47    } else {
48        instructions.push(Instruction::LoadLocal(array_local));
49        instructions.push(Instruction::GetSize);
50    }
51    instructions.push(Instruction::StoreLocal(end_local));
52
53    // Clamp start to >= 0
54    let clamp_start_label = ctx.next_label();
55    let clamp_start_done = ctx.next_label();
56    instructions.push(Instruction::LoadLocal(start_local));
57    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
58    instructions.push(Instruction::BinaryOp(BinaryOperator::Ge));
59    instructions.push(Instruction::JumpIf {
60        target: clamp_start_label,
61    });
62    instructions.push(Instruction::Jump {
63        target: clamp_start_done,
64    });
65    instructions.push(Instruction::Label(clamp_start_label));
66    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
67    instructions.push(Instruction::StoreLocal(start_local));
68    instructions.push(Instruction::Label(clamp_start_done));
69
70    // Clamp end to array length
71    let size_local = ctx.allocate_local("__slice_size".to_string(), None);
72    instructions.push(Instruction::LoadLocal(array_local));
73    instructions.push(Instruction::GetSize);
74    instructions.push(Instruction::StoreLocal(size_local));
75
76    let clamp_end_label = ctx.next_label();
77    let clamp_end_done = ctx.next_label();
78    instructions.push(Instruction::LoadLocal(end_local));
79    instructions.push(Instruction::LoadLocal(size_local));
80    instructions.push(Instruction::BinaryOp(BinaryOperator::Le));
81    instructions.push(Instruction::JumpIf {
82        target: clamp_end_label,
83    });
84    instructions.push(Instruction::Jump {
85        target: clamp_end_done,
86    });
87    instructions.push(Instruction::Label(clamp_end_label));
88    instructions.push(Instruction::LoadLocal(size_local));
89    instructions.push(Instruction::StoreLocal(end_local));
90    instructions.push(Instruction::Label(clamp_end_done));
91
92    let len_local = ctx.allocate_local("__slice_len".to_string(), None);
93    instructions.push(Instruction::LoadLocal(end_local));
94    instructions.push(Instruction::LoadLocal(start_local));
95    instructions.push(Instruction::BinaryOp(BinaryOperator::Sub));
96    instructions.push(Instruction::StoreLocal(len_local));
97
98    let clamp_label = ctx.next_label();
99    let clamp_done = ctx.next_label();
100    instructions.push(Instruction::LoadLocal(len_local));
101    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
102    instructions.push(Instruction::BinaryOp(BinaryOperator::Ge));
103    instructions.push(Instruction::JumpIf { target: clamp_label });
104    instructions.push(Instruction::Jump { target: clamp_done });
105    instructions.push(Instruction::Label(clamp_label));
106    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
107    instructions.push(Instruction::StoreLocal(len_local));
108    instructions.push(Instruction::Label(clamp_done));
109
110    let element_type = infer_array_element_type(array, ctx).unwrap_or(ValueType::Any);
111    let slice_array_type = ValueType::Array(Box::new(element_type.clone()));
112    let out_local = ctx.allocate_local("__slice_out".to_string(), Some(slice_array_type));
113    instructions.push(Instruction::LoadLocal(len_local));
114    instructions.push(Instruction::NewArray { element_type });
115    instructions.push(Instruction::StoreLocal(out_local));
116
117    let idx_local = ctx.allocate_local("__slice_index".to_string(), None);
118    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
119    instructions.push(Instruction::StoreLocal(idx_local));
120
121    let loop_label = ctx.next_label();
122    let end_label = ctx.next_label();
123
124    instructions.push(Instruction::Label(loop_label));
125    instructions.push(Instruction::LoadLocal(idx_local));
126    instructions.push(Instruction::LoadLocal(len_local));
127    instructions.push(Instruction::BinaryOp(BinaryOperator::Lt));
128    instructions.push(Instruction::JumpIf { target: end_label });
129
130    instructions.push(Instruction::LoadLocal(out_local));
131    instructions.push(Instruction::LoadLocal(idx_local));
132    instructions.push(Instruction::LoadLocal(array_local));
133    instructions.push(Instruction::LoadLocal(start_local));
134    instructions.push(Instruction::LoadLocal(idx_local));
135    instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
136    instructions.push(Instruction::ArrayGet);
137    instructions.push(Instruction::ArraySet);
138
139    instructions.push(Instruction::LoadLocal(idx_local));
140    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(1u8))));
141    instructions.push(Instruction::BinaryOp(BinaryOperator::Add));
142    instructions.push(Instruction::StoreLocal(idx_local));
143
144    instructions.push(Instruction::Jump { target: loop_label });
145    instructions.push(Instruction::Label(end_label));
146    instructions.push(Instruction::LoadLocal(out_local));
147    true
148}
149
150fn lower_array_literal_expression(
151    elements: &[Expression],
152    ctx: &mut LoweringContext,
153    instructions: &mut Vec<Instruction>,
154) -> bool {
155    let element_type = infer_literal_array_element_type(elements);
156    let array_local = ctx.allocate_local(
157        "__array_literal".to_string(),
158        Some(ValueType::Array(Box::new(element_type.clone()))),
159    );
160    instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
161        elements.len(),
162    ))));
163    instructions.push(Instruction::NewArray { element_type });
164    instructions.push(Instruction::StoreLocal(array_local));
165
166    for (index, element) in elements.iter().enumerate() {
167        instructions.push(Instruction::LoadLocal(array_local));
168        instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::from(
169            index as u64,
170        ))));
171        if !lower_expression(element, ctx, instructions) {
172            instructions.push(Instruction::PushLiteral(LiteralValue::Integer(BigInt::zero())));
173        }
174        instructions.push(Instruction::ArraySet);
175    }
176
177    instructions.push(Instruction::LoadLocal(array_local));
178    true
179}