Skip to content

Commit

Permalink
Remove type dependency from cast translation (#1064)
Browse files Browse the repository at this point in the history
* Remove type dependency from cast translation

When casting to int, there is a special case when an int is casted (no cast necessary). This was no longer detected correctly with eliminated local types. Now this unnecessary cast is removed in advance, before local types are lost.

* Unit tests for local type elimination
  • Loading branch information
Jampi0n committed Jul 13, 2022
1 parent db253e0 commit 18ca8ab
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 6 deletions.
Expand Up @@ -8,10 +8,11 @@ public class EliminateLocalTypes {
private static ImType localSimpleType = JassIm.ImSimpleType("localSimpleType");

public static void eliminateLocalTypesProg(ImProg imProg, ImTranslator translator) {
// While local types are still there, perform transformation specifically for strings:
// null string -> ""
// string1 + string2 -> stringConcat(string1, string2)
transformStrings(imProg, translator);
// While local types are still there, perform transformation, such that the lua translator does not need to know variable types
// null string -> "" (avoids type dependency in null translation)
// string1 + string2 -> stringConcat(string1, string2) (avoids type dependency in operator translation)
// int castTo int -> remove cast (avoids type dependency in cast translation)
transformProgram(imProg, translator);
// Eliminates local types to be able to merge more locals in Lua.
for (ImFunction f : ImHelper.calculateFunctionsOfProg(imProg)) {
eliminateLocalTypesFunc(f, translator);
Expand All @@ -28,7 +29,7 @@ private static void eliminateLocalTypesFunc(ImFunction f, final ImTranslator tra
}
}

private static void transformStrings(ImProg imProg, ImTranslator translator) {
private static void transformProgram(ImProg imProg, ImTranslator translator) {
imProg.accept(new Element.DefaultVisitor() {
@Override
public void visit(ImOperatorCall imOperatorCall) {
Expand All @@ -48,6 +49,14 @@ public void visit(ImNull imNull) {
imNull.replaceBy(JassIm.ImStringVal(""));
}
}

@Override
public void visit(ImCast imCast) {
super.visit(imCast);
if(TypesHelper.isIntType(imCast.getExpr().attrTyp()) && TypesHelper.isIntType(imCast.getToType())) {
imCast.replaceBy(imCast.getExpr().copy());
}
}
});
}
}
Expand Up @@ -326,7 +326,7 @@ public static LuaExpr translate(ImTypeVarDispatch imTypeVarDispatch, LuaTranslat

public static LuaExpr translate(ImCast imCast, LuaTranslator tr) {
LuaExpr translated = imCast.getExpr().translateToLua(tr);
if (TypesHelper.isIntType(imCast.getToType()) && !TypesHelper.isIntType(imCast.getExpr().attrTyp())) {
if (TypesHelper.isIntType(imCast.getToType())) {
return LuaAst.LuaExprFunctionCall(tr.toIndexFunction, LuaAst.LuaExprlist(translated));
} else if (imCast.getToType() instanceof ImClassType
|| imCast.getToType() instanceof ImAnyType) {
Expand Down
Expand Up @@ -40,6 +40,20 @@ private void assertFunctionCall(String output, String functionName, String argum
assertTrue("Function call to function " + functionName + " with arguments (" + arguments + ") was not found.", findAtLeastOne);
}

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);
while (matcher.find()) {
String body = matcher.group(1);
if(!body.contains(search) && mustContain) {
fail("Function " + functionName + " must contain " + search + ".");
}
if(body.contains(search) && !mustContain) {
fail("Function " + functionName + " must not contain " + search + ".");
}
}
}

@Test
public void testStdLib() throws IOException {
test().testLua(true).withStdLib().lines(
Expand Down Expand Up @@ -157,5 +171,67 @@ public void nullUnit2() throws IOException {
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_nullUnit2.lua"), Charsets.UTF_8);
assertFunctionCall(compiled, "takesUnit", "nil");
}

@Test
public void stringConcatenation() throws IOException {
// Use local variables to test if it works even when local types are eliminated.
test().testLua(true).lines(
"package Test",
"native takesString(string s)",
"function test()",
" let s1 = \"1\"",
" let s2 = \"2\"",
" takesString(s1 + s2)",
"init",
" test()"
);
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_stringConcatenation.lua"), Charsets.UTF_8);
// strings use the stringConcat function in lua instead of + operator
assertFunctionBodyContains(compiled, "test", "+", false);
assertFunctionBodyContains(compiled, "test", "stringConcat", true);
}

@Test
public void intCasting() throws IOException {
// Use local variables to test if it works even when local types are eliminated.
test().testLua(true).lines(
"package Test",
"native takesInt(int i)",
"enum MyEnum",
" ZERO",
" ONE",
" TWO",
"function testEnum()",
" let zeroEnum = MyEnum.ZERO",
" let zeroInt = zeroEnum castTo int",
" let zeroEnum2 = zeroInt castTo MyEnum",
" takesInt(zeroEnum castTo int)",
" takesInt(zeroInt)",
" takesInt(zeroEnum2 castTo int)",
"class C",
"native takesC(C c)",
"function testClass()",
" let cObj = new C()",
" let cInt = cObj castTo int",
" let cObj2 = cInt castTo C",
" takesC(cObj)",
" takesInt(cInt)",
" takesC(cObj2)",
"init",
" testEnum()",
" testClass()"
);
String compiled = Files.toString(new File("test-output/lua/LuaTranslationTests_intCasting.lua"), Charsets.UTF_8);
// enums are cast implicitly, because they are ints
assertFunctionBodyContains(compiled, "testEnum", "objectToIndex", false);
assertFunctionBodyContains(compiled, "testEnum", "zeroEnum = 0", true);
assertFunctionBodyContains(compiled, "testEnum", "zeroInt = zeroEnum", true);
assertFunctionBodyContains(compiled, "testEnum", "zeroEnum2 = zeroInt", true);
// classes are cast with objectToIndex and objectFromIndex in lua
assertFunctionBodyContains(compiled, "testClass", "objectToIndex", true);
assertFunctionBodyContains(compiled, "testClass", "objectFromIndex", true);
assertFunctionBodyContains(compiled, "testClass", "cInt = cObj", false);
assertFunctionBodyContains(compiled, "testClass", "cObj2 = cInt", false);
}
}

0 comments on commit 18ca8ab

Please sign in to comment.