Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import de.peeeq.wurstscript.ast.AstElementWithTypeParameters;
import de.peeeq.wurstscript.ast.ExprClosure;
import de.peeeq.wurstscript.ast.FuncDef;
import de.peeeq.wurstscript.ast.WPackage;
import de.peeeq.wurstscript.jassIm.ImArrayType;
import de.peeeq.wurstscript.jassIm.ImArrayTypeMulti;
import de.peeeq.wurstscript.jassIm.ImClass;
import de.peeeq.wurstscript.jassIm.ImClassType;
import de.peeeq.wurstscript.jassIm.Element;
import de.peeeq.wurstscript.jassIm.ImFunction;
import de.peeeq.wurstscript.jassIm.ImMethod;
import de.peeeq.wurstscript.jassIm.ImProg;
Expand Down Expand Up @@ -100,7 +102,10 @@ private static void assignDispatchGroupKeys(List<ImMethod> allMethods) {
if (partition.isEmpty()) {
continue;
}
String key = methodSortKey(partition.get(0)) + "|" + dispatchSignatureKey(partition.get(0));
// The sort key names the class only; two packages may each declare one of that name.
ImClass owner = partition.get(0).attrClass();
String key = methodSortKey(partition.get(0)) + "|" + dispatchSignatureKey(partition.get(0))
+ OWNERS + (owner == null ? "" : packageOf(owner.getTrace()));
for (int k = 0; k < partition.size(); k++) {
ImMethod method = partition.get(k);
method.setLuaDispatchGroupKey(key);
Expand Down Expand Up @@ -467,6 +472,11 @@ private static String dispatchSignatureKey(ImMethod method) {
}
sb.append(typeKey(params.get(i).getType()));
}
sb.append(OWNERS);
appendOwners(implementation.getReturnType(), sb);
for (int i = 1; i < params.size(); i++) {
appendOwners(params.get(i).getType(), sb);
}
return sb.toString();
}

Expand All @@ -489,6 +499,10 @@ private static String dispatchParameterSignatureKey(ImMethod method) {
}
sb.append(typeKey(params.get(i).getType()));
}
sb.append(OWNERS);
for (int i = 1; i < params.size(); i++) {
appendOwners(params.get(i).getType(), sb);
Comment thread
Frotty marked this conversation as resolved.
}
return sb.toString();
}

Expand Down Expand Up @@ -657,6 +671,43 @@ public static String typeKey(ImType type) {
return type.toString();
}

/**
* Separates a signature from the packages of what it names. It sorts below every character a type key holds, so
* two signatures which differed before them keep their order, and two which named equal types stay together.
*/
private static final char OWNERS = '';

/**
* The packages of the classes and of the owners of the type variables {@code type} names, in order. Two packages may
* each declare a class of the same name, and {@link #typeKey} names a class by its name only: without these, the
* methods of two such classes shared a dispatch group and their signatures compared equal for an alias.
*/
private static void appendOwners(ImType type, StringBuilder sb) {
if (type instanceof ImTypeVarRef ref) {
ImTypeVar typeVar = ref.getTypeVariable();
Element owner = typeVar.getParent() instanceof ImTypeVars vars ? vars.getParent() : null;
sb.append(owner instanceof ImClass c ? packageOf(c.getTrace())
: owner instanceof ImFunction f ? packageOf(f.getTrace()) : "").append(';');
} else if (type instanceof ImClassType classType) {
sb.append(packageOf(classType.getClassDef().getTrace())).append(';');
for (int i = 0; i < classType.getTypeArguments().size(); i++) {
appendOwners(classType.getTypeArguments().get(i).getType(), sb);
}
} else if (type instanceof ImArrayType arrayType) {
appendOwners(arrayType.getEntryType(), sb);
} else if (type instanceof ImArrayTypeMulti arrayType) {
appendOwners(arrayType.getEntryType(), sb);
} else if (type instanceof ImTupleType tupleType) {
for (int i = 0; i < tupleType.getTypes().size(); i++) {
appendOwners(tupleType.getTypes().get(i), sb);
}
}
}

private static String packageOf(de.peeeq.wurstscript.ast.Element trace) {
return trace != null && trace.attrNearestPackage() instanceof WPackage p ? p.getName() : "";
}

private static String typeVarKey(ImTypeVar typeVar) {
String owner = "?";
int position = -1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,109 @@ public void sameNamedClassesInSeveralPackagesEmitTheSameLuaInAnyUnitOrder() thro
"same-named receivers must bind in an order independent of the compilation units");
}

