Add UnusedMethodTransformer #83
Manually merged
major
merged 1 commits from :unused_methods
into master
4 years ago
@ -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 |
||||||
|
} |
@ -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 } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
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.) |
|||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
logger.info { "Removed $methodsRemoved unused methods" } |
||||||
|
} |
||||||
|
|
||||||
|
private companion object { |
||||||
|
private val logger = InlineLogger() |
||||||
gpe
commented 4 years ago
Review
Maybe replace with More concise, and we use |
|||||||
|
|
||||||
|
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…
Reference in new issue
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 byif (references.isNotEmpty()) {
would be clearer? (albeit slightly slower, I expect)