diff --git a/common/src/main/java/dev/openrs2/common/collect/IterableUtils.kt b/common/src/main/java/dev/openrs2/common/collect/IterableUtils.kt new file mode 100644 index 00000000..687a8d89 --- /dev/null +++ b/common/src/main/java/dev/openrs2/common/collect/IterableUtils.kt @@ -0,0 +1,13 @@ +package dev.openrs2.common.collect + +inline fun MutableIterable.removeFirst(predicate: (T) -> Boolean): Boolean { + val iterator = iterator() + for (element in iterator) { + if (predicate(element)) { + iterator.remove() + return true + } + } + + return false +} diff --git a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt index d5e37bf3..b48eb443 100644 --- a/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt +++ b/deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt @@ -32,6 +32,7 @@ import dev.openrs2.deob.transform.RemapTransformer import dev.openrs2.deob.transform.ResetTransformer import dev.openrs2.deob.transform.StaticScramblingTransformer import dev.openrs2.deob.transform.UnusedArgTransformer +import dev.openrs2.deob.transform.UnusedMethodTransformer import dev.openrs2.deob.transform.VisibilityTransformer import java.nio.file.Files import java.nio.file.Path @@ -168,6 +169,7 @@ class Deobfuscator(private val input: Path, private val output: Path) { DummyArgTransformer(), DummyLocalTransformer(), UnusedArgTransformer(), + UnusedMethodTransformer(), CounterTransformer(), ResetTransformer(), ClassLiteralTransformer(), diff --git a/deob/src/main/java/dev/openrs2/deob/transform/UnusedMethodTransformer.kt b/deob/src/main/java/dev/openrs2/deob/transform/UnusedMethodTransformer.kt new file mode 100644 index 00000000..aa32056e --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/transform/UnusedMethodTransformer.kt @@ -0,0 +1,86 @@ +package dev.openrs2.deob.transform + +import com.github.michaelbull.logging.InlineLogger +import com.google.common.collect.HashMultimap +import com.google.common.collect.Multimap +import dev.openrs2.asm.MemberRef +import dev.openrs2.asm.classpath.ClassPath +import dev.openrs2.asm.classpath.Library +import dev.openrs2.asm.transform.Transformer +import dev.openrs2.common.collect.DisjointSet +import dev.openrs2.common.collect.removeFirst +import dev.openrs2.deob.remap.TypedRemapper +import org.objectweb.asm.Opcodes +import org.objectweb.asm.tree.ClassNode +import org.objectweb.asm.tree.MethodInsnNode +import org.objectweb.asm.tree.MethodNode + +class UnusedMethodTransformer : Transformer() { + private lateinit var inheritedMethodSets: DisjointSet + private val methodReferences = HashMultimap.create, MemberRef>() + + override fun preTransform(classPath: ClassPath) { + inheritedMethodSets = classPath.createInheritedMethodSets() + methodReferences.clear() + } + + override fun transformCode(classPath: ClassPath, library: Library, clazz: ClassNode, method: MethodNode): Boolean { + for (insn in method.instructions) { + if (insn is MethodInsnNode) { + addReference(methodReferences, inheritedMethodSets, MemberRef(insn), MemberRef(clazz, method)) + } + } + + return false + } + + override fun postTransform(classPath: ClassPath) { + var methodsRemoved = 0 + + for (library in classPath.libraries) { + for (clazz in library) { + val methods = clazz.methods.iterator() + + for (method in methods) { + if (method.access and Opcodes.ACC_NATIVE != 0 || method.name in TypedRemapper.EXCLUDED_METHODS) { + continue + } + + val member = MemberRef(clazz, method) + val partition = inheritedMethodSets[member]!! + + if (partition.any { classPath[it.owner]!!.dependency }) { + continue + } + + val references = methodReferences[partition] + if (references.isEmpty() || references.size == 1 && references.first() == member) { + methods.remove() + methodsRemoved++ + + for (ref in partition) { + val owner = library[ref.owner]!! + owner.methods.removeFirst { it.name == ref.name && it.desc == ref.desc } + } + } + } + } + } + + logger.info { "Removed $methodsRemoved unused methods" } + } + + private companion object { + private val logger = InlineLogger() + + private fun addReference( + references: Multimap, MemberRef>, + disjointSet: DisjointSet, + member: MemberRef, + className: MemberRef + ) { + val partition = disjointSet[member] ?: return + references.put(partition, className) + } + } +}