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..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 @@ -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(a)) { name = uniqueName(name); + } else if (isFixedEntryPoint(a)) { + 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(ImFunction function) { + return function == imTr.getMainFunc() || function == imTr.getConfFunc(); + } + 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..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 @@ -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,46 @@ 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", + "native takesInt(int i)", + "function helper()", + " let main = 1", + " let config = 2", + " takesInt(main)", + " takesInt(config)", + "init", + " 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(")); + } + + @Test public void intCasting() throws IOException { // Use local variables to test if it works even when local types are eliminated. @@ -234,4 +277,3 @@ public void intCasting() throws IOException { assertFunctionBodyContains(compiled, "testClass", "cObj2 = cInt", false); } } -