From 64ba68bac9278262cea106ad41a55791db073704 Mon Sep 17 00:00:00 2001 From: Graham Date: Sun, 1 Mar 2020 15:56:38 +0000 Subject: [PATCH] Move static fields without initializers --- .../transform/StaticScramblingTransformer.kt | 86 ++++++++++++------- 1 file changed, 55 insertions(+), 31 deletions(-) diff --git a/deob/src/main/java/dev/openrs2/deob/transform/StaticScramblingTransformer.kt b/deob/src/main/java/dev/openrs2/deob/transform/StaticScramblingTransformer.kt index 5fb2696b..d3de2744 100644 --- a/deob/src/main/java/dev/openrs2/deob/transform/StaticScramblingTransformer.kt +++ b/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 dev.openrs2.asm.ClassVersionUtils +import dev.openrs2.asm.MemberDesc import dev.openrs2.asm.MemberRef import dev.openrs2.asm.classpath.ClassPath import dev.openrs2.asm.classpath.Library @@ -69,32 +70,60 @@ class StaticScramblingTransformer : Transformer() { return Pair(clazz, clinit) } - private fun extractInitializers(clazz: ClassNode, clinit: MethodNode, block: List) { - val putstatics = block.filterIsInstance() + private fun MethodNode.extractEntryExitBlocks(): List { + /* + * Most (or all?) of the 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 { + val initializers = mutableMapOf() + + val putstatics = extractEntryExitBlocks() + .filterIsInstance() .filter { it.opcode == Opcodes.PUTSTATIC } 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 } - val node = clazz.fields.find { it.name == putstatic.name && it.desc == putstatic.desc } ?: continue // TODO(gpe): use a filter here (pure with no *LOADs?) val expr = getExpression(putstatic) ?: continue val initializer = InsnList() for (insn in expr) { - clinit.instructions.remove(insn) + instructions.remove(insn) initializer.add(insn) } - clinit.instructions.remove(putstatic) + instructions.remove(putstatic) initializer.add(putstatic) - clazz.fields.remove(node) - - val ref = MemberRef(putstatic) - fields[ref] = Field(node, initializer, clazz.version, clinit.maxStack) + initializers[desc] = initializer } + + return initializers } private fun spliceInitializers() { @@ -142,28 +171,23 @@ class StaticScramblingTransformer : Transformer() { } val clinit = clazz.methods.find { it.name == "" } - if (clinit != null) { - val insns = clinit.instructions.toMutableList() - - /* - * Most (or all?) of the 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 = insns.takeWhile { it.sequential } - extractInitializers(clazz, clinit, entry) - - val last = insns.lastOrNull() - if (last != null && last.opcode == Opcodes.RETURN) { - insns.removeAt(insns.size - 1) - - val exit = insns.takeLastWhile { it.sequential } - extractInitializers(clazz, clinit, exit) + val initializers = clinit?.extractInitializers(clazz.name) ?: emptyMap() + + clazz.fields.removeIf { field -> + if (field.access and Opcodes.ACC_STATIC == 0) { + return@removeIf false + } else if (field.name in TypedRemapper.EXCLUDED_METHODS) { + return@removeIf false } + + val desc = MemberDesc(field) + val initializer = initializers[desc] ?: InsnList() + val maxStack = clinit?.maxStack ?: 0 + + val ref = MemberRef(clazz, field) + fields[ref] = Field(field, initializer, clazz.version, maxStack) + + return@removeIf true } clazz.methods.removeIf { method ->