Add UnusedMethodTransformer #83

Manually merged
major merged 1 commits from :unused_methods into master 4 years ago
  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) {
gpe commented 4 years ago
Review

Finding this logic a little bit hard to follow. Is it to ensure methods which only reference themselves are deleted?

I wonder if val references = methodReferences[partition].minus(member) followed by if (references.isNotEmpty()) { would be clearer? (albeit slightly slower, I expect)

Finding this logic a little bit hard to follow. Is it to ensure methods which only reference themselves are deleted? I wonder if `val references = methodReferences[partition].minus(member)` followed by `if (references.isNotEmpty()) {` would be clearer? (albeit slightly slower, I expect)
methods.remove()
methodsRemoved++
for (ref in partition) {
val owner = library[ref.owner]!!
owner.methods.removeFirst { it.name == ref.name && it.desc == ref.desc }
}
}
}
gpe commented 4 years ago
Review

Remove one of these blank lines (I think IDEA's formatter should be configured to only allow a max of one blank line inside method code? I'm not sure if you ran it or not.)

Remove one of these blank lines (I think IDEA's formatter should be configured to only allow a max of one blank line inside method code? I'm not sure if you ran it or not.)
}
}
logger.info { "Removed $methodsRemoved unused methods" }
}
private companion object {
private val logger = InlineLogger()
gpe commented 4 years ago
Review

Maybe replace with owner.methods.removeIf { it.name == ref.name && it.desc == ref.desc }?

More concise, and we use removeIf similarly elsewhere.

Maybe replace with `owner.methods.removeIf { it.name == ref.name && it.desc == ref.desc }`? More concise, and we use `removeIf` similarly elsewhere.
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