Skip to content

Commit 732f1fa

Browse files
committed
an attempt at interface merging
1 parent d924aa2 commit 732f1fa

File tree

9 files changed

+138
-20
lines changed

9 files changed

+138
-20
lines changed

build.gradle.kts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,8 @@ repositories {
1414
minecraft.pistonMeta()
1515
}
1616

17-
val gerald: SourceSet by sourceSets.creating
18-
val geraldImplementation: Configuration by configurations.getting
19-
2017
dependencies {
21-
geraldImplementation(minecraft.client("1.14.4"))
22-
implementation(minecraft.client("1.21.4"))
18+
implementation(minecraft.client("1.17.1"))
2319
// compile against Cichlid API
2420
// compileOnly(cichlid.loaderApi("0.1.0"))
2521
// run with full Cichlid
@@ -28,15 +24,13 @@ dependencies {
2824

2925
cichlid {
3026
runs {
31-
register("client 1.14.4") {
32-
version = "1.14.4"
33-
template = "client"
34-
sourceSet(gerald)
35-
}
36-
register("client 1.21.4") {
37-
version = "1.21.4"
38-
template = "client"
27+
register("client") {
28+
version = "1.17.1"
3929
}
30+
// register("client 1.21.4") {
31+
// version = "1.21.4"
32+
// template = "client"
33+
// }
4034

4135
// register("serverButCooler") {
4236
// template = "server"

plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ repositories {
1515
}
1616

1717
dependencies {
18-
implementation("io.github.cichlidmc:DistributionMarker:1.0.0")
18+
implementation("io.github.cichlidmc:DistributionMarker:1.0.1")
1919
implementation("io.github.cichlidmc:PistonMetaParser:2.0.2")
2020
implementation("net.neoforged:AutoRenamingTool:2.0.3")
2121
implementation("org.ow2.asm:asm-tree:9.7")

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/extension/dep/MinecraftDepsExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public Dependency server(String version) {
2020
return this.deps.create("net.minecraft:minecraft-server:" + version);
2121
}
2222

23-
public Dependency clientAndServer(String version) {
23+
public Dependency merged(String version) {
2424
return this.deps.create("net.minecraft:minecraft-merged:" + version);
2525
}
2626

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/ClassMerger.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.github.cichlidmc.cichlid_gradle.merge;
22

3+
import io.github.cichlidmc.cichlid_gradle.merge.element.FieldMerger;
4+
import io.github.cichlidmc.cichlid_gradle.merge.element.InterfacesMerger;
5+
import io.github.cichlidmc.cichlid_gradle.merge.element.MethodMerger;
36
import io.github.cichlidmc.cichlid_gradle.util.FileUtils;
47
import io.github.cichlidmc.distmarker.Dist;
58
import io.github.cichlidmc.distmarker.Distribution;
@@ -18,7 +21,6 @@ public class ClassMerger {
1821

1922
public static void mergeClass(Map<MergeSource, Path> sources, Path dest) throws IOException {
2023
// merge fields, methods, and constructors
21-
// note: this code path shouldn't be hit after 1.17
2224
Map<MergeSource, ClassNode> classes = new HashMap<>();
2325
for (Map.Entry<MergeSource, Path> entry : sources.entrySet()) {
2426
classes.put(entry.getKey(), readClass(entry.getValue()));
@@ -35,6 +37,7 @@ public static void mergeClass(Map<MergeSource, Path> sources, Path dest) throws
3537
mergedNode.methods.clear();
3638
fieldMerger.merge(classes, mergedNode);
3739
methodMerger.merge(classes, mergedNode);
40+
InterfacesMerger.merge(classes, mergedNode);
3841

3942
writeClass(mergedNode, dest);
4043
}

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/FieldMerger.java renamed to plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/element/FieldMerger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.cichlidmc.cichlid_gradle.merge;
1+
package io.github.cichlidmc.cichlid_gradle.merge.element;
22

33
import org.objectweb.asm.tree.AnnotationNode;
44
import org.objectweb.asm.tree.ClassNode;
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package io.github.cichlidmc.cichlid_gradle.merge.element;
2+
3+
import io.github.cichlidmc.cichlid_gradle.merge.JarMerger;
4+
import io.github.cichlidmc.cichlid_gradle.merge.MergeSource;
5+
import io.github.cichlidmc.distmarker.Dist;
6+
import io.github.cichlidmc.distmarker.Distribution;
7+
import org.jetbrains.annotations.Nullable;
8+
import org.objectweb.asm.Type;
9+
import org.objectweb.asm.TypeReference;
10+
import org.objectweb.asm.tree.ClassNode;
11+
import org.objectweb.asm.tree.TypeAnnotationNode;
12+
13+
import java.util.ArrayList;
14+
import java.util.Collection;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.Objects;
18+
import java.util.stream.Collectors;
19+
20+
public class InterfacesMerger {
21+
public static void merge(Map<MergeSource, ClassNode> classes, ClassNode output) {
22+
// no need to do anything when they already match
23+
if (JarMerger.allEqual(classes.values(), node -> node.interfaces, Objects::equals))
24+
return;
25+
26+
if (true) {
27+
// I can't find anything wrong with this code, but the added annotations
28+
// never show up. Might be an ASM issue? From a GitHub search, I couldn't
29+
// find a single use of class type annotations, so it might just be broken.
30+
// FIXME: make a minimal reproduction case and submit an issue
31+
throw new RuntimeException("Interface merging is currently broken.");
32+
}
33+
34+
System.out.println("merging interfaces on " + output.name);
35+
output.interfaces.clear();
36+
37+
// handling this is annoying and I don't think they're ever present.
38+
// throw in case that assumption is wrong.
39+
assertEmpty(output.visibleTypeAnnotations, "visibleTypeAnnotations", output.name);
40+
assertEmpty(output.invisibleTypeAnnotations, "invisibleTypeAnnotations", output.name);
41+
42+
List<InterfaceInfo> interfaces = getAllInterfaces(classes);
43+
System.out.println(interfaces);
44+
for (int i = 0; i < interfaces.size(); i++) {
45+
InterfaceInfo info = interfaces.get(i);
46+
output.interfaces.add(info.name);
47+
if (info.dist == null)
48+
continue;
49+
50+
if (output.visibleTypeAnnotations == null) {
51+
output.visibleTypeAnnotations = new ArrayList<>();
52+
}
53+
54+
TypeReference reference = TypeReference.newSuperTypeReference(i);
55+
output.visibleTypeAnnotations.add(makeDistAnnotation(reference, info.dist));
56+
}
57+
}
58+
59+
private static List<InterfaceInfo> getAllInterfaces(Map<MergeSource, ClassNode> classes) {
60+
// start with all common interfaces
61+
List<String> common = getCommonInterfaces(classes);
62+
List<InterfaceInfo> infos = common.stream()
63+
.map(InterfaceInfo::new)
64+
.collect(Collectors.toCollection(ArrayList::new));
65+
66+
// then add all exclusives, one dist at a time
67+
classes.forEach((source, node) -> {
68+
for (String classInterface : node.interfaces) {
69+
if (!common.contains(classInterface)) {
70+
infos.add(new InterfaceInfo(classInterface, source.dist));
71+
}
72+
}
73+
});
74+
75+
return infos;
76+
}
77+
78+
private static List<String> getCommonInterfaces(Map<MergeSource, ClassNode> classes) {
79+
// pick an arbitrary base to initialize the common set
80+
MergeSource anySource = classes.keySet().iterator().next();
81+
ClassNode base = classes.get(anySource);
82+
83+
List<String> common = new ArrayList<>(base.interfaces);
84+
// go through each source and remove interfaces that aren't actually common
85+
for (ClassNode source : classes.values()) {
86+
// skip the one that was used as a base
87+
if (source != base) {
88+
common.removeIf(commonInterface -> !source.interfaces.contains(commonInterface));
89+
}
90+
}
91+
92+
return common;
93+
}
94+
95+
private static TypeAnnotationNode makeDistAnnotation(TypeReference reference, Dist dist) {
96+
TypeAnnotationNode node = new TypeAnnotationNode(
97+
reference.getSuperTypeIndex(), null, Type.getDescriptor(Distribution.class)
98+
);
99+
100+
node.visitEnum("value", Type.getDescriptor(Dist.class), dist.name());
101+
return node;
102+
}
103+
104+
private static void assertEmpty(@Nullable Collection<?> collection, String name, String className) {
105+
if (collection != null && !collection.isEmpty()) {
106+
throw new IllegalStateException(name + " is not empty in " + className);
107+
}
108+
}
109+
110+
private record InterfaceInfo(String name, @Nullable Dist dist) {
111+
private InterfaceInfo(String name) {
112+
this(name, null);
113+
}
114+
}
115+
}

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/MemberMerger.java renamed to plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/element/MemberMerger.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
package io.github.cichlidmc.cichlid_gradle.merge;
1+
package io.github.cichlidmc.cichlid_gradle.merge.element;
22

3+
import io.github.cichlidmc.cichlid_gradle.merge.ClassMerger;
4+
import io.github.cichlidmc.cichlid_gradle.merge.JarMerger;
5+
import io.github.cichlidmc.cichlid_gradle.merge.MergeSource;
36
import org.objectweb.asm.ClassWriter;
47
import org.objectweb.asm.Opcodes;
58
import org.objectweb.asm.tree.AnnotationNode;

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/MethodMerger.java renamed to plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/merge/element/MethodMerger.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package io.github.cichlidmc.cichlid_gradle.merge;
1+
package io.github.cichlidmc.cichlid_gradle.merge.element;
22

33
import org.objectweb.asm.tree.AnnotationNode;
44
import org.objectweb.asm.tree.ClassNode;

plugin/src/main/java/io/github/cichlidmc/cichlid_gradle/run/RunTaskGeneration.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ private static void generateTask(RunConfiguration config, CichlidCache cache, Pr
3737
Map<String, RunsStorage.DefaultRunConfig> templateMap = cache.runs.getDefaultRuns(version);
3838
RunsStorage.DefaultRunConfig template = templateMap.get(templateName);
3939
if (template == null) {
40-
throw new IllegalArgumentException("There is no run config template named '" + templateName + "'. Options: " + templateMap.keySet());
40+
throw new IllegalArgumentException(
41+
"There is no run config template named '" + templateName
42+
+ "' for version '" + version + "'. Options: " + templateMap.keySet()
43+
);
4144
}
4245

4346
project.getTasks().register(taskName, JavaExec.class, task -> {

0 commit comments

Comments
 (0)