neo_solidity/ir/expressions/member_access/
address_ops.rs1fn try_lower_address_balance(
2 inner: &Expression,
3 member: &Identifier,
4 ctx: &mut LoweringContext,
5 instructions: &mut Vec<Instruction>,
6) -> Option<bool> {
7 if member.name != "balance" {
8 return None;
9 }
10
11 if matches!(
12 infer_type_from_expression(inner, ctx),
13 Some(ValueType::Address)
14 ) {
15 if !lower_expression(inner, ctx, instructions) {
16 return Some(false);
17 }
18 instructions.push(Instruction::CallBuiltin {
19 builtin: BuiltinCall::NativeCall {
20 contract: NativeContract::Gas,
21 method: "balanceOf".to_string(),
22 },
23 arg_count: 1,
24 });
25 return Some(true);
26 }
27
28 None
29}
30
31fn try_lower_length_property(
32 inner: &Expression,
33 member: &Identifier,
34 ctx: &mut LoweringContext,
35 instructions: &mut Vec<Instruction>,
36) -> Option<bool> {
37 if member.name != "length" {
38 return None;
39 }
40
41 if let Expression::MemberAccess(_, code_inner, code_member) = inner {
47 if code_member.name == "code"
48 && matches!(
49 infer_type_from_expression(code_inner.as_ref(), ctx),
50 Some(ValueType::Address)
51 )
52 {
53 if !lower_expression(code_inner.as_ref(), ctx, instructions) {
54 return Some(false);
55 }
56
57 instructions.push(Instruction::CallBuiltin {
58 builtin: BuiltinCall::NativeCall {
59 contract: NativeContract::ContractManagement,
60 method: "isContract".to_string(),
61 },
62 arg_count: 1,
63 });
64
65 let not_contract_label = ctx.next_label();
66 let end_label = ctx.next_label();
67
68 instructions.push(Instruction::JumpIf {
70 target: not_contract_label,
71 });
72 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
73 BigInt::one(),
74 )));
75 instructions.push(Instruction::Jump { target: end_label });
76 instructions.push(Instruction::Label(not_contract_label));
77 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
78 BigInt::zero(),
79 )));
80 instructions.push(Instruction::Label(end_label));
81 return Some(true);
82 }
83 }
84
85 if let Expression::Variable(base) = inner {
86 if let Some(state_index) = ctx.state_index_map.get(&base.name) {
87 if matches!(ctx.state_type(*state_index), Some(ValueType::Array(_))) {
88 instructions.push(Instruction::LoadState(*state_index));
90 return Some(true);
91 }
92 }
93 }
94
95 if lower_expression(inner, ctx, instructions) {
96 instructions.push(Instruction::GetSize);
97 return Some(true);
98 }
99
100 Some(false)
101}
102
103fn try_lower_current_key(
104 inner: &Expression,
105 member: &Identifier,
106 ctx: &mut LoweringContext,
107 instructions: &mut Vec<Instruction>,
108) -> Option<bool> {
109 if member.name != "currentKey" {
110 return None;
111 }
112
113 if !lower_expression(inner, ctx, instructions) {
116 return Some(false);
117 }
118 instructions.push(Instruction::CallBuiltin {
119 builtin: BuiltinCall::Syscall("System.Iterator.Value".to_string()),
120 arg_count: 1,
121 });
122 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
123 BigInt::zero(),
124 )));
125 instructions.push(Instruction::ArrayGet);
126 Some(true)
127}
128
129fn try_lower_current_value(
130 inner: &Expression,
131 member: &Identifier,
132 ctx: &mut LoweringContext,
133 instructions: &mut Vec<Instruction>,
134) -> Option<bool> {
135 if member.name != "currentValue" {
136 return None;
137 }
138
139 if !lower_expression(inner, ctx, instructions) {
142 return Some(false);
143 }
144 instructions.push(Instruction::CallBuiltin {
145 builtin: BuiltinCall::Syscall("System.Iterator.Value".to_string()),
146 arg_count: 1,
147 });
148 instructions.push(Instruction::PushLiteral(LiteralValue::Integer(
149 BigInt::one(),
150 )));
151 instructions.push(Instruction::ArrayGet);
152 Some(true)
153}
154
155fn try_lower_code_property(
156 inner: &Expression,
157 member: &Identifier,
158 ctx: &mut LoweringContext,
159 instructions: &mut Vec<Instruction>,
160) -> Option<bool> {
161 if member.name == "codehash" {
162 if matches!(
163 infer_type_from_expression(inner, ctx),
164 Some(ValueType::Address)
165 ) {
166 eprintln!(
170 "warning: address.codehash auto-mapped to the contract script hash \
171 on Neo N3. For contract addresses this returns the address itself; \
172 for non-contract addresses it returns bytes32(0)."
173 );
174 if !lower_expression(inner, ctx, instructions) {
175 return Some(false);
176 }
177 instructions.push(Instruction::Dup);
179 instructions.push(Instruction::CallBuiltin {
180 builtin: BuiltinCall::NativeCall {
181 contract: NativeContract::ContractManagement,
182 method: "isContract".to_string(),
183 },
184 arg_count: 1,
185 });
186 let not_contract_label = ctx.next_label();
187 let end_label = ctx.next_label();
188 instructions.push(Instruction::JumpIf {
189 target: not_contract_label,
190 });
191 instructions.push(Instruction::Jump { target: end_label });
193 instructions.push(Instruction::Label(not_contract_label));
194 instructions.push(Instruction::Drop(ValueType::Any));
196 instructions.push(Instruction::PushLiteral(LiteralValue::ByteArray(
197 vec![0u8; 32],
198 )));
199 instructions.push(Instruction::Label(end_label));
200 return Some(true);
201 }
202 return None;
203 }
204
205 if member.name != "code" {
206 return None;
207 }
208
209 if lower_expression(inner, ctx, instructions) {
210 instructions.push(Instruction::Drop(ValueType::Any));
211 instructions.push(Instruction::PushLiteral(LiteralValue::ByteArray(vec![])));
212 return Some(true);
213 }
214
215 Some(false)
216}