Skip to content

Commit c367623

Browse files
committed
more fixes & tests
1 parent 83bc137 commit c367623

5 files changed

Lines changed: 265 additions & 9 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaNatives.java

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,23 +103,56 @@ public class LuaNatives {
103103
f.getBody().add(LuaAst.LuaLiteral("return math.modf(x)"));
104104
});
105105

106-
addNative("InitHashtable", f -> f.getBody().add(LuaAst.LuaLiteral("return {}")));
106+
addNative(Arrays.asList("InitHashtable", "__wurst_InitHashtable"), f -> f.getBody().add(LuaAst.LuaLiteral("return {}")));
107107

108-
addNative(Arrays.asList("SaveInteger", "SaveBoolean", "SaveReal", "SaveStr", "SaveBoolean"), f -> {
108+
addNative(Arrays.asList(
109+
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
110+
"__wurst_SaveInteger", "__wurst_SaveBoolean", "__wurst_SaveReal", "__wurst_SaveStr"), f -> {
109111
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
110112
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
111113
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
112114
f.getParams().add(LuaAst.LuaVariable("i", LuaAst.LuaNoExpr()));
113115
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then h[p] = {} end h[p][c] = i"));
114116
});
115117

116-
addNative(Arrays.asList("LoadInteger", "LoadBoolean", "LoadReal", "LoadStr", "LoadBoolean"), f -> {
118+
addNative(Arrays.asList(
119+
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
120+
"__wurst_LoadInteger", "__wurst_LoadBoolean", "__wurst_LoadReal", "__wurst_LoadStr"), f -> {
117121
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
118122
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
119123
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
120124
f.getBody().add(LuaAst.LuaLiteral("if not h[p] then return nil end return h[p][c]"));
121125
});
122126

127+
addNative(Arrays.asList(
128+
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
129+
"__wurst_HaveSavedInteger", "__wurst_HaveSavedBoolean", "__wurst_HaveSavedReal", "__wurst_HaveSavedString"), f -> {
130+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
131+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
132+
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
133+
f.getBody().add(LuaAst.LuaLiteral("return h[p] ~= nil and h[p][c] ~= nil"));
134+
});
135+
136+
addNative(Arrays.asList("FlushChildHashtable", "__wurst_FlushChildHashtable"), f -> {
137+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
138+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
139+
f.getBody().add(LuaAst.LuaLiteral("h[p] = nil"));
140+
});
141+
142+
addNative(Arrays.asList("FlushParentHashtable", "__wurst_FlushParentHashtable"), f -> {
143+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
144+
f.getBody().add(LuaAst.LuaLiteral("for k in pairs(h) do h[k] = nil end"));
145+
});
146+
147+
addNative(Arrays.asList(
148+
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString",
149+
"__wurst_RemoveSavedInteger", "__wurst_RemoveSavedBoolean", "__wurst_RemoveSavedReal", "__wurst_RemoveSavedString"), f -> {
150+
f.getParams().add(LuaAst.LuaVariable("h", LuaAst.LuaNoExpr()));
151+
f.getParams().add(LuaAst.LuaVariable("p", LuaAst.LuaNoExpr()));
152+
f.getParams().add(LuaAst.LuaVariable("c", LuaAst.LuaNoExpr()));
153+
f.getBody().add(LuaAst.LuaLiteral("if h[p] then h[p][c] = nil end"));
154+
});
155+
123156
addNative("typeIdToTypeName", f -> {
124157
f.getParams().add(LuaAst.LuaVariable("typeId", LuaAst.LuaNoExpr()));
125158
f.getBody().add(LuaAst.LuaLiteral("return \"\""));

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
import static de.peeeq.wurstscript.translation.lua.translation.ExprTranslation.WURST_SUPERTYPES;
2222

2323
public class LuaTranslator {
24+
private static final Set<String> HASHTABLE_NATIVE_NAMES = new HashSet<>(Arrays.asList(
25+
"InitHashtable",
26+
"SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
27+
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr",
28+
"HaveSavedInteger", "HaveSavedBoolean", "HaveSavedReal", "HaveSavedString",
29+
"FlushChildHashtable", "FlushParentHashtable",
30+
"RemoveSavedInteger", "RemoveSavedBoolean", "RemoveSavedReal", "RemoveSavedString"
31+
));
2432

2533
final ImProg prog;
2634
final LuaCompilationUnit luaModel;
@@ -74,7 +82,7 @@ public LuaVariable initFor(ImVar a) {
7482

7583
@Override
7684
public LuaFunction initFor(ImFunction a) {
77-
String name = a.getName();
85+
String name = remapNativeName(a.getName());
7886
if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(a)) {
7987
name = uniqueName(name);
8088
} else if (isFixedEntryPoint(a)) {
@@ -163,6 +171,13 @@ public LuaTranslator(ImProg prog, ImTranslator imTr) {
163171
luaModel = LuaAst.LuaCompilationUnit();
164172
}
165173

174+
private String remapNativeName(String name) {
175+
if (HASHTABLE_NATIVE_NAMES.contains(name)) {
176+
return "__wurst_" + name;
177+
}
178+
return name;
179+
}
180+
166181
protected String uniqueName(String name) {
167182
int i = 0;
168183
String rname = name;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package tests.wurstscript.tests;
2+
3+
import de.peeeq.wurstscript.luaAst.LuaAst;
4+
import de.peeeq.wurstscript.luaAst.LuaFunction;
5+
import de.peeeq.wurstscript.translation.lua.translation.LuaNatives;
6+
import org.testng.annotations.Test;
7+
8+
import static org.testng.AssertJUnit.assertTrue;
9+
10+
public class LuaNativesTests {
11+
12+
private static String renderNative(String name) {
13+
LuaFunction f = LuaAst.LuaFunction(name, LuaAst.LuaParams(), LuaAst.LuaStatements());
14+
LuaNatives.get(f);
15+
StringBuilder sb = new StringBuilder();
16+
f.print(sb, 0);
17+
return sb.toString();
18+
}
19+
20+
@Test
21+
public void s2iUsesPrefixIntegerParsing() {
22+
String rendered = renderNative("S2I");
23+
assertTrue(rendered.contains("string.match(tostring(x), \"^[%+%-]?%d+\")"));
24+
assertTrue(rendered.contains("return tonumber(m)"));
25+
}
26+
27+
@Test
28+
public void r2iUsesTruncationTowardZero() {
29+
String rendered = renderNative("R2I");
30+
assertTrue(rendered.contains("return math.modf(x)"));
31+
}
32+
33+
@Test
34+
public void getRandomRealUsesRangeFormula() {
35+
String rendered = renderNative("GetRandomReal");
36+
assertTrue(rendered.contains("return l + math.random() * (h - l)"));
37+
}
38+
39+
@Test
40+
public void triggerEvaluateReturnsBoolInFallback() {
41+
String rendered = renderNative("TriggerEvaluate");
42+
assertTrue(rendered.contains("for i,a in ipairs(t.actions) do a() end"));
43+
assertTrue(rendered.contains("return true"));
44+
}
45+
}
46+

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ private void assertFunctionBodyContains(String output, String functionName, Stri
5757
assertTrue("Function " + functionName + " was not found.", found);
5858
}
5959

60+
private void assertDoesNotContainRegex(String output, String regex) {
61+
Pattern pattern = Pattern.compile(regex);
62+
Matcher matcher = pattern.matcher(output);
63+
assertFalse("Pattern must not occur: " + regex, matcher.find());
64+
}
65+
6066
@Test
6167
public void testStdLib() throws IOException {
6268
test().testLua(true).withStdLib().lines(
@@ -321,6 +327,28 @@ public void objectIndexFunctionsDoNotCollideWithUserFunctions() throws IOExcepti
321327
assertFunctionBodyContains(compiled, "testClass", "__wurst_objectFromIndex", true);
322328
}
323329

330+
@Test
331+
public void oldGenericsCastingDoesNotUseGetHandleId() throws IOException {
332+
test().testLua(true).withStdLib().lines(
333+
"package Test",
334+
"class C",
335+
"native takesInt(int i)",
336+
"native takesC(C c)",
337+
"function testCast()",
338+
" let cObj = new C()",
339+
" let cInt = cObj castTo int",
340+
" let cObj2 = cInt castTo C",
341+
" takesInt(cInt)",
342+
" takesC(cObj2)",
343+
"init",
344+
" testCast()"
345+
);
346+
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_oldGenericsCastingDoesNotUseGetHandleId.lua"), Charsets.UTF_8);
347+
assertDoesNotContainRegex(compiled, "\\bGetHandleId\\(");
348+
assertFunctionBodyContains(compiled, "testCast", "__wurst_objectToIndex", true);
349+
assertFunctionBodyContains(compiled, "testCast", "__wurst_objectFromIndex", true);
350+
}
351+
324352
@Test
325353
public void reflectionNativesStubbedForLua() throws IOException {
326354
test().testLua(true).lines(
@@ -405,4 +433,38 @@ public void stdLibInitUsesTriggerEvaluateGuardInMain() throws IOException {
405433
assertTrue(compiled.contains("TriggerClearConditions"));
406434
}
407435

436+
@Test
437+
public void stdLibDoesNotEmitTimerBjNatives() throws IOException {
438+
test().testLua(true).withStdLib().lines(
439+
"package Test",
440+
"init",
441+
" skip"
442+
);
443+
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_stdLibDoesNotEmitTimerBjNatives.lua"), Charsets.UTF_8);
444+
assertDoesNotContainRegex(compiled, "\\bCreateTimerBJ\\(");
445+
assertDoesNotContainRegex(compiled, "\\bStartTimerBJ\\(");
446+
assertDoesNotContainRegex(compiled, "\\bGetLastCreatedTimerBJ\\(");
447+
assertFalse(compiled.contains("bj_lastStartedTimer"));
448+
}
449+
450+
@Test
451+
public void stdLibDoesNotEmitWar3HashtableNatives() throws IOException {
452+
test().testLua(true).withStdLib().lines(
453+
"package Test",
454+
"init",
455+
" skip"
456+
);
457+
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_stdLibDoesNotEmitWar3HashtableNatives.lua"), Charsets.UTF_8);
458+
assertDoesNotContainRegex(compiled, "\\bInitHashtable\\(");
459+
assertDoesNotContainRegex(compiled, "\\bSaveInteger\\(");
460+
assertDoesNotContainRegex(compiled, "\\bSaveBoolean\\(");
461+
assertDoesNotContainRegex(compiled, "\\bSaveReal\\(");
462+
assertDoesNotContainRegex(compiled, "\\bSaveStr\\(");
463+
assertDoesNotContainRegex(compiled, "\\bLoadInteger\\(");
464+
assertDoesNotContainRegex(compiled, "\\bLoadBoolean\\(");
465+
assertDoesNotContainRegex(compiled, "\\bLoadReal\\(");
466+
assertDoesNotContainRegex(compiled, "\\bLoadStr\\(");
467+
assertDoesNotContainRegex(compiled, "\\bFlushChildHashtable\\(");
468+
}
469+
408470
}

de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstScriptTest.java

Lines changed: 105 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import de.peeeq.wurstscript.jassinterpreter.TestSuccessException;
2525
import de.peeeq.wurstscript.jassprinter.JassPrinter;
2626
import de.peeeq.wurstscript.luaAst.LuaCompilationUnit;
27+
import de.peeeq.wurstscript.luaAst.*;
2728
import de.peeeq.wurstscript.translation.imtranslation.ImTranslator;
2829
import de.peeeq.wurstscript.translation.imtranslation.RecycleCodeGeneratorQueue;
2930
import de.peeeq.wurstscript.utils.Utils;
@@ -460,6 +461,7 @@ private void translateAndTestLua(String name, boolean executeProg, WurstGui gui,
460461
compiler.runCompiletime(new WurstProjectConfigData(), false, false);
461462

462463
LuaCompilationUnit luaCode = compiler.transformProgToLua();
464+
checkLuaRootPurity(luaCode);
463465
StringBuilder sb = new StringBuilder();
464466
luaCode.print(sb, 0);
465467

@@ -543,16 +545,114 @@ private void checkLuaSyntax(String luaExecutable, File luaFile) throws IOExcepti
543545

544546
private String getLuaExecutable() {
545547
File bundledLuaWin = new File("src/test/resources/lua53.exe");
546-
if (bundledLuaWin.exists()) {
547-
return bundledLuaWin.getPath();
548-
}
549548
File bundledLuaUnix = new File("src/test/resources/lua53");
550-
if (bundledLuaUnix.exists()) {
551-
return bundledLuaUnix.getPath();
549+
String osName = System.getProperty("os.name", "").toLowerCase();
550+
boolean isWindows = osName.contains("win");
551+
552+
if (isWindows) {
553+
if (bundledLuaWin.exists()) {
554+
return bundledLuaWin.getPath();
555+
}
556+
if (bundledLuaUnix.exists()) {
557+
return bundledLuaUnix.getPath();
558+
}
559+
} else {
560+
if (bundledLuaUnix.exists()) {
561+
// best effort in case execute bit was lost by checkout settings
562+
// (e.g. core.filemode false on some environments)
563+
bundledLuaUnix.setExecutable(true);
564+
if (bundledLuaUnix.canExecute()) {
565+
return bundledLuaUnix.getPath();
566+
}
567+
}
568+
if (bundledLuaWin.exists() && bundledLuaWin.canExecute()) {
569+
return bundledLuaWin.getPath();
570+
}
552571
}
553572
return "lua";
554573
}
555574

575+
private void checkLuaRootPurity(LuaCompilationUnit luaCode) {
576+
Set<String> forbiddenRootCalls = Set.of(
577+
"CreateTrigger", "CreateTimer", "CreateUnit", "DestroyTrigger", "TimerStart",
578+
"CreateTimerBJ", "StartTimerBJ", "GetLastCreatedTimerBJ",
579+
"InitHashtable", "SaveInteger", "SaveBoolean", "SaveReal", "SaveStr",
580+
"LoadInteger", "LoadBoolean", "LoadReal", "LoadStr", "FlushChildHashtable"
581+
);
582+
583+
for (LuaStatement stmt : luaCode) {
584+
if (stmt instanceof LuaVariable) {
585+
LuaExprOpt initialValue = ((LuaVariable) stmt).getInitialValue();
586+
if (initialValue instanceof LuaExpr) {
587+
assertNoForbiddenRootCall((LuaExpr) initialValue, forbiddenRootCalls);
588+
}
589+
} else if (stmt instanceof LuaAssignment) {
590+
assertNoForbiddenRootCall(((LuaAssignment) stmt).getRight(), forbiddenRootCalls);
591+
} else if (stmt instanceof LuaExprFunctionCallByName) {
592+
String funcName = ((LuaExprFunctionCallByName) stmt).getFuncName();
593+
if (forbiddenRootCalls.contains(funcName)) {
594+
throw new Error("Lua root purity violation: forbidden root call to " + funcName);
595+
}
596+
} else if (stmt instanceof LuaExprFunctionCall) {
597+
String funcName = ((LuaExprFunctionCall) stmt).getFunc().getName();
598+
if (forbiddenRootCalls.contains(funcName)) {
599+
throw new Error("Lua root purity violation: forbidden root call to " + funcName);
600+
}
601+
}
602+
}
603+
}
604+
605+
private void assertNoForbiddenRootCall(LuaExpr expr, Set<String> forbiddenRootCalls) {
606+
if (expr instanceof LuaExprFunctionCallByName) {
607+
String name = ((LuaExprFunctionCallByName) expr).getFuncName();
608+
if (forbiddenRootCalls.contains(name)) {
609+
throw new Error("Lua root purity violation: forbidden root call to " + name);
610+
}
611+
for (LuaExpr arg : ((LuaExprFunctionCallByName) expr).getArguments()) {
612+
assertNoForbiddenRootCall(arg, forbiddenRootCalls);
613+
}
614+
} else if (expr instanceof LuaExprFunctionCall) {
615+
String name = ((LuaExprFunctionCall) expr).getFunc().getName();
616+
if (forbiddenRootCalls.contains(name)) {
617+
throw new Error("Lua root purity violation: forbidden root call to " + name);
618+
}
619+
for (LuaExpr arg : ((LuaExprFunctionCall) expr).getArguments()) {
620+
assertNoForbiddenRootCall(arg, forbiddenRootCalls);
621+
}
622+
} else if (expr instanceof LuaExprFunctionCallE) {
623+
LuaExprFunctionCallE call = (LuaExprFunctionCallE) expr;
624+
assertNoForbiddenRootCall(call.getFuncExpr(), forbiddenRootCalls);
625+
for (LuaExpr arg : call.getArguments()) {
626+
assertNoForbiddenRootCall(arg, forbiddenRootCalls);
627+
}
628+
} else if (expr instanceof LuaExprArrayAccess) {
629+
LuaExprArrayAccess a = (LuaExprArrayAccess) expr;
630+
assertNoForbiddenRootCall(a.getLeft(), forbiddenRootCalls);
631+
for (LuaExpr index : a.getIndexes()) {
632+
assertNoForbiddenRootCall(index, forbiddenRootCalls);
633+
}
634+
} else if (expr instanceof LuaExprBinary) {
635+
LuaExprBinary b = (LuaExprBinary) expr;
636+
assertNoForbiddenRootCall(b.getLeftExpr(), forbiddenRootCalls);
637+
assertNoForbiddenRootCall(b.getRight(), forbiddenRootCalls);
638+
} else if (expr instanceof LuaExprUnary) {
639+
assertNoForbiddenRootCall(((LuaExprUnary) expr).getRight(), forbiddenRootCalls);
640+
} else if (expr instanceof LuaExprFieldAccess) {
641+
assertNoForbiddenRootCall(((LuaExprFieldAccess) expr).getReceiver(), forbiddenRootCalls);
642+
} else if (expr instanceof LuaTableConstructor) {
643+
for (LuaTableField field : ((LuaTableConstructor) expr).getTableFields()) {
644+
if (field instanceof LuaTableNamedField) {
645+
assertNoForbiddenRootCall(((LuaTableNamedField) field).getVal(), forbiddenRootCalls);
646+
} else if (field instanceof LuaTableExprField) {
647+
assertNoForbiddenRootCall(((LuaTableExprField) field).getFieldKey(), forbiddenRootCalls);
648+
assertNoForbiddenRootCall(((LuaTableExprField) field).getVal(), forbiddenRootCalls);
649+
} else if (field instanceof LuaTableSingleField) {
650+
assertNoForbiddenRootCall(((LuaTableSingleField) field).getVal(), forbiddenRootCalls);
651+
}
652+
}
653+
}
654+
}
655+
556656
private void translateAndTest(String name, boolean executeProg,
557657
boolean executeTests, WurstGui gui, WurstCompilerJassImpl compiler,
558658
WurstModel model, boolean executeProgOnlyAfterTransforms) throws Error {

0 commit comments

Comments
 (0)