Improve initializer extraction in StaticFieldUnscrambler

This commit makes the following improvements:

- Converts the list of instructions in the entry and exit block to a
  set, which makes checking for containment more efficient.

- Removes redundant excluded field filtering, which is already handled
  by the unscramble() method.

- Treats fields written with PUTSTATIC outside the entry or exit block
  as complex, instead of just fields read with GETSTATIC.

- Treats fields with multiple simple initializers as complex.

- Treats fields where we fail to extract a simple initializer as
  complex. This fixes a bug where we accidentally thought those fields
  had no initializer.

Signed-off-by: Graham <gpe@openrs2.org>
master
Graham 4 months ago
parent b1ddd98187
commit 293bf83e30
  1. 29
      deob-bytecode/src/main/kotlin/org/openrs2/deob/bytecode/remap/StaticFieldUnscrambler.kt

@ -57,7 +57,7 @@ public class StaticFieldUnscrambler(
return fields return fields
} }
private fun MethodNode.extractEntryExitBlocks(): List<AbstractInsnNode> { private fun MethodNode.extractEntryExitBlocks(): Set<AbstractInsnNode> {
/* /*
* Most (or all?) of the <clinit> methods have "simple" initializers * Most (or all?) of the <clinit> methods have "simple" initializers
* that we're capable of moving in the first and last basic blocks of * that we're capable of moving in the first and last basic blocks of
@ -69,14 +69,14 @@ public class StaticFieldUnscrambler(
val last = instructions.lastOrNull() val last = instructions.lastOrNull()
if (last == null || last.opcode != Opcodes.RETURN) { if (last == null || last.opcode != Opcodes.RETURN) {
return entry return entry.toSet()
} }
val exit = instructions.toList() val exit = instructions.toList()
.dropLast(1) .dropLast(1)
.takeLastWhile { it.isSequential } .takeLastWhile { it.isSequential }
return entry.plus(exit) return (entry + exit).toSet()
} }
private fun MethodNode.extractInitializers( private fun MethodNode.extractInitializers(
@ -86,26 +86,35 @@ public class StaticFieldUnscrambler(
val simpleInitializers = mutableMapOf<MemberDesc, List<AbstractInsnNode>>() val simpleInitializers = mutableMapOf<MemberDesc, List<AbstractInsnNode>>()
val complexInitializers = instructions.asSequence() val complexInitializers = instructions.asSequence()
.filter { !entryExitBlocks.contains(it) } .filter { it !in entryExitBlocks }
.filterIsInstance<FieldInsnNode>() .filterIsInstance<FieldInsnNode>()
.filter { it.opcode == Opcodes.GETSTATIC && it.owner == owner } .filter { it.owner == owner }
.filter { !excludedFields.matches(it.owner, it.name, it.desc) }
.map(::MemberDesc) .map(::MemberDesc)
.toSet() .toMutableSet()
val putstatics = entryExitBlocks val putstatics = entryExitBlocks
.filterIsInstance<FieldInsnNode>() .filterIsInstance<FieldInsnNode>()
.filter { it.opcode == Opcodes.PUTSTATIC && it.owner == owner } .filter { it.opcode == Opcodes.PUTSTATIC && it.owner == owner }
.filter { !excludedFields.matches(it.owner, it.name, it.desc) }
for (putstatic in putstatics) { for (putstatic in putstatics) {
val desc = MemberDesc(putstatic) val desc = MemberDesc(putstatic)
if (simpleInitializers.containsKey(desc) || complexInitializers.contains(desc)) { if (desc in complexInitializers) {
continue
}
if (desc in simpleInitializers) {
simpleInitializers -= desc
complexInitializers += desc
continue continue
} }
// TODO(gpe): use a filter here (pure with no *LOADs?) // TODO(gpe): use a filter here (pure with no *LOADs?)
simpleInitializers[desc] = getExpression(putstatic) ?: continue val initializer = getExpression(putstatic)
if (initializer != null) {
simpleInitializers[desc] = initializer
} else {
complexInitializers += desc
}
} }
return Pair(simpleInitializers, complexInitializers) return Pair(simpleInitializers, complexInitializers)

Loading…
Cancel
Save