mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-11 19:14:11 +00:00
Derive modded bukkit baseline
- Read the modded bukkit-supertype baseline from core/purity-allowlist.txt - Match nested classes against their top-level source entry - Exempt VolmLib classes, which Iris' core ratchet does not govern - Delete the hand-frozen knownBukkitSupertypeClasses inventory
This commit is contained in:
+14
-35
@@ -440,44 +440,21 @@ List<String> commonModdedArtifactEntries = [
|
||||
'assets/irisworldgen/icon.png'
|
||||
]
|
||||
|
||||
// Frozen inventory of shipped classes that extend or implement an org.bukkit type. They come from
|
||||
// core and VolmLib, sit on Bukkit-only code paths the mod loaders never resolve, and are inert dead
|
||||
// weight rather than a boot hazard - excluding them means chasing the whole core reference graph.
|
||||
// The verifier fails on anything NEW outside this set, which is the part that would be a regression.
|
||||
// Shrinking the set (WP4 deletions, retypes) needs no edit here; growing it does.
|
||||
Set<String> knownBukkitSupertypeClasses = [
|
||||
'art/arcane/iris/core/events/IrisEngineEvent.class',
|
||||
'art/arcane/iris/core/events/IrisLootEvent.class',
|
||||
'art/arcane/iris/core/events/IrisLootEvent$1.class',
|
||||
'art/arcane/iris/core/events/IrisLootEvent$2.class',
|
||||
'art/arcane/iris/core/link/data/ItemAdderDataProvider.class',
|
||||
'art/arcane/iris/core/link/data/MythicMobsDataProvider.class',
|
||||
'art/arcane/iris/core/link/data/OraxenDataProvider.class',
|
||||
'art/arcane/iris/core/link/MultiverseGuardListener.class',
|
||||
'art/arcane/iris/engine/framework/EngineAssignedWorldManager.class',
|
||||
'art/arcane/iris/engine/object/IrisEntity$BukkitOps$1.class',
|
||||
'art/arcane/iris/engine/object/LegacyTileData$SignTags$1.class',
|
||||
'art/arcane/iris/engine/platform/BukkitChunkGenerator.class',
|
||||
'art/arcane/iris/engine/platform/DummyBiomeProvider.class',
|
||||
'art/arcane/iris/engine/platform/DummyChunkGenerator.class',
|
||||
'art/arcane/iris/util/common/data/IrisCustomData.class',
|
||||
'art/arcane/iris/util/common/plugin/IrisService.class',
|
||||
'art/arcane/iris/util/common/plugin/VolmitPlugin.class',
|
||||
'art/arcane/iris/util/common/plugin/VolmitSender.class',
|
||||
'art/arcane/iris/util/common/plugin/chunk/ChunkTickets.class',
|
||||
'art/arcane/volmlib/util/board/BoardUpdateTask.class',
|
||||
'art/arcane/volmlib/util/bukkit/Events.class',
|
||||
'art/arcane/volmlib/util/data/Cuboid.class',
|
||||
'art/arcane/volmlib/util/data/InvertedBiomeGrid.class',
|
||||
'art/arcane/volmlib/util/data/IrisBiomeStorage.class',
|
||||
'art/arcane/volmlib/util/inventorygui/UIWindow.class'
|
||||
] as Set
|
||||
// The modded jars ship the Bukkit-only code paths inert - excluding them means chasing the whole core
|
||||
// reference graph. What must not slip in is new Bukkit coupling in core, which core already declares
|
||||
// in purity-allowlist.txt, so the baseline is read from there instead of frozen by hand.
|
||||
File bukkitPurityAllowlist = layout.projectDirectory.file('core/purity-allowlist.txt').asFile
|
||||
Set<String> bukkitCoupledCoreClasses = bukkitPurityAllowlist.readLines()
|
||||
.findAll { String line -> !line.isBlank() }
|
||||
.collect { String line -> line.trim() - ~/\.java$/ }
|
||||
.toSet()
|
||||
|
||||
tasks.register('verifyFabricArtifact') {
|
||||
group = 'verification'
|
||||
dependsOn('fabricJar')
|
||||
File artifact = layout.projectDirectory.file("adapters/fabric/build/libs/${fabricArtifactName}").asFile
|
||||
inputs.file(artifact)
|
||||
inputs.file(bukkitPurityAllowlist)
|
||||
doLast {
|
||||
List<String> requiredEntries = commonModdedArtifactEntries + [
|
||||
'fabric.mod.json',
|
||||
@@ -485,7 +462,7 @@ tasks.register('verifyFabricArtifact') {
|
||||
'irisworldgen.accesswidener',
|
||||
'art/arcane/iris/fabric/IrisFabricBootstrap.class'
|
||||
]
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, knownBukkitSupertypeClasses)
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses)
|
||||
logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars")
|
||||
}
|
||||
}
|
||||
@@ -495,13 +472,14 @@ tasks.register('verifyForgeArtifact') {
|
||||
dependsOn('forgeJar')
|
||||
File artifact = layout.projectDirectory.file("adapters/forge/build/libs/${forgeArtifactName}").asFile
|
||||
inputs.file(artifact)
|
||||
inputs.file(bukkitPurityAllowlist)
|
||||
doLast {
|
||||
List<String> requiredEntries = commonModdedArtifactEntries + [
|
||||
'META-INF/mods.toml',
|
||||
'META-INF/accesstransformer.cfg',
|
||||
'art/arcane/iris/forge/IrisForgeBootstrap.class'
|
||||
]
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, knownBukkitSupertypeClasses)
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses)
|
||||
logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars")
|
||||
}
|
||||
}
|
||||
@@ -511,13 +489,14 @@ tasks.register('verifyNeoforgeArtifact') {
|
||||
dependsOn('neoforgeJar')
|
||||
File artifact = layout.projectDirectory.file("adapters/neoforge/build/libs/${neoForgeArtifactName}").asFile
|
||||
inputs.file(artifact)
|
||||
inputs.file(bukkitPurityAllowlist)
|
||||
doLast {
|
||||
List<String> requiredEntries = commonModdedArtifactEntries + [
|
||||
'META-INF/neoforge.mods.toml',
|
||||
'META-INF/accesstransformer.cfg',
|
||||
'art/arcane/iris/neoforge/IrisNeoForgeBootstrap.class'
|
||||
]
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, knownBukkitSupertypeClasses)
|
||||
ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses)
|
||||
logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ public final class ModdedArtifactVerifier {
|
||||
// Only the Bukkit platform binding is allowed to sit on org.bukkit types, and the modded jars
|
||||
// exclude it outright. Kept as an exemption so the same check can run over the Bukkit artifact.
|
||||
private static final String BUKKIT_PLATFORM_PREFIX = "art/arcane/iris/platform/bukkit/";
|
||||
// VolmLib is a separate library with its own build; Iris' core purity ratchet does not govern it.
|
||||
private static final String SHARED_LIBRARY_PREFIX = "art/arcane/volmlib/";
|
||||
private static final String BUKKIT_TYPE_PREFIX = "org/bukkit/";
|
||||
private static final String FABRIC_METADATA = "fabric.mod.json";
|
||||
private static final String NEOFORGE_METADATA = "META-INF/neoforge.mods.toml";
|
||||
@@ -65,12 +67,14 @@ public final class ModdedArtifactVerifier {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bukkitSupertypeBaseline entry names (as they appear in the jar) that are known to
|
||||
* extend or implement an {@code org.bukkit} type. New offenders
|
||||
* outside this frozen set fail the build; entries that disappear
|
||||
* from the artifact are not an error.
|
||||
* @param bukkitCoupledCoreClasses top-level internal class names (no {@code .class} suffix, no
|
||||
* nested-class part) whose source file is listed in
|
||||
* core/purity-allowlist.txt. A class with an {@code org.bukkit}
|
||||
* supertype is accepted only when it is one of those, sits under
|
||||
* the Bukkit platform package, or comes from VolmLib. Anything
|
||||
* else is new Bukkit coupling in core and fails the build.
|
||||
*/
|
||||
public static void verify(File artifact, List<String> requiredEntries, Set<String> bukkitSupertypeBaseline) {
|
||||
public static void verify(File artifact, List<String> requiredEntries, Set<String> bukkitCoupledCoreClasses) {
|
||||
if (!artifact.isFile()) {
|
||||
throw new GradleException("Missing modded Iris artifact: " + artifact.getAbsolutePath());
|
||||
}
|
||||
@@ -129,7 +133,8 @@ public final class ModdedArtifactVerifier {
|
||||
}
|
||||
}
|
||||
if (!name.startsWith(BUKKIT_PLATFORM_PREFIX)
|
||||
&& !bukkitSupertypeBaseline.contains(name)
|
||||
&& !name.startsWith(SHARED_LIBRARY_PREFIX)
|
||||
&& !bukkitCoupledCoreClasses.contains(topLevelClass(name))
|
||||
&& hasBukkitSupertype(classFile)) {
|
||||
bukkitSupertypeClasses.add(name);
|
||||
}
|
||||
@@ -154,7 +159,8 @@ public final class ModdedArtifactVerifier {
|
||||
}
|
||||
if (!bukkitSupertypeClasses.isEmpty()) {
|
||||
throw new GradleException(artifact.getName() + " ships classes with an org.bukkit supertype outside "
|
||||
+ BUKKIT_PLATFORM_PREFIX + ": " + preview(bukkitSupertypeClasses));
|
||||
+ BUKKIT_PLATFORM_PREFIX + " whose source is not declared Bukkit-coupled in"
|
||||
+ " core/purity-allowlist.txt: " + preview(bukkitSupertypeClasses));
|
||||
}
|
||||
|
||||
verifyNestedJarDeclarations(artifact, jar, shippedNestedJars);
|
||||
@@ -251,6 +257,12 @@ public final class ModdedArtifactVerifier {
|
||||
}
|
||||
}
|
||||
|
||||
private static String topLevelClass(String entryName) {
|
||||
String internalName = entryName.substring(0, entryName.length() - ".class".length());
|
||||
int nested = internalName.indexOf('$');
|
||||
return nested < 0 ? internalName : internalName.substring(0, nested);
|
||||
}
|
||||
|
||||
private static boolean hasBukkitSupertype(byte[] classFile) {
|
||||
ClassReader reader;
|
||||
try {
|
||||
|
||||
@@ -253,14 +253,51 @@ public class ModdedArtifactVerifierTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsBaselinedBukkitSupertype() throws Exception {
|
||||
String entryName = "art/arcane/iris/engine/platform/BukkitChunkGenerator.class";
|
||||
public void acceptsBukkitSupertypeDeclaredInThePurityAllowlist() throws Exception {
|
||||
Map<String, byte[]> entries = validEntries();
|
||||
entries.put(entryName, classExtending("art/arcane/iris/engine/platform/BukkitChunkGenerator",
|
||||
"org/bukkit/generator/ChunkGenerator"));
|
||||
entries.put("art/arcane/iris/engine/platform/BukkitChunkGenerator.class",
|
||||
classExtending("art/arcane/iris/engine/platform/BukkitChunkGenerator",
|
||||
"org/bukkit/generator/ChunkGenerator"));
|
||||
File artifact = createArtifact(entries);
|
||||
|
||||
ModdedArtifactVerifier.verify(artifact, REQUIRED_ENTRIES, Set.of(entryName));
|
||||
ModdedArtifactVerifier.verify(artifact, REQUIRED_ENTRIES,
|
||||
Set.of("art/arcane/iris/engine/platform/BukkitChunkGenerator"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsBukkitSupertypeOnNestedClassOfAllowlistedSource() throws Exception {
|
||||
Map<String, byte[]> entries = validEntries();
|
||||
entries.put("art/arcane/iris/engine/object/IrisEntity$BukkitOps$1.class",
|
||||
classImplementing("art/arcane/iris/engine/object/IrisEntity$BukkitOps$1",
|
||||
"org/bukkit/event/Listener"));
|
||||
File artifact = createArtifact(entries);
|
||||
|
||||
ModdedArtifactVerifier.verify(artifact, REQUIRED_ENTRIES,
|
||||
Set.of("art/arcane/iris/engine/object/IrisEntity"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptsBukkitSupertypeFromSharedLibrary() throws Exception {
|
||||
Map<String, byte[]> entries = validEntries();
|
||||
entries.put("art/arcane/volmlib/util/bukkit/Events.class",
|
||||
classImplementing("art/arcane/volmlib/util/bukkit/Events", "org/bukkit/event/Listener"));
|
||||
File artifact = createArtifact(entries);
|
||||
|
||||
ModdedArtifactVerifier.verify(artifact, REQUIRED_ENTRIES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectsBukkitSupertypeMissingFromThePurityAllowlist() throws Exception {
|
||||
Map<String, byte[]> entries = validEntries();
|
||||
entries.put("art/arcane/iris/core/link/data/OraxenDataProvider.class",
|
||||
classImplementing("art/arcane/iris/core/link/data/OraxenDataProvider",
|
||||
"org/bukkit/event/Listener"));
|
||||
File artifact = createArtifact(entries);
|
||||
|
||||
GradleException failure = assertThrows(GradleException.class,
|
||||
() -> ModdedArtifactVerifier.verify(artifact, REQUIRED_ENTRIES,
|
||||
Set.of("art/arcane/iris/core/link/data/ItemAdderDataProvider")));
|
||||
assertTrue(failure.getMessage().contains("core/purity-allowlist.txt"));
|
||||
}
|
||||
|
||||
private Map<String, byte[]> validEntries() {
|
||||
|
||||
Reference in New Issue
Block a user