Move static fields without initializers

bzip2
Graham 5 years ago
parent 84c37f4300
commit 64ba68bac9
  1. 80
      deob/src/main/java/dev/openrs2/deob/transform/StaticScramblingTransformer.kt

@ -2,6 +2,7 @@ package dev.openrs2.deob.transform
import com.github.michaelbull.logging.InlineLogger import com.github.michaelbull.logging.InlineLogger
import dev.openrs2.asm.ClassVersionUtils import dev.openrs2.asm.ClassVersionUtils
import dev.openrs2.asm.MemberDesc
import dev.openrs2.asm.MemberRef import dev.openrs2.asm.MemberRef
import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.ClassPath
import dev.openrs2.asm.classpath.Library import dev.openrs2.asm.classpath.Library
@ -69,32 +70,60 @@ class StaticScramblingTransformer : Transformer() {
return Pair(clazz, clinit) return Pair(clazz, clinit)
} }
private fun extractInitializers(clazz: ClassNode, clinit: MethodNode, block: List<AbstractInsnNode>) { private fun MethodNode.extractEntryExitBlocks(): List<AbstractInsnNode> {
val putstatics = block.filterIsInstance<FieldInsnNode>() /*
* Most (or all?) of the <clinit> methods have "simple" initializers
* that we're capable of moving in the first and last basic blocks of
* the method. The last basic block is always at the end of the code
* and ends in a RETURN. This allows us to avoid worrying about making
* a full basic block control flow graph here.
*/
val entry = instructions.takeWhile { it.sequential }
val last = instructions.lastOrNull()
if (last == null || last.opcode != Opcodes.RETURN) {
return entry
}
val exit = instructions.toList()
.dropLast(1)
.takeLastWhile { it.sequential }
return entry.plus(exit)
}
private fun MethodNode.extractInitializers(owner: String): Map<MemberDesc, InsnList> {
val initializers = mutableMapOf<MemberDesc, InsnList>()
val putstatics = extractEntryExitBlocks()
.filterIsInstance<FieldInsnNode>()
.filter { it.opcode == Opcodes.PUTSTATIC } .filter { it.opcode == Opcodes.PUTSTATIC }
for (putstatic in putstatics) { for (putstatic in putstatics) {
if (putstatic.owner != clazz.name || putstatic.name in TypedRemapper.EXCLUDED_FIELDS) { if (putstatic.owner != owner || putstatic.name in TypedRemapper.EXCLUDED_FIELDS) {
continue
}
val desc = MemberDesc(putstatic)
if (initializers.containsKey(desc)) {
continue continue
} }
val node = clazz.fields.find { it.name == putstatic.name && it.desc == putstatic.desc } ?: continue
// TODO(gpe): use a filter here (pure with no *LOADs?) // TODO(gpe): use a filter here (pure with no *LOADs?)
val expr = getExpression(putstatic) ?: continue val expr = getExpression(putstatic) ?: continue
val initializer = InsnList() val initializer = InsnList()
for (insn in expr) { for (insn in expr) {
clinit.instructions.remove(insn) instructions.remove(insn)
initializer.add(insn) initializer.add(insn)
} }
clinit.instructions.remove(putstatic) instructions.remove(putstatic)
initializer.add(putstatic) initializer.add(putstatic)
clazz.fields.remove(node) initializers[desc] = initializer
val ref = MemberRef(putstatic)
fields[ref] = Field(node, initializer, clazz.version, clinit.maxStack)
} }
return initializers
} }
private fun spliceInitializers() { private fun spliceInitializers() {
@ -142,28 +171,23 @@ class StaticScramblingTransformer : Transformer() {
} }
val clinit = clazz.methods.find { it.name == "<clinit>" } val clinit = clazz.methods.find { it.name == "<clinit>" }
if (clinit != null) { val initializers = clinit?.extractInitializers(clazz.name) ?: emptyMap()
val insns = clinit.instructions.toMutableList()
/* clazz.fields.removeIf { field ->
* Most (or all?) of the <clinit> methods have "simple" if (field.access and Opcodes.ACC_STATIC == 0) {
* initializers that we're capable of moving in the first return@removeIf false
* and last basic blocks of the method. The last basic } else if (field.name in TypedRemapper.EXCLUDED_METHODS) {
* block is always at the end of the code and ends in a return@removeIf false
* RETURN. This allows us to avoid worrying about making a }
* full basic block control flow graph here.
*/
val entry = insns.takeWhile { it.sequential } val desc = MemberDesc(field)
extractInitializers(clazz, clinit, entry) val initializer = initializers[desc] ?: InsnList()
val maxStack = clinit?.maxStack ?: 0
val last = insns.lastOrNull() val ref = MemberRef(clazz, field)
if (last != null && last.opcode == Opcodes.RETURN) { fields[ref] = Field(field, initializer, clazz.version, maxStack)
insns.removeAt(insns.size - 1)
val exit = insns.takeLastWhile { it.sequential } return@removeIf true
extractInitializers(clazz, clinit, exit)
}
} }
clazz.methods.removeIf { method -> clazz.methods.removeIf { method ->

Loading…
Cancel
Save