From 112a38be2a40f9f29a6d110171bc7192bcb90d94 Mon Sep 17 00:00:00 2001
From: modmuss50 <modmuss50@gmail.com>
Date: Sun, 11 Dec 2022 13:50:46 +0000
Subject: [PATCH] Generate access wideners for DataProvider.getName
 implementations (#2736)

* Generate AW for DataProvider.getName impls

* Remove final

* Typo + improve memory usage
---
 fabric-data-generation-api-v1/build.gradle    | 96 +++++++++++++++----
 .../v1/provider/FabricLanguageProvider.java   |  2 +-
 ...abric-data-generation-api-v1.accesswidener | 13 +++
 3 files changed, 94 insertions(+), 17 deletions(-)

diff --git a/fabric-data-generation-api-v1/build.gradle b/fabric-data-generation-api-v1/build.gradle
index 7506fe753..77d21e241 100644
--- a/fabric-data-generation-api-v1/build.gradle
+++ b/fabric-data-generation-api-v1/build.gradle
@@ -56,6 +56,8 @@ import org.objectweb.asm.ClassReader
 import org.objectweb.asm.Opcodes
 import org.objectweb.asm.tree.ClassNode
 
+import java.lang.reflect.Modifier
+import java.util.zip.ZipEntry
 import java.util.zip.ZipFile
 
 task generateAccessWidener() {
@@ -63,14 +65,16 @@ task generateAccessWidener() {
 		File inputJar = loom.namedMinecraftProvider.parentMinecraftProvider.commonJar.toFile()
 		String accessWidener = file("template.accesswidener").text + "\n"
 
-		visitMethods(inputJar, "net/minecraft/data/server/recipe/RecipeProvider.class") { name, desc, owner ->
+		def classes = getClasses(inputJar)
+
+		visitMethods(classes["net/minecraft/data/server/recipe/RecipeProvider"]) { name, desc, owner ->
 			if (it.name == "generate")
 				return
 
 			accessWidener += "transitive-accessible\tmethod\t${owner}\t${name}\t${desc}\n"
 		}
 
-		visitMethods(inputJar, "net/minecraft/data/client/BlockStateModelGenerator.class") { name, desc, owner ->
+		visitMethods(classes["net/minecraft/data/client/BlockStateModelGenerator"]) { name, desc, owner ->
 			if (desc == "()V")
 				// Skip over methods that dont take any arguments, as they are specific to minecraft.
 				return
@@ -78,40 +82,100 @@ task generateAccessWidener() {
 			accessWidener += "transitive-accessible\tmethod\t${owner}\t${name}\t${desc}\n"
 		}
 
-		visitMethods(inputJar, "net/minecraft/data/server/loottable/BlockLootTableGenerator.class") { name, desc, owner ->
+		visitMethods(classes["net/minecraft/data/server/loottable/BlockLootTableGenerator"]) { name, desc, owner ->
 			accessWidener += "transitive-accessible\tmethod\t${owner}\t${name}\t${desc}\n"
 		}
 
-		visitMethods(inputJar, "net/minecraft/data/client/ItemModelGenerator.class") { name, desc, owner ->
+		visitMethods(classes["net/minecraft/data/client/ItemModelGenerator"]) { name, desc, owner ->
 			accessWidener += "transitive-accessible\tmethod\t${owner}\t${name}\t${desc}\n"
 		}
 
+		classes.values().forEach { classNode ->
+			visitFinalMethods(classNode) { name, desc, owner ->
+				if (name != "getName" || desc != "()Ljava/lang/String;") {
+					// Not the method we are after
+					return
+				}
+
+				if (!hasAncestor(classNode, classes, "net/minecraft/data/DataProvider")) {
+					// Not a descendant of DataProvider
+					return
+				}
+
+				accessWidener += "transitive-extendable\tmethod\t${owner}\t${name}\t${desc}\n"
+			}
+		}
+
 		file("src/main/resources/fabric-data-generation-api-v1.accesswidener").text = accessWidener
 	}
 }
 
-def visitMethods(File input, String className, closure) {
-	def clazz = getClassNode(input, className)
-
-	clazz.methods.forEach {
+def visitMethods(ClassNode classNode, closure) {
+	classNode.methods.forEach {
 		if ((it.access & Opcodes.ACC_SYNTHETIC) != 0 || (it.access & Opcodes.ACC_PUBLIC) != 0)
 			return
 
 		if (it.name.startsWith("<"))
 			return
 
-		closure(it.name, it.desc, clazz.name)
+		closure(it.name, it.desc, classNode.name)
 	}
 }
 
-ClassNode getClassNode(File input, String className) {
-	new ZipFile(input).withCloseable { ZipFile zip  ->
-		zip.getInputStream(zip.getEntry(className)).withCloseable { is ->
-			ClassReader reader = new ClassReader(is)
-			ClassNode classNode = new ClassNode()
-			reader.accept(classNode, 0)
+def visitFinalMethods(ClassNode classNode, closure) {
+	classNode.methods.forEach {
+		if (!Modifier.isFinal(it.access))
+			return
 
-			return classNode
+		if (it.name.startsWith("<"))
+			return
+
+		closure(it.name, it.desc, classNode.name)
+	}
+}
+
+// Return a map of all class names to classNodes
+def getClasses(File input) {
+	Map<String, ClassNode> classes = [:]
+
+	new ZipFile(input).withCloseable { ZipFile zip  ->
+		zip.entries().toList().forEach { ZipEntry entry ->
+			if (!entry.name.endsWith(".class")) {
+				return
+			}
+
+			zip.getInputStream(entry).withCloseable { is ->
+				ClassReader reader = new ClassReader(is)
+				ClassNode classNode = new ClassNode()
+				reader.accept(classNode, ClassReader.SKIP_CODE)
+
+				classes.put(classNode.name, classNode)
+			}
+		}
+	}
+
+	return classes
+}
+
+def hasAncestor(ClassNode classNode, Map<String, ClassNode> classes, String ancestorName) {
+	if (classNode.superName == ancestorName) {
+		return true
+	}
+
+	// Recuse through the super classes
+	def superClass = classes.get(classNode.superName)
+	if (superClass != null && hasAncestor(superClass, classes, ancestorName)) {
+		return true
+	}
+
+	for (def interfaceName : classNode.interfaces) {
+		if (interfaceName == ancestorName) {
+			return true
+		}
+
+		def ifaceClass = classes.get(interfaceName)
+		if (ifaceClass != null && hasAncestor(ifaceClass, classes, ancestorName)) {
+			return true
 		}
 	}
 }
diff --git a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricLanguageProvider.java b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricLanguageProvider.java
index f4ab8c8cb..3250b4743 100644
--- a/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricLanguageProvider.java
+++ b/fabric-data-generation-api-v1/src/main/java/net/fabricmc/fabric/api/datagen/v1/provider/FabricLanguageProvider.java
@@ -102,7 +102,7 @@ public abstract class FabricLanguageProvider implements DataProvider {
 	}
 
 	@Override
-	public final String getName() {
+	public String getName() {
 		return "Language (%s)".formatted(languageCode);
 	}
 
diff --git a/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener b/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
index b1a727ac5..1bd8475d3 100644
--- a/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
+++ b/fabric-data-generation-api-v1/src/main/resources/fabric-data-generation-api-v1.accesswidener
@@ -284,3 +284,16 @@ transitive-accessible	method	net/minecraft/data/client/ItemModelGenerator	regist
 transitive-accessible	method	net/minecraft/data/client/ItemModelGenerator	register	(Lnet/minecraft/item/Item;Lnet/minecraft/item/Item;Lnet/minecraft/data/client/Model;)V
 transitive-accessible	method	net/minecraft/data/client/ItemModelGenerator	registerCompass	(Lnet/minecraft/item/Item;)V
 transitive-accessible	method	net/minecraft/data/client/ItemModelGenerator	registerClock	(Lnet/minecraft/item/Item;)V
+transitive-extendable	method	net/minecraft/data/server/advancement/AdvancementProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/report/CommandSyntaxProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/report/BlockListProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/report/RegistryDumpProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/server/loottable/LootTableProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/MetadataProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/client/ModelProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/server/BiomeParametersProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/dev/NbtProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/SnbtProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/server/recipe/RecipeProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/report/DynamicRegistriesProvider	getName	()Ljava/lang/String;
+transitive-extendable	method	net/minecraft/data/server/tag/AbstractTagProvider	getName	()Ljava/lang/String;