1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 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 */
21package proguard.classfile.util;
22
23import proguard.classfile.*;
24import proguard.classfile.attribute.CodeAttribute;
25import proguard.classfile.constant.*;
26import proguard.classfile.constant.visitor.ConstantVisitor;
27import proguard.classfile.instruction.*;
28import proguard.classfile.instruction.visitor.InstructionVisitor;
29
30import java.util.Arrays;
31
32/**
33 * This InstructionVisitor checks whether a given pattern instruction sequence
34 * occurs in the instructions that are visited. The arguments of the
35 * instruction sequence can be wildcards that are matched.
36 *
37 * @author Eric Lafortune
38 */
39public class InstructionSequenceMatcher
40extends      SimplifiedVisitor
41implements   InstructionVisitor,
42             ConstantVisitor
43{
44    //*
45    private static final boolean DEBUG      = false;
46    private static final boolean DEBUG_MORE = false;
47    /*/
48    public  static       boolean DEBUG      = true;
49    public  static       boolean DEBUG_MORE = true;
50    //*/
51
52    public static final int X = 0x40000000;
53    public static final int Y = 0x40000001;
54    public static final int Z = 0x40000002;
55
56    public static final int A = 0x40000003;
57    public static final int B = 0x40000004;
58    public static final int C = 0x40000005;
59    public static final int D = 0x40000006;
60
61
62    private final Constant[]    patternConstants;
63    private final Instruction[] patternInstructions;
64
65    private boolean      matching;
66    private int          patternInstructionIndex;
67    private final int[]  matchedInstructionOffsets;
68    private int          matchedArgumentFlags;
69    private final int[]  matchedArguments = new int[7];
70    private final long[] matchedConstantFlags;
71    private final int[]  matchedConstantIndices;
72    private int          constantFlags;
73    private int          previousConstantFlags;
74
75    // Fields acting as a parameter and a return value for visitor methods.
76    private Constant patternConstant;
77    private boolean  matchingConstant;
78
79
80    /**
81     * Creates a new InstructionSequenceMatcher.
82     * @param patternConstants        any constants referenced by the pattern
83     *                                instruction.
84     * @param patternInstructions     the pattern instruction sequence.
85     */
86    public InstructionSequenceMatcher(Constant[]    patternConstants,
87                                      Instruction[] patternInstructions)
88    {
89        this.patternConstants    = patternConstants;
90        this.patternInstructions = patternInstructions;
91
92        matchedInstructionOffsets = new int[patternInstructions.length];
93        matchedConstantFlags      = new long[(patternConstants.length + 63) / 64];
94        matchedConstantIndices    = new int[patternConstants.length];
95    }
96
97
98    /**
99     * Starts matching from the first instruction again next time.
100     */
101    public void reset()
102    {
103        patternInstructionIndex = 0;
104        matchedArgumentFlags    = 0;
105
106        Arrays.fill(matchedConstantFlags, 0L);
107
108        previousConstantFlags = constantFlags;
109        constantFlags         = 0;
110    }
111
112
113    /**
114     * Returns whether the complete pattern sequence has been matched.
115     */
116    public boolean isMatching()
117    {
118        return matching;
119    }
120
121
122    /**
123     * Returns the number of instructions in the pattern sequence.
124     */
125    public int instructionCount()
126    {
127        return patternInstructions.length;
128    }
129
130
131    /**
132     * Returns the matched instruction offset of the specified pattern
133     * instruction.
134     */
135    public int matchedInstructionOffset(int index)
136    {
137        return matchedInstructionOffsets[index];
138    }
139
140
141    /**
142     * Returns whether the specified wildcard argument was a constant from
143     * the constant pool in the most recent match.
144     */
145    public boolean wasConstant(int argument)
146    {
147        return (previousConstantFlags & (1 << (argument - X))) != 0;
148    }
149
150
151    /**
152     * Returns the value of the specified matched argument (wildcard or not).
153     */
154    public int matchedArgument(int argument)
155    {
156        int argumentIndex = argument - X;
157        return argumentIndex < 0 ?
158            argument :
159            matchedArguments[argumentIndex];
160    }
161
162
163    /**
164     * Returns the values of the specified matched arguments (wildcard or not).
165     */
166    public int[] matchedArguments(int[] arguments)
167    {
168        int[] matchedArguments = new int[arguments.length];
169
170        for (int index = 0; index < arguments.length; index++)
171        {
172            matchedArguments[index] = matchedArgument(arguments[index]);
173        }
174
175        return matchedArguments;
176    }
177
178
179    /**
180     * Returns the index of the specified matched constant (wildcard or not).
181     */
182    public int matchedConstantIndex(int constantIndex)
183    {
184        int argumentIndex = constantIndex - X;
185        return argumentIndex < 0 ?
186            matchedConstantIndices[constantIndex] :
187            matchedArguments[argumentIndex];
188    }
189
190
191    /**
192     * Returns the value of the specified matched branch offset (wildcard or
193     * not).
194     */
195    public int matchedBranchOffset(int offset, int branchOffset)
196    {
197        int argumentIndex = branchOffset - X;
198        return argumentIndex < 0 ?
199            branchOffset :
200            matchedArguments[argumentIndex] - offset;
201    }
202
203
204    /**
205     * Returns the values of the specified matched jump offsets (wildcard or
206     * not).
207     */
208    public int[] matchedJumpOffsets(int offset, int[] jumpOffsets)
209    {
210        int[] matchedJumpOffsets = new int[jumpOffsets.length];
211
212        for (int index = 0; index < jumpOffsets.length; index++)
213        {
214            matchedJumpOffsets[index] = matchedBranchOffset(offset,
215                                                            jumpOffsets[index]);
216        }
217
218        return matchedJumpOffsets;
219    }
220
221
222    // Implementations for InstructionVisitor.
223
224    public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
225    {
226        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
227
228        // Check if the instruction matches the next instruction in the sequence.
229        boolean condition =
230            matchingOpcodes(simpleInstruction, patternInstruction) &&
231            matchingArguments(simpleInstruction.constant,
232                              ((SimpleInstruction)patternInstruction).constant);
233
234        // Check if the instruction sequence is matching now.
235        checkMatch(condition,
236                   clazz,
237                   method,
238                   codeAttribute,
239                   offset,
240                   simpleInstruction);
241    }
242
243
244    public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
245    {
246        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
247
248        // Check if the instruction matches the next instruction in the sequence.
249        boolean condition =
250            matchingOpcodes(variableInstruction, patternInstruction) &&
251            matchingArguments(variableInstruction.variableIndex,
252                              ((VariableInstruction)patternInstruction).variableIndex) &&
253            matchingArguments(variableInstruction.constant,
254                              ((VariableInstruction)patternInstruction).constant);
255
256        // Check if the instruction sequence is matching now.
257        checkMatch(condition,
258                   clazz,
259                   method,
260                   codeAttribute,
261                   offset,
262                   variableInstruction);
263    }
264
265
266    public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
267    {
268        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
269
270        // Check if the instruction matches the next instruction in the sequence.
271        boolean condition =
272            matchingOpcodes(constantInstruction, patternInstruction) &&
273            matchingConstantIndices(clazz,
274                                    constantInstruction.constantIndex,
275                                    ((ConstantInstruction)patternInstruction).constantIndex) &&
276            matchingArguments(constantInstruction.constant,
277                              ((ConstantInstruction)patternInstruction).constant);
278
279        // Check if the instruction sequence is matching now.
280        checkMatch(condition,
281                   clazz,
282                   method,
283                   codeAttribute,
284                   offset,
285                   constantInstruction);
286    }
287
288
289    public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
290    {
291        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
292
293        // Check if the instruction matches the next instruction in the from
294        // sequence.
295        boolean condition =
296            matchingOpcodes(branchInstruction, patternInstruction) &&
297            matchingBranchOffsets(offset,
298                                  branchInstruction.branchOffset,
299                                  ((BranchInstruction)patternInstruction).branchOffset);
300
301        // Check if the instruction sequence is matching now.
302        checkMatch(condition,
303                   clazz,
304                   method,
305                   codeAttribute,
306                   offset,
307                   branchInstruction);
308    }
309
310
311    public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
312    {
313        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
314
315        // Check if the instruction matches the next instruction in the sequence.
316        boolean condition =
317            matchingOpcodes(tableSwitchInstruction, patternInstruction) &&
318            matchingBranchOffsets(offset,
319                                  tableSwitchInstruction.defaultOffset,
320                                  ((TableSwitchInstruction)patternInstruction).defaultOffset) &&
321            matchingArguments(tableSwitchInstruction.lowCase,
322                              ((TableSwitchInstruction)patternInstruction).lowCase)  &&
323            matchingArguments(tableSwitchInstruction.highCase,
324                              ((TableSwitchInstruction)patternInstruction).highCase) &&
325            matchingJumpOffsets(offset,
326                                tableSwitchInstruction.jumpOffsets,
327                                ((TableSwitchInstruction)patternInstruction).jumpOffsets);
328
329        // Check if the instruction sequence is matching now.
330        checkMatch(condition,
331                   clazz,
332                   method,
333                   codeAttribute,
334                   offset,
335                   tableSwitchInstruction);
336    }
337
338
339    public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
340    {
341        Instruction patternInstruction = patternInstructions[patternInstructionIndex];
342
343        // Check if the instruction matches the next instruction in the sequence.
344        boolean condition =
345            matchingOpcodes(lookUpSwitchInstruction, patternInstruction) &&
346            matchingBranchOffsets(offset,
347                                  lookUpSwitchInstruction.defaultOffset,
348                                  ((LookUpSwitchInstruction)patternInstruction).defaultOffset) &&
349            matchingArguments(lookUpSwitchInstruction.cases,
350                              ((LookUpSwitchInstruction)patternInstruction).cases) &&
351            matchingJumpOffsets(offset,
352                                lookUpSwitchInstruction.jumpOffsets,
353                                ((LookUpSwitchInstruction)patternInstruction).jumpOffsets);
354
355        // Check if the instruction sequence is matching now.
356        checkMatch(condition,
357                   clazz,
358                   method,
359                   codeAttribute,
360                   offset,
361                   lookUpSwitchInstruction);
362    }
363
364
365    // Implementations for ConstantVisitor.
366
367    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)
368    {
369        IntegerConstant integerPatternConstant = (IntegerConstant)patternConstant;
370
371        // Compare the integer values.
372        matchingConstant = integerConstant.getValue() ==
373                           integerPatternConstant.getValue();
374    }
375
376
377    public void visitLongConstant(Clazz clazz, LongConstant longConstant)
378    {
379        LongConstant longPatternConstant = (LongConstant)patternConstant;
380
381        // Compare the long values.
382        matchingConstant = longConstant.getValue() ==
383                           longPatternConstant.getValue();
384    }
385
386
387    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant)
388    {
389        FloatConstant floatPatternConstant = (FloatConstant)patternConstant;
390
391        // Compare the float values.
392        matchingConstant = floatConstant.getValue() ==
393                           floatPatternConstant.getValue();
394    }
395
396
397    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)
398    {
399        DoubleConstant doublePatternConstant = (DoubleConstant)patternConstant;
400
401        // Compare the double values.
402        matchingConstant = doubleConstant.getValue() ==
403                           doublePatternConstant.getValue();
404    }
405
406
407    public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
408    {
409        StringConstant stringPatternConstant = (StringConstant)patternConstant;
410
411        // Check the UTF-8 constant.
412        matchingConstant =
413            matchingConstantIndices(clazz,
414                                    stringConstant.u2stringIndex,
415                                    stringPatternConstant.u2stringIndex);
416    }
417
418
419    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)
420    {
421        Utf8Constant utf8PatternConstant = (Utf8Constant)patternConstant;
422
423        // Compare the actual strings.
424        matchingConstant = utf8Constant.getString().equals(
425                           utf8PatternConstant.getString());
426    }
427
428
429    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
430    {
431        InvokeDynamicConstant invokeDynamicPatternConstant = (InvokeDynamicConstant)patternConstant;
432
433        // Check the bootstrap method and the name and type.
434        matchingConstant =
435            matchingConstantIndices(clazz,
436                                    invokeDynamicConstant.getBootstrapMethodAttributeIndex(),
437                                    invokeDynamicPatternConstant.getBootstrapMethodAttributeIndex()) &&
438            matchingConstantIndices(clazz,
439                                    invokeDynamicConstant.getNameAndTypeIndex(),
440                                    invokeDynamicPatternConstant.getNameAndTypeIndex());
441    }
442
443
444    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
445    {
446        MethodHandleConstant methodHandlePatternConstant = (MethodHandleConstant)patternConstant;
447
448        // Check the handle type and the name and type.
449        matchingConstant =
450            matchingArguments(methodHandleConstant.getReferenceKind(),
451                              methodHandlePatternConstant.getReferenceKind()) &&
452            matchingConstantIndices(clazz,
453                                    methodHandleConstant.getReferenceIndex(),
454                                    methodHandlePatternConstant.getReferenceIndex());
455    }
456
457
458    public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant)
459    {
460        RefConstant refPatternConstant = (RefConstant)patternConstant;
461
462        // Check the class and the name and type.
463        matchingConstant =
464            matchingConstantIndices(clazz,
465                                    refConstant.getClassIndex(),
466                                    refPatternConstant.getClassIndex()) &&
467            matchingConstantIndices(clazz,
468                                    refConstant.getNameAndTypeIndex(),
469                                    refPatternConstant.getNameAndTypeIndex());
470    }
471
472
473    public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
474    {
475        ClassConstant classPatternConstant = (ClassConstant)patternConstant;
476
477        // Check the class name.
478        matchingConstant =
479            matchingConstantIndices(clazz,
480                                    classConstant.u2nameIndex,
481                                    classPatternConstant.u2nameIndex);
482    }
483
484
485    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
486    {
487        MethodTypeConstant typePatternConstant = (MethodTypeConstant)patternConstant;
488
489        // Check the descriptor.
490        matchingConstant =
491            matchingConstantIndices(clazz,
492                                    methodTypeConstant.u2descriptorIndex,
493                                    typePatternConstant.u2descriptorIndex);
494    }
495
496
497    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
498    {
499        NameAndTypeConstant typePatternConstant = (NameAndTypeConstant)patternConstant;
500
501        // Check the name and the descriptor.
502        matchingConstant =
503            matchingConstantIndices(clazz,
504                                    nameAndTypeConstant.u2nameIndex,
505                                    typePatternConstant.u2nameIndex) &&
506            matchingConstantIndices(clazz,
507                                    nameAndTypeConstant.u2descriptorIndex,
508                                    typePatternConstant.u2descriptorIndex);
509    }
510
511
512    // Small utility methods.
513
514    private boolean matchingOpcodes(Instruction instruction1,
515                                    Instruction instruction2)
516    {
517        // Check the opcode.
518        return instruction1.opcode            == instruction2.opcode ||
519               instruction1.canonicalOpcode() == instruction2.opcode;
520    }
521
522
523    private boolean matchingArguments(int argument1,
524                                      int argument2)
525    {
526        int argumentIndex = argument2 - X;
527        if (argumentIndex < 0)
528        {
529            // Check the literal argument.
530            return argument1 == argument2;
531        }
532        else if (!isMatchingArgumentIndex(argumentIndex))
533        {
534            // Store the wildcard argument.
535            setMatchingArgument(argumentIndex, argument1);
536
537            return true;
538        }
539        else
540        {
541            // Check the previously stored wildcard argument.
542            return matchedArguments[argumentIndex] == argument1;
543        }
544    }
545
546
547    /**
548     * Marks the specified argument (by index) as matching the specified
549     * argument value.
550     */
551    private void setMatchingArgument(int argumentIndex,
552                                     int argument)
553    {
554        matchedArguments[argumentIndex] = argument;
555        matchedArgumentFlags |= 1 << argumentIndex;
556    }
557
558
559    /**
560     * Returns whether the specified wildcard argument (by index) has been
561     * matched.
562     */
563    private boolean isMatchingArgumentIndex(int argumentIndex)
564    {
565        return (matchedArgumentFlags & (1 << argumentIndex)) != 0;
566    }
567
568
569    private boolean matchingArguments(int[] arguments1,
570                                      int[] arguments2)
571    {
572        if (arguments1.length != arguments2.length)
573        {
574            return false;
575        }
576
577        for (int index = 0; index < arguments1.length; index++)
578        {
579            if (!matchingArguments(arguments1[index], arguments2[index]))
580            {
581                return false;
582            }
583        }
584
585        return true;
586    }
587
588
589    private boolean matchingConstantIndices(Clazz clazz,
590                                            int   constantIndex1,
591                                            int   constantIndex2)
592    {
593        if (constantIndex2 >= X)
594        {
595            // Remember that we are trying to match a constant.
596            constantFlags |= 1 << (constantIndex2 - X);
597
598            // Check the constant index.
599            return matchingArguments(constantIndex1, constantIndex2);
600        }
601        else if (!isMatchingConstantIndex(constantIndex2))
602        {
603            // Check the actual constant.
604            matchingConstant = false;
605            patternConstant  = patternConstants[constantIndex2];
606
607            if (clazz.getTag(constantIndex1) == patternConstant.getTag())
608            {
609                clazz.constantPoolEntryAccept(constantIndex1, this);
610
611                if (matchingConstant)
612                {
613                    // Store the constant index.
614                    setMatchingConstant(constantIndex2, constantIndex1);
615                }
616            }
617
618            return matchingConstant;
619        }
620        else
621        {
622            // Check a previously stored constant index.
623            return matchedConstantIndices[constantIndex2] == constantIndex1;
624        }
625    }
626
627
628    /**
629     * Marks the specified constant (by index) as matching the specified
630     * constant index value.
631     */
632    private void setMatchingConstant(int constantIndex,
633                                     int constantIndex1)
634    {
635        matchedConstantIndices[constantIndex] = constantIndex1;
636        matchedConstantFlags[constantIndex / 64] |= 1L << constantIndex;
637    }
638
639
640    /**
641     * Returns whether the specified wildcard constant has been matched.
642     */
643    private boolean isMatchingConstantIndex(int constantIndex)
644    {
645        return (matchedConstantFlags[constantIndex / 64] & (1L << constantIndex)) != 0;
646    }
647
648
649    private boolean matchingBranchOffsets(int offset,
650                                          int branchOffset1,
651                                          int branchOffset2)
652    {
653        int argumentIndex = branchOffset2 - X;
654        if (argumentIndex < 0)
655        {
656            // Check the literal argument.
657            return branchOffset1 == branchOffset2;
658        }
659        else if (!isMatchingArgumentIndex(argumentIndex))
660        {
661            // Store a wildcard argument.
662            setMatchingArgument(argumentIndex, offset + branchOffset1);
663
664            return true;
665        }
666        else
667        {
668            // Check the previously stored wildcard argument.
669            return matchedArguments[argumentIndex] == offset + branchOffset1;
670        }
671    }
672
673
674    private boolean matchingJumpOffsets(int   offset,
675                                        int[] jumpOffsets1,
676                                        int[] jumpOffsets2)
677    {
678        if (jumpOffsets1.length != jumpOffsets2.length)
679        {
680            return false;
681        }
682
683        for (int index = 0; index < jumpOffsets1.length; index++)
684        {
685            if (!matchingBranchOffsets(offset,
686                                       jumpOffsets1[index],
687                                       jumpOffsets2[index]))
688            {
689                return false;
690            }
691        }
692
693        return true;
694    }
695
696
697    private void checkMatch(boolean       condition,
698                            Clazz         clazz,
699                            Method        method,
700                            CodeAttribute codeAttribute,
701                            int           offset,
702                            Instruction   instruction)
703    {
704        if (DEBUG_MORE)
705        {
706            System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]: "+patternInstructions[patternInstructionIndex].toString(patternInstructionIndex)+(condition?"\t== ":"\t   ")+instruction.toString(offset));
707        }
708
709        // Did the instruction match?
710        if (condition)
711        {
712            // Remember the offset of the matching instruction.
713            matchedInstructionOffsets[patternInstructionIndex] = offset;
714
715            // Try to match the next instruction next time.
716            patternInstructionIndex++;
717
718            // Did we match all instructions in the sequence?
719            matching = patternInstructionIndex == patternInstructions.length;
720
721            if (matching)
722            {
723                if (DEBUG)
724                {
725                    System.out.println("InstructionSequenceMatcher: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
726                    for (int index = 0; index < patternInstructionIndex; index++)
727                    {
728                        System.out.println("    "+InstructionFactory.create(codeAttribute.code, matchedInstructionOffsets[index]).toString(matchedInstructionOffsets[index]));
729                    }
730                }
731
732                // Start matching from the first instruction again next time.
733                reset();
734            }
735        }
736        else
737        {
738            // The instruction didn't match.
739            matching = false;
740
741            // Is this a failed second instruction?
742            boolean retry = patternInstructionIndex == 1;
743
744            // Start matching from the first instruction next time.
745            reset();
746
747            // Retry a failed second instruction as a first instruction.
748            if (retry)
749            {
750                instruction.accept(clazz, method, codeAttribute, offset, this);
751            }
752        }
753    }
754}
755