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.optimize.evaluation; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.visitor.*; 26import proguard.classfile.constant.ClassConstant; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.instruction.*; 29import proguard.classfile.instruction.visitor.InstructionVisitor; 30import proguard.classfile.util.*; 31import proguard.classfile.visitor.*; 32import proguard.evaluation.*; 33import proguard.evaluation.value.*; 34import proguard.optimize.info.SimpleEnumMarker; 35 36/** 37 * This ClassVisitor marks enums that can't be simplified due to the way they 38 * are used in the classes that it visits. 39 * 40 * @see SimpleEnumMarker 41 * @author Eric Lafortune 42 */ 43public class SimpleEnumUseChecker 44extends SimplifiedVisitor 45implements ClassVisitor, 46 MemberVisitor, 47 AttributeVisitor, 48 InstructionVisitor, 49 ConstantVisitor, 50 ParameterVisitor 51{ 52 //* 53 private static final boolean DEBUG = false; 54 /*/ 55 private static boolean DEBUG = System.getProperty("enum") != null; 56 //*/ 57 58 private final PartialEvaluator partialEvaluator; 59 private final MemberVisitor methodCodeChecker = new AllAttributeVisitor(this); 60 private final ConstantVisitor invokedMethodChecker = new ReferencedMemberVisitor(this); 61 private final ConstantVisitor parameterChecker = new ReferencedMemberVisitor(new AllParameterVisitor(this)); 62 private final ClassVisitor complexEnumMarker = new SimpleEnumMarker(false); 63 private final ReferencedClassVisitor referencedComplexEnumMarker = new ReferencedClassVisitor(complexEnumMarker); 64 65 66 // Fields acting as parameters and return values for the visitor methods. 67 private int invocationOffset; 68 69 70 /** 71 * Creates a new SimpleEnumUseSimplifier. 72 */ 73 public SimpleEnumUseChecker() 74 { 75 this(new PartialEvaluator()); 76 } 77 78 79 /** 80 * Creates a new SimpleEnumUseChecker. 81 * @param partialEvaluator the partial evaluator that will execute the code 82 * and provide information about the results. 83 */ 84 public SimpleEnumUseChecker(PartialEvaluator partialEvaluator) 85 { 86 this.partialEvaluator = partialEvaluator; 87 } 88 89 90 // Implementations for ClassVisitor. 91 92 public void visitProgramClass(ProgramClass programClass) 93 { 94 if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) != 0) 95 { 96 // Unmark the simple enum classes in annotations. 97 programClass.methodsAccept(referencedComplexEnumMarker); 98 } 99 else 100 { 101 // Unmark the simple enum classes that are used in a complex way. 102 programClass.methodsAccept(methodCodeChecker); 103 } 104 } 105 106 107 // Implementations for AttributeVisitor. 108 109 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 110 111 112 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 113 { 114 // Evaluate the method. 115 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 116 117 int codeLength = codeAttribute.u4codeLength; 118 119 // Check all traced instructions. 120 for (int offset = 0; offset < codeLength; offset++) 121 { 122 if (partialEvaluator.isTraced(offset)) 123 { 124 Instruction instruction = InstructionFactory.create(codeAttribute.code, 125 offset); 126 127 instruction.accept(clazz, method, codeAttribute, offset, this); 128 129 // Check generalized stacks and variables at branch targets. 130 if (partialEvaluator.isBranchOrExceptionTarget(offset)) 131 { 132 checkMixedStackEntriesBefore(offset); 133 134 checkMixedVariablesBefore(offset); 135 } 136 } 137 } 138 } 139 140 141 // Implementations for InstructionVisitor. 142 143 public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) 144 { 145 switch (simpleInstruction.opcode) 146 { 147 case InstructionConstants.OP_AASTORE: 148 { 149 // Check if the instruction is storing a simple enum in a 150 // more general array. 151 if (!isPoppingSimpleEnumType(offset, 2)) 152 { 153 if (DEBUG) 154 { 155 if (isPoppingSimpleEnumType(offset)) 156 { 157 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] stores enum ["+ 158 partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] in more general array ["+ 159 partialEvaluator.getStackBefore(offset).getTop(2).referenceValue().getType()+"]"); 160 } 161 } 162 163 markPoppedComplexEnumType(offset); 164 } 165 break; 166 } 167 case InstructionConstants.OP_ARETURN: 168 { 169 // Check if the instruction is returning a simple enum as a 170 // more general type. 171 if (!isReturningSimpleEnumType(clazz, method)) 172 { 173 if (DEBUG) 174 { 175 if (isPoppingSimpleEnumType(offset)) 176 { 177 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] returns enum [" + 178 partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as more general type"); 179 } 180 } 181 182 markPoppedComplexEnumType(offset); 183 } 184 break; 185 } 186 case InstructionConstants.OP_MONITORENTER: 187 case InstructionConstants.OP_MONITOREXIT: 188 { 189 // Make sure the popped type is not a simple enum type. 190 if (DEBUG) 191 { 192 if (isPoppingSimpleEnumType(offset)) 193 { 194 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] uses enum ["+ 195 partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as monitor"); 196 } 197 } 198 199 markPoppedComplexEnumType(offset); 200 201 break; 202 } 203 } 204 } 205 206 207 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 208 { 209 } 210 211 212 public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) 213 { 214 switch (constantInstruction.opcode) 215 { 216 case InstructionConstants.OP_PUTSTATIC: 217 case InstructionConstants.OP_PUTFIELD: 218 { 219 // Check if the instruction is generalizing a simple enum to a 220 // different type. 221 invocationOffset = offset; 222 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 223 parameterChecker); 224 break; 225 } 226 case InstructionConstants.OP_INVOKEVIRTUAL: 227 { 228 // Check if the instruction is calling a simple enum. 229 String invokedMethodName = 230 clazz.getRefName(constantInstruction.constantIndex); 231 String invokedMethodType = 232 clazz.getRefType(constantInstruction.constantIndex); 233 int stackEntryIndex = 234 ClassUtil.internalMethodParameterSize(invokedMethodType); 235 if (isPoppingSimpleEnumType(offset, stackEntryIndex) && 236 !isSupportedMethod(invokedMethodName, 237 invokedMethodType)) 238 { 239 if (DEBUG) 240 { 241 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] calls ["+partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue().getType()+"."+invokedMethodName+"]"); 242 } 243 244 markPoppedComplexEnumType(offset, stackEntryIndex); 245 } 246 247 // Check if any of the parameters is generalizing a simple 248 // enum to a different type. 249 invocationOffset = offset; 250 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 251 parameterChecker); 252 break; 253 } 254 case InstructionConstants.OP_INVOKESPECIAL: 255 case InstructionConstants.OP_INVOKESTATIC: 256 case InstructionConstants.OP_INVOKEINTERFACE: 257 { 258 // Check if it is calling a method that we can't simplify. 259 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 260 invokedMethodChecker); 261 262 // Check if any of the parameters is generalizing a simple 263 // enum to a different type. 264 invocationOffset = offset; 265 clazz.constantPoolEntryAccept(constantInstruction.constantIndex, 266 parameterChecker); 267 break; 268 } 269 case InstructionConstants.OP_CHECKCAST: 270 case InstructionConstants.OP_INSTANCEOF: 271 { 272 // Check if the instruction is popping a different type. 273 if (!isPoppingExpectedType(offset, 274 clazz, 275 constantInstruction.constantIndex)) 276 { 277 if (DEBUG) 278 { 279 if (isPoppingSimpleEnumType(offset)) 280 { 281 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ 282 partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ 283 clazz.getClassName(constantInstruction.constantIndex)+"]"); 284 } 285 } 286 287 // Make sure the popped type is not a simple enum type. 288 markPoppedComplexEnumType(offset); 289 290 // Make sure the checked type is not a simple enum type. 291 // We're somewhat arbitrarily skipping casts in static 292 // methods of simple enum classes, because they do occur 293 // in values() and valueOf(String), without obstructing 294 // simplification. 295 if (!isSimpleEnum(clazz) || 296 (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 || 297 constantInstruction.opcode != InstructionConstants.OP_CHECKCAST) 298 { 299 if (DEBUG) 300 { 301 if (isSimpleEnum(((ClassConstant)((ProgramClass)clazz).getConstant(constantInstruction.constantIndex)).referencedClass)) 302 { 303 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ 304 partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ 305 clazz.getClassName(constantInstruction.constantIndex)+"]"); 306 } 307 } 308 309 markConstantComplexEnumType(clazz, constantInstruction.constantIndex); 310 } 311 } 312 break; 313 } 314 } 315 } 316 317 318 public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) 319 { 320 switch (branchInstruction.opcode) 321 { 322 case InstructionConstants.OP_IFACMPEQ: 323 case InstructionConstants.OP_IFACMPNE: 324 { 325 // Check if the instruction is comparing different types. 326 if (!isPoppingIdenticalTypes(offset, 0, 1)) 327 { 328 if (DEBUG) 329 { 330 if (isPoppingSimpleEnumType(offset, 0)) 331 { 332 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] to plain type"); 333 } 334 335 if (isPoppingSimpleEnumType(offset, 1)) 336 { 337 System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(1).referenceValue().getType()+"] to plain type"); 338 } 339 } 340 341 // Make sure the first popped type is not a simple enum type. 342 markPoppedComplexEnumType(offset, 0); 343 344 // Make sure the second popped type is not a simple enum type. 345 markPoppedComplexEnumType(offset, 1); 346 } 347 break; 348 } 349 } 350 } 351 352 353 public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) 354 { 355 } 356 357 358 // Implementations for MemberVisitor. 359 360 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} 361 362 363 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 364 { 365 if (isSimpleEnum(programClass) && 366 isUnsupportedMethod(programMethod.getName(programClass), 367 programMethod.getDescriptor(programClass))) 368 { 369 if (DEBUG) 370 { 371 System.out.println("SimpleEnumUseChecker: invocation of ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]"); 372 } 373 374 complexEnumMarker.visitProgramClass(programClass); 375 } 376 } 377 378 379 // Implementations for ParameterVisitor. 380 381 public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) 382 { 383 // Check if the parameter is passing a simple enum as a more general 384 // type. 385 int stackEntryIndex = parameterSize - parameterOffset - 1; 386 if (ClassUtil.isInternalClassType(parameterType) && 387 !isPoppingExpectedType(invocationOffset, stackEntryIndex, 388 ClassUtil.isInternalArrayType(parameterType) ? 389 parameterType : 390 ClassUtil.internalClassNameFromClassType(parameterType))) 391 { 392 if (DEBUG) 393 { 394 ReferenceValue poppedValue = 395 partialEvaluator.getStackBefore(invocationOffset).getTop(stackEntryIndex).referenceValue(); 396 if (isSimpleEnumType(poppedValue)) 397 { 398 System.out.println("SimpleEnumUseChecker: ["+poppedValue.getType()+"] "+ 399 (member instanceof Field ? 400 ("is stored as more general type ["+parameterType+"] in field ["+clazz.getName()+"."+member.getName(clazz)+"]") : 401 ("is passed as more general argument #"+parameterIndex+" ["+parameterType+"] to ["+clazz.getName()+"."+member.getName(clazz)+"]"))); 402 } 403 } 404 405 // Make sure the popped type is not a simple enum type. 406 markPoppedComplexEnumType(invocationOffset, stackEntryIndex); 407 } 408 } 409 410 411 // Small utility methods. 412 413 /** 414 * Returns whether the specified enum method is supported for simple enums. 415 */ 416 private boolean isSupportedMethod(String name, String type) 417 { 418 return 419 name.equals(ClassConstants.METHOD_NAME_ORDINAL) && 420 type.equals(ClassConstants.METHOD_TYPE_ORDINAL) || 421 422 name.equals(ClassConstants.METHOD_NAME_CLONE) && 423 type.equals(ClassConstants.METHOD_TYPE_CLONE); 424 } 425 426 427 /** 428 * Returns whether the specified enum method is unsupported for simple enums. 429 */ 430 private boolean isUnsupportedMethod(String name, String type) 431 { 432 return 433 name.equals(ClassConstants.METHOD_NAME_VALUEOF); 434 } 435 436 437 /** 438 * Unmarks simple enum classes that are mixed with incompatible reference 439 * types in the stack before the given instruction offset. 440 */ 441 private void checkMixedStackEntriesBefore(int offset) 442 { 443 TracedStack stackBefore = partialEvaluator.getStackBefore(offset); 444 445 // Check all stack entries. 446 int stackSize = stackBefore.size(); 447 448 for (int stackEntryIndex = 0; stackEntryIndex < stackSize; stackEntryIndex++) 449 { 450 // Check reference entries. 451 Value stackEntry = stackBefore.getBottom(stackEntryIndex); 452 if (stackEntry.computationalType() == Value.TYPE_REFERENCE) 453 { 454 // Check reference entries with multiple producers. 455 InstructionOffsetValue producerOffsets = 456 stackBefore.getBottomActualProducerValue(stackEntryIndex).instructionOffsetValue(); 457 458 int producerCount = producerOffsets.instructionOffsetCount(); 459 if (producerCount > 1) 460 { 461 // Is the consumed stack entry not a simple enum? 462 ReferenceValue consumedStackEntry = 463 stackEntry.referenceValue(); 464 465 if (!isSimpleEnumType(consumedStackEntry)) 466 { 467 // Check all producers. 468 for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) 469 { 470 int producerOffset = 471 producerOffsets.instructionOffset(producerIndex); 472 473 if (producerOffset >= 0) 474 { 475 if (DEBUG) 476 { 477 ReferenceValue producedValue = 478 partialEvaluator.getStackAfter(producerOffset).getTop(0).referenceValue(); 479 if (isSimpleEnumType(producedValue)) 480 { 481 System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type on stack"); 482 } 483 } 484 485 // Make sure the produced stack entry isn't a 486 // simple enum either. 487 markPushedComplexEnumType(producerOffset); 488 } 489 } 490 } 491 } 492 } 493 } 494 } 495 496 497 /** 498 * Unmarks simple enum classes that are mixed with incompatible reference 499 * types in the variables before the given instruction offset. 500 */ 501 private void checkMixedVariablesBefore(int offset) 502 { 503 TracedVariables variablesBefore = 504 partialEvaluator.getVariablesBefore(offset); 505 506 // Check all variables. 507 int variablesSize = variablesBefore.size(); 508 509 for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) 510 { 511 // Check reference variables. 512 Value variable = variablesBefore.getValue(variableIndex); 513 if (variable != null && 514 variable.computationalType() == Value.TYPE_REFERENCE) 515 { 516 // Check reference variables with multiple producers. 517 InstructionOffsetValue producerOffsets = 518 variablesBefore.getProducerValue(variableIndex).instructionOffsetValue(); 519 520 int producerCount = producerOffsets.instructionOffsetCount(); 521 if (producerCount > 1) 522 { 523 // Is the consumed variable not a simple enum? 524 ReferenceValue consumedVariable = 525 variable.referenceValue(); 526 527 if (!isSimpleEnumType(consumedVariable)) 528 { 529 // Check all producers. 530 for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) 531 { 532 int producerOffset = 533 producerOffsets.instructionOffset(producerIndex); 534 535 if (producerOffset >= 0) 536 { 537 if (DEBUG) 538 { 539 ReferenceValue producedValue = 540 partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue(); 541 if (isSimpleEnumType(producedValue)) 542 { 543 System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type in variables"); 544 } 545 } 546 547 // Make sure the stored variable entry isn't a 548 // simple enum either. 549 markStoredComplexEnumType(producerOffset, variableIndex); 550 } 551 } 552 } 553 } 554 } 555 } 556 } 557 558 559 /** 560 * Returns whether the instruction at the given offset is popping two 561 * identical reference types. 562 */ 563 private boolean isPoppingIdenticalTypes(int offset, 564 int stackEntryIndex1, 565 int stackEntryIndex2) 566 { 567 TracedStack stackBefore = partialEvaluator.getStackBefore(offset); 568 569 String type1 = 570 stackBefore.getTop(stackEntryIndex1).referenceValue().getType(); 571 String type2 = 572 stackBefore.getTop(stackEntryIndex2).referenceValue().getType(); 573 574 return type1 == null ? type2 == null : type1.equals(type2); 575 } 576 577 578 /** 579 * Returns whether the instruction at the given offset is popping exactly 580 * the reference type of the specified class constant. 581 */ 582 private boolean isPoppingExpectedType(int offset, 583 Clazz clazz, 584 int constantIndex) 585 { 586 return isPoppingExpectedType(offset, 0, clazz, constantIndex); 587 } 588 589 590 /** 591 * Returns whether the instruction at the given offset is popping exactly 592 * the reference type of the specified class constant. 593 */ 594 private boolean isPoppingExpectedType(int offset, 595 int stackEntryIndex, 596 Clazz clazz, 597 int constantIndex) 598 { 599 return isPoppingExpectedType(offset, 600 stackEntryIndex, 601 clazz.getClassName(constantIndex)); 602 } 603 604 605 /** 606 * Returns whether the instruction at the given offset is popping exactly 607 * the given reference type. 608 */ 609 private boolean isPoppingExpectedType(int offset, 610 int stackEntryIndex, 611 String expectedType) 612 { 613 TracedStack stackBefore = partialEvaluator.getStackBefore(offset); 614 615 String poppedType = 616 stackBefore.getTop(stackEntryIndex).referenceValue().getType(); 617 618 return expectedType.equals(poppedType); 619 } 620 621 622 /** 623 * Returns whether the given method is returning a simple enum type. 624 * This includes simple enum arrays. 625 */ 626 private boolean isReturningSimpleEnumType(Clazz clazz, Method method) 627 { 628 String descriptor = method.getDescriptor(clazz); 629 String returnType = ClassUtil.internalMethodReturnType(descriptor); 630 631 if (ClassUtil.isInternalClassType(returnType)) 632 { 633 Clazz[] referencedClasses = 634 ((ProgramMethod)method).referencedClasses; 635 636 if (referencedClasses != null) 637 { 638 int returnedClassIndex = 639 new DescriptorClassEnumeration(descriptor).classCount() - 1; 640 641 Clazz returnedClass = referencedClasses[returnedClassIndex]; 642 643 return isSimpleEnum(returnedClass); 644 } 645 } 646 647 return false; 648 } 649 650 651 /** 652 * Returns whether the instruction at the given offset is popping a type 653 * with a simple enum class. This includes simple enum arrays. 654 */ 655 private boolean isPoppingSimpleEnumType(int offset) 656 { 657 return isPoppingSimpleEnumType(offset, 0); 658 } 659 660 661 /** 662 * Returns whether the instruction at the given offset is popping a type 663 * with a simple enum class. This includes simple enum arrays. 664 */ 665 private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) 666 { 667 ReferenceValue referenceValue = 668 partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); 669 670 return isSimpleEnumType(referenceValue); 671 } 672 673 674 /** 675 * Returns whether the given value is a simple enum type. This includes 676 * simple enum arrays. 677 */ 678 private boolean isSimpleEnumType(ReferenceValue referenceValue) 679 { 680 return isSimpleEnum(referenceValue.getReferencedClass()); 681 } 682 683 684 /** 685 * Returns whether the given class is not null and a simple enum class. 686 */ 687 private boolean isSimpleEnum(Clazz clazz) 688 { 689 return clazz != null && 690 SimpleEnumMarker.isSimpleEnum(clazz); 691 } 692 693 694 /** 695 * Marks the enum class of the popped type as complex. 696 */ 697 private void markConstantComplexEnumType(Clazz clazz, int constantIndex) 698 { 699 clazz.constantPoolEntryAccept(constantIndex, 700 referencedComplexEnumMarker); 701 } 702 703 704 /** 705 * Marks the enum class of the popped type as complex. 706 */ 707 private void markPoppedComplexEnumType(int offset) 708 { 709 markPoppedComplexEnumType(offset, 0); 710 } 711 712 713 /** 714 * Marks the enum class of the specified popped type as complex. 715 */ 716 private void markPoppedComplexEnumType(int offset, int stackEntryIndex) 717 { 718 ReferenceValue referenceValue = 719 partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); 720 721 markComplexEnumType(referenceValue); 722 } 723 724 725 /** 726 * Marks the enum class of the specified pushed type as complex. 727 */ 728 private void markPushedComplexEnumType(int offset) 729 { 730 ReferenceValue referenceValue = 731 partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); 732 733 markComplexEnumType(referenceValue); 734 } 735 736 737 /** 738 * Marks the enum class of the specified stored type as complex. 739 */ 740 private void markStoredComplexEnumType(int offset, int variableIndex) 741 { 742 ReferenceValue referenceValue = 743 partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).referenceValue(); 744 745 markComplexEnumType(referenceValue); 746 } 747 748 749 /** 750 * Marks the enum class of the specified value as complex. 751 */ 752 private void markComplexEnumType(ReferenceValue referenceValue) 753 { 754 Clazz clazz = referenceValue.getReferencedClass(); 755 if (clazz != null) 756 { 757 clazz.accept(complexEnumMarker); 758 } 759 } 760} 761