solstat/analyzer/optimizations/
pack_struct_variables.rs1use std::collections::HashSet;
2
3use solang_parser::pt::{ContractPart, Loc, StructDefinition};
4use solang_parser::{self, pt::SourceUnit, pt::SourceUnitPart};
5
6use crate::analyzer::ast::{self, Target};
7use crate::analyzer::utils;
8
9pub fn pack_struct_variables_optimization(source_unit: SourceUnit) -> HashSet<Loc> {
11 let mut optimization_locations: HashSet<Loc> = HashSet::new();
12
13 let target_nodes = ast::extract_target_from_node(Target::StructDefinition, source_unit.into());
14
15 for node in target_nodes {
16 if node.is_source_unit_part() {
17 if let SourceUnitPart::StructDefinition(struct_definition) =
18 node.source_unit_part().unwrap()
19 {
20 let struct_location = struct_definition.loc;
21 if struct_can_be_packed(*struct_definition) {
22 optimization_locations.insert(struct_location);
23 }
24 }
25 } else if node.is_contract_part() {
26 if let ContractPart::StructDefinition(struct_definition) = node.contract_part().unwrap()
27 {
28 let struct_location = struct_definition.loc;
29 if struct_can_be_packed(*struct_definition) {
30 optimization_locations.insert(struct_location);
31 }
32 }
33 }
34 }
35 optimization_locations
36}
37
38fn struct_can_be_packed(struct_definition: StructDefinition) -> bool {
39 let mut variable_sizes: Vec<u16> = vec![];
40
41 for variable_declaration in struct_definition.fields {
42 variable_sizes.push(utils::get_type_size(variable_declaration.ty));
43 }
44
45 let unordered_variable_sizes = variable_sizes.clone();
47
48 variable_sizes.sort();
50
51 utils::storage_slots_used(unordered_variable_sizes) > utils::storage_slots_used(variable_sizes)
53}
54
55#[test]
56fn test_pack_struct_variables_optimization() {
57 let file_contents = r#"
58
59 //should not match
60 struct Ex {
61 uint256 spotPrice;
62 uint128 res0;
63 uint128 res1;
64 }
65
66 //should match
67 struct Ex1 {
68 bool isUniV2;
69 bytes32 salt;
70 bytes16 initBytecode;
71 }
72
73
74contract OrderRouter {
75
76
77 //should not match
78 struct Ex2 {
79 bool isUniV2;
80 address factoryAddress;
81 bytes16 initBytecode;
82 }
83
84 //should match
85 struct Ex3 {
86 bool isUniV2;
87 bytes32 salt;
88 bytes16 initBytecode;
89 }
90
91 //should not match
92 struct Ex4 {
93 bytes16 initBytecode;
94 bool isUniV2;
95 address factoryAddress;
96 }
97
98 //should not match
99 struct Ex5 {
100 bool isUniV2;
101 bytes16 initBytecode;
102 address factoryAddress;
103 }
104
105 //should match
106 struct Ex6 {
107 uint128 thing3;
108 uint256 thing1;
109 uint128 thing2;
110 }
111
112 // Should match
113 struct Ex7 {
114 address owner; // 160 bits
115 uint256 num0; // 256 bits
116 bytes4 b0; // 32 bits
117 uint64 num1; // 64 bits
118 }
119}
120 "#;
121
122 let source_unit = solang_parser::parse(file_contents, 0).unwrap().0;
123
124 let optimization_locations = pack_struct_variables_optimization(source_unit);
125
126 assert_eq!(optimization_locations.len(), 4)
127}