diff --git a/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.java b/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.java deleted file mode 100644 index ec2f28d1c4..0000000000 --- a/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.java +++ /dev/null @@ -1,239 +0,0 @@ -package dev.openrs2.deob.analysis; - -import java.util.List; - -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; -import dev.openrs2.asm.InsnNodeUtilsKt; -import org.objectweb.asm.Opcodes; -import org.objectweb.asm.Type; -import org.objectweb.asm.tree.AbstractInsnNode; -import org.objectweb.asm.tree.IincInsnNode; -import org.objectweb.asm.tree.analysis.AnalyzerException; -import org.objectweb.asm.tree.analysis.BasicInterpreter; -import org.objectweb.asm.tree.analysis.BasicValue; -import org.objectweb.asm.tree.analysis.Interpreter; - -public final class IntInterpreter extends Interpreter { - private static final int MAX_TRACKED_VALUES = 1; - - private final Interpreter basicInterpreter = new BasicInterpreter(); - private final ImmutableSet[] parameters; - - public IntInterpreter(ImmutableSet[] parameters) { - super(Opcodes.ASM7); - this.parameters = parameters; - } - - @Override - public IntValue newValue(Type type) { - var basicValue = basicInterpreter.newValue(type); - if (basicValue == null) { - return null; - } - - return IntValue.newUnknown(basicValue); - } - - @Override - public IntValue newParameterValue(boolean isInstanceMethod, int local, Type type) { - var basicValue = basicInterpreter.newParameterValue(isInstanceMethod, local, type); - if (basicValue == null) { - return null; - } - - if (parameters != null) { - int parameterIndex; - if (isInstanceMethod) { - if (local == 0) { - return IntValue.newUnknown(basicValue); - } - parameterIndex = local - 1; - } else { - parameterIndex = local; - } - - var parameter = parameters[parameterIndex]; - if (parameter != null) { - return IntValue.newConstant(basicValue, parameter); - } - } - - return IntValue.newUnknown(basicValue); - } - - @Override - public IntValue newOperation(AbstractInsnNode insn) throws AnalyzerException { - var basicValue = basicInterpreter.newOperation(insn); - if (basicValue == null) { - return null; - } - - var v = InsnNodeUtilsKt.getIntConstant(insn); - if (v != null) { - return IntValue.newConstant(basicValue, v); - } - - return IntValue.newUnknown(basicValue); - } - - @Override - public IntValue copyOperation(AbstractInsnNode insn, IntValue value) { - return value; - } - - @Override - public IntValue unaryOperation(AbstractInsnNode insn, IntValue value) throws AnalyzerException { - var basicValue = basicInterpreter.unaryOperation(insn, value.getBasicValue()); - if (basicValue == null) { - return null; - } - - if (value.isUnknown()) { - return IntValue.newUnknown(basicValue); - } - - var set = ImmutableSet.builder(); - - for (var v : value.getIntValues()) { - switch (insn.getOpcode()) { - case Opcodes.INEG: - set.add(-v); - break; - case Opcodes.IINC: - var iinc = (IincInsnNode) insn; - set.add(v + iinc.incr); - break; - case Opcodes.I2B: - set.add((int) (byte) (int) v); - break; - case Opcodes.I2C: - set.add((int) (char) (int) v); - break; - case Opcodes.I2S: - set.add((int) (short) (int) v); - break; - default: - return IntValue.newUnknown(basicValue); - } - } - - return IntValue.newConstant(basicValue, set.build()); - } - - @Override - public IntValue binaryOperation(AbstractInsnNode insn, IntValue value1, IntValue value2) throws AnalyzerException { - var basicValue = basicInterpreter.binaryOperation(insn, value1.getBasicValue(), value2.getBasicValue()); - if (basicValue == null) { - return null; - } - - if (value1.isUnknown() || value2.isUnknown()) { - return IntValue.newUnknown(basicValue); - } - - var set = ImmutableSet.builder(); - - for (var v1 : value1.getIntValues()) { - for (var v2 : value2.getIntValues()) { - switch (insn.getOpcode()) { - case Opcodes.IADD: - set.add(v1 + v2); - break; - case Opcodes.ISUB: - set.add(v1 - v2); - break; - case Opcodes.IMUL: - set.add(v1 * v2); - break; - case Opcodes.IDIV: - if (v2 == 0) { - return IntValue.newUnknown(basicValue); - } - set.add(v1 / v2); - break; - case Opcodes.IREM: - if (v2 == 0) { - return IntValue.newUnknown(basicValue); - } - set.add(v1 % v2); - break; - case Opcodes.ISHL: - set.add(v1 << v2); - break; - case Opcodes.ISHR: - set.add(v1 >> v2); - break; - case Opcodes.IUSHR: - set.add(v1 >>> v2); - break; - case Opcodes.IAND: - set.add(v1 & v2); - break; - case Opcodes.IOR: - set.add(v1 | v2); - break; - case Opcodes.IXOR: - set.add(v1 ^ v2); - break; - default: - return IntValue.newUnknown(basicValue); - } - } - } - - return IntValue.newConstant(basicValue, set.build()); - } - - @Override - public IntValue ternaryOperation(AbstractInsnNode insn, IntValue value1, IntValue value2, IntValue value3) throws AnalyzerException { - var basicValue = basicInterpreter.ternaryOperation(insn, value1.getBasicValue(), value2.getBasicValue(), value3.getBasicValue()); - if (basicValue == null) { - return null; - } - - return IntValue.newUnknown(basicValue); - } - - @Override - public IntValue naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException { - var args = values.stream() - .map(IntValue::getBasicValue) - .collect(ImmutableList.toImmutableList()); - var basicValue = basicInterpreter.naryOperation(insn, args); - if (basicValue == null) { - return null; - } - - return IntValue.newUnknown(basicValue); - } - - @Override - public void returnOperation(AbstractInsnNode insn, IntValue value, IntValue expected) throws AnalyzerException { - basicInterpreter.returnOperation(insn, value.getBasicValue(), expected.getBasicValue()); - } - - @Override - public IntValue merge(IntValue value1, IntValue value2) { - var basicValue = basicInterpreter.merge(value1.getBasicValue(), value2.getBasicValue()); - if (basicValue == null) { - return null; - } - - if (value1.equals(value2)) { - return value1; - } - - if (value1.isUnknown() || value2.isUnknown()) { - return IntValue.newUnknown(basicValue); - } - - var set = ImmutableSet.copyOf(Sets.union(value1.getIntValues(), value2.getIntValues())); - if (set.size() > MAX_TRACKED_VALUES) { - return IntValue.newUnknown(basicValue); - } - - return IntValue.newConstant(basicValue, set); - } -} diff --git a/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.kt b/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.kt new file mode 100644 index 0000000000..1d3022a3f0 --- /dev/null +++ b/deob/src/main/java/dev/openrs2/deob/analysis/IntInterpreter.kt @@ -0,0 +1,167 @@ +package dev.openrs2.deob.analysis + +import com.google.common.collect.ImmutableSet +import com.google.common.collect.Sets +import dev.openrs2.asm.intConstant +import org.objectweb.asm.Opcodes +import org.objectweb.asm.Type +import org.objectweb.asm.tree.AbstractInsnNode +import org.objectweb.asm.tree.IincInsnNode +import org.objectweb.asm.tree.analysis.AnalyzerException +import org.objectweb.asm.tree.analysis.BasicInterpreter +import org.objectweb.asm.tree.analysis.Interpreter + +class IntInterpreter(private val parameters: Array?>?) : Interpreter(Opcodes.ASM7) { + private val basicInterpreter = BasicInterpreter() + + override fun newValue(type: Type?): IntValue? { + val basicValue = basicInterpreter.newValue(type) ?: return null + return IntValue.newUnknown(basicValue) + } + + override fun newParameterValue(isInstanceMethod: Boolean, local: Int, type: Type): IntValue { + val basicValue = basicInterpreter.newParameterValue(isInstanceMethod, local, type) + + if (parameters != null) { + val parameterIndex = when { + isInstanceMethod && local == 0 -> return IntValue.newUnknown(basicValue) + isInstanceMethod -> local - 1 + else -> local + } + + val parameter = parameters[parameterIndex] + if (parameter != null) { + return IntValue.newConstant(basicValue, parameter) + } + } + + return IntValue.newUnknown(basicValue) + } + + @Throws(AnalyzerException::class) + override fun newOperation(insn: AbstractInsnNode): IntValue { + val basicValue = basicInterpreter.newOperation(insn) + val v = insn.intConstant + return if (v != null) { + IntValue.newConstant(basicValue, v) + } else { + IntValue.newUnknown(basicValue) + } + } + + override fun copyOperation(insn: AbstractInsnNode, value: IntValue): IntValue { + return value + } + + @Throws(AnalyzerException::class) + override fun unaryOperation(insn: AbstractInsnNode, value: IntValue): IntValue? { + val basicValue = basicInterpreter.unaryOperation(insn, value.basicValue) ?: return null + + if (value.isUnknown) { + return IntValue.newUnknown(basicValue) + } + + val set = ImmutableSet.builder() + for (v in value.intValues) { + val result = when { + insn.opcode == Opcodes.INEG -> -v + insn is IincInsnNode -> v + insn.incr + insn.opcode == Opcodes.I2B -> v.toByte().toInt() + insn.opcode == Opcodes.I2C -> v.toChar().toInt() + insn.opcode == Opcodes.I2S -> v.toShort().toInt() + else -> return IntValue.newUnknown(basicValue) + } + set.add(result) + } + return IntValue.newConstant(basicValue, set.build()) + } + + @Throws(AnalyzerException::class) + override fun binaryOperation(insn: AbstractInsnNode, value1: IntValue, value2: IntValue): IntValue? { + val basicValue = basicInterpreter.binaryOperation(insn, value1.basicValue, value2.basicValue) ?: return null + + if (value1.isUnknown || value2.isUnknown) { + return IntValue.newUnknown(basicValue) + } + + val set = ImmutableSet.builder() + for (v1 in value1.intValues) { + for (v2 in value2.intValues) { + val result = when (insn.opcode) { + Opcodes.IADD -> v1 + v2 + Opcodes.ISUB -> v1 - v2 + Opcodes.IMUL -> v1 * v2 + Opcodes.IDIV -> { + if (v2 == 0) { + return IntValue.newUnknown(basicValue) + } + v1 / v2 + } + Opcodes.IREM -> { + if (v2 == 0) { + return IntValue.newUnknown(basicValue) + } + v1 % v2 + } + Opcodes.ISHL -> v1 shl v2 + Opcodes.ISHR -> v1 shr v2 + Opcodes.IUSHR -> v1 ushr v2 + Opcodes.IAND -> v1 and v2 + Opcodes.IOR -> v1 or v2 + Opcodes.IXOR -> v1 xor v2 + else -> return IntValue.newUnknown(basicValue) + } + set.add(result) + } + } + return IntValue.newConstant(basicValue, set.build()) + } + + @Throws(AnalyzerException::class) + override fun ternaryOperation( + insn: AbstractInsnNode, + value1: IntValue, + value2: IntValue, + value3: IntValue + ): IntValue? { + val basicValue = + basicInterpreter.ternaryOperation(insn, value1.basicValue, value2.basicValue, value3.basicValue) + ?: return null + return IntValue.newUnknown(basicValue) + } + + @Throws(AnalyzerException::class) + override fun naryOperation(insn: AbstractInsnNode, values: List): IntValue? { + val args = values.map { it.basicValue }.toList() + val basicValue = basicInterpreter.naryOperation(insn, args) ?: return null + return IntValue.newUnknown(basicValue) + } + + @Throws(AnalyzerException::class) + override fun returnOperation(insn: AbstractInsnNode, value: IntValue, expected: IntValue) { + basicInterpreter.returnOperation(insn, value.basicValue, expected.basicValue) + } + + override fun merge(value1: IntValue, value2: IntValue): IntValue { + val basicValue = basicInterpreter.merge(value1.basicValue, value2.basicValue) + + if (value1 == value2) { + return value1 + } + + if (value1.isUnknown || value2.isUnknown) { + return IntValue.newUnknown(basicValue) + } + + val set = ImmutableSet.copyOf(Sets.union(value1.intValues, value2.intValues)) + return if (set.size > MAX_TRACKED_VALUES) { + IntValue.newUnknown(basicValue) + } else { + IntValue.newConstant(basicValue, set) + } + } + + companion object { + private const val MAX_TRACKED_VALUES = 1 + } +}