Add UnusedMethodTransformer

The transformer does _not_ check for use via reflection. The only cases
in the 550 and OSRS clients where methods are accessed via reflection
are either 1) JRE classes, 2) when the method name is sent from the
server.

PSVM and methods declared in TypedRemapper.EXCLUDED_METHODS are never
removed.

Signed-off-by: Major <major@emulate.rs>
bzip2
Major 5 years ago
parent 9617cc4326
commit 488e8ef8c3
  1. 13
      common/src/main/java/dev/openrs2/common/collect/IterableUtils.kt
  2. 2
      deob/src/main/java/dev/openrs2/deob/Deobfuscator.kt
  3. 86
      deob/src/main/java/dev/openrs2/deob/transform/UnusedMethodTransformer.kt

@ -0,0 +1,13 @@
package dev.openrs2.common.collect
inline fun <T> MutableIterable<T>.removeFirst(predicate: (T) -> Boolean): Boolean {
val iterator = iterator()
for (element in iterator) {
if (predicate(element)) {
iterator.remove()
return true
}
}
return false
}

@ -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(),

@ -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<MemberRef>
private val methodReferences = HashMultimap.create<DisjointSet.Partition<MemberRef>, 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<DisjointSet.Partition<MemberRef>, MemberRef>,
disjointSet: DisjointSet<MemberRef>,
member: MemberRef,
className: MemberRef
) {
val partition = disjointSet[member] ?: return
references.put(partition, className)
}
}
}
Loading…
Cancel
Save