mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-30 21:23:00 +00:00 
			
		
		
		
	Replace Fabric JUnit hacks with fabric-loader-junit
Also configure some of our common JUnit run configurations via Gradle - I end up setting these up in every worktree anyway - so let's just do it once.
This commit is contained in:
		| @@ -2,7 +2,11 @@ | |||||||
| // | // | ||||||
| // SPDX-License-Identifier: MPL-2.0 | // SPDX-License-Identifier: MPL-2.0 | ||||||
| 
 | 
 | ||||||
|  | import cc.tweaked.gradle.JUnitExt | ||||||
|  | import net.fabricmc.loom.api.LoomGradleExtensionAPI | ||||||
|  | import net.fabricmc.loom.util.gradle.SourceSetHelper | ||||||
| import org.jetbrains.gradle.ext.compiler | import org.jetbrains.gradle.ext.compiler | ||||||
|  | import org.jetbrains.gradle.ext.runConfigurations | ||||||
| import org.jetbrains.gradle.ext.settings | import org.jetbrains.gradle.ext.settings | ||||||
| 
 | 
 | ||||||
| plugins { | plugins { | ||||||
| @@ -38,6 +42,50 @@ githubRelease { | |||||||
| 
 | 
 | ||||||
| tasks.publish { dependsOn(tasks.githubRelease) } | tasks.publish { dependsOn(tasks.githubRelease) } | ||||||
| 
 | 
 | ||||||
|  | idea.project.settings.runConfigurations { | ||||||
|  |     register<JUnitExt>("Core Tests") { | ||||||
|  |         vmParameters = "-ea" | ||||||
|  |         moduleName = "${idea.project.name}.core.test" | ||||||
|  |         packageName = "" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     register<JUnitExt>("CraftOS Tests") { | ||||||
|  |         vmParameters = "-ea" | ||||||
|  |         moduleName = "${idea.project.name}.core.test" | ||||||
|  |         className = "dan200.computercraft.core.ComputerTestDelegate" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     register<JUnitExt>("CraftOS Tests (Fast)") { | ||||||
|  |         vmParameters = "-ea -Dcc.skip_keywords=slow" | ||||||
|  |         moduleName = "${idea.project.name}.core.test" | ||||||
|  |         className = "dan200.computercraft.core.ComputerTestDelegate" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     register<JUnitExt>("Common Tests") { | ||||||
|  |         vmParameters = "-ea" | ||||||
|  |         moduleName = "${idea.project.name}.common.test" | ||||||
|  |         packageName = "" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     register<JUnitExt>("Fabric Tests") { | ||||||
|  |         val fabricProject = evaluationDependsOn(":fabric") | ||||||
|  |         val classPathGroup = fabricProject.extensions.getByType<LoomGradleExtensionAPI>().mods | ||||||
|  |             .joinToString(File.pathSeparator + File.pathSeparator) { modSettings -> | ||||||
|  |                 SourceSetHelper.getClasspath(modSettings, project).joinToString(File.pathSeparator) { it.absolutePath } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |         vmParameters = "-ea -Dfabric.classPathGroups=$classPathGroup" | ||||||
|  |         moduleName = "${idea.project.name}.fabric.test" | ||||||
|  |         packageName = "" | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     register<JUnitExt>("Forge Tests") { | ||||||
|  |         vmParameters = "-ea" | ||||||
|  |         moduleName = "${idea.project.name}.forge.test" | ||||||
|  |         packageName = "" | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| idea.project.settings.compiler.javac { | idea.project.settings.compiler.javac { | ||||||
|     // We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings |     // We want ErrorProne to be present when compiling via IntelliJ, as it offers some helpful warnings | ||||||
|     // and errors. Loop through our source sets and find the appropriate flags. |     // and errors. Loop through our source sets and find the appropriate flags. | ||||||
|   | |||||||
| @@ -50,10 +50,11 @@ dependencies { | |||||||
|     implementation(libs.curseForgeGradle) |     implementation(libs.curseForgeGradle) | ||||||
|     implementation(libs.fabric.loom) |     implementation(libs.fabric.loom) | ||||||
|     implementation(libs.forgeGradle) |     implementation(libs.forgeGradle) | ||||||
|  |     implementation(libs.ideaExt) | ||||||
|     implementation(libs.librarian) |     implementation(libs.librarian) | ||||||
|     implementation(libs.minotaur) |     implementation(libs.minotaur) | ||||||
|     implementation(libs.vineflower) |  | ||||||
|     implementation(libs.vanillaGradle) |     implementation(libs.vanillaGradle) | ||||||
|  |     implementation(libs.vineflower) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| gradlePlugin { | gradlePlugin { | ||||||
|   | |||||||
| @@ -9,6 +9,10 @@ import org.gradle.api.Project | |||||||
| import org.gradle.api.plugins.JavaPlugin | import org.gradle.api.plugins.JavaPlugin | ||||||
| import org.gradle.api.plugins.JavaPluginExtension | import org.gradle.api.plugins.JavaPluginExtension | ||||||
| import org.gradle.jvm.toolchain.JavaLanguageVersion | import org.gradle.jvm.toolchain.JavaLanguageVersion | ||||||
|  | import org.gradle.plugins.ide.idea.model.IdeaModel | ||||||
|  | import org.jetbrains.gradle.ext.IdeaExtPlugin | ||||||
|  | import org.jetbrains.gradle.ext.runConfigurations | ||||||
|  | import org.jetbrains.gradle.ext.settings | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Configures projects to match a shared configuration. |  * Configures projects to match a shared configuration. | ||||||
| @@ -21,6 +25,20 @@ class CCTweakedPlugin : Plugin<Project> { | |||||||
|             val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets |             val sourceSets = project.extensions.getByType(JavaPluginExtension::class.java).sourceSets | ||||||
|             cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main"))) |             cct.sourceDirectories.add(SourceSetReference.internal(sourceSets.getByName("main"))) | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         project.plugins.withType(IdeaExtPlugin::class.java) { extendIdea(project) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Extend the [IdeaExtPlugin] plugin's `runConfiguration` container to also support [JUnitExt]. | ||||||
|  |      */ | ||||||
|  |     private fun extendIdea(project: Project) { | ||||||
|  |         val ideaModel = project.extensions.findByName("idea") as IdeaModel? ?: return | ||||||
|  |         val ideaProject = ideaModel.project ?: return | ||||||
|  | 
 | ||||||
|  |         ideaProject.settings.runConfigurations { | ||||||
|  |             registerFactory(JUnitExt::class.java) { name -> project.objects.newInstance(JUnitExt::class.java, name) } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     companion object { |     companion object { | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								buildSrc/src/main/kotlin/cc/tweaked/gradle/IdeaExt.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | // SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers | ||||||
|  | // | ||||||
|  | // SPDX-License-Identifier: MPL-2.0 | ||||||
|  | 
 | ||||||
|  | package cc.tweaked.gradle | ||||||
|  | 
 | ||||||
|  | import org.jetbrains.gradle.ext.JUnit | ||||||
|  | import javax.inject.Inject | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A version of [JUnit] with a functional [className]. | ||||||
|  |  * | ||||||
|  |  * See [#92](https://github.com/JetBrains/gradle-idea-ext-plugin/issues/92). | ||||||
|  |  */ | ||||||
|  | open class JUnitExt @Inject constructor(nameParam: String) : JUnit(nameParam) { | ||||||
|  |     override fun toMap(): MutableMap<String, *> { | ||||||
|  |         val map = HashMap(super.toMap()) | ||||||
|  |         // Should be "class" instead of "className". | ||||||
|  |         // See https://github.com/JetBrains/intellij-community/blob/9ba394021dc73a3926f13d6d6cdf434f9ee7046d/plugins/junit/src/com/intellij/execution/junit/JUnitRunConfigurationImporter.kt#L39 | ||||||
|  |         map["class"] = className | ||||||
|  |         return map | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -45,7 +45,6 @@ rubidium = "0.6.1" | |||||||
| sodium = "mc1.20-0.4.10" | sodium = "mc1.20-0.4.10" | ||||||
|  |  | ||||||
| # Testing | # Testing | ||||||
| byteBuddy = "1.14.7" |  | ||||||
| hamcrest = "2.2" | hamcrest = "2.2" | ||||||
| jqwik = "1.7.4" | jqwik = "1.7.4" | ||||||
| junit = "5.10.0" | junit = "5.10.0" | ||||||
| @@ -97,6 +96,7 @@ slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } | |||||||
| # Minecraft mods | # Minecraft mods | ||||||
| fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } | fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabric-api" } | ||||||
| fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } | fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabric-loader" } | ||||||
|  | fabric-junit = { module = "net.fabricmc:fabric-loader-junit", version.ref = "fabric-loader" } | ||||||
| fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" } | fabricPermissions = { module = "me.lucko:fabric-permissions-api", version.ref = "fabricPermissions" } | ||||||
| emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" } | emi = { module = "dev.emi:emi-xplat-mojmap", version.ref = "emi" } | ||||||
| iris = { module = "maven.modrinth:iris", version.ref = "iris" } | iris = { module = "maven.modrinth:iris", version.ref = "iris" } | ||||||
| @@ -114,8 +114,6 @@ rubidium = { module = "maven.modrinth:rubidium", version.ref = "rubidium" } | |||||||
| sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" } | sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" } | ||||||
|  |  | ||||||
| # Testing | # Testing | ||||||
| byteBuddyAgent = { module = "net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" } |  | ||||||
| byteBuddy = { module = "net.bytebuddy:byte-buddy", version.ref = "byteBuddy" } |  | ||||||
| hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } | hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" } | ||||||
| jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" } | jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" } | ||||||
| jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" } | jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" } | ||||||
| @@ -135,6 +133,7 @@ errorProne-plugin = { module = "net.ltgt.gradle:gradle-errorprone-plugin", versi | |||||||
| errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" } | errorProne-testHelpers = { module = "com.google.errorprone:error_prone_test_helpers", version.ref = "errorProne-core" } | ||||||
| fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" } | fabric-loom = { module = "net.fabricmc:fabric-loom", version.ref = "fabric-loom" } | ||||||
| forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" } | forgeGradle = { module = "net.minecraftforge.gradle:ForgeGradle", version.ref = "forgeGradle" } | ||||||
|  | ideaExt = { module = "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext", version.ref = "ideaExt" } | ||||||
| kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } | kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } | ||||||
| librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" } | librarian = { module = "org.parchmentmc:librarian", version.ref = "librarian" } | ||||||
| minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" } | minotaur = { module = "com.modrinth.minotaur:Minotaur", version.ref = "minotaur" } | ||||||
| @@ -154,7 +153,6 @@ vineflower = { module = "io.github.juuxel:loom-vineflower", version.ref = "vinef | |||||||
| [plugins] | [plugins] | ||||||
| forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" } | forgeGradle = { id = "net.minecraftforge.gradle", version.ref = "forgeGradle" } | ||||||
| githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" } | githubRelease = { id = "com.github.breadmoirai.github-release", version.ref = "githubRelease" } | ||||||
| ideaExt = { id = "org.jetbrains.gradle.plugin.idea-ext", version.ref = "ideaExt" } |  | ||||||
| kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } | kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } | ||||||
| librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" } | librarian = { id = "org.parchmentmc.librarian.forgegradle", version.ref = "librarian" } | ||||||
| mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" } | mixinGradle = { id = "org.spongepowered.mixin", version.ref = "mixinGradle" } | ||||||
|   | |||||||
| @@ -87,10 +87,9 @@ dependencies { | |||||||
|     testModImplementation(testFixtures(project(":core"))) |     testModImplementation(testFixtures(project(":core"))) | ||||||
|     testModImplementation(testFixtures(project(":fabric"))) |     testModImplementation(testFixtures(project(":fabric"))) | ||||||
| 
 | 
 | ||||||
|     testImplementation(libs.byteBuddy) |  | ||||||
|     testImplementation(libs.byteBuddyAgent) |  | ||||||
|     testImplementation(libs.bundles.test) |     testImplementation(libs.bundles.test) | ||||||
|     testRuntimeOnly(libs.bundles.testRuntime) |     testRuntimeOnly(libs.bundles.testRuntime) | ||||||
|  |     testRuntimeOnly(libs.fabric.junit) | ||||||
| 
 | 
 | ||||||
|     testFixturesImplementation(testFixtures(project(":core"))) |     testFixturesImplementation(testFixtures(project(":core"))) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,249 +0,0 @@ | |||||||
| // SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers |  | ||||||
| // |  | ||||||
| // SPDX-License-Identifier: MPL-2.0 |  | ||||||
| 
 |  | ||||||
| package dan200.computercraft.shared; |  | ||||||
| 
 |  | ||||||
| import com.google.auto.service.AutoService; |  | ||||||
| import com.google.common.base.Splitter; |  | ||||||
| import com.google.common.io.ByteStreams; |  | ||||||
| import net.bytebuddy.agent.ByteBuddyAgent; |  | ||||||
| import net.bytebuddy.dynamic.loading.ClassInjector; |  | ||||||
| import net.fabricmc.api.EnvType; |  | ||||||
| import net.fabricmc.loader.impl.FabricLoaderImpl; |  | ||||||
| import net.fabricmc.loader.impl.game.minecraft.MinecraftGameProvider; |  | ||||||
| import net.fabricmc.loader.impl.game.minecraft.Slf4jLogHandler; |  | ||||||
| import net.fabricmc.loader.impl.launch.FabricLauncherBase; |  | ||||||
| import net.fabricmc.loader.impl.launch.FabricMixinBootstrap; |  | ||||||
| import net.fabricmc.loader.impl.launch.knot.MixinServiceKnot; |  | ||||||
| import net.fabricmc.loader.impl.transformer.FabricTransformer; |  | ||||||
| import net.fabricmc.loader.impl.util.LoaderUtil; |  | ||||||
| import net.fabricmc.loader.impl.util.log.Log; |  | ||||||
| import org.junit.jupiter.api.extension.Extension; |  | ||||||
| import org.slf4j.Logger; |  | ||||||
| import org.slf4j.LoggerFactory; |  | ||||||
| import org.spongepowered.asm.mixin.MixinEnvironment; |  | ||||||
| import org.spongepowered.asm.mixin.transformer.IMixinTransformer; |  | ||||||
| 
 |  | ||||||
| import javax.annotation.Nullable; |  | ||||||
| import java.io.File; |  | ||||||
| import java.io.IOException; |  | ||||||
| import java.io.InputStream; |  | ||||||
| import java.lang.instrument.ClassFileTransformer; |  | ||||||
| import java.lang.instrument.IllegalClassFormatException; |  | ||||||
| import java.lang.instrument.Instrumentation; |  | ||||||
| import java.nio.file.Files; |  | ||||||
| import java.nio.file.Path; |  | ||||||
| import java.nio.file.Paths; |  | ||||||
| import java.security.ProtectionDomain; |  | ||||||
| import java.util.*; |  | ||||||
| import java.util.jar.Manifest; |  | ||||||
| import java.util.stream.Collectors; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Loads Fabric mods as part of this test run. |  | ||||||
|  * <p> |  | ||||||
|  * This sets up a minimalistic {@link FabricLauncherBase}, uses that to load mods, and then acquires an |  | ||||||
|  * {@link Instrumentation} instance, registering a {@link ClassFileTransformer} to apply mixins and access wideners. |  | ||||||
|  * |  | ||||||
|  * @see net.fabricmc.loader.impl.launch.knot.Knot |  | ||||||
|  */ |  | ||||||
| @AutoService(Extension.class) |  | ||||||
| public class FabricBootstrap implements Extension { |  | ||||||
|     private static final Logger LOG = LoggerFactory.getLogger(FabricBootstrap.class); |  | ||||||
| 
 |  | ||||||
|     public FabricBootstrap() throws ReflectiveOperationException, IOException { |  | ||||||
|         Log.init(new Slf4jLogHandler()); |  | ||||||
| 
 |  | ||||||
|         readProperties(); |  | ||||||
|         { |  | ||||||
|             var method = FabricLauncherBase.class.getDeclaredMethod("setProperties", Map.class); |  | ||||||
|             method.setAccessible(true); |  | ||||||
|             method.invoke(null, new HashMap<>()); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         var provider = new MinecraftGameProvider(); |  | ||||||
|         if (!provider.locateGame(new BasicLauncher(), new String[0])) { |  | ||||||
|             throw new IllegalStateException("Cannot setup game"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         var loader = FabricLoaderImpl.INSTANCE; |  | ||||||
|         loader.setGameProvider(provider); |  | ||||||
|         loader.load(); |  | ||||||
|         loader.freeze(); |  | ||||||
|         loader.loadAccessWideners(); |  | ||||||
| 
 |  | ||||||
|         FabricMixinBootstrap.init(EnvType.CLIENT, loader); |  | ||||||
|         { |  | ||||||
|             var method = FabricLauncherBase.class.getDeclaredMethod("finishMixinBootstrapping"); |  | ||||||
|             method.setAccessible(true); |  | ||||||
|             method.invoke(null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         IMixinTransformer transformer; |  | ||||||
|         { |  | ||||||
|             var method = MixinServiceKnot.class.getDeclaredMethod("getTransformer"); |  | ||||||
|             method.setAccessible(true); |  | ||||||
|             transformer = (IMixinTransformer) method.invoke(null); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         ByteBuddyAgent.install().addTransformer(new ClassTransformer(transformer)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static void readProperties() throws IOException { |  | ||||||
|         try (var reader = Files.newBufferedReader(Path.of(".gradle/loom-cache/launch.cfg"))) { |  | ||||||
|             var interesting = false; |  | ||||||
| 
 |  | ||||||
|             String line; |  | ||||||
|             while ((line = reader.readLine()) != null) { |  | ||||||
|                 if (line.startsWith(" ") || line.startsWith("\t")) { |  | ||||||
|                     if (!interesting) continue; |  | ||||||
| 
 |  | ||||||
|                     line = line.strip(); |  | ||||||
|                     var index = line.indexOf('='); |  | ||||||
| 
 |  | ||||||
|                     if (index >= 0) { |  | ||||||
|                         System.setProperty(line.substring(0, index), line.substring(index + 1)); |  | ||||||
|                     } else { |  | ||||||
|                         System.setProperty(line, ""); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     interesting = line.equals("commonProperties") || line.equals("clientProperties"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static final class BasicLauncher extends FabricLauncherBase { |  | ||||||
|         private final List<Path> classpath = new ArrayList<>(); |  | ||||||
| 
 |  | ||||||
|         BasicLauncher() { |  | ||||||
|             for (var entry : Splitter.on(File.pathSeparatorChar).split(System.getProperty("java.class.path"))) { |  | ||||||
|                 var path = Paths.get(entry); |  | ||||||
|                 if (Files.exists(path)) classpath.add(LoaderUtil.normalizeExistingPath(path)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void addToClassPath(Path path, String... allowedPrefixes) { |  | ||||||
|             classpath.add(path); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void setAllowedPrefixes(Path path, String... prefixes) { |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public void setValidParentClassPath(Collection<Path> paths) { |  | ||||||
|             throw new UnsupportedOperationException("setValidParentClassPath"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public EnvType getEnvironmentType() { |  | ||||||
|             return EnvType.CLIENT; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public boolean isClassLoaded(String name) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Class<?> loadIntoTarget(String name) { |  | ||||||
|             throw new UnsupportedOperationException("loadIntoTarget"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public ClassLoader getTargetClassLoader() { |  | ||||||
|             return Thread.currentThread().getContextClassLoader(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public @Nullable InputStream getResourceAsStream(String name) { |  | ||||||
|             return BasicLauncher.class.getClassLoader().getResourceAsStream(name); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public @Nullable byte[] getClassByteArray(String name, boolean runTransformers) throws IOException { |  | ||||||
|             try (var stream = BasicLauncher.class.getClassLoader().getResourceAsStream(LoaderUtil.getClassFileName(name))) { |  | ||||||
|                 if (stream == null) return null; |  | ||||||
|                 return ByteStreams.toByteArray(stream); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public Manifest getManifest(Path originPath) { |  | ||||||
|             throw new UnsupportedOperationException("getManifest"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public boolean isDevelopment() { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public String getEntrypoint() { |  | ||||||
|             throw new UnsupportedOperationException("getEntrypoint"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public String getTargetNamespace() { |  | ||||||
|             return "named"; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public List<Path> getClassPath() { |  | ||||||
|             return classpath; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     private static final class ClassTransformer implements ClassFileTransformer { |  | ||||||
|         private final IMixinTransformer transformer; |  | ||||||
|         private final Set<String> definedClasses = new HashSet<>(); |  | ||||||
|         private final Set<String> generatedClasses; |  | ||||||
| 
 |  | ||||||
|         private ClassTransformer(IMixinTransformer transformer) { |  | ||||||
|             this.transformer = transformer; |  | ||||||
| 
 |  | ||||||
|             try { |  | ||||||
|                 // As we can't hook into classloading itself, we need to track all the classes Mixin has generated. Yes, |  | ||||||
|                 // this is nasty. |  | ||||||
|                 var syntheticRegistryField = transformer.getClass().getDeclaredField("syntheticClassRegistry"); |  | ||||||
|                 syntheticRegistryField.setAccessible(true); |  | ||||||
|                 var syntheticRegistry = syntheticRegistryField.get(transformer); |  | ||||||
| 
 |  | ||||||
|                 var classesField = syntheticRegistry.getClass().getDeclaredField("classes"); |  | ||||||
|                 classesField.setAccessible(true); |  | ||||||
|                 @SuppressWarnings("unchecked") var classes = (Map<String, ?>) classesField.get(syntheticRegistry); |  | ||||||
|                 generatedClasses = classes.keySet(); |  | ||||||
|             } catch (ReflectiveOperationException e) { |  | ||||||
|                 throw new RuntimeException(e); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         @Override |  | ||||||
|         public synchronized @Nullable byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] bytes) throws IllegalClassFormatException { |  | ||||||
|             var name = className.replace('/', '.'); |  | ||||||
|             var transformed = FabricTransformer.transform(true, EnvType.CLIENT, name, bytes); |  | ||||||
|             transformed = transformer.transformClassBytes(name, name, transformed); |  | ||||||
| 
 |  | ||||||
|             // Keep track of all generated classes that we've seen, and define any new ones. We use ByteBuddy to inject |  | ||||||
|             // the new class definitions, as doing it ourselves is hard. |  | ||||||
|             if (generatedClasses.size() > definedClasses.size()) { |  | ||||||
|                 var toDefine = generatedClasses.stream().filter(definedClasses::add).collect(Collectors.toUnmodifiableMap( |  | ||||||
|                     genName -> genName.replace('/', '.'), |  | ||||||
|                     genName -> transformer.generateClass(MixinEnvironment.getDefaultEnvironment(), genName) |  | ||||||
|                 )); |  | ||||||
| 
 |  | ||||||
|                 LOG.info("Defining {}", toDefine.keySet()); |  | ||||||
|                 try { |  | ||||||
|                     new ClassInjector.UsingReflection(loader).injectRaw(toDefine); |  | ||||||
|                 } catch (Exception e) { |  | ||||||
|                     LOG.error("Failed to define {}", className, e); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             return transformed == bytes ? null : transformed; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates