From 19def4b8899d3aa9d3b94e12a01904b01824a3e0 Mon Sep 17 00:00:00 2001 From: IzzelAliz Date: Sun, 4 Feb 2024 22:09:34 +0800 Subject: [PATCH] Add inline processor for better mod mixin compatibility Cleanup unused code in gradle source. --- .../world/level/block/FireBlockMixin.java | 2 + .../common/mod/ArclightMixinPlugin.java | 4 +- .../common/mod/mixins/InlineProcessor.java | 161 ++++++++++++++++++ .../common/mod/mixins/annotation/Inline.java | 16 ++ arclight-forge-bootstrap/build.gradle | 2 +- .../arclight/gradle/ArclightExtension.groovy | 9 - .../gradle/ArclightGradlePlugin.groovy | 145 ---------------- .../arclight/gradle/util/AwWriter.groovy | 15 -- 8 files changed, 183 insertions(+), 171 deletions(-) create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/InlineProcessor.java create mode 100644 arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/annotation/Inline.java diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/FireBlockMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/FireBlockMixin.java index f25a31156..197b25008 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/FireBlockMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/core/world/level/block/FireBlockMixin.java @@ -1,6 +1,7 @@ package io.izzel.arclight.common.mixin.core.world.level.block; import io.izzel.arclight.common.bridge.core.world.level.block.FireBlockBridge; +import io.izzel.arclight.common.mod.mixins.annotation.Inline; import io.izzel.arclight.common.mod.util.ArclightCaptures; import it.unimi.dsi.fastutil.objects.Object2IntMap; import net.minecraft.core.BlockPos; @@ -39,6 +40,7 @@ public abstract class FireBlockMixin extends BaseFireBlockMixin implements FireB @Shadow @Final private Object2IntMap burnOdds; // @formatter:on + @Inline @Redirect(method = "tick", at = @At(value = "INVOKE", ordinal = 1, target = "Lnet/minecraft/server/level/ServerLevel;setBlock(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;I)Z")) public boolean arclight$fireSpread(ServerLevel world, BlockPos mutablePos, BlockState newState, int flags, BlockState state, ServerLevel worldIn, BlockPos pos) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMixinPlugin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMixinPlugin.java index e5a2cecdb..9f758c574 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMixinPlugin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/ArclightMixinPlugin.java @@ -2,6 +2,7 @@ import io.izzel.arclight.api.ArclightPlatform; import io.izzel.arclight.common.mod.mixins.CreateConstructorProcessor; +import io.izzel.arclight.common.mod.mixins.InlineProcessor; import io.izzel.arclight.common.mod.mixins.MixinProcessor; import io.izzel.arclight.common.mod.mixins.RenameIntoProcessor; import io.izzel.arclight.common.mod.mixins.TransformAccessProcessor; @@ -22,7 +23,8 @@ public class ArclightMixinPlugin implements IMixinConfigPlugin, IEnvironmentToke private final List postProcessors = List.of( new RenameIntoProcessor(), new TransformAccessProcessor(), - new CreateConstructorProcessor() + new CreateConstructorProcessor(), + new InlineProcessor() ); @Override diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/InlineProcessor.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/InlineProcessor.java new file mode 100644 index 000000000..19ccedd43 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/InlineProcessor.java @@ -0,0 +1,161 @@ +package io.izzel.arclight.common.mod.mixins; + +import io.izzel.arclight.common.mod.mixins.annotation.Inline; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.tree.ClassNode; +import org.objectweb.asm.tree.LocalVariableNode; +import org.objectweb.asm.tree.MethodInsnNode; +import org.objectweb.asm.tree.MethodNode; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; +import org.spongepowered.asm.util.Locals; + +import java.lang.reflect.Modifier; + +public class InlineProcessor implements MixinProcessor { + + private static final String TYPE = Type.getDescriptor(Inline.class); + private static final String MERGED = Type.getDescriptor(Inline.Merged.class); + + @Override + public void accept(String className, ClassNode classNode, IMixinInfo mixinInfo) { + for (var methodNode : classNode.methods) { + if (methodNode.invisibleAnnotations != null) { + for (var ann : methodNode.invisibleAnnotations) { + if (ann.desc.equals(TYPE)) { + new MethodInliner(methodNode, classNode).accept(); + ann.desc = MERGED; + } + } + } + } + } + + private record MethodInliner(MethodNode target, ClassNode callerClass) { + + public void accept() { + for (var node : callerClass.methods) { + this.accept(node); + } + } + + public void accept(MethodNode node) { + for (var iterator = node.instructions.iterator(); iterator.hasNext(); ) { + var insn = iterator.next(); + if (insn instanceof MethodInsnNode method) { + if (method.owner.equals(callerClass.name) && method.name.equals(target.name) && method.desc.equals(target.desc)) { + var buf = new MethodNode(Opcodes.ASM9, node.access, node.name, node.desc, node.signature, node.exceptions.toArray(String[]::new)); + target.accept(new RemappingVisitor(Opcodes.ASM9, buf, Locals.getLocalsAt(callerClass, node, insn, Locals.Settings.DEFAULT))); + node.instructions.insertBefore(insn, buf.instructions); + node.tryCatchBlocks.addAll(buf.tryCatchBlocks); + node.localVariables.addAll(buf.localVariables); + node.maxLocals = Math.max(node.maxLocals, buf.maxLocals); + node.maxStack = Math.max(node.maxStack, buf.maxStack); + iterator.remove(); + } + } + } + } + + protected class RemappingVisitor extends MethodVisitor { + + private final Label end = new Label(); + private final Type returnType = Type.getReturnType(target.desc); + + private final LocalVariableNode[] locals; + private final int localOffset, localCount; + + protected RemappingVisitor(int api, MethodVisitor mv, LocalVariableNode[] locals) { + super(api, mv); + this.locals = locals; + var offset = 0; + var count = 0; + for (int i = 0; i < locals.length; i++) { + var local = locals[i]; + if (local != null) { + offset = Math.max(offset, local.index + Type.getType(local.desc).getSize()); + count = i + 1; + } + } + this.localOffset = offset; + this.localCount = count; + this.visitStart(); + } + + protected void visitStart() { + var offset = Modifier.isStatic(target.access) ? 0 : 1; + var args = Type.getArgumentTypes(target.desc); + for (var i = args.length - 1; i >= 0; i--) { + super.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), localOffset + i + offset); + } + if (offset > 0) { + super.visitVarInsn(Opcodes.ASTORE, localOffset); + } + } + + @Override + public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { + if (owner.equals(callerClass.name) && name.equals(target.name) && descriptor.equals(target.desc)) { + throw new IllegalStateException("Inlining recursive method"); + } + super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); + } + + @Override + public void visitVarInsn(int opcode, int varIndex) { + super.visitVarInsn(opcode, localOffset + varIndex); + } + + @Override + public void visitIincInsn(int varIndex, int increment) { + super.visitIincInsn(localOffset + varIndex, increment); + } + + @Override + public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { + super.visitLocalVariable(name, descriptor, signature, start, end, localOffset + index); + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + super.visitMaxs(maxStack, localOffset + maxLocals); + } + + @Override + public void visitInsn(int opcode) { + if (opcode == returnType.getOpcode(Opcodes.IRETURN)) { + super.visitJumpInsn(Opcodes.GOTO, this.end); + } else { + super.visitInsn(opcode); + } + } + + @Override + public void visitEnd() { + super.visitLabel(this.end); + Object[] locals = new Object[localCount]; + for (int i = 0; i < localCount; i++) { + var local = this.locals[i]; + if (local == null) { + locals[i] = null; + } else { + var type = Type.getType(local.desc); + Object v = switch (type.getSort()) { + case Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT, Type.INT -> Opcodes.INTEGER; + case Type.FLOAT -> Opcodes.FLOAT; + case Type.LONG -> Opcodes.LONG; + case Type.DOUBLE -> Opcodes.DOUBLE; + case Type.ARRAY, Type.OBJECT -> type.getInternalName(); + default -> Opcodes.TOP; + }; + locals[i] = v; + } + } + super.visitFrame(Opcodes.F_FULL, localCount, locals, 0, null); + super.visitEnd(); + } + } + } +} diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/annotation/Inline.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/annotation/Inline.java new file mode 100644 index 000000000..2bce53e94 --- /dev/null +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/mixins/annotation/Inline.java @@ -0,0 +1,16 @@ +package io.izzel.arclight.common.mod.mixins.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.METHOD) +public @interface Inline { + + @Retention(RetentionPolicy.CLASS) + @Target(ElementType.METHOD) + @interface Merged { + } +} diff --git a/arclight-forge-bootstrap/build.gradle b/arclight-forge-bootstrap/build.gradle index 9cfb9b2d0..64fde6bc2 100644 --- a/arclight-forge-bootstrap/build.gradle +++ b/arclight-forge-bootstrap/build.gradle @@ -72,7 +72,7 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-api:2.17.2' implementation 'org.apache.logging.log4j:log4j-core:2.17.2' implementation 'org.jetbrains:annotations:23.0.0' - implementation 'org.spongepowered:mixin:0.8.3' + implementation 'org.spongepowered:mixin:0.8.5' implementation 'org.apache.logging.log4j:log4j-jul:2.17.2' implementation "net.minecraftforge:fmlloader:${minecraftVersion}-${forgeVersion}" for (def lib : embedLibs) { diff --git a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightExtension.groovy b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightExtension.groovy index d5fcee1e9..92af461b3 100644 --- a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightExtension.groovy +++ b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightExtension.groovy @@ -10,7 +10,6 @@ class ArclightExtension { private String forgeVersion private File accessTransformer private File extraMapping - private List installerInfo = new ArrayList<>() private final MappingsConfiguration mappingsConfiguration = new MappingsConfiguration() ArclightExtension(Project project) { @@ -41,14 +40,6 @@ class ArclightExtension { this.accessTransformer = accessTransformer } - List getInstallerInfo() { - return installerInfo - } - - void setInstallerInfo(List installerInfo) { - this.installerInfo = installerInfo - } - String getForgeVersion() { return forgeVersion } diff --git a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightGradlePlugin.groovy b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightGradlePlugin.groovy index 36c4b0a4b..58796c7c8 100644 --- a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightGradlePlugin.groovy +++ b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/ArclightGradlePlugin.groovy @@ -1,6 +1,5 @@ package io.izzel.arclight.gradle -import groovy.json.JsonOutput import io.izzel.arclight.gradle.tasks.BuildSpigotTask import io.izzel.arclight.gradle.tasks.DownloadBuildToolsTask import io.izzel.arclight.gradle.tasks.ProcessMappingTask @@ -8,16 +7,6 @@ import io.izzel.arclight.gradle.tasks.RemapSpigotTask import net.fabricmc.loom.configuration.mods.dependency.LocalMavenHelper import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.DependencyArtifact -import org.gradle.api.tasks.Copy -import org.gradle.api.tasks.StopExecutionException - -import java.nio.file.FileVisitResult -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.SimpleFileVisitor -import java.nio.file.attribute.BasicFileAttributes -import java.security.MessageDigest class ArclightGradlePlugin implements Plugin { @@ -42,104 +31,6 @@ class ArclightGradlePlugin implements Plugin { project.afterEvaluate { setupSpigot(project, arclightRepo) } - if (true) return - def processMapping = project.tasks.create('processMapping', ProcessMappingTask) - def remapSpigot = project.tasks.create('remapSpigotJar', RemapSpigotTask) - def generateMeta = project.tasks.create('generateArclightMeta', Copy) - //def processAt = project.tasks.create('processAT', ProcessAccessTransformerTask) - def downloadInstaller = project.tasks.create('downloadInstaller') - generateMeta.dependsOn(downloadInstaller) - def metaFolder = project.file("${project.buildDir}/arclight_cache/meta") - project.sourceSets.main.output.dir metaFolder, builtBy: generateMeta - generateMeta.configure { Copy task -> - task.into(metaFolder) - task.outputs.upToDateWhen { false } - task.dependsOn(remapSpigot) - } - project.afterEvaluate { - if (true) return - //processAt.configure { ProcessAccessTransformerTask task -> - // task.buildData = new File(buildTools, 'BuildData') - // task.mcVersion = arclightExt.mcVersion - // task.outDir = project.file("${project.buildDir}/arclight_cache/tmp_srg") - // task.inSrg = extractSrg.output.get().asFile - // task.dependsOn(extractSrg, createSrgToMcp, buildSpigot) - //} - def installerJar = project.file("${project.buildDir}/arclight_cache/forge-${arclightExt.mcVersion}-${arclightExt.forgeVersion}-installer.jar") - downloadInstaller.doFirst { - if (installerJar.exists()) throw new StopExecutionException() - if (installerJar.parentFile != null) { - installerJar.parentFile.mkdirs() - } - def installerUrl = "https://files.minecraftforge.net/maven/net/minecraftforge/forge/${arclightExt.mcVersion}-${arclightExt.forgeVersion}/forge-${arclightExt.mcVersion}-${arclightExt.forgeVersion}-installer.jar" - Utils.download(installerUrl, installerJar) - } - generateMeta.configure { Copy task -> - task.doFirst { - Files.walkFileTree(metaFolder.toPath(), new SimpleFileVisitor() { - @Override - FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Files.delete(file) - return FileVisitResult.CONTINUE - } - - @Override - FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir) - return FileVisitResult.CONTINUE - } - }) - } - task.into(metaFolder) { - task.from(project.zipTree(project.file("${project.buildDir}/arclight_cache/spigot-${arclightExt.mcVersion}-mapped-deobf.jar"))) - task.from(new File(project.file("${project.buildDir}/arclight_cache/tmp_srg"), 'inheritanceMap.txt')) - task.from(new File(project.file("${project.buildDir}/arclight_cache/tmp_srg"), 'bukkit_srg.srg')) - } - task.outputs.file(new File(metaFolder as File, 'META-INF/installer.json')) - task.doLast { - def installer = new File(metaFolder, 'META-INF/installer.json') - def libs = project.configurations.arclight.dependencies.collect { - def classifier = null - if (it.artifacts) { - it.artifacts.each { DependencyArtifact artifact -> - if (artifact.classifier) { - classifier = artifact.classifier - } - } - } - if (classifier) { - return "${it.group}:${it.name}:${it.version}:$classifier" - } else { - return "${it.group}:${it.name}:${it.version}" - } - } - def output = [ - installer: [ - minecraft: arclightExt.mcVersion, - forge : arclightExt.forgeVersion, - hash : ArclightGradlePlugin.sha1(installerJar) - ], - libraries: ArclightGradlePlugin.artifacts(project, libs) - ] - installer.text = JsonOutput.toJson(output) - } - } - if (remapSpigot.outDeobf.exists()) { - project.configurations.implementation.dependencies.add(project.dependencies.create(project.files(remapSpigot.outDeobf))) - } - if (arclightExt.reobfVersion) { - File map = project.file("${project.buildDir}/arclight_cache/tmp_srg/reobf_version_${arclightExt.bukkitVersion}.srg") - if (!map.exists()) { - map.parentFile.mkdirs() - map.createNewFile() - } - map.text = "PK: org/bukkit/craftbukkit/v org/bukkit/craftbukkit/${arclightExt.bukkitVersion}" - project.tasks.withType(RenameJarInPlace).each { task -> - project.logger.info "Contributing tsrg mappings ({}) to {} in {}", map, task.name, task.project - task.extraMappings.from(map) - } - } - } } private static def setupSpigot(Project project, File arclightRepo) { @@ -200,40 +91,4 @@ class ArclightGradlePlugin implements Plugin { remapSpigot.inExtraSrg = arclightExt.extraMapping remapSpigot.run() } - - private static def sha1(file) { - MessageDigest md = MessageDigest.getInstance('SHA-1') - file.eachByte 4096, { bytes, size -> - md.update(bytes, 0 as byte, size) - } - return md.digest().collect { String.format "%02x", it }.join() - } - - private static Map artifacts(Project project, List arts) { - def ret = new HashMap() - def cfg = project.configurations.create("art_rev_" + System.currentTimeMillis()) - cfg.transitive = false - arts.each { - def dep = project.dependencies.create(it) - cfg.dependencies.add(dep) - } - cfg.resolve() - cfg.resolvedConfiguration.resolvedArtifacts.each { it -> - def art = [ - group : it.moduleVersion.id.group, - name : it.moduleVersion.id.name, - version : it.moduleVersion.id.version, - classifier: it.classifier, - extension : it.extension, - file : it.file - ] - def desc = "${art.group}:${art.name}:${art.version}" - if (art.classifier != null) - desc += ":${art.classifier}" - if (art.extension != 'jar') - desc += "@${art.extension}" - ret.put(desc.toString(), sha1(art.file)) - } - return arts.collectEntries { [(it.toString()): ret.get(it.toString())] } - } } diff --git a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/util/AwWriter.groovy b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/util/AwWriter.groovy index 653ef6aa5..c6d5b8743 100644 --- a/buildSrc/src/main/groovy/io/izzel/arclight/gradle/util/AwWriter.groovy +++ b/buildSrc/src/main/groovy/io/izzel/arclight/gradle/util/AwWriter.groovy @@ -32,21 +32,6 @@ class AwWriter { }) } - private static String getAccessModifier(AccessChange change) { - switch (change) { - case AccessChange.PUBLIC: - return "public" - case AccessChange.PROTECTED: - return "protected" - case AccessChange.PACKAGE_PRIVATE: - return "default" - case AccessChange.PRIVATE: - return "private" - default: - throw new AssertionError(change) - } - } - private void writeAccessTransform(String remaining, boolean field, AccessTransform transform) throws IOException { if (transform.access == AccessChange.PUBLIC) { this.writer.write('accessible ')