Skip to content

Commit c0e12fc

Browse files
committed
lua again
1 parent 91cdc24 commit c0e12fc

2 files changed

Lines changed: 189 additions & 0 deletions

File tree

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

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,10 +1192,14 @@ private String typeKey(ImType type) {
11921192
private Set<String> collectDispatchSlotNames(ImClass receiverClass, List<ImMethod> groupMethods) {
11931193
Set<String> slotNames = new TreeSet<>();
11941194
Set<String> semanticNames = new TreeSet<>();
1195+
Set<String> dispatchKeys = new TreeSet<>();
1196+
Set<String> closureRuntimeKeys = new TreeSet<>();
11951197
for (ImMethod m : groupMethods) {
11961198
if (m == null) {
11971199
continue;
11981200
}
1201+
dispatchKeys.add(dispatchSignatureKey(m));
1202+
closureRuntimeKeys.add(closureRuntimeDispatchKey(m));
11991203
String methodName = m.getName();
12001204
if (!methodName.isEmpty()) {
12011205
slotNames.add(methodName);
@@ -1215,6 +1219,12 @@ private Set<String> collectDispatchSlotNames(ImClass receiverClass, List<ImMetho
12151219
slotNames.add(owner.getName() + "_" + sourceSemanticName);
12161220
}
12171221
}
1222+
if (receiverClass != null && !dispatchKeys.isEmpty() && !semanticNames.isEmpty()) {
1223+
collectEquivalentHierarchyMethodNames(receiverClass, dispatchKeys, semanticNames, slotNames, new HashSet<>());
1224+
if (isClosureGeneratedClass(receiverClass) && !closureRuntimeKeys.isEmpty()) {
1225+
collectEquivalentClosureFamilyMethodNames(receiverClass, closureRuntimeKeys, semanticNames, slotNames);
1226+
}
1227+
}
12181228
if (receiverClass != null && !semanticNames.isEmpty()) {
12191229
Set<String> classNames = new TreeSet<>();
12201230
collectClassNamesInHierarchy(receiverClass, classNames, new HashSet<>());
@@ -1227,6 +1237,105 @@ private Set<String> collectDispatchSlotNames(ImClass receiverClass, List<ImMetho
12271237
return slotNames;
12281238
}
12291239

1240+
private void collectEquivalentHierarchyMethodNames(ImClass c, Set<String> dispatchKeys, Set<String> semanticNames,
1241+
Set<String> slotNames, Set<ImClass> visited) {
1242+
if (c == null || !visited.add(c)) {
1243+
return;
1244+
}
1245+
for (ImMethod method : c.getMethods()) {
1246+
if (method == null) {
1247+
continue;
1248+
}
1249+
if (!dispatchKeys.contains(dispatchSignatureKey(method))) {
1250+
continue;
1251+
}
1252+
String methodName = method.getName();
1253+
String semanticName = semanticNameFromMethodName(methodName);
1254+
String sourceSemanticName = sourceSemanticName(method);
1255+
if (!semanticNames.contains(semanticName) && !semanticNames.contains(sourceSemanticName)) {
1256+
continue;
1257+
}
1258+
if (!methodName.isEmpty()) {
1259+
slotNames.add(methodName);
1260+
slotNames.add(c.getName() + "_" + methodName);
1261+
}
1262+
}
1263+
for (ImClassType sc : c.getSuperClasses()) {
1264+
collectEquivalentHierarchyMethodNames(sc.getClassDef(), dispatchKeys, semanticNames, slotNames, visited);
1265+
}
1266+
}
1267+
1268+
private void collectEquivalentClosureFamilyMethodNames(ImClass receiverClass, Set<String> closureRuntimeKeys,
1269+
Set<String> semanticNames, Set<String> slotNames) {
1270+
Set<ImClass> anchors = new HashSet<>();
1271+
collectClosureFamilyAnchors(receiverClass, anchors, new HashSet<>());
1272+
if (anchors.isEmpty()) {
1273+
return;
1274+
}
1275+
List<ImClass> classes = new ArrayList<>(prog.getClasses());
1276+
classes.sort(Comparator.comparing(this::classSortKey));
1277+
for (ImClass candidateClass : classes) {
1278+
if (!sharesClosureFamilyAnchor(candidateClass, anchors, new HashSet<>())) {
1279+
continue;
1280+
}
1281+
List<ImMethod> methods = new ArrayList<>(candidateClass.getMethods());
1282+
methods.sort(Comparator.comparing(this::methodSortKey));
1283+
for (ImMethod method : methods) {
1284+
if (!closureRuntimeKeys.contains(closureRuntimeDispatchKey(method))) {
1285+
continue;
1286+
}
1287+
String methodName = method.getName();
1288+
String semanticName = semanticNameFromMethodName(methodName);
1289+
String sourceSemanticName = sourceSemanticName(method);
1290+
if (!semanticNames.contains(semanticName) && !semanticNames.contains(sourceSemanticName)) {
1291+
continue;
1292+
}
1293+
if (!methodName.isEmpty()) {
1294+
slotNames.add(methodName);
1295+
slotNames.add(candidateClass.getName() + "_" + methodName);
1296+
}
1297+
}
1298+
}
1299+
}
1300+
1301+
private String closureRuntimeDispatchKey(ImMethod method) {
1302+
if (method == null) {
1303+
return "<null>";
1304+
}
1305+
ImFunction implementation = resolveDispatchSignatureImplementation(method, new HashSet<>());
1306+
if (implementation == null) {
1307+
return "<abstract>";
1308+
}
1309+
return "" + Math.max(0, implementation.getParameters().size() - 1);
1310+
}
1311+
1312+
private void collectClosureFamilyAnchors(ImClass c, Set<ImClass> anchors, Set<ImClass> visited) {
1313+
if (c == null || !visited.add(c)) {
1314+
return;
1315+
}
1316+
if (!isClosureGeneratedClass(c)) {
1317+
anchors.add(c);
1318+
}
1319+
for (ImClassType sc : c.getSuperClasses()) {
1320+
collectClosureFamilyAnchors(sc.getClassDef(), anchors, visited);
1321+
}
1322+
}
1323+
1324+
private boolean sharesClosureFamilyAnchor(ImClass c, Set<ImClass> anchors, Set<ImClass> visited) {
1325+
if (c == null || !visited.add(c)) {
1326+
return false;
1327+
}
1328+
if (anchors.contains(c)) {
1329+
return true;
1330+
}
1331+
for (ImClassType sc : c.getSuperClasses()) {
1332+
if (sharesClosureFamilyAnchor(sc.getClassDef(), anchors, visited)) {
1333+
return true;
1334+
}
1335+
}
1336+
return false;
1337+
}
1338+
12301339
private void collectClassNamesInHierarchy(ImClass c, Set<String> out, Set<ImClass> visited) {
12311340
if (c == null || !visited.add(c)) {
12321341
return;

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

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ private List<String> nonBaseSubclassBindings(String output, String baseName, Str
128128
return result;
129129
}
130130

131+
private List<String> subclassCreateClasses(String output, String baseName) {
132+
Matcher matcher = Pattern.compile("function\\s+(" + Pattern.quote(baseName) + "_[A-Za-z0-9_]+):create\\d*\\s*\\(").matcher(output);
133+
List<String> result = new ArrayList<>();
134+
while (matcher.find()) {
135+
String owner = matcher.group(1);
136+
if (!result.contains(owner)) {
137+
result.add(owner);
138+
}
139+
}
140+
return result;
141+
}
142+
131143
private String compileLuaWithRunArgs(String testName, boolean withStdLib, String... lines) {
132144
RunArgs runArgs = new RunArgs().with("-lua", "-inline", "-localOptimizations", "-stacktraces");
133145
WurstGui gui = new WurstGuiCliImpl();
@@ -903,6 +915,74 @@ public void nestedGenericLinkedListClosuresKeepPrefixedLuaSlotNames() throws IOE
903915
assertContainsRegex(compiled, "LLItrClosure_[A-Za-z0-9_]+\\.LLItrClosure_run\\s*=");
904916
}
905917

918+
@Test
919+
public void erasedLinkedListClosureCallsitesKeepSuffixedRunSlotsAlignedAcrossAllSubclasses() {
920+
String compiled = compileLuaWithRunArgs(
921+
"LuaTranslationTests_erasedLinkedListClosureCallsitesKeepSuffixedRunSlotsAlignedAcrossAllSubclasses",
922+
false,
923+
"package Test",
924+
"interface Alpha0",
925+
" function run()",
926+
"interface Alpha1",
927+
" function run()",
928+
"interface Alpha2",
929+
" function run()",
930+
"interface Alpha3",
931+
" function run()",
932+
"interface Alpha4",
933+
" function run()",
934+
"interface Alpha5",
935+
" function run()",
936+
"public interface MapClosure<T,S>",
937+
" function run(T t) returns S",
938+
"public interface LLItrClosure<T>",
939+
" function run(T t)",
940+
"class Node<T>",
941+
" T elem",
942+
" Node<T> next = null",
943+
"class LinkedList<T>",
944+
" Node<T> first = null",
945+
" function add(T elem)",
946+
" let n = new Node<T>()",
947+
" n.elem = elem",
948+
" n.next = first",
949+
" first = n",
950+
" function forEach(LLItrClosure<T> itr)",
951+
" var cur = first",
952+
" while cur != null",
953+
" itr.run(cur.elem)",
954+
" cur = cur.next",
955+
" function map<S>(MapClosure<T,S> itr) returns LinkedList<S>",
956+
" let out = new LinkedList<S>()",
957+
" forEach() t ->",
958+
" out.add(itr.run(t))",
959+
" return out",
960+
"function usePlain(LinkedList<int> xs)",
961+
" xs.forEach() x ->",
962+
" skip",
963+
"function useMapped(LinkedList<int> xs)",
964+
" xs.map((int x) -> x + 1)",
965+
"function useNested(LinkedList<int> xs)",
966+
" xs.forEach() x ->",
967+
" xs.map((int y) -> y + x)",
968+
"init",
969+
" let xs = new LinkedList<int>()",
970+
" xs.add(1)",
971+
" usePlain(xs)",
972+
" useMapped(xs)",
973+
" useNested(xs)"
974+
);
975+
976+
List<String> subclasses = subclassCreateClasses(compiled, "LLItrClosure");
977+
assertTrue("Expected multiple generated LLItrClosure subclasses", subclasses.size() >= 3);
978+
List<String> suffixedSlots = uniqueMatches(compiled, "LLItrClosure_[A-Za-z0-9_]+\\.(run\\d+)\\s*=", 1);
979+
assertTrue("Expected at least one suffixed LLItrClosure run slot", !suffixedSlots.isEmpty());
980+
String slotName = suffixedSlots.get(0);
981+
for (String subclass : subclasses) {
982+
assertContainsRegex(compiled, Pattern.quote(subclass) + "\\." + Pattern.quote(slotName) + "\\s*=");
983+
}
984+
}
985+
906986
@Test
907987
public void mainAndConfigNamesFixed() throws IOException {
908988
test().testLua(true).lines(

0 commit comments

Comments
 (0)