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