luaur_compiler/methods/
compiler_compile_expr_table.rs1use crate::enums::type_constant_folding::Type;
2use crate::functions::sref_compiler::sref_ast_name;
3use crate::functions::sref_compiler_alt_c::sref_ast_array_c_char;
4use crate::records::compile_error::CompileError;
5use crate::records::compiler::Compiler;
6use crate::records::constant::Constant;
7use crate::records::reg_scope::RegScope;
8use luaur_ast::records::ast_expr::AstExpr;
9use luaur_ast::records::ast_expr_constant_string::AstExprConstantString;
10use luaur_ast::records::ast_expr_table::AstExprTable;
11use luaur_ast::records::ast_expr_varargs::AstExprVarargs;
12use luaur_ast::rtti;
13use luaur_common::enums::luau_opcode::LuauOpcode;
14use luaur_common::macros::luau_assert::LUAU_ASSERT;
15use luaur_common::records::insertion_ordered_map::InsertionOrderedMap;
16
17impl Compiler {
18 pub fn compile_expr_table(&mut self, expr: *mut AstExprTable, target: u8, target_temp: bool) {
19 unsafe {
20 let expr_ref = &*expr;
21 if expr_ref.items.size == 0 {
22 let shape = self.table_shapes.find(&expr).copied().unwrap_or_default();
27 (*self.bytecode)
28 .add_debug_remark(format_args!("allocation: table hash {}", shape.hash_size));
29 (*self.bytecode).emit_abc(
30 LuauOpcode::LOP_NEWTABLE,
31 target,
32 Self::encode_hash_size(shape.hash_size),
33 0,
34 );
35 (*self.bytecode).emit_aux(shape.array_size);
36 return;
37 }
38
39 let mut array_size = 0;
40 let mut hash_size = 0;
41 let mut record_size = 0;
42 for item in expr_ref.items.iter() {
43 array_size +=
44 (item.kind == luaur_ast::records::ast_expr_table::ItemKind::List) as u32;
45 hash_size +=
46 (item.kind != luaur_ast::records::ast_expr_table::ItemKind::List) as u32;
47 record_size +=
48 (item.kind == luaur_ast::records::ast_expr_table::ItemKind::Record) as u32;
49 }
50
51 let mut index_size = 0;
52 if array_size == 0 && hash_size > 0 {
53 for item in expr_ref.items.iter() {
54 LUAU_ASSERT!(!item.key.is_null());
55 if let Some(ckey) = self.constants.find(&item.key) {
56 if ckey.r#type == Type::Type_Number {
57 let val = unsafe { ckey.data.value_number };
58 if val == (index_size + 1) as f64 {
59 index_size += 1;
60 }
61 }
62 }
63 }
64 if hash_size == record_size + index_size {
65 hash_size = record_size;
66 } else {
67 index_size = 0;
68 }
69 }
70
71 let encoded_hash_size = Self::encode_hash_size(hash_size);
72 let _rs = self.reg_scope_compiler();
73 let reg = if target_temp {
75 target
76 } else {
77 self.alloc_reg(expr as *mut _, 1)
78 };
79
80 use luaur_ast::records::ast_expr_table::ItemKind;
81 type BcTableShape = luaur_bytecode::records::table_shape::TableShape;
82
83 let mut last_key_val: InsertionOrderedMap<i32, i32> = InsertionOrderedMap::new();
85
86 if array_size == 0
88 && index_size == 0
89 && hash_size == record_size
90 && record_size >= 1
91 && record_size <= BcTableShape::kMaxLength
92 {
93 let mut shape = BcTableShape::default();
94
95 if luaur_common::FFlag::LuauCompileDuptableConstantPack2.get() {
96 for i in 0..expr_ref.items.size {
97 let item = &*expr_ref.items.data.add(i as usize);
98 LUAU_ASSERT!(item.kind == ItemKind::Record);
99 let ckey = rtti::ast_node_as::<AstExprConstantString>(item.key as *mut _);
100 LUAU_ASSERT!(!ckey.is_null());
101 let key_cid = (*self.bytecode)
102 .add_constant_string(sref_ast_array_c_char((*ckey).value));
103 if key_cid < 0 {
104 CompileError::raise(
105 &(*ckey).base.base.location,
106 format_args!(
107 "Exceeded constant limit; simplify the code to compile"
108 ),
109 );
110 }
111 let value_cid = self.get_constant_index(item.value);
112 if let Some(existing) = last_key_val.get(&key_cid) {
113 if *existing == -1 {
114 continue;
115 }
116 }
117 *last_key_val.get_or_default(key_cid) = value_cid;
123 }
124
125 for (key_cid, value_cid) in last_key_val.iter() {
126 LUAU_ASSERT!(shape.length < BcTableShape::kMaxLength);
127 let idx = shape.length as usize;
128 shape.keys[idx] = *key_cid;
129 shape.constants[idx] = *value_cid;
130 if *value_cid >= 0 {
131 shape.hasConstants = true;
132 }
133 shape.length += 1;
134 }
135 } else {
136 for i in 0..expr_ref.items.size {
137 let item = &*expr_ref.items.data.add(i as usize);
138 LUAU_ASSERT!(item.kind == ItemKind::Record);
139 let ckey = rtti::ast_node_as::<AstExprConstantString>(item.key as *mut _);
140 LUAU_ASSERT!(!ckey.is_null());
141 let cid = (*self.bytecode)
142 .add_constant_string(sref_ast_array_c_char((*ckey).value));
143 if cid < 0 {
144 CompileError::raise(
145 &(*ckey).base.base.location,
146 format_args!(
147 "Exceeded constant limit; simplify the code to compile"
148 ),
149 );
150 }
151 LUAU_ASSERT!(shape.length < BcTableShape::kMaxLength);
152 shape.keys[shape.length as usize] = cid;
153 shape.length += 1;
154 }
155 }
156
157 let tid = (*self.bytecode).add_constant_table(&shape);
158 if tid < 0 {
159 CompileError::raise(
160 &expr_ref.base.base.location,
161 format_args!("Exceeded constant limit; simplify the code to compile"),
162 );
163 }
164 (*self.bytecode)
165 .add_debug_remark(format_args!("allocation: table template {}", hash_size));
166
167 if tid < 32768 {
168 (*self.bytecode).emit_ad(LuauOpcode::LOP_DUPTABLE, reg, tid as i16);
169 } else {
170 if luaur_common::FFlag::LuauCompileDuptableConstantPack2.get() {
172 shape.hasConstants = false;
173 last_key_val.clear();
174 }
175 (*self.bytecode).emit_abc(LuauOpcode::LOP_NEWTABLE, reg, encoded_hash_size, 0);
176 (*self.bytecode).emit_aux(0);
177 }
178 } else {
179 let last: *const luaur_ast::records::ast_expr_table::Item =
181 if expr_ref.items.size > 0 {
182 &*expr_ref.items.data.add((expr_ref.items.size - 1) as usize)
183 } else {
184 core::ptr::null()
185 };
186 let trailing_varargs = !last.is_null()
187 && (*last).kind == ItemKind::List
188 && !rtti::ast_node_as::<AstExprVarargs>((*last).value as *mut _).is_null();
189 LUAU_ASSERT!(!trailing_varargs || array_size > 0);
190
191 let array_allocation = array_size - (trailing_varargs as u32) + index_size;
192
193 if hash_size == 0 {
194 (*self.bytecode).add_debug_remark(format_args!(
195 "allocation: table array {}",
196 array_allocation
197 ));
198 } else if array_allocation == 0 {
199 (*self.bytecode)
200 .add_debug_remark(format_args!("allocation: table hash {}", hash_size));
201 } else {
202 (*self.bytecode).add_debug_remark(format_args!(
203 "allocation: table hash {} array {}",
204 hash_size, array_allocation
205 ));
206 }
207
208 (*self.bytecode).emit_abc(LuauOpcode::LOP_NEWTABLE, reg, encoded_hash_size, 0);
209 (*self.bytecode).emit_aux(array_allocation);
210 }
211
212 let array_chunk_size = core::cmp::min(16u32, array_size);
213 let array_chunk_reg = self.alloc_reg(expr as *mut _, array_chunk_size);
214 let mut array_chunk_current: u32 = 0;
215 let mut array_index: u32 = 1;
216 let mut mult_ret = false;
217
218 for i in 0..expr_ref.items.size {
219 let item = &*expr_ref.items.data.add(i as usize);
220 let key = item.key;
221 let value = item.value;
222
223 if luaur_common::FFlag::LuauCompileDuptableConstantPack2.get()
224 && last_key_val.size() > 0
225 && !key.is_null()
226 && !rtti::ast_node_as::<AstExprConstantString>(key as *mut _).is_null()
227 {
228 let ckey = rtti::ast_node_as::<AstExprConstantString>(item.key as *mut _);
229 LUAU_ASSERT!(!ckey.is_null());
230 let key_cid =
231 (*self.bytecode).add_constant_string(sref_ast_array_c_char((*ckey).value));
232 if let Some(value_cid) = last_key_val.get(&key_cid) {
233 if *value_cid >= 0 {
235 continue;
236 }
237 }
238 }
239
240 self.set_debug_line_ast_node(value as *mut luaur_ast::records::ast_node::AstNode);
242
243 if self.options.coverage_level >= 2 {
244 (*self.bytecode).emit_abc(LuauOpcode::LOP_COVERAGE, 0, 0, 0);
245 }
246
247 if array_chunk_current > 0
249 && (!key.is_null() || array_chunk_current == array_chunk_size)
250 {
251 (*self.bytecode).emit_abc(
252 LuauOpcode::LOP_SETLIST,
253 reg,
254 array_chunk_reg,
255 (array_chunk_current + 1) as u8,
256 );
257 (*self.bytecode).emit_aux(array_index);
258 array_index += array_chunk_current;
259 array_chunk_current = 0;
260 }
261
262 if !key.is_null() {
263 let mut rsi = self.reg_scope_compiler();
265 let lv = self.compile_l_value_index(reg, key, &mut rsi);
266 let rv = self.compile_expr_auto(value, &mut rsi);
267 self.compile_assign(&lv, rv, core::ptr::null_mut());
268 } else {
269 let temp = (array_chunk_reg as u32 + array_chunk_current) as u8;
271 if i + 1 == expr_ref.items.size {
272 mult_ret = self.compile_expr_temp_mult_ret(value, temp);
273 } else {
274 self.compile_expr_temp_top(value, temp);
275 }
276 array_chunk_current += 1;
277 }
278 }
279
280 if array_chunk_current != 0 {
282 (*self.bytecode).emit_abc(
283 LuauOpcode::LOP_SETLIST,
284 reg,
285 array_chunk_reg,
286 if mult_ret {
287 0
288 } else {
289 (array_chunk_current + 1) as u8
290 },
291 );
292 (*self.bytecode).emit_aux(array_index);
293 }
294
295 if target != reg {
296 (*self.bytecode).emit_abc(LuauOpcode::LOP_MOVE, target, reg, 0);
297 }
298 }
299 }
300}