neo_solidity/cli/bytecode/bytecode_helpers/storage/
state.rs

1fn emit_load_state(bytecode: &mut Vec<u8>, module: &ir::Module, index: usize) {
2    let key = module
3        .state_variables
4        .get(index)
5        .map(|state| state.storage_key.as_slice())
6        .unwrap_or(&[]);
7    let value_type = module.state_variables.get(index).map(|state| &state.ty);
8
9    push_data(bytecode, key);
10    emit_syscall(bytecode, "System.Storage.GetContext");
11    emit_syscall(bytecode, "System.Storage.Get");
12    if let Some(ty) = value_type {
13        emit_coerce_storage_value(bytecode, ty);
14    }
15}
16
17fn emit_store_state(bytecode: &mut Vec<u8>, module: &ir::Module, index: usize) {
18    let key = module
19        .state_variables
20        .get(index)
21        .map(|state| state.storage_key.as_slice())
22        .unwrap_or(&[]);
23
24    push_data(bytecode, key);
25    emit_syscall(bytecode, "System.Storage.GetContext");
26    // Stack order for System.Storage.Put: [value, key, context]
27    emit_syscall(bytecode, "System.Storage.Put");
28}
29
30fn emit_coerce_storage_value(bytecode: &mut Vec<u8>, ty: &ValueType) {
31    fn with_null_default(
32        bytecode: &mut Vec<u8>,
33        default_value: impl Fn(&mut Vec<u8>),
34        not_null: impl FnOnce(&mut Vec<u8>),
35    ) {
36        // Stack before: [value]
37        // After this block: [coerced_or_default]
38        bytecode.push(0x4A); // DUP
39        bytecode.push(0xD8); // ISNULL
40
41        // if !(value is null) -> jump to not_null
42        let jmp_not_null_pos = bytecode.len();
43        bytecode.push(0x27); // JMPIFNOT_L
44        let jmp_not_null_operand = bytecode.len();
45        bytecode.extend_from_slice(&[0, 0, 0, 0]);
46
47        // null case: drop null and push default
48        bytecode.push(0x45); // DROP
49        default_value(bytecode);
50
51        // jump to end
52        let jmp_end_pos = bytecode.len();
53        bytecode.push(0x23); // JMP_L
54        let jmp_end_operand = bytecode.len();
55        bytecode.extend_from_slice(&[0, 0, 0, 0]);
56
57        // not_null:
58        let not_null_pos = bytecode.len();
59        // Treat an empty ByteString/Buffer (missing storage entry) as default.
60        // Stack before: [value]
61        // Stack after:  [value] (if not missing) or [default]
62        bytecode.push(0x4A); // DUP
63        bytecode.push(0xCA); // SIZE
64        bytecode.push(0x10); // PUSH0
65        bytecode.push(0x97); // EQUAL
66
67        // if !(size == 0) -> jump to not_missing
68        let jmp_not_missing_pos = bytecode.len();
69        bytecode.push(0x27); // JMPIFNOT_L
70        let jmp_not_missing_operand = bytecode.len();
71        bytecode.extend_from_slice(&[0, 0, 0, 0]);
72
73        // missing case: drop value and push default
74        bytecode.push(0x45); // DROP
75        default_value(bytecode);
76
77        // jump to end
78        let jmp_end2_pos = bytecode.len();
79        bytecode.push(0x23); // JMP_L
80        let jmp_end2_operand = bytecode.len();
81        bytecode.extend_from_slice(&[0, 0, 0, 0]);
82
83        // not_missing:
84        let not_missing_pos = bytecode.len();
85        not_null(bytecode);
86
87        // end:
88        let end_pos = bytecode.len();
89
90        let rel_not_null = (not_null_pos as i32)
91            .checked_sub(jmp_not_null_pos as i32)
92            .unwrap_or(0);
93        bytecode[jmp_not_null_operand..jmp_not_null_operand + 4]
94            .copy_from_slice(&rel_not_null.to_le_bytes());
95
96        let rel_not_missing = (not_missing_pos as i32)
97            .checked_sub(jmp_not_missing_pos as i32)
98            .unwrap_or(0);
99        bytecode[jmp_not_missing_operand..jmp_not_missing_operand + 4]
100            .copy_from_slice(&rel_not_missing.to_le_bytes());
101
102        let rel_end = (end_pos as i32).checked_sub(jmp_end_pos as i32).unwrap_or(0);
103        bytecode[jmp_end_operand..jmp_end_operand + 4].copy_from_slice(&rel_end.to_le_bytes());
104
105        let rel_end2 = (end_pos as i32).checked_sub(jmp_end2_pos as i32).unwrap_or(0);
106        bytecode[jmp_end2_operand..jmp_end2_operand + 4].copy_from_slice(&rel_end2.to_le_bytes());
107    }
108
109    match ty {
110        ValueType::Integer { .. } => {
111            with_null_default(
112                bytecode,
113                |bytecode| bytecode.push(0x10), // PUSH0
114                |bytecode| {
115                    // Coerce ByteString -> Integer via `value + 0`.
116                    bytecode.push(0x10); // PUSH0
117                    bytecode.push(0x9E); // ADD
118                },
119            );
120        }
121        ValueType::Boolean => {
122            with_null_default(
123                bytecode,
124                |bytecode| bytecode.push(0x09), // PUSHF
125                |bytecode| {
126                    // Coerce ByteString -> Boolean via NZ.
127                    bytecode.push(0xB1); // NZ
128                },
129            );
130        }
131        ValueType::String => {
132            with_null_default(bytecode, |bytecode| push_data(bytecode, &[]), |_bytecode| {});
133        }
134        ValueType::Address => {
135            with_null_default(bytecode, |bytecode| push_data(bytecode, &[0u8; 20]), |_bytecode| {});
136        }
137        ValueType::ByteArray { fixed_len } => {
138            let default_bytes = fixed_len
139                .map(|len| vec![0u8; len as usize])
140                .unwrap_or_default();
141            with_null_default(bytecode, |bytecode| push_data(bytecode, &default_bytes), |_bytecode| {});
142        }
143        ValueType::Array(_) => {
144            with_null_default(
145                bytecode,
146                |bytecode| bytecode.push(0x10), // PUSH0
147                |bytecode| {
148                    // Storage arrays store their length at the base slot. `System.Storage.Get` returns a
149                    // ByteString, so coerce to Integer to keep slot hashing / arithmetic stable.
150                    bytecode.push(0x10); // PUSH0
151                    bytecode.push(0x9E); // ADD
152                },
153            );
154        }
155        _ => {}
156    }
157}