• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
3  *             of Java bytecode.
4  *
5  * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15  * more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20  */
21 package proguard.classfile.util;
22 
23 import proguard.classfile.*;
24 import proguard.classfile.attribute.CodeAttribute;
25 import proguard.classfile.constant.*;
26 import proguard.classfile.constant.visitor.ConstantVisitor;
27 import proguard.classfile.instruction.*;
28 import proguard.classfile.instruction.visitor.InstructionVisitor;
29 
30 /**
31  * This InstructionVisitor checks whether a given pattern instruction sequence
32  * occurs in the instructions that are visited. The arguments of the
33  * instruction sequence can be wildcards that are matched.
34  *
35  * @author Eric Lafortune
36  */
37 public class InstructionSequenceMatcher
38 extends      SimplifiedVisitor
39 implements   InstructionVisitor,
40              ConstantVisitor
41 {
42     /*
43     private static       boolean DEBUG      = false;
44     public  static       boolean DEBUG_MORE = false;
45     /*/
46     private static final boolean DEBUG      = false;
47     private static final boolean DEBUG_MORE = false;
48     //*/
49 
50     public static final int X = 0x40000000;
51     public static final int Y = 0x40000001;
52     public static final int Z = 0x40000002;
53 
54     public static final int A = 0x40000003;
55     public static final int B = 0x40000004;
56     public static final int C = 0x40000005;
57     public static final int D = 0x40000006;
58 
59 
60     private final Constant[]    patternConstants;
61     private final Instruction[] patternInstructions;
62 
63     private boolean     matching;
64     private boolean     matchingAnyWildCards;
65     private int         patternInstructionIndex;
66     private final int[] matchedInstructionOffsets;
67     private int         matchedArgumentFlags;
68     private final int[] matchedArguments = new int[7];
69     private long        matchedConstantFlags;
70     private final int[] matchedConstantIndices;
71 
72     // Fields acting as a parameter and a return value for visitor methods.
73     private Constant patternConstant;
74     private boolean  matchingConstant;
75 
76 
77     /**
78      * Creates a new InstructionSequenceMatcher.
79      * @param patternConstants        any constants referenced by the pattern
80      *                                instruction.
81      * @param patternInstructions     the pattern instruction sequence.
82      */
InstructionSequenceMatcher(Constant[] patternConstants, Instruction[] patternInstructions)83     public InstructionSequenceMatcher(Constant[]    patternConstants,
84                                       Instruction[] patternInstructions)
85     {
86         this.patternConstants    = patternConstants;
87         this.patternInstructions = patternInstructions;
88 
89         matchedInstructionOffsets = new int[patternInstructions.length];
90         matchedConstantIndices    = new int[patternConstants.length];
91     }
92 
93 
94     /**
95      * Starts matching from the first instruction again next time.
96      */
reset()97     public void reset()
98     {
99         patternInstructionIndex = 0;
100         matchedArgumentFlags    = 0;
101         matchedConstantFlags    = 0L;
102     }
103 
104 
isMatching()105     public boolean isMatching()
106     {
107         return matching;
108     }
109 
110 
isMatchingAnyWildcards()111     public boolean isMatchingAnyWildcards()
112     {
113         return matchingAnyWildCards;
114     }
115 
116 
instructionCount()117     public int instructionCount()
118     {
119         return patternInstructions.length;
120     }
121 
122 
matchedInstructionOffset(int index)123     public int matchedInstructionOffset(int index)
124     {
125         return matchedInstructionOffsets[index];
126     }
127 
128 
matchedArgument(int argument)129     public int matchedArgument(int argument)
130     {
131         int argumentIndex = argument - X;
132         return argumentIndex < 0 ?
133             argument :
134             matchedArguments[argumentIndex];
135     }
136 
137 
matchedArguments(int[] arguments)138     public int[] matchedArguments(int[] arguments)
139     {
140         int[] matchedArguments = new int[arguments.length];
141 
142         for (int index = 0; index < arguments.length; index++)
143         {
144             matchedArguments[index] = matchedArgument(arguments[index]);
145         }
146 
147         return matchedArguments;
148     }
149 
150 
matchedConstantIndex(int constantIndex)151     public int matchedConstantIndex(int constantIndex)
152     {
153         int argumentIndex = constantIndex - X;
154         return argumentIndex < 0 ?
155             matchedConstantIndices[constantIndex] :
156             matchedArguments[argumentIndex];
157     }
158 
159 
matchedBranchOffset(int offset, int branchOffset)160     public int matchedBranchOffset(int offset, int branchOffset)
161     {
162         int argumentIndex = branchOffset - X;
163         return argumentIndex < 0 ?
164             branchOffset :
165             matchedArguments[argumentIndex] - offset;
166     }
167 
168 
matchedJumpOffsets(int offset, int[] jumpOffsets)169     public int[] matchedJumpOffsets(int offset, int[] jumpOffsets)
170     {
171         int[] matchedJumpOffsets = new int[jumpOffsets.length];
172 
173         for (int index = 0; index < jumpOffsets.length; index++)
174         {
175             matchedJumpOffsets[index] = matchedBranchOffset(offset,
176                                                             jumpOffsets[index]);
177         }
178 
179         return matchedJumpOffsets;
180     }
181 
182 
183     // Implementations for InstructionVisitor.
184 
visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)185     public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
186     {
187         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
188 
189         // Check if the instruction matches the next instruction in the sequence.
190         boolean condition =
191             matchingOpcodes(simpleInstruction, patternInstruction) &&
192             matchingArguments(simpleInstruction.constant,
193                               ((SimpleInstruction)patternInstruction).constant);
194 
195         // Check if the instruction sequence is matching now.
196         checkMatch(condition,
197                    clazz,
198                    method,
199                    codeAttribute,
200                    offset,
201                    simpleInstruction);
202     }
203 
204 
visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)205     public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
206     {
207         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
208 
209         // Check if the instruction matches the next instruction in the sequence.
210         boolean condition =
211             matchingOpcodes(variableInstruction, patternInstruction) &&
212             matchingArguments(variableInstruction.variableIndex,
213                               ((VariableInstruction)patternInstruction).variableIndex) &&
214             matchingArguments(variableInstruction.constant,
215                               ((VariableInstruction)patternInstruction).constant);
216 
217         // Check if the instruction sequence is matching now.
218         checkMatch(condition,
219                    clazz,
220                    method,
221                    codeAttribute,
222                    offset,
223                    variableInstruction);
224     }
225 
226 
visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)227     public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
228     {
229         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
230 
231         // Check if the instruction matches the next instruction in the sequence.
232         boolean condition =
233             matchingOpcodes(constantInstruction, patternInstruction) &&
234             matchingConstantIndices(clazz,
235                                     constantInstruction.constantIndex,
236                                     ((ConstantInstruction)patternInstruction).constantIndex) &&
237             matchingArguments(constantInstruction.constant,
238                               ((ConstantInstruction)patternInstruction).constant);
239 
240         // Check if the instruction sequence is matching now.
241         checkMatch(condition,
242                    clazz,
243                    method,
244                    codeAttribute,
245                    offset,
246                    constantInstruction);
247     }
248 
249 
visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)250     public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
251     {
252         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
253 
254         // Check if the instruction matches the next instruction in the from
255         // sequence.
256         boolean condition =
257             matchingOpcodes(branchInstruction, patternInstruction) &&
258             matchingBranchOffsets(offset,
259                                   branchInstruction.branchOffset,
260                                   ((BranchInstruction)patternInstruction).branchOffset);
261 
262         // Check if the instruction sequence is matching now.
263         checkMatch(condition,
264                    clazz,
265                    method,
266                    codeAttribute,
267                    offset,
268                    branchInstruction);
269     }
270 
271 
visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)272     public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
273     {
274         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
275 
276         // Check if the instruction matches the next instruction in the sequence.
277         boolean condition =
278             matchingOpcodes(tableSwitchInstruction, patternInstruction) &&
279             matchingBranchOffsets(offset,
280                                   tableSwitchInstruction.defaultOffset,
281                                   ((TableSwitchInstruction)patternInstruction).defaultOffset) &&
282             matchingArguments(tableSwitchInstruction.lowCase,
283                               ((TableSwitchInstruction)patternInstruction).lowCase)  &&
284             matchingArguments(tableSwitchInstruction.highCase,
285                               ((TableSwitchInstruction)patternInstruction).highCase) &&
286             matchingJumpOffsets(offset,
287                                 tableSwitchInstruction.jumpOffsets,
288                                 ((TableSwitchInstruction)patternInstruction).jumpOffsets);
289 
290         // Check if the instruction sequence is matching now.
291         checkMatch(condition,
292                    clazz,
293                    method,
294                    codeAttribute,
295                    offset,
296                    tableSwitchInstruction);
297     }
298 
299 
visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)300     public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
301     {
302         Instruction patternInstruction = patternInstructions[patternInstructionIndex];
303 
304         // Check if the instruction matches the next instruction in the sequence.
305         boolean condition =
306             matchingOpcodes(lookUpSwitchInstruction, patternInstruction) &&
307             matchingBranchOffsets(offset,
308                                   lookUpSwitchInstruction.defaultOffset,
309                                   ((LookUpSwitchInstruction)patternInstruction).defaultOffset) &&
310             matchingArguments(lookUpSwitchInstruction.cases,
311                               ((LookUpSwitchInstruction)patternInstruction).cases) &&
312             matchingJumpOffsets(offset,
313                                 lookUpSwitchInstruction.jumpOffsets,
314                                 ((LookUpSwitchInstruction)patternInstruction).jumpOffsets);
315 
316         // Check if the instruction sequence is matching now.
317         checkMatch(condition,
318                    clazz,
319                    method,
320                    codeAttribute,
321                    offset,
322                    lookUpSwitchInstruction);
323     }
324 
325 
326     // Implementations for ConstantVisitor.
327 
visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)328     public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
329     {
330         IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant;
331 
332         // Compare the integer values.
333         matchingConstant = integerConstant.getValue() ==
334                            integerPatternConstant.getValue();
335     }
336 
337 
visitLongConstant(Clazz clazz, LongConstant longConstant)338     public void visitLongConstant(Clazz clazz, LongConstant longConstant)
339     {
340         LongConstant longPatternConstant = (LongConstant)patternConstant;
341 
342         // Compare the long values.
343         matchingConstant = longConstant.getValue() ==
344                            longPatternConstant.getValue();
345     }
346 
347 
visitFloatConstant(Clazz clazz, FloatConstant floatConstant)348     public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
349     {
350         FloatConstant floatPatternConstant = (FloatConstant)patternConstant;
351 
352         // Compare the float values.
353         matchingConstant = floatConstant.getValue() ==
354                            floatPatternConstant.getValue();
355     }
356 
357 
visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)358     public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
359     {
360         DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant;
361 
362         // Compare the double values.
363         matchingConstant = doubleConstant.getValue() ==
364                            doublePatternConstant.getValue();
365     }
366 
367 
visitStringConstant(Clazz clazz, StringConstant stringConstant)368     public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
369     {
370         StringConstant stringPatternConstant = (StringConstant)patternConstant;
371 
372         // Check the UTF-8 constant.
373         matchingConstant =
374             matchingConstantIndices(clazz,
375                                     stringConstant.u2stringIndex,
376                                     stringPatternConstant.u2stringIndex);
377     }
378 
379 
visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)380     public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
381     {
382         Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant;
383 
384         // Compare the actual strings.
385         matchingConstant = utf8Constant.getString().equals(
386                            utf8PatternConstant.getString());
387     }
388 
389 
visitAnyRefConstant(Clazz clazz, RefConstant refConstant)390     public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
391     {
392         RefConstant refPatternConstant = (RefConstant)patternConstant;
393 
394         // Check the class and the name and type.
395         matchingConstant =
396             matchingConstantIndices(clazz,
397                                     refConstant.getClassIndex(),
398                                     refPatternConstant.getClassIndex()) &&
399             matchingConstantIndices(clazz,
400                                     refConstant.getNameAndTypeIndex(),
401                                     refPatternConstant.getNameAndTypeIndex());
402     }
403 
404 
visitClassConstant(Clazz clazz, ClassConstant classConstant)405     public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
406     {
407         ClassConstant classPatternConstant = (ClassConstant)patternConstant;
408 
409         // Check the class name.
410         matchingConstant =
411             matchingConstantIndices(clazz,
412                                     classConstant.u2nameIndex,
413                                     classPatternConstant.u2nameIndex);
414     }
415 
416 
visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)417     public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
418     {
419         NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant;
420 
421         // Check the name and the descriptor.
422         matchingConstant =
423             matchingConstantIndices(clazz,
424                                     nameAndTypeConstant.u2nameIndex,
425                                     typePatternConstant.u2nameIndex) &&
426             matchingConstantIndices(clazz,
427                                     nameAndTypeConstant.u2descriptorIndex,
428                                     typePatternConstant.u2descriptorIndex);
429     }
430 
431 
432     // Small utility methods.
433 
matchingOpcodes(Instruction instruction1, Instruction instruction2)434     private boolean matchingOpcodes(Instruction instruction1,
435                                     Instruction instruction2)
436     {
437         // Check the opcode.
438         return instruction1.opcode            == instruction2.opcode ||
439                instruction1.canonicalOpcode() == instruction2.opcode;
440     }
441 
442 
matchingArguments(int argument1, int argument2)443     private boolean matchingArguments(int argument1,
444                                       int argument2)
445     {
446         int argumentIndex = argument2 - X;
447         if (argumentIndex < 0)
448         {
449             // Check the literal argument.
450             return argument1 == argument2;
451         }
452         else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0)
453         {
454             // Store a wildcard argument.
455             matchedArguments[argumentIndex] = argument1;
456             matchedArgumentFlags |= 1 << argumentIndex;
457 
458             return true;
459         }
460         else
461         {
462             // Check the previously stored wildcard argument.
463             return matchedArguments[argumentIndex] == argument1;
464         }
465     }
466 
467 
matchingArguments(int[] arguments1, int[] arguments2)468     private boolean matchingArguments(int[] arguments1,
469                                       int[] arguments2)
470     {
471         if (arguments1.length != arguments2.length)
472         {
473             return false;
474         }
475 
476         for (int index = 0; index < arguments1.length; index++)
477         {
478             if (!matchingArguments(arguments1[index], arguments2[index]))
479             {
480                 return false;
481             }
482         }
483 
484         return true;
485     }
486 
487 
matchingConstantIndices(Clazz clazz, int constantIndex1, int constantIndex2)488     private boolean matchingConstantIndices(Clazz clazz,
489                                             int   constantIndex1,
490                                             int   constantIndex2)
491     {
492         if (constantIndex2 >= X)
493         {
494             // Check the constant index.
495             return matchingArguments(constantIndex1, constantIndex2);
496         }
497         else if ((matchedConstantFlags & (1L << constantIndex2)) == 0)
498         {
499             // Check the actual constant.
500             matchingConstant = false;
501             patternConstant  = patternConstants[constantIndex2];
502 
503             if (clazz.getTag(constantIndex1) == patternConstant.getTag())
504             {
505                 clazz.constantPoolEntryAccept(constantIndex1, this);
506 
507                 if (matchingConstant)
508                 {
509                     // Store the constant index.
510                     matchedConstantIndices[constantIndex2] = constantIndex1;
511                     matchedConstantFlags |= 1L << constantIndex2;
512                 }
513             }
514 
515             return matchingConstant;
516         }
517         else
518         {
519             // Check a previously stored constant index.
520             return matchedConstantIndices[constantIndex2] == constantIndex1;
521         }
522     }
523 
524 
matchingBranchOffsets(int offset, int branchOffset1, int branchOffset2)525     private boolean matchingBranchOffsets(int offset,
526                                           int branchOffset1,
527                                           int branchOffset2)
528     {
529         int argumentIndex = branchOffset2 - X;
530         if (argumentIndex < 0)
531         {
532             // Check the literal argument.
533             return branchOffset1 == branchOffset2;
534         }
535         else if ((matchedArgumentFlags & (1 << argumentIndex)) == 0)
536         {
537             // Store a wildcard argument.
538             matchedArguments[argumentIndex] = offset + branchOffset1;
539             matchedArgumentFlags |= 1 << argumentIndex;
540 
541             return true;
542         }
543         else
544         {
545             // Check the previously stored wildcard argument.
546             return matchedArguments[argumentIndex] == offset + branchOffset1;
547         }
548     }
549 
550 
matchingJumpOffsets(int offset, int[] jumpOffsets1, int[] jumpOffsets2)551     private boolean matchingJumpOffsets(int   offset,
552                                         int[] jumpOffsets1,
553                                         int[] jumpOffsets2)
554     {
555         if (jumpOffsets1.length != jumpOffsets2.length)
556         {
557             return false;
558         }
559 
560         for (int index = 0; index < jumpOffsets1.length; index++)
561         {
562             if (!matchingBranchOffsets(offset,
563                                        jumpOffsets1[index],
564                                        jumpOffsets2[index]))
565             {
566                 return false;
567             }
568         }
569 
570         return true;
571     }
572 
573 
checkMatch(boolean condition, Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)574     private void checkMatch(boolean       condition,
575                             Clazz         clazz,
576                             Method        method,
577                             CodeAttribute codeAttribute,
578                             int           offset,
579                             Instruction   instruction)
580     {
581         if (DEBUG_MORE)
582         {
583             System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t   ")+instruction.toString(offset));
584         }
585 
586         // Did the instruction match?
587         if (condition)
588         {
589             // Remember the offset of the matching instruction.
590             matchedInstructionOffsets[patternInstructionIndex] = offset;
591 
592             // Try to match the next instruction next time.
593             patternInstructionIndex++;
594 
595             // Did we match all instructions in the sequence?
596             matching = patternInstructionIndex == patternInstructions.length;
597 
598             // Did we match any wildcards along the way?
599             matchingAnyWildCards = matchedArgumentFlags != 0;
600 
601             if (matching)
602             {
603                 if (DEBUG)
604                 {
605                     System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
606                     for (int index = 0; index < patternInstructionIndex; index++)
607                     {
608                         System.out.println("    "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index]));
609                     }
610                 }
611 
612                 // Start matching from the first instruction again next time.
613                 reset();
614             }
615         }
616         else
617         {
618             // The instruction didn't match.
619             matching = false;
620 
621             // Is this a failed second instruction?
622             boolean retry = patternInstructionIndex == 1;
623 
624             // Start matching from the first instruction next time.
625             reset();
626 
627             // Retry a failed second instruction as a first instruction.
628             if (retry)
629             {
630                 instruction.accept(clazz, method, codeAttribute, offset, this);
631             }
632         }
633     }
634 }
635