Skip to content

Commit 63726d9

Browse files
authored
[Lua] Genlua cleanup (#12480)
* [lua] Clean up TODOs and fix switch side-effect bug - Fix: switch with empty cases now evaluates subject for side effects - Simplify newprop (both branches were identical) - Replace TODO comments with explanatory comments: - kwds: explain purpose for field quoting and identifier escaping - index_of: note OCaml 5.1 added List.find_index - IIFE patterns: explain why they're needed for correctness - enum generation: clarify _hxClasses and _estr usage - Remove obsolete TODOs (temp variables, array helpers) * [lua] Add test for switch subject side-effect evaluation Adds a test to verify that switch statements with only a default case (empty cases array) still evaluate the subject expression for its side effects. This test covers the bug fixed in the previous commit.
1 parent b621f2b commit 63726d9

File tree

2 files changed

+41
-20
lines changed

2 files changed

+41
-20
lines changed

src/generators/genlua.ml

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ let s_escape_lua ?(dec=true) s =
104104
done;
105105
Buffer.contents b
106106

107-
(* TODO: are all these kwds necessary for field quotes *and* id escapes? *)
107+
(* Lua reserved keywords - used for both field quoting (obj["end"]) and identifier escaping (_end) *)
108108
let kwds =
109109
let h = Hashtbl.create 0 in
110110
List.iter (fun s -> Hashtbl.add h s ()) [
@@ -174,11 +174,8 @@ let basename path =
174174
String.sub path (idx + 1) (String.length path - idx - 1)
175175
with Not_found -> path
176176

177-
(* TODO : is this necessary any more?*)
178177
let newprop ctx =
179-
match Buffer.nth ctx.buf (Buffer.length ctx.buf - 1) with
180-
| '{' -> print ctx "\n%s" ctx.tabs
181-
| _ -> print ctx "\n%s" ctx.tabs
178+
print ctx "\n%s" ctx.tabs
182179

183180
let semicolon ctx =
184181
match Buffer.nth ctx.buf (Buffer.length ctx.buf - 1) with
@@ -241,10 +238,8 @@ let is_dot_access e cf =
241238
| _ ->
242239
false
243240

244-
(*
245-
return index of a first element in the list for which `f` returns true
246-
TODO: is there some standard function to do that?
247-
*)
241+
(* Return index of first element in the list for which `f` returns true.
242+
Note: List.find_index was added in OCaml 5.1, keeping custom impl for compatibility. *)
248243
let index_of f l =
249244
let rec find lst idx =
250245
match lst with
@@ -471,7 +466,6 @@ and gen_call ctx e el =
471466
| [] -> ()
472467
| e :: _ -> gen_value ctx e)
473468
| TIdent "__resources__", [] ->
474-
(* TODO: Array declaration helper *)
475469
spr ctx "_hx_tab_array({";
476470
let count = ref 0 in
477471
concat ctx "," (fun (name,data) ->
@@ -894,7 +888,7 @@ and gen_expr ?(local=true) ctx e = begin
894888
spr ctx "end";
895889
ctx.iife_assign <- false;
896890
| TUnop ((Increment|Decrement) as op,unop_flag, e) ->
897-
(* TODO: Refactor this mess *)
891+
(* Uses IIFE to handle pre/post increment on arrays and fields correctly *)
898892
println ctx "(function() ";
899893
(match e.eexpr, unop_flag with
900894
| TArray(e1,e2), _ ->
@@ -986,7 +980,6 @@ and gen_expr ?(local=true) ctx e = begin
986980
spr ctx "})";
987981
ctx.separator <- true
988982
| TTry (e,catchs) ->
989-
(* TODO: add temp variables *)
990983
let old_in_loop_try = ctx.in_loop_try in
991984
if ctx.in_loop then
992985
ctx.in_loop_try <- true;
@@ -1099,8 +1092,11 @@ and gen_block_element ctx e =
10991092
| Increment -> print ctx " + 1;"
11001093
| _ -> print ctx " - 1;"
11011094
)
1102-
| TSwitch {switch_subject = e; switch_cases = [];switch_default = def} ->
1103-
(* TODO: this omits the subject which is not correct in the general case *)
1095+
| TSwitch {switch_subject = subj; switch_cases = [];switch_default = def} ->
1096+
(* Evaluate the subject for side effects even when there are no cases *)
1097+
newline ctx;
1098+
gen_expr ctx subj;
1099+
semicolon ctx;
11041100
(match def with
11051101
| None -> ()
11061102
| Some e -> gen_block_element ctx e)
@@ -1387,7 +1383,7 @@ and gen_tbinop ctx op e1 e2 =
13871383
spr ctx " end)()";
13881384
end;
13891385
| Ast.OpAssignOp(op2), TArray(e3,e4), _ ->
1390-
(* TODO: Figure out how to rewrite this expression more cleanly *)
1386+
(* IIFE ensures array index is evaluated once for compound assignment like arr[i] += 1 *)
13911387
println ctx "(function() ";
13921388
let idx = alloc_var VGenerated "idx" e4.etype e4.epos in
13931389
let idx_var = mk (TVar( idx , Some(e4))) e4.etype e4.epos in
@@ -1405,7 +1401,7 @@ and gen_tbinop ctx op e1 e2 =
14051401
spr ctx "return arr[idx]";
14061402
spr ctx " end)()";
14071403
| Ast.OpAssignOp(op2), TField(e3,e4), _ ->
1408-
(* TODO: Figure out how to rewrite this expression more cleanly *)
1404+
(* IIFE ensures field target is evaluated once for compound assignment like obj.x += 1 *)
14091405
println ctx "(function() ";
14101406
let obj = alloc_var VGenerated "obj" e3.etype e3.epos in
14111407
spr ctx "local fld = ";
@@ -1432,7 +1428,7 @@ and gen_tbinop ctx op e1 e2 =
14321428
spr ctx "return obj[fld]";
14331429
spr ctx " end)()";
14341430
| Ast.OpAssignOp(op2),_,_ ->
1435-
(* TODO: Rewrite expression *)
1431+
(* IIFE for compound assignment to return the new value *)
14361432
spr ctx "(function() "; gen_value ctx e1;
14371433
spr ctx " = "; gen_tbinop ctx op2 e1 e2;
14381434
spr ctx " return "; gen_value ctx e1;
@@ -1750,7 +1746,7 @@ let generate_enum ctx e =
17501746
let p = s_path ctx e.e_path in
17511747
let ename = List.map s_escape_lua (fst e.e_path @ [snd e.e_path]) in
17521748

1753-
(* TODO: Unify the _hxClasses declaration *)
1749+
(* Register enum in _hxClasses for reflection - different features need different metadata *)
17541750
if has_feature ctx "Type.resolveEnum" then begin
17551751
print ctx "_hxClasses[\"%s\"] = %s" (dot_path e.e_path) p; semicolon ctx; newline ctx;
17561752
end;
@@ -1759,7 +1755,6 @@ let generate_enum ctx e =
17591755
if has_feature ctx "lua.Boot.isEnum" then begin
17601756
print ctx " __ename__ = %s," (if has_feature ctx "Type.getEnumName" then "\"" ^ String.concat "." ename ^ "\"" else "true");
17611757
end;
1762-
(* TODO : Come up with a helper function for _hx_tab_array declarations *)
17631758
spr ctx " __constructs__ = _hx_tab_array({";
17641759
if ((List.length e.e_names) > 0) then begin
17651760
spr ctx "[0]=";
@@ -1783,7 +1778,7 @@ let generate_enum ctx e =
17831778
let sargs = String.concat "," (List.map (fun (n,_,_) -> ident n) args) in
17841779
print ctx "function(%s) local _x = _hx_tab_array({[0]=\"%s\",%d,%s,__enum__=%s}, %i);" sargs f.ef_name f.ef_index sargs p (count + 2);
17851780
if has_feature ctx "may_print_enum" then
1786-
(* TODO: better namespacing for _estr *)
1781+
(* _estr is the enum toString helper defined in the runtime *)
17871782
spr ctx " rawset(_x, 'toString', _estr);";
17881783
spr ctx " return _x; end ";
17891784
ctx.separator <- true;

tests/unit/src/unit/TestLua.hx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,32 @@ class TestLua extends Test {
4747
untyped _hx_box_mr = old_hx_box_mr;
4848
}
4949

50+
function testSwitchSubjectSideEffects() {
51+
// Test that switch subject is evaluated for side effects even with empty cases.
52+
// This was a bug where switch with only default (no explicit cases) would
53+
// skip evaluating the subject expression entirely.
54+
var counter = 0;
55+
var getValue = function() {
56+
counter++;
57+
return 42;
58+
};
59+
60+
// Switch with only default (empty cases array) - subject must still be evaluated
61+
switch (getValue()) {
62+
default:
63+
}
64+
eq(counter, 1);
65+
66+
// Switch with only default that has a body
67+
counter = 0;
68+
var result = 0;
69+
switch (getValue()) {
70+
default: result = 99;
71+
}
72+
eq(counter, 1);
73+
eq(result, 99);
74+
}
75+
5076
function testMetatablesAreShared() {
5177

5278
// New class instances get metatables assigned to them

0 commit comments

Comments
 (0)