From f808b6997f33223400e5cbeb90b457ac862a760c Mon Sep 17 00:00:00 2001 From: Frotty Date: Tue, 13 Jan 2026 15:08:16 +0100 Subject: [PATCH 1/2] Fix lua name normalization --- .../lua/translation/LuaTranslator.java | 46 ++++++++++++++++++- .../tests/LuaTranslationTests.java | 40 +++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index 97db9fa86..abfd80d5a 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -27,6 +27,7 @@ public class LuaTranslator { private final Set usedNames = new HashSet<>(Arrays.asList( // reserved function names "print", "tostring", "error", + "main", "config", // keywords: "and", "break", @@ -74,8 +75,10 @@ public LuaVariable initFor(ImVar a) { @Override public LuaFunction initFor(ImFunction a) { String name = a.getName(); - if (!a.isExtern() && !a.isBj() && !a.isNative()) { + if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(name)) { name = uniqueName(name); + } else if (isFixedEntryPoint(name)) { + usedNames.add(name); } LuaFunction lf = LuaAst.LuaFunction(name, LuaAst.LuaParams(), LuaAst.LuaStatements()); @@ -178,6 +181,7 @@ public LuaCompilationUnit translate() { normalizeMethodNames(); + normalizeFieldNames(); // NormalizeNames.normalizeNames(prog); @@ -214,6 +218,10 @@ public LuaCompilationUnit translate() { return luaModel; } + private boolean isFixedEntryPoint(String name) { + return "main".equals(name) || "config".equals(name); + } + private void collectPredefinedNames() { for (ImFunction function : prog.getFunctions()) { if (function.isBj() || function.isExtern() || function.isNative()) { @@ -258,6 +266,42 @@ private void normalizeMethodNames() { } } + private void normalizeFieldNames() { + for (ImClass c : prog.getClasses()) { + Set methodNames = new HashSet<>(); + collectMethodNames(c, methodNames, new HashSet<>()); + if (methodNames.isEmpty()) { + continue; + } + Set reserved = new HashSet<>(methodNames); + for (ImVar field : c.getFields()) { + if (reserved.contains(field.getName())) { + String base = field.getName() + "_field"; + String candidate = base; + int i = 1; + while (reserved.contains(candidate)) { + candidate = base + i++; + } + field.setName(candidate); + } + reserved.add(field.getName()); + } + } + } + + private void collectMethodNames(ImClass c, Set methodNames, Set visited) { + if (visited.contains(c)) { + return; + } + visited.add(c); + for (ImMethod method : c.getMethods()) { + methodNames.add(method.getName()); + } + for (ImClassType sc : c.getSuperClasses()) { + collectMethodNames(sc.getClassDef(), methodNames, visited); + } + } + private void createStringConcatFunction() { String[] code = { "if x then", diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java index f9a7d38a9..68a621843 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java @@ -43,7 +43,9 @@ private void assertFunctionCall(String output, String functionName, String argum private void assertFunctionBodyContains(String output, String functionName, String search, boolean mustContain) { Pattern pattern = Pattern.compile("function\\s*" + functionName + "\\s*\\(.*\\).*\\n" + "((?:\\n|.)*?)end"); Matcher matcher = pattern.matcher(output); + boolean found = false; while (matcher.find()) { + found = true; String body = matcher.group(1); if(!body.contains(search) && mustContain) { fail("Function " + functionName + " must contain " + search + "."); @@ -52,6 +54,7 @@ private void assertFunctionBodyContains(String output, String functionName, Stri fail("Function " + functionName + " must not contain " + search + "."); } } + assertTrue("Function " + functionName + " was not found.", found); } @Test @@ -191,6 +194,42 @@ public void stringConcatenation() throws IOException { assertFunctionBodyContains(compiled, "test", "stringConcat", true); } + @Test + public void methodFieldNameCollision() throws IOException { + test().testLua(true).lines( + "package Test", + "class Foo", + " int size = 3", + " function size() returns int", + " return size", + "init", + " let f = new Foo()", + " f.size()" + ); + String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_methodFieldNameCollision.lua"), Charsets.UTF_8); + assertFunctionBodyContains(compiled, "Foo_Foo_size", "Foo_size_field", true); + assertFunctionBodyContains(compiled, "Foo_Foo_size", "return this.Foo_size\n", false); + } + + @Test + public void mainAndConfigNamesFixed() throws IOException { + test().testLua(true).lines( + "package Test", + "int main = 1", + "int config = 2", + "init", + " if main > 0 and config > 0", + " skip" + ); + String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_mainAndConfigNamesFixed.lua"), Charsets.UTF_8); + assertTrue(compiled.contains("function main(")); + assertTrue(compiled.contains("function config(")); + assertFalse(compiled.contains("function main2(")); + assertFalse(compiled.contains("function config2(")); + assertFalse(compiled.contains("local main =")); + assertFalse(compiled.contains("local config =")); + } + @Test public void intCasting() throws IOException { // Use local variables to test if it works even when local types are eliminated. @@ -234,4 +273,3 @@ public void intCasting() throws IOException { assertFunctionBodyContains(compiled, "testClass", "cObj2 = cInt", false); } } - From dbebab30101b977372ddcce02f2ae126c293567f Mon Sep 17 00:00:00 2001 From: Frotty Date: Tue, 13 Jan 2026 15:24:27 +0100 Subject: [PATCH 2/2] only keep actual entry points --- .../lua/translation/LuaTranslator.java | 8 ++++---- .../wurstscript/tests/LuaTranslationTests.java | 16 ++++++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java index abfd80d5a..9d5688e6c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/lua/translation/LuaTranslator.java @@ -75,9 +75,9 @@ public LuaVariable initFor(ImVar a) { @Override public LuaFunction initFor(ImFunction a) { String name = a.getName(); - if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(name)) { + if (!a.isExtern() && !a.isBj() && !a.isNative() && !isFixedEntryPoint(a)) { name = uniqueName(name); - } else if (isFixedEntryPoint(name)) { + } else if (isFixedEntryPoint(a)) { usedNames.add(name); } @@ -218,8 +218,8 @@ public LuaCompilationUnit translate() { return luaModel; } - private boolean isFixedEntryPoint(String name) { - return "main".equals(name) || "config".equals(name); + private boolean isFixedEntryPoint(ImFunction function) { + return function == imTr.getMainFunc() || function == imTr.getConfFunc(); } private void collectPredefinedNames() { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java index 68a621843..aed5a833a 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/LuaTranslationTests.java @@ -215,21 +215,25 @@ public void methodFieldNameCollision() throws IOException { public void mainAndConfigNamesFixed() throws IOException { test().testLua(true).lines( "package Test", - "int main = 1", - "int config = 2", + "native takesInt(int i)", + "function helper()", + " let main = 1", + " let config = 2", + " takesInt(main)", + " takesInt(config)", "init", - " if main > 0 and config > 0", - " skip" + " helper()" ); String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_mainAndConfigNamesFixed.lua"), Charsets.UTF_8); + assertFunctionBodyContains(compiled, "helper", "local main1", true); + assertFunctionBodyContains(compiled, "helper", "local config1", true); assertTrue(compiled.contains("function main(")); assertTrue(compiled.contains("function config(")); assertFalse(compiled.contains("function main2(")); assertFalse(compiled.contains("function config2(")); - assertFalse(compiled.contains("local main =")); - assertFalse(compiled.contains("local config =")); } + @Test public void intCasting() throws IOException { // Use local variables to test if it works even when local types are eliminated.