/**
* Two packages each declare the generic hierarchy Box, First and Second. The key of a dispatch group named its class,
* its method and the types of its signature by name only, so both families of pick had the same key and shared
* one group: the packages of the class and of what the signature names keep them apart, in any order of the
* compilation units.
*/
@Test
public void sameNamedGenericHierarchiesInTwoPackagesKeepTheirOwnDispatchGroups() throws IOException {
assertOwnDispatchGroups("sameNamedOldGenericHierarchies", "T");
}

/** The same with new generics, whose signatures name the type variable and so its owner. */
@Test
public void sameNamedNewGenericHierarchiesInTwoPackagesKeepTheirOwnDispatchGroups() throws IOException {
assertOwnDispatchGroups("sameNamedNewGenericHierarchies", "T:");
}

/**
* One family across packages: Box in a shared package, and a class First overriding pick in each of two others.
* Both overrides belong to Box's group and must bind the slot a call through Box uses.
*/
@Test
public void sameNamedOverridesInTwoPackagesShareTheirRootSlot() throws IOException {
assertSharedRootSlot("sameNamedOldGenericOverrides", "T");
}

@Test
public void sameNamedNewGenericOverridesInTwoPackagesShareTheirRootSlot() throws IOException {
assertSharedRootSlot("sameNamedNewGenericOverrides", "T:");
}

private void assertSharedRootSlot(String name, String typeParameter) throws IOException {
List<CU> units = new ArrayList<>();
units.add(compilationUnit("Common.wurst",
"package Common",
"public abstract class Box<" + typeParameter + ">",
" abstract function pick(T a, T b) returns T",
"public function pickOf(Box<int> box) returns int",
" return box.pick(10, 20)"));
for (String p : List.of("PA", "PB")) {
boolean first = p.equals("PA");
units.add(compilationUnit(p + ".wurst",
"package " + p,
"import Common",
"public class First<" + typeParameter + "> extends Box<T>",
" override function pick(T a, T b) returns T",
" return " + (first ? "a" : "b"),
"public function run" + p + "() returns int",
" return pickOf(new First<int>())"));
}
units.add(compilationUnit("Main.wurst",
"package Main",
"import PA",
"import PB",
"native testSuccess()",
"init",
" if runPA() == 10 and runPB() == 20",
" testSuccess()"));
String lua = compileAndRunToLua(name, units.toArray(new CU[0]));
List<CU> shuffled = List.of(units.get(3), units.get(2), units.get(0), units.get(1));
assertEquals(compileToLua(name, shuffled), lua, "the same in any order of the compilation units");
}

private void assertOwnDispatchGroups(String name, String typeParameter) throws IOException {
List<CU> units = new ArrayList<>();
for (String p : List.of("PA", "PB")) {
boolean first = p.equals("PA");
units.add(compilationUnit(p + ".wurst",
"package " + p,
"public abstract class Box<" + typeParameter + ">",
" abstract function pick(T a, T b) returns T",
"public class First<" + typeParameter + "> extends Box<T>",
" override function pick(T a, T b) returns T",
" return " + (first ? "a" : "b"),
"public class Second<" + typeParameter + "> extends Box<T>",
" override function pick(T a, T b) returns T",
" return " + (first ? "b" : "a"),
"public function run" + p + "() returns int",
" Box<int> x = new First<int>()",
" Box<int> y = new Second<int>()",
" return x.pick(10, 20) * 100 + y.pick(10, 20)"));
}
units.add(compilationUnit("Main.wurst",
"package Main",
"import PA",
"import PB",
"native testSuccess()",
"init",
" if runPA() == 1020 and runPB() == 2010",
" testSuccess()"));
String lua = compileAndRunToLua(name, units.toArray(new CU[0]));
java.util.Set<String> slots = new java.util.TreeSet<>();
java.util.regex.Matcher binding = java.util.regex.Pattern.compile("\\n\\t\\w+\\.(\\w+) = First_First_pick\\w*\\n")
.matcher(lua);
while (binding.find()) {
slots.add(binding.group(1));
}
assertEquals(slots.size(), 2, "each package's First binds pick to a slot of its own:\n" + lua);
List<CU> shuffled = List.of(units.get(2), units.get(1), units.get(0));
assertEquals(compileToLua(name, shuffled), lua,
"the slots are the same in any order of the compilation units");
}

/** Compiles {@code units} for every backend, runs them, and returns the Lua script written under {@code name}. */
private String compileAndRunToLua(String name, CU... units) throws IOException {
testNamed(name).testLua(true).luaOnly(false).executeProg().compilationUnits(units);
Expand Down
Loading