1use crate::lcnf::*;
6use std::collections::HashMap;
7
8use super::types::{
9 LuaAnalysisCache, LuaBackend, LuaClass, LuaConstantFoldingHelper, LuaDepGraph,
10 LuaDominatorTree, LuaExpr, LuaExtCache, LuaExtConfig, LuaExtConstFolder, LuaExtDepGraph,
11 LuaExtDiagCollector, LuaExtDiagMsg, LuaExtDomTree, LuaExtEmitStats, LuaExtEventLog,
12 LuaExtFeatures, LuaExtIdGen, LuaExtIncrKey, LuaExtLiveness, LuaExtNameScope, LuaExtPassConfig,
13 LuaExtPassPhase, LuaExtPassRegistry, LuaExtPassStats, LuaExtPassTiming, LuaExtProfiler,
14 LuaExtSourceBuffer, LuaExtVersion, LuaExtWorklist, LuaFunction, LuaLivenessInfo, LuaModule,
15 LuaPassConfig, LuaPassPhase, LuaPassRegistry, LuaPassStats, LuaStmt, LuaTableField, LuaType,
16 LuaWorklist,
17};
18
19pub(super) fn emit_stmts(stmts: &[LuaStmt], indent: usize) -> std::string::String {
20 let pad = " ".repeat(indent);
21 stmts
22 .iter()
23 .map(|s| format!("{}{}", pad, emit_stmt(s, indent)))
24 .collect::<Vec<_>>()
25 .join("\n")
26}
27pub(super) fn emit_stmt(stmt: &LuaStmt, indent: usize) -> std::string::String {
28 match stmt {
29 LuaStmt::Assign { targets, values } => {
30 let ts: Vec<_> = targets.iter().map(|t| t.to_string()).collect();
31 let vs: Vec<_> = values.iter().map(|v| v.to_string()).collect();
32 format!("{} = {}", ts.join(", "), vs.join(", "))
33 }
34 LuaStmt::LocalAssign {
35 names,
36 attribs,
37 values,
38 } => {
39 let ns: Vec<_> = names
40 .iter()
41 .enumerate()
42 .map(|(i, n)| {
43 if let Some(Some(attr)) = attribs.get(i) {
44 format!("{} <{}>", n, attr)
45 } else {
46 n.clone()
47 }
48 })
49 .collect();
50 if values.is_empty() {
51 format!("local {}", ns.join(", "))
52 } else {
53 let vs: Vec<_> = values.iter().map(|v| v.to_string()).collect();
54 format!("local {} = {}", ns.join(", "), vs.join(", "))
55 }
56 }
57 LuaStmt::Do(body) => {
58 format!(
59 "do\n{}\n{}end",
60 emit_stmts(body, indent + 1),
61 " ".repeat(indent)
62 )
63 }
64 LuaStmt::While { cond, body } => {
65 format!(
66 "while {} do\n{}\n{}end",
67 cond,
68 emit_stmts(body, indent + 1),
69 " ".repeat(indent)
70 )
71 }
72 LuaStmt::Repeat { body, cond } => {
73 format!(
74 "repeat\n{}\n{}until {}",
75 emit_stmts(body, indent + 1),
76 " ".repeat(indent),
77 cond
78 )
79 }
80 LuaStmt::If {
81 cond,
82 then_body,
83 elseif_clauses,
84 else_body,
85 } => {
86 let mut out = format!("if {} then\n{}", cond, emit_stmts(then_body, indent + 1));
87 for (ei_cond, ei_body) in elseif_clauses {
88 out.push_str(&format!(
89 "\n{}elseif {} then\n{}",
90 " ".repeat(indent),
91 ei_cond,
92 emit_stmts(ei_body, indent + 1)
93 ));
94 }
95 if let Some(eb) = else_body {
96 out.push_str(&format!(
97 "\n{}else\n{}",
98 " ".repeat(indent),
99 emit_stmts(eb, indent + 1)
100 ));
101 }
102 out.push_str(&format!("\n{}end", " ".repeat(indent)));
103 out
104 }
105 LuaStmt::For {
106 var,
107 start,
108 limit,
109 step,
110 body,
111 } => {
112 let step_str = step
113 .as_ref()
114 .map(|s| format!(", {}", s))
115 .unwrap_or_default();
116 format!(
117 "for {} = {}, {}{} do\n{}\n{}end",
118 var,
119 start,
120 limit,
121 step_str,
122 emit_stmts(body, indent + 1),
123 " ".repeat(indent)
124 )
125 }
126 LuaStmt::ForIn { names, exprs, body } => {
127 let es: Vec<_> = exprs.iter().map(|e| e.to_string()).collect();
128 format!(
129 "for {} in {} do\n{}\n{}end",
130 names.join(", "),
131 es.join(", "),
132 emit_stmts(body, indent + 1),
133 " ".repeat(indent)
134 )
135 }
136 LuaStmt::Function(func) => emit_function(func, indent, false),
137 LuaStmt::Local(func) => emit_function(func, indent, true),
138 LuaStmt::Return(exprs) => {
139 if exprs.is_empty() {
140 "return".to_string()
141 } else {
142 let es: Vec<_> = exprs.iter().map(|e| e.to_string()).collect();
143 format!("return {}", es.join(", "))
144 }
145 }
146 LuaStmt::Break => "break".to_string(),
147 LuaStmt::Goto(label) => format!("goto {}", label),
148 LuaStmt::Label(label) => format!("::{}::", label),
149 LuaStmt::Call(expr) => expr.to_string(),
150 }
151}
152pub(super) fn emit_function(
153 func: &LuaFunction,
154 indent: usize,
155 force_local: bool,
156) -> std::string::String {
157 let local_kw = if force_local || func.is_local {
158 "local "
159 } else {
160 ""
161 };
162 let name_part = func.name.as_deref().unwrap_or("_anon");
163 let sep = if func.is_method { ":" } else { "." };
164 let func_name = if func.is_method {
165 if let Some(dot) = name_part.rfind('.') {
166 format!("{}{}{}", &name_part[..dot], sep, &name_part[dot + 1..])
167 } else {
168 name_part.to_string()
169 }
170 } else {
171 name_part.to_string()
172 };
173 let mut all_params = func.params.clone();
174 if func.vararg {
175 all_params.push("...".to_string());
176 }
177 format!(
178 "{}function {}({})\n{}\n{}end",
179 local_kw,
180 func_name,
181 all_params.join(", "),
182 emit_stmts(&func.body, indent + 1),
183 " ".repeat(indent)
184 )
185}
186pub const LUA_KEYWORDS: &[&str] = &[
188 "and", "break", "do", "else", "elseif", "end", "false", "for", "function", "goto", "if", "in",
189 "local", "nil", "not", "or", "repeat", "return", "then", "true", "until", "while",
190];
191#[cfg(test)]
192mod tests {
193 use super::*;
194 #[test]
195 pub(super) fn test_lua_type_display_nil() {
196 assert_eq!(LuaType::Nil.to_string(), "nil");
197 }
198 #[test]
199 pub(super) fn test_lua_type_display_boolean() {
200 assert_eq!(LuaType::Boolean.to_string(), "boolean");
201 }
202 #[test]
203 pub(super) fn test_lua_type_display_number_int() {
204 assert_eq!(LuaType::Number(true).to_string(), "integer");
205 }
206 #[test]
207 pub(super) fn test_lua_type_display_number_float() {
208 assert_eq!(LuaType::Number(false).to_string(), "float");
209 }
210 #[test]
211 pub(super) fn test_lua_type_display_custom() {
212 assert_eq!(
213 LuaType::Custom("MyClass".to_string()).to_string(),
214 "MyClass"
215 );
216 }
217 #[test]
218 pub(super) fn test_lua_expr_nil() {
219 assert_eq!(LuaExpr::Nil.to_string(), "nil");
220 }
221 #[test]
222 pub(super) fn test_lua_expr_bool() {
223 assert_eq!(LuaExpr::True.to_string(), "true");
224 assert_eq!(LuaExpr::False.to_string(), "false");
225 }
226 #[test]
227 pub(super) fn test_lua_expr_int() {
228 assert_eq!(LuaExpr::Int(42).to_string(), "42");
229 assert_eq!(LuaExpr::Int(-7).to_string(), "-7");
230 }
231 #[test]
232 pub(super) fn test_lua_expr_float() {
233 assert_eq!(LuaExpr::Float(3.14).to_string(), "3.14");
234 assert_eq!(LuaExpr::Float(1.0).to_string(), "1.0");
235 }
236 #[test]
237 pub(super) fn test_lua_expr_str_escape() {
238 let s = LuaExpr::Str("hello\nworld\"test".to_string());
239 assert_eq!(s.to_string(), r#""hello\nworld\"test""#);
240 }
241 #[test]
242 pub(super) fn test_lua_expr_var() {
243 assert_eq!(LuaExpr::Var("x".to_string()).to_string(), "x");
244 }
245 #[test]
246 pub(super) fn test_lua_expr_binop() {
247 let e = LuaExpr::BinOp {
248 op: "+".to_string(),
249 lhs: Box::new(LuaExpr::Var("a".to_string())),
250 rhs: Box::new(LuaExpr::Int(1)),
251 };
252 assert_eq!(e.to_string(), "(a + 1)");
253 }
254 #[test]
255 pub(super) fn test_lua_expr_unary_not() {
256 let e = LuaExpr::UnaryOp {
257 op: "not".to_string(),
258 operand: Box::new(LuaExpr::True),
259 };
260 assert_eq!(e.to_string(), "(not true)");
261 }
262 #[test]
263 pub(super) fn test_lua_expr_call() {
264 let e = LuaExpr::Call {
265 func: Box::new(LuaExpr::Var("print".to_string())),
266 args: vec![LuaExpr::Str("hi".to_string())],
267 };
268 assert_eq!(e.to_string(), "print(\"hi\")");
269 }
270 #[test]
271 pub(super) fn test_lua_expr_method_call() {
272 let e = LuaExpr::MethodCall {
273 obj: Box::new(LuaExpr::Var("obj".to_string())),
274 method: "greet".to_string(),
275 args: vec![LuaExpr::Str("world".to_string())],
276 };
277 assert_eq!(e.to_string(), "obj:greet(\"world\")");
278 }
279 #[test]
280 pub(super) fn test_lua_expr_table_constructor() {
281 let e = LuaExpr::TableConstructor(vec![
282 LuaTableField::NamedField("x".to_string(), LuaExpr::Int(1)),
283 LuaTableField::ArrayItem(LuaExpr::Int(2)),
284 ]);
285 assert_eq!(e.to_string(), "{x = 1, 2}");
286 }
287 #[test]
288 pub(super) fn test_lua_expr_index_access() {
289 let e = LuaExpr::IndexAccess {
290 table: Box::new(LuaExpr::Var("t".to_string())),
291 key: Box::new(LuaExpr::Int(1)),
292 };
293 assert_eq!(e.to_string(), "t[1]");
294 }
295 #[test]
296 pub(super) fn test_lua_expr_field_access() {
297 let e = LuaExpr::FieldAccess {
298 table: Box::new(LuaExpr::Var("obj".to_string())),
299 field: "name".to_string(),
300 };
301 assert_eq!(e.to_string(), "obj.name");
302 }
303 #[test]
304 pub(super) fn test_lua_expr_ellipsis() {
305 assert_eq!(LuaExpr::Ellipsis.to_string(), "...");
306 }
307 #[test]
308 pub(super) fn test_lua_table_field_indexed() {
309 let f = LuaTableField::IndexedField(LuaExpr::Str("key".to_string()), LuaExpr::Int(99));
310 assert_eq!(f.to_string(), "[\"key\"] = 99");
311 }
312 #[test]
313 pub(super) fn test_lua_stmt_local_assign() {
314 let s = LuaStmt::LocalAssign {
315 names: vec!["x".to_string()],
316 attribs: vec![None],
317 values: vec![LuaExpr::Int(5)],
318 };
319 assert_eq!(s.to_string(), "local x = 5");
320 }
321 #[test]
322 pub(super) fn test_lua_stmt_local_attrib() {
323 let s = LuaStmt::LocalAssign {
324 names: vec!["x".to_string()],
325 attribs: vec![Some("const".to_string())],
326 values: vec![LuaExpr::Int(5)],
327 };
328 assert_eq!(s.to_string(), "local x <const> = 5");
329 }
330 #[test]
331 pub(super) fn test_lua_stmt_assign() {
332 let s = LuaStmt::Assign {
333 targets: vec![LuaExpr::Var("x".to_string())],
334 values: vec![LuaExpr::Int(42)],
335 };
336 assert_eq!(s.to_string(), "x = 42");
337 }
338 #[test]
339 pub(super) fn test_lua_stmt_return_empty() {
340 assert_eq!(LuaStmt::Return(vec![]).to_string(), "return");
341 }
342 #[test]
343 pub(super) fn test_lua_stmt_return_multi() {
344 let s = LuaStmt::Return(vec![LuaExpr::Int(1), LuaExpr::Int(2)]);
345 assert_eq!(s.to_string(), "return 1, 2");
346 }
347 #[test]
348 pub(super) fn test_lua_stmt_break() {
349 assert_eq!(LuaStmt::Break.to_string(), "break");
350 }
351 #[test]
352 pub(super) fn test_lua_stmt_goto_label() {
353 assert_eq!(
354 LuaStmt::Goto("continue".to_string()).to_string(),
355 "goto continue"
356 );
357 assert_eq!(
358 LuaStmt::Label("continue".to_string()).to_string(),
359 "::continue::"
360 );
361 }
362 #[test]
363 pub(super) fn test_lua_function_basic() {
364 let func = LuaFunction::new(
365 "add",
366 vec!["a".to_string(), "b".to_string()],
367 vec![LuaStmt::Return(vec![LuaExpr::BinOp {
368 op: "+".to_string(),
369 lhs: Box::new(LuaExpr::Var("a".to_string())),
370 rhs: Box::new(LuaExpr::Var("b".to_string())),
371 }])],
372 );
373 let s = func.to_string();
374 assert!(s.contains("function add(a, b)"));
375 assert!(s.contains("return (a + b)"));
376 assert!(s.contains("end"));
377 }
378 #[test]
379 pub(super) fn test_lua_function_local() {
380 let func = LuaFunction::new_local(
381 "helper",
382 vec!["x".to_string()],
383 vec![LuaStmt::Return(vec![LuaExpr::Var("x".to_string())])],
384 );
385 let s = format!("{}", LuaStmt::Local(func));
386 assert!(s.starts_with("local function helper(x)"));
387 }
388 #[test]
389 pub(super) fn test_lua_function_vararg() {
390 let func = LuaFunction {
391 name: Some("sum".to_string()),
392 params: vec![],
393 vararg: true,
394 body: vec![LuaStmt::Return(vec![LuaExpr::Ellipsis])],
395 is_local: false,
396 is_method: false,
397 };
398 let s = func.to_string();
399 assert!(s.contains("function sum(...)"));
400 }
401 #[test]
402 pub(super) fn test_lua_class_emit() {
403 let cls = LuaClass::new("Animal");
404 let s = cls.emit();
405 assert!(s.contains("Animal = {}"));
406 assert!(s.contains("Animal.__index = Animal"));
407 assert!(s.contains("function Animal:new(o)"));
408 }
409 #[test]
410 pub(super) fn test_lua_class_with_tostring() {
411 let mut cls = LuaClass::new("Dog");
412 cls.tostring_body = Some(vec![LuaStmt::Return(vec![LuaExpr::Str("Dog".to_string())])]);
413 let s = cls.emit();
414 assert!(s.contains("Dog.__tostring"));
415 assert!(s.contains("return \"Dog\""));
416 }
417 #[test]
418 pub(super) fn test_lua_module_emit_empty() {
419 let m = LuaModule::new();
420 assert_eq!(m.emit(), "");
421 }
422 #[test]
423 pub(super) fn test_lua_module_emit_with_require() {
424 let mut m = LuaModule::new();
425 m.requires.push(("json".to_string(), "dkjson".to_string()));
426 let s = m.emit();
427 assert!(s.contains("local json = require(\"dkjson\")"));
428 }
429 #[test]
430 pub(super) fn test_lua_module_emit_main_block() {
431 let mut m = LuaModule::new();
432 m.main_block.push(LuaStmt::Call(LuaExpr::Call {
433 func: Box::new(LuaExpr::Var("print".to_string())),
434 args: vec![LuaExpr::Str("hello".to_string())],
435 }));
436 let s = m.emit();
437 assert!(s.contains("print(\"hello\")"));
438 }
439 #[test]
440 pub(super) fn test_mangle_name_dot() {
441 let mut b = LuaBackend::new();
442 assert_eq!(b.mangle_name("Nat.add"), "Nat_add");
443 }
444 #[test]
445 pub(super) fn test_mangle_name_keyword() {
446 let mut b = LuaBackend::new();
447 assert_eq!(b.mangle_name("and"), "_and");
448 assert_eq!(b.mangle_name("end"), "_end");
449 }
450 #[test]
451 pub(super) fn test_mangle_name_empty() {
452 let mut b = LuaBackend::new();
453 assert_eq!(b.mangle_name(""), "_anon");
454 }
455 #[test]
456 pub(super) fn test_fresh_var() {
457 let mut b = LuaBackend::new();
458 assert_eq!(b.fresh_var(), "_t0");
459 assert_eq!(b.fresh_var(), "_t1");
460 }
461 #[test]
462 pub(super) fn test_compile_decl_simple() {
463 let decl = LcnfFunDecl {
464 name: "answer".to_string(),
465 original_name: None,
466 params: vec![],
467 ret_type: LcnfType::Nat,
468 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(42))),
469 is_recursive: false,
470 is_lifted: false,
471 inline_cost: 0,
472 };
473 let mut b = LuaBackend::new();
474 let func = b
475 .compile_decl(&decl)
476 .expect("func compilation should succeed");
477 assert_eq!(func.name, Some("answer".to_string()));
478 let s = func.to_string();
479 assert!(s.contains("return 42"), "Expected return 42, got: {}", s);
480 }
481 #[test]
482 pub(super) fn test_compile_decl_with_param() {
483 let x_id = LcnfVarId(0);
484 let decl = LcnfFunDecl {
485 name: "identity".to_string(),
486 original_name: None,
487 params: vec![LcnfParam {
488 id: x_id,
489 name: "x".to_string(),
490 ty: LcnfType::Nat,
491 erased: false,
492 borrowed: false,
493 }],
494 ret_type: LcnfType::Nat,
495 body: LcnfExpr::Return(LcnfArg::Var(x_id)),
496 is_recursive: false,
497 is_lifted: false,
498 inline_cost: 0,
499 };
500 let mut b = LuaBackend::new();
501 let func = b
502 .compile_decl(&decl)
503 .expect("func compilation should succeed");
504 let s = func.to_string();
505 assert!(s.contains("function identity("), "Got: {}", s);
506 assert!(s.contains("return"), "Got: {}", s);
507 }
508 #[test]
509 pub(super) fn test_emit_module_multiple_decls() {
510 let decl1 = LcnfFunDecl {
511 name: "one".to_string(),
512 original_name: None,
513 params: vec![],
514 ret_type: LcnfType::Nat,
515 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(1))),
516 is_recursive: false,
517 is_lifted: false,
518 inline_cost: 0,
519 };
520 let decl2 = LcnfFunDecl {
521 name: "two".to_string(),
522 original_name: None,
523 params: vec![],
524 ret_type: LcnfType::Nat,
525 body: LcnfExpr::Return(LcnfArg::Lit(LcnfLit::Nat(2))),
526 is_recursive: false,
527 is_lifted: false,
528 inline_cost: 0,
529 };
530 let mut b = LuaBackend::new();
531 let module = b.emit_module(&[decl1, decl2]);
532 assert_eq!(module.functions.len(), 2);
533 let s = module.emit();
534 assert!(s.contains("function one()"));
535 assert!(s.contains("function two()"));
536 }
537 #[test]
538 pub(super) fn test_for_loop_emit() {
539 let s = LuaStmt::For {
540 var: "i".to_string(),
541 start: LuaExpr::Int(1),
542 limit: LuaExpr::Int(10),
543 step: None,
544 body: vec![LuaStmt::Break],
545 };
546 let out = s.to_string();
547 assert!(out.contains("for i = 1, 10 do"));
548 assert!(out.contains("break"));
549 assert!(out.contains("end"));
550 }
551 #[test]
552 pub(super) fn test_for_loop_with_step() {
553 let s = LuaStmt::For {
554 var: "i".to_string(),
555 start: LuaExpr::Int(10),
556 limit: LuaExpr::Int(1),
557 step: Some(LuaExpr::Int(-1)),
558 body: vec![],
559 };
560 let out = s.to_string();
561 assert!(out.contains("for i = 10, 1, -1 do"));
562 }
563 #[test]
564 pub(super) fn test_for_in_emit() {
565 let s = LuaStmt::ForIn {
566 names: vec!["k".to_string(), "v".to_string()],
567 exprs: vec![LuaExpr::Call {
568 func: Box::new(LuaExpr::Var("pairs".to_string())),
569 args: vec![LuaExpr::Var("t".to_string())],
570 }],
571 body: vec![],
572 };
573 let out = s.to_string();
574 assert!(out.contains("for k, v in pairs(t) do"));
575 }
576 #[test]
577 pub(super) fn test_repeat_until_emit() {
578 let s = LuaStmt::Repeat {
579 body: vec![LuaStmt::Call(LuaExpr::Call {
580 func: Box::new(LuaExpr::Var("tick".to_string())),
581 args: vec![],
582 })],
583 cond: LuaExpr::Var("done".to_string()),
584 };
585 let out = s.to_string();
586 assert!(out.contains("repeat"));
587 assert!(out.contains("until done"));
588 }
589 #[test]
590 pub(super) fn test_do_block_emit() {
591 let s = LuaStmt::Do(vec![LuaStmt::LocalAssign {
592 names: vec!["x".to_string()],
593 attribs: vec![None],
594 values: vec![LuaExpr::Int(1)],
595 }]);
596 let out = s.to_string();
597 assert!(out.contains("do"));
598 assert!(out.contains("local x = 1"));
599 assert!(out.contains("end"));
600 }
601}
602#[cfg(test)]
603mod tests_lua_ext_extra {
604 use super::*;
605 #[test]
606 pub(super) fn test_lua_ext_config() {
607 let mut cfg = LuaExtConfig::new();
608 cfg.set("mode", "release");
609 cfg.set("verbose", "true");
610 assert_eq!(cfg.get("mode"), Some("release"));
611 assert!(cfg.get_bool("verbose"));
612 assert!(cfg.get_int("mode").is_none());
613 assert_eq!(cfg.len(), 2);
614 }
615 #[test]
616 pub(super) fn test_lua_ext_source_buffer() {
617 let mut buf = LuaExtSourceBuffer::new();
618 buf.push_line("fn main() {");
619 buf.indent();
620 buf.push_line("println!(\"hello\");");
621 buf.dedent();
622 buf.push_line("}");
623 assert!(buf.as_str().contains("fn main()"));
624 assert!(buf.as_str().contains(" println!"));
625 assert_eq!(buf.line_count(), 3);
626 buf.reset();
627 assert!(buf.is_empty());
628 }
629 #[test]
630 pub(super) fn test_lua_ext_name_scope() {
631 let mut scope = LuaExtNameScope::new();
632 assert!(scope.declare("x"));
633 assert!(!scope.declare("x"));
634 assert!(scope.is_declared("x"));
635 let scope = scope.push_scope();
636 assert_eq!(scope.depth(), 1);
637 let mut scope = scope.pop_scope();
638 assert_eq!(scope.depth(), 0);
639 scope.declare("y");
640 assert_eq!(scope.len(), 2);
641 }
642 #[test]
643 pub(super) fn test_lua_ext_diag_collector() {
644 let mut col = LuaExtDiagCollector::new();
645 col.emit(LuaExtDiagMsg::warning("pass_a", "slow"));
646 col.emit(LuaExtDiagMsg::error("pass_b", "fatal"));
647 assert!(col.has_errors());
648 assert_eq!(col.errors().len(), 1);
649 assert_eq!(col.warnings().len(), 1);
650 col.clear();
651 assert!(col.is_empty());
652 }
653 #[test]
654 pub(super) fn test_lua_ext_id_gen() {
655 let mut gen = LuaExtIdGen::new();
656 assert_eq!(gen.next_id(), 0);
657 assert_eq!(gen.next_id(), 1);
658 gen.skip(10);
659 assert_eq!(gen.next_id(), 12);
660 gen.reset();
661 assert_eq!(gen.peek_next(), 0);
662 }
663 #[test]
664 pub(super) fn test_lua_ext_incr_key() {
665 let k1 = LuaExtIncrKey::new(100, 200);
666 let k2 = LuaExtIncrKey::new(100, 200);
667 let k3 = LuaExtIncrKey::new(999, 200);
668 assert!(k1.matches(&k2));
669 assert!(!k1.matches(&k3));
670 }
671 #[test]
672 pub(super) fn test_lua_ext_profiler() {
673 let mut p = LuaExtProfiler::new();
674 p.record(LuaExtPassTiming::new("pass_a", 1000, 50, 200, 100));
675 p.record(LuaExtPassTiming::new("pass_b", 500, 30, 100, 200));
676 assert_eq!(p.total_elapsed_us(), 1500);
677 assert_eq!(
678 p.slowest_pass()
679 .expect("slowest pass should exist")
680 .pass_name,
681 "pass_a"
682 );
683 assert_eq!(p.profitable_passes().len(), 1);
684 }
685 #[test]
686 pub(super) fn test_lua_ext_event_log() {
687 let mut log = LuaExtEventLog::new(3);
688 log.push("event1");
689 log.push("event2");
690 log.push("event3");
691 assert_eq!(log.len(), 3);
692 log.push("event4");
693 assert_eq!(log.len(), 3);
694 assert_eq!(
695 log.iter()
696 .next()
697 .expect("iterator should have next element"),
698 "event2"
699 );
700 }
701 #[test]
702 pub(super) fn test_lua_ext_version() {
703 let v = LuaExtVersion::new(1, 2, 3).with_pre("alpha");
704 assert!(!v.is_stable());
705 assert_eq!(format!("{}", v), "1.2.3-alpha");
706 let stable = LuaExtVersion::new(2, 0, 0);
707 assert!(stable.is_stable());
708 assert!(stable.is_compatible_with(&LuaExtVersion::new(2, 0, 0)));
709 assert!(!stable.is_compatible_with(&LuaExtVersion::new(3, 0, 0)));
710 }
711 #[test]
712 pub(super) fn test_lua_ext_features() {
713 let mut f = LuaExtFeatures::new();
714 f.enable("sse2");
715 f.enable("avx2");
716 assert!(f.is_enabled("sse2"));
717 assert!(!f.is_enabled("avx512"));
718 f.disable("avx2");
719 assert!(!f.is_enabled("avx2"));
720 let mut g = LuaExtFeatures::new();
721 g.enable("sse2");
722 g.enable("neon");
723 let union = f.union(&g);
724 assert!(union.is_enabled("sse2") && union.is_enabled("neon"));
725 let inter = f.intersection(&g);
726 assert!(inter.is_enabled("sse2"));
727 }
728 #[test]
729 pub(super) fn test_lua_ext_emit_stats() {
730 let mut s = LuaExtEmitStats::new();
731 s.bytes_emitted = 50_000;
732 s.items_emitted = 500;
733 s.elapsed_ms = 100;
734 assert!(s.is_clean());
735 assert!((s.throughput_bps() - 500_000.0).abs() < 1.0);
736 let disp = format!("{}", s);
737 assert!(disp.contains("bytes=50000"));
738 }
739}
740#[cfg(test)]
741mod Lua_infra_tests {
742 use super::*;
743 #[test]
744 pub(super) fn test_pass_config() {
745 let config = LuaPassConfig::new("test_pass", LuaPassPhase::Transformation);
746 assert!(config.enabled);
747 assert!(config.phase.is_modifying());
748 assert_eq!(config.phase.name(), "transformation");
749 }
750 #[test]
751 pub(super) fn test_pass_stats() {
752 let mut stats = LuaPassStats::new();
753 stats.record_run(10, 100, 3);
754 stats.record_run(20, 200, 5);
755 assert_eq!(stats.total_runs, 2);
756 assert!((stats.average_changes_per_run() - 15.0).abs() < 0.01);
757 assert!((stats.success_rate() - 1.0).abs() < 0.01);
758 let s = stats.format_summary();
759 assert!(s.contains("Runs: 2/2"));
760 }
761 #[test]
762 pub(super) fn test_pass_registry() {
763 let mut reg = LuaPassRegistry::new();
764 reg.register(LuaPassConfig::new("pass_a", LuaPassPhase::Analysis));
765 reg.register(LuaPassConfig::new("pass_b", LuaPassPhase::Transformation).disabled());
766 assert_eq!(reg.total_passes(), 2);
767 assert_eq!(reg.enabled_count(), 1);
768 reg.update_stats("pass_a", 5, 50, 2);
769 let stats = reg.get_stats("pass_a").expect("stats should exist");
770 assert_eq!(stats.total_changes, 5);
771 }
772 #[test]
773 pub(super) fn test_analysis_cache() {
774 let mut cache = LuaAnalysisCache::new(10);
775 cache.insert("key1".to_string(), vec![1, 2, 3]);
776 assert!(cache.get("key1").is_some());
777 assert!(cache.get("key2").is_none());
778 assert!((cache.hit_rate() - 0.5).abs() < 0.01);
779 cache.invalidate("key1");
780 assert!(!cache.entries["key1"].valid);
781 assert_eq!(cache.size(), 1);
782 }
783 #[test]
784 pub(super) fn test_worklist() {
785 let mut wl = LuaWorklist::new();
786 assert!(wl.push(1));
787 assert!(wl.push(2));
788 assert!(!wl.push(1));
789 assert_eq!(wl.len(), 2);
790 assert_eq!(wl.pop(), Some(1));
791 assert!(!wl.contains(1));
792 assert!(wl.contains(2));
793 }
794 #[test]
795 pub(super) fn test_dominator_tree() {
796 let mut dt = LuaDominatorTree::new(5);
797 dt.set_idom(1, 0);
798 dt.set_idom(2, 0);
799 dt.set_idom(3, 1);
800 assert!(dt.dominates(0, 3));
801 assert!(dt.dominates(1, 3));
802 assert!(!dt.dominates(2, 3));
803 assert!(dt.dominates(3, 3));
804 }
805 #[test]
806 pub(super) fn test_liveness() {
807 let mut liveness = LuaLivenessInfo::new(3);
808 liveness.add_def(0, 1);
809 liveness.add_use(1, 1);
810 assert!(liveness.defs[0].contains(&1));
811 assert!(liveness.uses[1].contains(&1));
812 }
813 #[test]
814 pub(super) fn test_constant_folding() {
815 assert_eq!(LuaConstantFoldingHelper::fold_add_i64(3, 4), Some(7));
816 assert_eq!(LuaConstantFoldingHelper::fold_div_i64(10, 0), None);
817 assert_eq!(LuaConstantFoldingHelper::fold_div_i64(10, 2), Some(5));
818 assert_eq!(
819 LuaConstantFoldingHelper::fold_bitand_i64(0b1100, 0b1010),
820 0b1000
821 );
822 assert_eq!(LuaConstantFoldingHelper::fold_bitnot_i64(0), -1);
823 }
824 #[test]
825 pub(super) fn test_dep_graph() {
826 let mut g = LuaDepGraph::new();
827 g.add_dep(1, 2);
828 g.add_dep(2, 3);
829 g.add_dep(1, 3);
830 assert_eq!(g.dependencies_of(2), vec![1]);
831 let topo = g.topological_sort();
832 assert_eq!(topo.len(), 3);
833 assert!(!g.has_cycle());
834 let pos: std::collections::HashMap<u32, usize> =
835 topo.iter().enumerate().map(|(i, &n)| (n, i)).collect();
836 assert!(pos[&1] < pos[&2]);
837 assert!(pos[&1] < pos[&3]);
838 assert!(pos[&2] < pos[&3]);
839 }
840}
841#[cfg(test)]
842mod luaext_pass_tests {
843 use super::*;
844 #[test]
845 pub(super) fn test_luaext_phase_order() {
846 assert_eq!(LuaExtPassPhase::Early.order(), 0);
847 assert_eq!(LuaExtPassPhase::Middle.order(), 1);
848 assert_eq!(LuaExtPassPhase::Late.order(), 2);
849 assert_eq!(LuaExtPassPhase::Finalize.order(), 3);
850 assert!(LuaExtPassPhase::Early.is_early());
851 assert!(!LuaExtPassPhase::Early.is_late());
852 }
853 #[test]
854 pub(super) fn test_luaext_config_builder() {
855 let c = LuaExtPassConfig::new("p")
856 .with_phase(LuaExtPassPhase::Late)
857 .with_max_iter(50)
858 .with_debug(1);
859 assert_eq!(c.name, "p");
860 assert_eq!(c.max_iterations, 50);
861 assert!(c.is_debug_enabled());
862 assert!(c.enabled);
863 let c2 = c.disabled();
864 assert!(!c2.enabled);
865 }
866 #[test]
867 pub(super) fn test_luaext_stats() {
868 let mut s = LuaExtPassStats::new();
869 s.visit();
870 s.visit();
871 s.modify();
872 s.iterate();
873 assert_eq!(s.nodes_visited, 2);
874 assert_eq!(s.nodes_modified, 1);
875 assert!(s.changed);
876 assert_eq!(s.iterations, 1);
877 let e = s.efficiency();
878 assert!((e - 0.5).abs() < 1e-9);
879 }
880 #[test]
881 pub(super) fn test_luaext_registry() {
882 let mut r = LuaExtPassRegistry::new();
883 r.register(LuaExtPassConfig::new("a").with_phase(LuaExtPassPhase::Early));
884 r.register(LuaExtPassConfig::new("b").disabled());
885 assert_eq!(r.len(), 2);
886 assert_eq!(r.enabled_passes().len(), 1);
887 assert_eq!(r.passes_in_phase(&LuaExtPassPhase::Early).len(), 1);
888 }
889 #[test]
890 pub(super) fn test_luaext_cache() {
891 let mut c = LuaExtCache::new(4);
892 assert!(c.get(99).is_none());
893 c.put(99, vec![1, 2, 3]);
894 let v = c.get(99).expect("v should be present in map");
895 assert_eq!(v, &[1u8, 2, 3]);
896 assert!(c.hit_rate() > 0.0);
897 assert_eq!(c.live_count(), 1);
898 }
899 #[test]
900 pub(super) fn test_luaext_worklist() {
901 let mut w = LuaExtWorklist::new(10);
902 w.push(5);
903 w.push(3);
904 w.push(5);
905 assert_eq!(w.len(), 2);
906 assert!(w.contains(5));
907 let first = w.pop().expect("first should be available to pop");
908 assert!(!w.contains(first));
909 }
910 #[test]
911 pub(super) fn test_luaext_dom_tree() {
912 let mut dt = LuaExtDomTree::new(5);
913 dt.set_idom(1, 0);
914 dt.set_idom(2, 0);
915 dt.set_idom(3, 1);
916 dt.set_idom(4, 1);
917 assert!(dt.dominates(0, 3));
918 assert!(dt.dominates(1, 4));
919 assert!(!dt.dominates(2, 3));
920 assert_eq!(dt.depth_of(3), 2);
921 }
922 #[test]
923 pub(super) fn test_luaext_liveness() {
924 let mut lv = LuaExtLiveness::new(3);
925 lv.add_def(0, 1);
926 lv.add_use(1, 1);
927 assert!(lv.var_is_def_in_block(0, 1));
928 assert!(lv.var_is_used_in_block(1, 1));
929 assert!(!lv.var_is_def_in_block(1, 1));
930 }
931 #[test]
932 pub(super) fn test_luaext_const_folder() {
933 let mut cf = LuaExtConstFolder::new();
934 assert_eq!(cf.add_i64(3, 4), Some(7));
935 assert_eq!(cf.div_i64(10, 0), None);
936 assert_eq!(cf.mul_i64(6, 7), Some(42));
937 assert_eq!(cf.and_i64(0b1100, 0b1010), 0b1000);
938 assert_eq!(cf.fold_count(), 3);
939 assert_eq!(cf.failure_count(), 1);
940 }
941 #[test]
942 pub(super) fn test_luaext_dep_graph() {
943 let mut g = LuaExtDepGraph::new(4);
944 g.add_edge(0, 1);
945 g.add_edge(1, 2);
946 g.add_edge(2, 3);
947 assert!(!g.has_cycle());
948 assert_eq!(g.topo_sort(), Some(vec![0, 1, 2, 3]));
949 assert_eq!(g.reachable(0).len(), 4);
950 let sccs = g.scc();
951 assert_eq!(sccs.len(), 4);
952 }
953}