Skip to content

Commit 1684d48

Browse files
committed
Add a macro to detect infinite loops
1 parent f334978 commit 1684d48

File tree

2 files changed

+95
-0
lines changed

2 files changed

+95
-0
lines changed

build-cli.hxml

+2
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,6 @@
22
--main loreline.cli.Cli
33
--library yaml
44
--library hscript
5+
--macro addGlobalMetadata('loreline', '@:build(loreline.macros.InfiniteLoop.build())', true, true, false)
6+
--macro addGlobalMetadata('hscript', '@:build(loreline.macros.InfiniteLoop.build())', true, true, false)
57
-D hscriptPos

src/loreline/macros/InfiniteLoop.hx

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package loreline.macros;
2+
3+
#if macro
4+
5+
import haxe.macro.Context;
6+
import haxe.macro.Expr;
7+
import haxe.macro.ExprTools;
8+
9+
class InfiniteLoop {
10+
11+
public static function build():Array<Field> {
12+
13+
final fields = Context.getBuildFields();
14+
15+
#if !(display || completion)
16+
17+
// Loop through all fields
18+
for (field in fields) {
19+
switch field.kind {
20+
case FVar(t, e):
21+
if (e != null) {
22+
field.kind = FVar(t, processWhileLoops(e));
23+
}
24+
case FProp(get, set, t, e):
25+
if (e != null) {
26+
field.kind = FProp(get, set, t, processWhileLoops(e));
27+
}
28+
case FFun(f):
29+
if (f.expr != null) {
30+
f.expr = processWhileLoops(f.expr);
31+
}
32+
}
33+
}
34+
35+
#end
36+
37+
return fields;
38+
39+
}
40+
41+
static function processWhileLoops(e:Expr):Expr {
42+
43+
return switch e.expr {
44+
45+
case EWhile(cond, body, normalWhile):
46+
// Transform while loops by adding infinite loop detection
47+
var loopCounter = macro var __loopCounter = 0;
48+
var posStr = ""+e.pos;
49+
var checkCounter = macro {
50+
__loopCounter++;
51+
if (__loopCounter > 1000000) {
52+
throw 'Infinite loop ' + $v{posStr};
53+
}
54+
};
55+
// Prepend the counter check to the loop body
56+
var newBody = macro {
57+
$checkCounter;
58+
${processWhileLoops(body)};
59+
};
60+
61+
// Create a block with loop counter initialization and the transformed while loop
62+
{
63+
expr: EBlock([
64+
loopCounter,
65+
{
66+
expr: EWhile(cond, newBody, normalWhile),
67+
pos: e.pos
68+
}
69+
]),
70+
pos: e.pos
71+
};
72+
73+
case _:
74+
ExprTools.map(e, e -> {
75+
try {
76+
return processWhileLoops(e);
77+
}
78+
catch (err:Any) {
79+
// Why is this happening when using haxe completion server?
80+
trace('Exception of type: ' + Type.getClass(err));
81+
if (Std.string(err) != 'Stack overflow') {
82+
throw err;
83+
}
84+
}
85+
return e;
86+
});
87+
}
88+
89+
}
90+
91+
}
92+
93+
#end

0 commit comments

Comments
 (0)