Rewrite regex search so that it pattern checks each instruction without building a string of the whole method first. Regex duration on mobile cpu (i5-7200U) goes down from 2635ms to 430ms for one specific test case.
This commit is contained in:
parent
2f65241269
commit
9f142781b0
2 changed files with 78 additions and 48 deletions
|
@ -255,48 +255,83 @@ public class RegexInsnFinder {
|
||||||
}
|
}
|
||||||
offsets[i] = insnString.length();
|
offsets[i] = insnString.length();
|
||||||
insnString += opcodes[ain.getOpcode()];
|
insnString += opcodes[ain.getOpcode()];
|
||||||
switch (ain.getType()) {
|
insnString = getInsString(ain);
|
||||||
case AbstractInsnNode.INT_INSN:
|
|
||||||
final IntInsnNode iin = (IntInsnNode) ain;
|
|
||||||
insnString += "{" + iin.operand + "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.LDC_INSN:
|
|
||||||
final LdcInsnNode lin = (LdcInsnNode) ain;
|
|
||||||
insnString += "{" + lin.cst.toString().replace("}", "\\}")
|
|
||||||
+ "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.VAR_INSN:
|
|
||||||
final VarInsnNode vin = (VarInsnNode) ain;
|
|
||||||
insnString += "_" + vin.var;
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.IINC_INSN:
|
|
||||||
final IincInsnNode iiin = (IincInsnNode) ain;
|
|
||||||
insnString += "{" + iiin.var + "," + iiin.incr + "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.FIELD_INSN:
|
|
||||||
final FieldInsnNode fin = (FieldInsnNode) ain;
|
|
||||||
insnString += "{" + fin.desc + "," + fin.owner + ","
|
|
||||||
+ fin.name + "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.METHOD_INSN:
|
|
||||||
final MethodInsnNode min = (MethodInsnNode) ain;
|
|
||||||
insnString += "{" + min.desc + "," + min.owner + ","
|
|
||||||
+ min.name + "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.TYPE_INSN:
|
|
||||||
final TypeInsnNode tin = (TypeInsnNode) ain;
|
|
||||||
insnString += "{" + tin.desc + "}";
|
|
||||||
break;
|
|
||||||
case AbstractInsnNode.MULTIANEWARRAY_INSN:
|
|
||||||
final MultiANewArrayInsnNode manain = (MultiANewArrayInsnNode) ain;
|
|
||||||
insnString += "{" + manain.dims + "," + manain.desc + "}";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
insnString += " ";
|
insnString += " ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do a pattern check against each instruction directly,
|
||||||
|
// without building a string of the whole method.
|
||||||
|
public static boolean staticScan(ClassNode node, MethodNode mn, Pattern pattern) {
|
||||||
|
final List<AbstractInsnNode> il = new ArrayList<AbstractInsnNode>();
|
||||||
|
for (final AbstractInsnNode ain : mn.instructions.toArray())
|
||||||
|
if (ain.getOpcode() >= 0) {
|
||||||
|
il.add(ain);
|
||||||
|
}
|
||||||
|
return il.stream().anyMatch(ain -> {
|
||||||
|
if (ain.getOpcode() >= 0) {
|
||||||
|
if (ain.getOpcode() >= opcodes.length) {
|
||||||
|
try {
|
||||||
|
throw new UnexpectedException(
|
||||||
|
"Unknown opcode encountered: "
|
||||||
|
+ ain.getOpcode());
|
||||||
|
} catch (final UnexpectedException e) {
|
||||||
|
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String insnString = getInsString(ain);
|
||||||
|
boolean result = pattern.matcher(insnString).find();
|
||||||
|
if(result) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getInsString(AbstractInsnNode ain) {
|
||||||
|
String insnString = "";
|
||||||
|
switch (ain.getType()) {
|
||||||
|
case AbstractInsnNode.INT_INSN:
|
||||||
|
final IntInsnNode iin = (IntInsnNode) ain;
|
||||||
|
insnString += "{" + iin.operand + "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.LDC_INSN:
|
||||||
|
final LdcInsnNode lin = (LdcInsnNode) ain;
|
||||||
|
insnString += "{" + lin.cst.toString().replace("}", "\\}")
|
||||||
|
+ "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.VAR_INSN:
|
||||||
|
final VarInsnNode vin = (VarInsnNode) ain;
|
||||||
|
insnString += "_" + vin.var;
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.IINC_INSN:
|
||||||
|
final IincInsnNode iiin = (IincInsnNode) ain;
|
||||||
|
insnString += "{" + iiin.var + "," + iiin.incr + "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.FIELD_INSN:
|
||||||
|
final FieldInsnNode fin = (FieldInsnNode) ain;
|
||||||
|
insnString += "{" + fin.desc + "," + fin.owner + ","
|
||||||
|
+ fin.name + "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.METHOD_INSN:
|
||||||
|
final MethodInsnNode min = (MethodInsnNode) ain;
|
||||||
|
insnString += "{" + min.desc + "," + min.owner + ","
|
||||||
|
+ min.name + "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.TYPE_INSN:
|
||||||
|
final TypeInsnNode tin = (TypeInsnNode) ain;
|
||||||
|
insnString += "{" + tin.desc + "}";
|
||||||
|
break;
|
||||||
|
case AbstractInsnNode.MULTIANEWARRAY_INSN:
|
||||||
|
final MultiANewArrayInsnNode manain = (MultiANewArrayInsnNode) ain;
|
||||||
|
insnString += "{" + manain.dims + "," + manain.desc + "}";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return insnString;
|
||||||
|
}
|
||||||
|
|
||||||
public void setMethod(final ClassNode ci, final MethodNode mi) {
|
public void setMethod(final ClassNode ci, final MethodNode mi) {
|
||||||
this.mn = mi;
|
this.mn = mi;
|
||||||
refresh();
|
refresh();
|
||||||
|
|
|
@ -2,6 +2,7 @@ package the.bytecode.club.bytecodeviewer.searching;
|
||||||
|
|
||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
@ -12,6 +13,8 @@ import org.objectweb.asm.tree.ClassNode;
|
||||||
import org.objectweb.asm.tree.MethodNode;
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
import the.bytecode.club.bytecodeviewer.util.FileContainer;
|
||||||
|
|
||||||
|
import static the.bytecode.club.bytecodeviewer.searching.RegexInsnFinder.processRegex;
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
* Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite *
|
||||||
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
|
* Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com *
|
||||||
|
@ -71,21 +74,13 @@ public class RegexSearch implements SearchTypeDetails {
|
||||||
|
|
||||||
if (srchText.isEmpty())
|
if (srchText.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
Pattern pattern = Pattern.compile(processRegex(srchText),
|
||||||
|
Pattern.MULTILINE);
|
||||||
while (methods.hasNext())
|
while (methods.hasNext())
|
||||||
{
|
{
|
||||||
final MethodNode method = methods.next();
|
final MethodNode method = methods.next();
|
||||||
|
|
||||||
if (regexFinder == null)
|
if (RegexInsnFinder.staticScan(node, method, pattern))
|
||||||
{
|
|
||||||
regexFinder = new RegexInsnFinder(node, method);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
regexFinder.setMethod(node, method);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (regexFinder.find(srchText).length > 0)
|
|
||||||
{
|
{
|
||||||
String desc2 = method.desc;
|
String desc2 = method.desc;
|
||||||
try
|
try
|
||||||
|
|
Loading…
Reference in a new issue