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 public class InstructionFinder {
64
65
66
67
68
69 public interface CodeConstraint {
70
71
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
247
248
249 public InstructionFinder(final InstructionList il) {
250 this.il = il;
251 reread();
252 }
253
254
255
256
257
258
259 public final InstructionList getInstructionList() {
260 return il;
261 }
262
263
264
265
266 private InstructionHandle[] getMatch(final int matchedFrom, final int matchLength) {
267 return Arrays.copyOfRange(handles, matchedFrom, matchedFrom + matchLength);
268 }
269
270
271
272
273 public final void reread() {
274 final int size = il.getLength();
275 final char[] buf = new char[size];
276 handles = il.getInstructionHandles();
277
278 for (int i = 0; i < size; i++) {
279 buf[i] = makeChar(handles[i].getInstruction().getOpcode());
280 }
281 ilString = new String(buf);
282 }
283
284
285
286
287
288
289
290 public final Iterator<InstructionHandle[]> search(final String pattern) {
291 return search(pattern, il.getStart(), null);
292 }
293
294
295
296
297
298
299
300
301 public final Iterator<InstructionHandle[]> search(final String pattern, final CodeConstraint constraint) {
302 return search(pattern, il.getStart(), constraint);
303 }
304
305
306
307
308
309
310
311
312 public final Iterator<InstructionHandle[]> search(final String pattern, final InstructionHandle from) {
313 return search(pattern, from, null);
314 }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343 public final Iterator<InstructionHandle[]> search(final String pattern, final InstructionHandle from, final CodeConstraint constraint) {
344 final String search = compilePattern(pattern);
345 int start = -1;
346 for (int i = 0; i < handles.length; i++) {
347 if (handles[i] == from) {
348 start = i;
349 break;
350 }
351 }
352 if (start == -1) {
353 throw new ClassGenException("Instruction handle " + from + " not found in instruction list.");
354 }
355 final Pattern regex = Pattern.compile(search);
356 final List<InstructionHandle[]> matches = new ArrayList<>();
357 final Matcher matcher = regex.matcher(ilString);
358 while (start < ilString.length() && matcher.find(start)) {
359 final int startExpr = matcher.start();
360 final int endExpr = matcher.end();
361 final int lenExpr = endExpr - startExpr;
362 final InstructionHandle[] match = getMatch(startExpr, lenExpr);
363 if (constraint == null || constraint.checkCode(match)) {
364 matches.add(match);
365 }
366 start = endExpr;
367 }
368 return matches.iterator();
369 }
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394 }