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
}
private fun MethodNode.extractEntryExitBlocks(): List<AbstractInsnNode> {
private fun MethodNode.extractEntryExitBlocks(): Set<AbstractInsnNode> {
/*
* Most (or all?) of the <clinit> methods have "simple" initializers
* 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()
if (last == null || last.opcode != Opcodes.RETURN) {
return entry
return entry.toSet()
}
val exit = instructions.toList()
.dropLast(1)
.takeLastWhile { it.isSequential }
return entry.plus(exit)
return (entry + exit).toSet()
}
private fun MethodNode.extractInitializers(
@ -86,26 +86,35 @@ public class StaticFieldUnscrambler(
val simpleInitializers = mutableMapOf<MemberDesc, List<AbstractInsnNode>>()
val complexInitializers = instructions.asSequence()
.filter { !entryExitBlocks.contains(it) }
.filter { it !in entryExitBlocks }
.filterIsInstance<FieldInsnNode>()
.filter { it.opcode == Opcodes.GETSTATIC && it.owner == owner }
.filter { !excludedFields.matches(it.owner, it.name, it.desc) }
.filter { it.owner == owner }
.map(::MemberDesc)
.toSet()
.toMutableSet()
val putstatics = entryExitBlocks
.filterIsInstance<FieldInsnNode>()
.filter { it.opcode == Opcodes.PUTSTATIC && it.owner == owner }
.filter { !excludedFields.matches(it.owner, it.name, it.desc) }
for (putstatic in putstatics) {
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
}
// 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)

Loading…
Cancel
Save