1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.bcel.util;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import org.apache.bcel.Const;
31 import org.apache.bcel.generic.ClassGenException;
32 import org.apache.bcel.generic.InstructionHandle;
33 import org.apache.bcel.generic.InstructionList;
34 import org.apache.commons.lang3.StringUtils;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 public class InstructionFinder {
66
67
68
69
70
71 public interface CodeConstraint {
72
73
74
75
76
77 boolean checkCode(InstructionHandle[] match);
78 }
79
80 private static final int OFFSET = 32767;
81 private static final int NO_OPCODES = 256;
82 private static final Map<String, String> map = new HashMap<>();
83
84
85 static {
86 map.put("arithmeticinstruction",
87 "(irem|lrem|iand|ior|ineg|isub|lneg|fneg|fmul|ldiv|fadd|lxor|frem|idiv|land|ixor|ishr|fsub|lshl|fdiv|iadd|lor|dmul|lsub|ishl|imul|lmul|lushr|dneg|iushr|lshr|ddiv|drem|dadd|ladd|dsub)");
88 map.put("invokeinstruction", "(invokevirtual|invokeinterface|invokestatic|invokespecial|invokedynamic)");
89 map.put("arrayinstruction",
90 "(baload|aastore|saload|caload|fastore|lastore|iaload|castore|iastore|aaload|bastore|sastore|faload|laload|daload|dastore)");
91 map.put("gotoinstruction", "(goto|goto_w)");
92 map.put("conversioninstruction", "(d2l|l2d|i2s|d2i|l2i|i2b|l2f|d2f|f2i|i2d|i2l|f2d|i2c|f2l|i2f)");
93 map.put("localvariableinstruction", "(fstore|iinc|lload|dstore|dload|iload|aload|astore|istore|fload|lstore)");
94 map.put("loadinstruction", "(fload|dload|lload|iload|aload)");
95 map.put("fieldinstruction", "(getfield|putstatic|getstatic|putfield)");
96 map.put("cpinstruction",
97 "(ldc2_w|invokeinterface|invokedynamic|multianewarray|putstatic|instanceof|getstatic|checkcast|getfield|invokespecial|ldc_w|invokestatic|invokevirtual|putfield|ldc|new|anewarray)");
98 map.put("stackinstruction", "(dup2|swap|dup2_x2|pop|pop2|dup|dup2_x1|dup_x2|dup_x1)");
99 map.put("branchinstruction",
100 "(ifle|if_acmpne|if_icmpeq|if_acmpeq|ifnonnull|goto_w|iflt|ifnull|if_icmpne|tableswitch|if_icmple|ifeq|if_icmplt|jsr_w|if_icmpgt|ifgt|jsr|goto|ifne|ifge|lookupswitch|if_icmpge)");
101 map.put("returninstruction", "(lreturn|ireturn|freturn|dreturn|areturn|return)");
102 map.put("storeinstruction", "(istore|fstore|dstore|astore|lstore)");
103 map.put("select", "(tableswitch|lookupswitch)");
104 map.put("ifinstruction",
105 "(ifeq|ifgt|if_icmpne|if_icmpeq|ifge|ifnull|ifne|if_icmple|if_icmpge|if_acmpeq|if_icmplt|if_acmpne|ifnonnull|iflt|if_icmpgt|ifle)");
106 map.put("jsrinstruction", "(jsr|jsr_w)");
107 map.put("variablelengthinstruction", "(tableswitch|jsr|goto|lookupswitch)");
108 map.put("unconditionalbranch", "(goto|jsr|jsr_w|athrow|goto_w)");
109 map.put("constantpushinstruction", "(dconst|bipush|sipush|fconst|iconst|lconst)");
110 map.put("typedinstruction",
111 "(imul|lsub|aload|fload|lor|new|aaload|fcmpg|iand|iaload|lrem|idiv|d2l|isub|dcmpg|dastore|ret|f2d|f2i|drem|iinc|i2c|checkcast|frem|lreturn|astore|lushr|daload|dneg|fastore|istore|lshl|ldiv|lstore|areturn|ishr|ldc_w|invokeinterface|invokedynamic|aastore|lxor|ishl|l2d|i2f|return|faload|sipush|iushr|caload|instanceof|invokespecial|putfield|fmul|ireturn|laload|d2f|lneg|ixor|i2l|fdiv|lastore|multianewarray|i2b|getstatic|i2d|putstatic|fcmpl|saload|ladd|irem|dload|jsr_w|dconst|dcmpl|fsub|freturn|ldc|aconst_null|castore|lmul|ldc2_w|dadd|iconst|f2l|ddiv|dstore|land|jsr|anewarray|dmul|bipush|dsub|sastore|d2i|i2s|lshr|iadd|l2i|lload|bastore|fstore|fneg|iload|fadd|baload|fconst|ior|ineg|dreturn|l2f|lconst|getfield|invokevirtual|invokestatic|iastore)");
112 map.put("popinstruction", "(fstore|dstore|pop|pop2|astore|putstatic|istore|lstore)");
113 map.put("allocationinstruction", "(multianewarray|new|anewarray|newarray)");
114 map.put("indexedinstruction",
115 "(lload|lstore|fload|ldc2_w|invokeinterface|invokedynamic|multianewarray|astore|dload|putstatic|instanceof|getstatic|checkcast|getfield|invokespecial|dstore|istore|iinc|ldc_w|ret|fstore|invokestatic|iload|putfield|invokevirtual|ldc|new|aload|anewarray)");
116 map.put("pushinstruction", "(dup|lload|dup2|bipush|fload|ldc2_w|sipush|lconst|fconst|dload|getstatic|ldc_w|aconst_null|dconst|iload|ldc|iconst|aload)");
117 map.put("stackproducer",
118 "(imul|lsub|aload|fload|lor|new|aaload|fcmpg|iand|iaload|lrem|idiv|d2l|isub|dcmpg|dup|f2d|f2i|drem|i2c|checkcast|frem|lushr|daload|dneg|lshl|ldiv|ishr|ldc_w|invokeinterface|invokedynamic|lxor|ishl|l2d|i2f|faload|sipush|iushr|caload|instanceof|invokespecial|fmul|laload|d2f|lneg|ixor|i2l|fdiv|getstatic|i2b|swap|i2d|dup2|fcmpl|saload|ladd|irem|dload|jsr_w|dconst|dcmpl|fsub|ldc|arraylength|aconst_null|tableswitch|lmul|ldc2_w|iconst|dadd|f2l|ddiv|land|jsr|anewarray|dmul|bipush|dsub|d2i|newarray|i2s|lshr|iadd|lload|l2i|fneg|iload|fadd|baload|fconst|lookupswitch|ior|ineg|lconst|l2f|getfield|invokevirtual|invokestatic)");
119 map.put("stackconsumer",
120 "(imul|lsub|lor|iflt|fcmpg|if_icmpgt|iand|ifeq|if_icmplt|lrem|ifnonnull|idiv|d2l|isub|dcmpg|dastore|if_icmpeq|f2d|f2i|drem|i2c|checkcast|frem|lreturn|astore|lushr|pop2|monitorexit|dneg|fastore|istore|lshl|ldiv|lstore|areturn|if_icmpge|ishr|monitorenter|invokeinterface|invokedynamic|aastore|lxor|ishl|l2d|i2f|return|iushr|instanceof|invokespecial|fmul|ireturn|d2f|lneg|ixor|pop|i2l|ifnull|fdiv|lastore|i2b|if_acmpeq|ifge|swap|i2d|putstatic|fcmpl|ladd|irem|dcmpl|fsub|freturn|ifgt|castore|lmul|dadd|f2l|ddiv|dstore|land|if_icmpne|if_acmpne|dmul|dsub|sastore|ifle|d2i|i2s|lshr|iadd|l2i|bastore|fstore|fneg|fadd|ior|ineg|ifne|dreturn|l2f|if_icmple|getfield|invokevirtual|invokestatic|iastore)");
121 map.put("exceptionthrower",
122 "(irem|lrem|laload|putstatic|baload|dastore|areturn|getstatic|ldiv|anewarray|iastore|castore|idiv|saload|lastore|fastore|putfield|lreturn|caload|getfield|return|aastore|freturn|newarray|instanceof|multianewarray|athrow|faload|iaload|aaload|dreturn|monitorenter|checkcast|bastore|arraylength|new|invokevirtual|sastore|ldc_w|ireturn|invokespecial|monitorexit|invokeinterface|invokedynamic|ldc|invokestatic|daload)");
123 map.put("loadclass",
124 "(multianewarray|invokeinterface|invokedynamic|instanceof|invokespecial|putfield|checkcast|putstatic|invokevirtual|new|getstatic|invokestatic|getfield|anewarray)");
125 map.put("instructiontargeter",
126 "(ifle|if_acmpne|if_icmpeq|if_acmpeq|ifnonnull|goto_w|iflt|ifnull|if_icmpne|tableswitch|if_icmple|ifeq|if_icmplt|jsr_w|if_icmpgt|ifgt|jsr|goto|ifne|ifge|lookupswitch|if_icmpge)");
127
128 map.put("if_icmp", "(if_icmpne|if_icmpeq|if_icmple|if_icmpge|if_icmplt|if_icmpgt)");
129 map.put("if_acmp", "(if_acmpeq|if_acmpne)");
130 map.put("if", "(ifeq|ifne|iflt|ifge|ifgt|ifle)");
131
132 map.put("iconst", precompile(Const.ICONST_0, Const.ICONST_5, Const.ICONST_M1));
133 map.put("lconst", new String(new char[] {'(', makeChar(Const.LCONST_0), '|', makeChar(Const.LCONST_1), ')'}));
134 map.put("dconst", new String(new char[] {'(', makeChar(Const.DCONST_0), '|', makeChar(Const.DCONST_1), ')'}));
135 map.put("fconst", new String(new char[] {'(', makeChar(Const.FCONST_0), '|', makeChar(Const.FCONST_1), '|', makeChar(Const.FCONST_2), ')'}));
136 map.put("lload", precompile(Const.LLOAD_0, Const.LLOAD_3, Const.LLOAD));
137 map.put("iload", precompile(Const.ILOAD_0, Const.ILOAD_3, Const.ILOAD));
138 map.put("dload", precompile(Const.DLOAD_0, Const.DLOAD_3, Const.DLOAD));
139 map.put("fload", precompile(Const.FLOAD_0, Const.FLOAD_3, Const.FLOAD));
140 map.put("aload", precompile(Const.ALOAD_0, Const.ALOAD_3, Const.ALOAD));
141 map.put("lstore", precompile(Const.LSTORE_0, Const.LSTORE_3, Const.LSTORE));
142 map.put("istore", precompile(Const.ISTORE_0, Const.ISTORE_3, Const.ISTORE));
143 map.put("dstore", precompile(Const.DSTORE_0, Const.DSTORE_3, Const.DSTORE));
144 map.put("fstore", precompile(Const.FSTORE_0, Const.FSTORE_3, Const.FSTORE));
145 map.put("astore", precompile(Const.ASTORE_0, Const.ASTORE_3, Const.ASTORE));
146
147 map.forEach((key, value) -> {
148 final char ch = value.charAt(1);
149 if (ch < OFFSET) {
150 map.put(key, compilePattern(value));
151 }
152 });
153
154 final StringBuilder buf = new StringBuilder("(");
155 for (short i = 0; i < NO_OPCODES; i++) {
156 if (Const.getNoOfOperands(i) != Const.UNDEFINED) {
157 buf.append(makeChar(i));
158 if (i < NO_OPCODES - 1) {
159 buf.append('|');
160 }
161 }
162 }
163 buf.append(')');
164 map.put("instruction", buf.toString());
165 }
166
167
168
169
170
171
172
173
174 private static String compilePattern(final String pattern) {
175
176 final String lower = StringUtils.toRootLowerCase(pattern);
177 final StringBuilder buf = new StringBuilder();
178 final int size = pattern.length();
179 for (int i = 0; i < size; i++) {
180 char ch = lower.charAt(i);
181 if (Character.isLetterOrDigit(ch)) {
182 final StringBuilder name = new StringBuilder();
183 while ((Character.isLetterOrDigit(ch) || ch == '_') && i < size) {
184 name.append(ch);
185 if (++i >= size) {
186 break;
187 }
188 ch = lower.charAt(i);
189 }
190 i--;
191 buf.append(mapName(name.toString()));
192 } else if (!Character.isWhitespace(ch)) {
193 buf.append(ch);
194 }
195 }
196 return buf.toString();
197 }
198
199
200
201
202 private static char makeChar(final short opcode) {
203 return (char) (opcode + OFFSET);
204 }
205
206
207
208
209
210
211
212 private static String mapName(final String pattern) {
213 final String result = map.get(pattern);
214 if (result != null) {
215 return result;
216 }
217 for (short i = 0; i < NO_OPCODES; i++) {
218 if (pattern.equals(Const.getOpcodeName(i))) {
219 return String.valueOf(makeChar(i));
220 }
221 }
222 throw new IllegalArgumentException("Instruction unknown: " + pattern);
223 }
224
225 private static String precompile(final short from, final short to, final short extra) {
226 final StringBuilder buf = new StringBuilder("(");
227 for (short i = from; i <= to; i++) {
228 buf.append(makeChar(i));
229 buf.append('|');
230 }
231 buf.append(makeChar(extra));
232 buf.append(")");
233 return buf.toString();
234 }
235
236 private final InstructionList il;
237
238 private String ilString;
239
240 private InstructionHandle[] handles;
241
242
243
244
245
246 public InstructionFinder(final InstructionList il) {
247 this.il = il;
248 reread();
249 }
250
251
252
253
254 public final InstructionList getInstructionList() {
255 return il;
256 }
257
258
259
260
261 private InstructionHandle[] getMatch(final int matchedFrom, final int matchLength) {
262 return Arrays.copyOfRange(handles, matchedFrom, matchedFrom + matchLength);
263 }
264
265
266
267
268 public final void reread() {
269 final int size = il.getLength();
270 final char[] buf = new char[size];
271 handles = il.getInstructionHandles();
272
273 for (int i = 0; i < size; i++) {
274 buf[i] = makeChar(handles[i].getInstruction().getOpcode());
275 }
276 ilString = new String(buf);
277 }
278
279
280
281
282
283
284
285 public final Iterator<InstructionHandle[]> search(final String pattern) {
286 return search(pattern, il.getStart(), null);
287 }
288
289
290
291
292
293
294
295
296 public final Iterator<InstructionHandle[]> search(final String pattern, final CodeConstraint constraint) {
297 return search(pattern, il.getStart(), constraint);
298 }
299
300
301
302
303
304
305
306
307 public final Iterator<InstructionHandle[]> search(final String pattern, final InstructionHandle from) {
308 return search(pattern, from, null);
309 }
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337 public final Iterator<InstructionHandle[]> search(final String pattern, final InstructionHandle from, final CodeConstraint constraint) {
338 final String search = compilePattern(pattern);
339 int start = -1;
340 for (int i = 0; i < handles.length; i++) {
341 if (handles[i] == from) {
342 start = i;
343 break;
344 }
345 }
346 if (start == -1) {
347 throw new ClassGenException("Instruction handle " + from + " not found in instruction list.");
348 }
349 final Pattern regex = Pattern.compile(search);
350 final List<InstructionHandle[]> matches = new ArrayList<>();
351 final Matcher matcher = regex.matcher(ilString);
352 while (start < ilString.length() && matcher.find(start)) {
353 final int startExpr = matcher.start();
354 final int endExpr = matcher.end();
355 final int lenExpr = endExpr - startExpr;
356 final InstructionHandle[] match = getMatch(startExpr, lenExpr);
357 if (constraint == null || constraint.checkCode(match)) {
358 matches.add(match);
359 }
360 start = endExpr;
361 }
362 return matches.iterator();
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388 }