All Downloads are FREE. Search and download functionalities are using the official Maven repository.

proguard.classfile.util.DynamicMemberReferenceInitializer Maven / Gradle / Ivy

There is a newer version: 6.3.0beta1
Show newest version
/*
 * ProGuard -- shrinking, optimization, obfuscation, and preverification
 *             of Java bytecode.
 *
 * Copyright (c) 2002-2018 GuardSquare NV
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package proguard.classfile.util;

import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.*;
import proguard.classfile.instruction.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.visitor.*;
import proguard.util.StringMatcher;

/**
 * This AttributeVisitor initializes any constant class member references of all
 * code that it visits. It currently handles invocations of
 *     Class#get[Declared]{Field,Constructor,Method} and
 *     Atomic{Integer,Long,Reference}FieldUpdater.newUpdater
 * with constant string arguments. It lets the corresponding string constants
 * refer to their class members in the program class pool or in the library
 * class pool. It may create new string constants and update the code, in order
 * to avoid clashes between identically named class members.
 * 

* The class hierarchy and references must be initialized before using this * visitor. * * @see ClassSuperHierarchyInitializer * @see ClassReferenceInitializer * * @author Eric Lafortune */ public class DynamicMemberReferenceInitializer extends SimplifiedVisitor implements AttributeVisitor, InstructionVisitor, ConstantVisitor, MemberVisitor { /* private static boolean DEBUG = true; /*/ private static final boolean DEBUG = false; //*/ private static final int CLASS_INDEX = InstructionSequenceMatcher.A; private static final int MEMBER_NAME_INDEX = InstructionSequenceMatcher.B; private static final int MEMBER_TYPE_INDEX = InstructionSequenceMatcher.C; private final ClassPool programClassPool; private final ClassPool libraryClassPool; private final WarningPrinter notePrinter; private final StringMatcher noteFieldExceptionMatcher; private final StringMatcher noteMethodExceptionMatcher; // Retrieving fields or methods from known, constant classes. private final InstructionSequenceMatcher knownItegerUpdaterMatcher; private final InstructionSequenceMatcher knownLongUpdaterMatcher; private final InstructionSequenceMatcher knownReferenceUpdaterMatcher; // Retrieving fields or methods from unknown classes. private final InstructionSequenceMatcher unknownIntegerUpdaterMatcher; private final InstructionSequenceMatcher unknownLongUpdaterMatcher; private final InstructionSequenceMatcher unknownReferenceUpdaterMatcher; private final MyDynamicMemberFinder dynamicMemberFinder = new MyDynamicMemberFinder(); private final MemberFinder memberFinder = new MemberFinder(true); private final MemberFinder declaredMemberFinder = new MemberFinder(false); private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); // Fields acting as parameters for the visitors. private Clazz referencedClass; /** * Creates a new DynamicMemberReferenceInitializer. */ public DynamicMemberReferenceInitializer(ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter notePrinter, StringMatcher noteFieldExceptionMatcher, StringMatcher noteMethodExceptionMatcher) { this.programClassPool = programClassPool; this.libraryClassPool = libraryClassPool; this.notePrinter = notePrinter; this.noteFieldExceptionMatcher = noteFieldExceptionMatcher; this.noteMethodExceptionMatcher = noteMethodExceptionMatcher; // Create the instruction sequences and matchers. InstructionSequenceBuilder builder = new InstructionSequenceBuilder(programClassPool, libraryClassPool); // AtomicIntegerFieldUpdater.newUpdater(A.class, "someField"). Instruction[] knownItegerUpdaterInstructions = builder .ldc_(CLASS_INDEX) .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_INTEGER_UPDATER) .instructions(); // AtomicLongFieldUpdater.newUpdater(A.class, "someField"). Instruction[] knownLongUpdaterInstructions = builder .ldc_(CLASS_INDEX) .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_LONG_UPDATER) .instructions(); // AtomicReferenceFieldUpdater.newUpdater(A.class, B.class, "someField"). Instruction[] knownReferenceUpdaterInstructions = builder .ldc_(CLASS_INDEX) .ldc_(MEMBER_TYPE_INDEX) .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_REFERENCE_UPDATER) .instructions(); // AtomicIntegerFieldUpdater.newUpdater(..., "someField"). Instruction[] unknownIntegerUpdaterInstructions = builder .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_INTEGER_UPDATER) .instructions(); // AtomicLongFieldUpdater.newUpdater(..., "someField"). Instruction[] unknownLongUpdaterInstructions = builder .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_LONG_UPDATER) .instructions(); // AtomicReferenceFieldUpdater.newUpdater(..., "someField"). final Instruction[] unknownReferenceUpdaterInstructions = builder .ldc_(MEMBER_NAME_INDEX) .invokestatic(ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER, ClassConstants.METHOD_NAME_NEW_UPDATER, ClassConstants.METHOD_TYPE_NEW_REFERENCE_UPDATER) .instructions(); Constant[] constants = builder.constants(); knownItegerUpdaterMatcher = new InstructionSequenceMatcher(constants, knownItegerUpdaterInstructions); knownLongUpdaterMatcher = new InstructionSequenceMatcher(constants, knownLongUpdaterInstructions); knownReferenceUpdaterMatcher = new InstructionSequenceMatcher(constants, knownReferenceUpdaterInstructions); unknownIntegerUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownIntegerUpdaterInstructions); unknownLongUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownLongUpdaterInstructions); unknownReferenceUpdaterMatcher = new InstructionSequenceMatcher(constants, unknownReferenceUpdaterInstructions); } // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { if (DEBUG) { System.out.println("DynamicMemberReferenceInitializer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); } // Set up the code attribute editor. codeAttributeEditor.reset(codeAttribute.u4codeLength); // Find the dynamic class member references. codeAttribute.instructionsAccept(clazz, method, this); // Apply any changes to the code. codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); } // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { // Try to match get[Declared]{Field,Constructor,Method} constructs. instruction.accept(clazz, method, codeAttribute, offset, dynamicMemberFinder); // Try to match the AtomicIntegerFieldUpdater.newUpdater( // SomeClass.class, "someField") construct. matchGetMember(clazz, method, codeAttribute, offset, instruction, knownItegerUpdaterMatcher, unknownIntegerUpdaterMatcher, true, false, false, "" + ClassConstants.TYPE_INT); // Try to match the AtomicLongFieldUpdater.newUpdater( // SomeClass.class, "someField") construct. matchGetMember(clazz, method, codeAttribute, offset, instruction, knownLongUpdaterMatcher, unknownLongUpdaterMatcher, true, false, false, "" + ClassConstants.TYPE_LONG); // Try to match the AtomicReferenceFieldUpdater.newUpdater( // SomeClass.class, SomeClass.class, "someField") construct. matchGetMember(clazz, method, codeAttribute, offset, instruction, knownReferenceUpdaterMatcher, unknownReferenceUpdaterMatcher, true, false, false, null); } /** * Tries to match the next instruction and fills out the string constant * or prints out a note accordingly. */ private void matchGetMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction, InstructionSequenceMatcher constantSequenceMatcher, InstructionSequenceMatcher variableSequenceMatcher, boolean isField, boolean isConstructor, boolean isDeclared, String memberDescriptor) { if (constantSequenceMatcher != null) { // Try to match the next instruction in the constant sequence. instruction.accept(clazz, method, codeAttribute, offset, constantSequenceMatcher); // Did we find a match to fill out the string constant? if (constantSequenceMatcher.isMatching()) { // Retrieve the offset of the instruction that loads the member // name. It's currently always the last but one instruction. int memberNameInstructionOffset = constantSequenceMatcher.matchedInstructionOffset( constantSequenceMatcher.instructionCount() - 2); // Get the member's class. int classIndex = constantSequenceMatcher.matchedConstantIndex(CLASS_INDEX); clazz.constantPoolEntryAccept(classIndex, this); if (referencedClass != null) { // Get the field's type, if applicable. int typeClassIndex = constantSequenceMatcher.matchedConstantIndex(MEMBER_TYPE_INDEX); if (typeClassIndex > 0) { memberDescriptor = ClassUtil.internalTypeFromClassName(clazz.getClassName(typeClassIndex)); } // Get the member's name. int memberNameIndex = constantSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); String memberName = clazz.getStringString(memberNameIndex); // Create a new string constant and update the instruction. initializeDynamicMemberReference(clazz, memberNameInstructionOffset, referencedClass, memberName, memberDescriptor, isField, isConstructor, isDeclared); } // Don't look for the dynamic construct. variableSequenceMatcher.reset(); } } // Try to match the next instruction in the variable sequence. instruction.accept(clazz, method, codeAttribute, offset, variableSequenceMatcher); // Did we find a match to print out a note? if (variableSequenceMatcher.isMatching()) { int memberNameIndex = variableSequenceMatcher.matchedConstantIndex(MEMBER_NAME_INDEX); String memberName = clazz.getStringString(memberNameIndex); // Print out a note about the dynamic invocation. printDynamicMemberAccessNote(clazz, memberName, memberDescriptor, isField, isConstructor, isDeclared); } } // Implementations for ConstantVisitor. public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { // Remember the referenced class. referencedClass = ClassUtil.isInternalArrayType(classConstant.getName(clazz)) ? null : classConstant.referencedClass; } // Small utility methods. /** * Creates a new string constant for the specified referenced class member, * and updates the instruction that loads it. */ private void initializeDynamicMemberReference(Clazz clazz, int memberNameInstructionOffset, Clazz referencedClass, String memberName, String memberDescriptor, boolean isField, boolean isConstructor, boolean isDeclared) { // See if we can find the referenced class member locally, or // somewhere in the hierarchy. MemberFinder referencedMemberFinder = isDeclared ? declaredMemberFinder : memberFinder; Member referencedMember = referencedMemberFinder.findMember(referencedClass, memberName, memberDescriptor, isField); if (DEBUG) { System.out.println("DynamicMemberReferenceInitializer: ["+clazz.getName()+"] matched string ["+memberName+"]: in ["+referencedClass+"] -> ["+referencedMember+"]"); } if (referencedMember != null) { if (!isDeclared) { referencedClass = referencedMemberFinder.correspondingClass(); } // Update the string constant. //stringConstant.referencedMember = referencedMember; //stringConstant.referencedClass = referencedClass; // Create a new string constant with the found references. int stringConstantIndex = new ConstantPoolEditor((ProgramClass)clazz).addStringConstant(memberName, referencedClass, referencedMember); // Update the instruction. codeAttributeEditor.replaceInstruction(memberNameInstructionOffset, new ConstantInstruction(InstructionConstants.OP_LDC, stringConstantIndex)); } } /** * Prints out a note on the matched dynamic invocation of a constructor. */ private void printDynamicConstructorAccessNote(Clazz clazz, Clazz referencedClass, String memberDescriptor, boolean isDeclared) { // Print out a note about the dynamic invocation. if (notePrinter != null && notePrinter.accepts(clazz.getName())) { // Is the class member name in the list of exceptions? if (noteMethodExceptionMatcher == null || !noteMethodExceptionMatcher.matches(ClassConstants.METHOD_NAME_INIT)) { // Print out the actual note. notePrinter.print(clazz.getName(), "Note: " + ClassUtil.externalClassName(clazz.getName()) + " retrieves a " + (isDeclared ? "declared " : "") + "constructor '" + ClassConstants.METHOD_NAME_INIT + JavaConstants.METHOD_ARGUMENTS_OPEN + ClassUtil.externalMethodArguments(memberDescriptor) + JavaConstants.METHOD_ARGUMENTS_CLOSE + "' dynamically"); // Print out notes about potential candidates. ClassVisitor classVisitor = new AllMethodVisitor( new MemberNameFilter(ClassConstants.METHOD_NAME_INIT, new MemberDescriptorFilter(memberDescriptor, this))); if (referencedClass != null) { referencedClass.hierarchyAccept(true, !isDeclared, false, false, classVisitor); } else { programClassPool.classesAcceptAlphabetically(classVisitor); libraryClassPool.classesAcceptAlphabetically(classVisitor); } } } } /** * Prints out a note on the matched dynamic access to a class member. */ private void printDynamicMemberAccessNote(Clazz clazz, String memberName, String memberDescriptor, boolean isField, boolean isConstructor, boolean isDeclared) { // Print out a note about the dynamic invocation. if (notePrinter != null && notePrinter.accepts(clazz.getName())) { // Is the class member name in the list of exceptions? StringMatcher noteExceptionMatcher = isField ? noteFieldExceptionMatcher : noteMethodExceptionMatcher; if (noteExceptionMatcher == null || !noteExceptionMatcher.matches(memberName)) { // Compose the external member name and partial descriptor. String externalMemberDescription = memberName; if (!isField) { externalMemberDescription += JavaConstants.METHOD_ARGUMENTS_OPEN + ClassUtil.externalMethodArguments(memberDescriptor) + JavaConstants.METHOD_ARGUMENTS_CLOSE; } // Print out the actual note. notePrinter.print(clazz.getName(), "Note: " + ClassUtil.externalClassName(clazz.getName()) + " accesses a " + (isDeclared ? "declared " : "") + (isField ? "field" : isConstructor ? "constructor" : "method") + " '" + externalMemberDescription + "' dynamically"); // Print out notes about potential candidates. ClassVisitor classVisitor; if (isField) { classVisitor = memberDescriptor == null ? new AllFieldVisitor( new MemberNameFilter(memberName, this)) : new AllFieldVisitor( new MemberNameFilter(memberName, new MemberDescriptorFilter(memberDescriptor, this))); } else { classVisitor = new AllMethodVisitor( new MemberNameFilter(memberName, new MemberDescriptorFilter(memberDescriptor, this))); } programClassPool.classesAcceptAlphabetically(classVisitor); libraryClassPool.classesAcceptAlphabetically(classVisitor); } } } // Implementations for MemberVisitor. public void visitProgramField(ProgramClass programClass, ProgramField programField) { if (notePrinter.accepts(programClass.getName())) { System.out.println(" Maybe this is program field '" + ClassUtil.externalFullClassDescription(0, programClass.getName()) + " { " + ClassUtil.externalFullFieldDescription(0, programField.getName(programClass), programField.getDescriptor(programClass)) + "; }'"); } } public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { if (notePrinter.accepts(programClass.getName())) { System.out.println(" Maybe this is program method '" + ClassUtil.externalFullClassDescription(0, programClass.getName()) + " { " + ClassUtil.externalFullMethodDescription(programClass.getName(), 0, programMethod.getName(programClass), programMethod.getDescriptor(programClass)) + "; }'"); } } public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) { if (notePrinter.accepts(libraryClass.getName())) { System.out.println(" Maybe this is library field '" + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + " { " + ClassUtil.externalFullFieldDescription(0, libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass)) + "; }'"); } } public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { if (notePrinter.accepts(libraryClass.getName())) { System.out.println(" Maybe this is library method '" + ClassUtil.externalFullClassDescription(0, libraryClass.getName()) + " { " + ClassUtil.externalFullMethodDescription(libraryClass.getName(), 0, libraryMethod.getName(libraryClass), libraryMethod.getDescriptor(libraryClass)) + "; }'"); } } /** * This InstructionVisitor finds get[Declared]{Field,Constructor,Method} * constructs with constant arguments. It then makes sure the class member * name strings point to the class members, or it prints out notes about the * possible alternatives. */ private class MyDynamicMemberFinder extends SimplifiedVisitor implements InstructionVisitor, ConstantVisitor { private static final int LABEL_START = 0; // ldc SomeClass.class private static final int LABEL_LOAD_MEMBER_NAME = 1; // [ ldc "someMethod"/"someField" ] private static final int LABEL_LOAD_CLASS_ARRAY_SIZE = 2; // [ sipush #someParameterCount private static final int LABEL_CREATE_CLASS_ARRAY = 3; // anewarray java/lang/Class private static final int LABEL_DUP_CLASS_ARRAY = 4; // [ dup private static final int LABEL_LOAD_PARAMETER_INDEX = 5; // sipush #someParameterIndex private static final int LABEL_LOAD_PARAMETER_TYPE = 6; // ldc SomeParameterClass.class / getstatic java/lang/SomePrimitive.TYPE private static final int LABEL_STORE_PARAMETER = 7; // aastore ]* ] / aconst_null private static final int LABEL_GET_MEMBER = 8; // invokevirtual java/lang/Class.getField/getConstructor/getMethod private int label; private int instructionOffset; private int memberNameInstructionOffset; private Clazz referencedClass; private String memberName; private int parameterCount; private int parameterIndex; private StringBuffer parameterTypes = new StringBuffer(); public void reset() { label = LABEL_START; referencedClass = null; memberName = null; parameterTypes.setLength(0); } // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { if (DEBUG) { System.out.println("Label ["+label+"] A "+instruction.toString(offset)); } reset(); } public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) { if (DEBUG) { System.out.println("Label ["+label+"] S "+simpleInstruction.toString(offset)); } int transition = label | simpleInstruction.canonicalOpcode() << 8; switch (transition) { case LABEL_START | InstructionConstants.OP_ICONST_0 << 8: case LABEL_LOAD_MEMBER_NAME | InstructionConstants.OP_ICONST_0 << 8: case LABEL_CREATE_CLASS_ARRAY | InstructionConstants.OP_ICONST_0 << 8: case LABEL_DUP_CLASS_ARRAY | InstructionConstants.OP_ICONST_0 << 8: case LABEL_LOAD_PARAMETER_TYPE | InstructionConstants.OP_ICONST_0 << 8: case LABEL_STORE_PARAMETER | InstructionConstants.OP_ICONST_0 << 8: case LABEL_GET_MEMBER | InstructionConstants.OP_ICONST_0 << 8: // This could be the start of creating a class array. reset(); parameterCount = simpleInstruction.constant; label = LABEL_CREATE_CLASS_ARRAY; break; case LABEL_LOAD_CLASS_ARRAY_SIZE | InstructionConstants.OP_ICONST_0 << 8: parameterCount = simpleInstruction.constant; label = LABEL_CREATE_CLASS_ARRAY; break; case LABEL_LOAD_CLASS_ARRAY_SIZE | InstructionConstants.OP_ACONST_NULL << 8: parameterCount = 0; label = LABEL_GET_MEMBER; break; case LABEL_DUP_CLASS_ARRAY | InstructionConstants.OP_DUP << 8: label = LABEL_LOAD_PARAMETER_INDEX; break; case LABEL_LOAD_PARAMETER_INDEX | InstructionConstants.OP_ICONST_0 << 8: // Is it pushing the expected parameter index? if (parameterIndex == simpleInstruction.constant) { label = LABEL_LOAD_PARAMETER_TYPE; } else { // This could be the start of creating a class array. reset(); parameterCount = simpleInstruction.constant; label = LABEL_CREATE_CLASS_ARRAY; } break; case LABEL_STORE_PARAMETER | InstructionConstants.OP_AASTORE << 8: // Are we still expecting more parameters? label = ++parameterIndex < parameterCount ? LABEL_DUP_CLASS_ARRAY : LABEL_GET_MEMBER; break; default: reset(); break; } } public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { if (DEBUG) { System.out.println("Label ["+label+"] C "+constantInstruction.toString(offset)); } // Let the constant figure out the transition. switch (constantInstruction.canonicalOpcode()) { case InstructionConstants.OP_LDC: instructionOffset = offset; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case InstructionConstants.OP_GETSTATIC: case InstructionConstants.OP_INVOKEVIRTUAL: clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case InstructionConstants.OP_ANEWARRAY: if (label == LABEL_CREATE_CLASS_ARRAY) { clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); } else { reset(); } break; default: reset(); break; } } // Implementations for ConstantVisitor. public void visitAnyConstant(Clazz clazz, Constant constant) { reset(); } public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { // Argument of ldc or anewarray instruction. switch (label) { case LABEL_START: referencedClass = classConstant.referencedClass; label = LABEL_LOAD_MEMBER_NAME; break; case LABEL_CREATE_CLASS_ARRAY: if (classConstant.getName(clazz).equals(ClassConstants.NAME_JAVA_LANG_CLASS)) { parameterIndex = 0; label = parameterCount > 0 ? LABEL_DUP_CLASS_ARRAY : LABEL_GET_MEMBER; } else { referencedClass = classConstant.referencedClass; label = LABEL_LOAD_MEMBER_NAME; } break; case LABEL_LOAD_PARAMETER_TYPE: String parameterType = ClassUtil.internalTypeFromClassType(classConstant.getName(clazz)); parameterTypes.append(parameterType); label = LABEL_STORE_PARAMETER; break; default: // For other states, we'll treat this as a potential // initial class name. referencedClass = classConstant.referencedClass; label = LABEL_LOAD_MEMBER_NAME; break; } } public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { // Argument of ldc instruction. switch (label) { case LABEL_LOAD_MEMBER_NAME: break; default: // For other states, we'll treat this as a potential // initial method name, without a known class. referencedClass = null; break; } // Whatever state, we'll treat this as a potential method name. memberNameInstructionOffset = instructionOffset; memberName = stringConstant.getString(clazz); label = LABEL_LOAD_CLASS_ARRAY_SIZE; } public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { // Argument of getstatic instruction. switch (label) { case LABEL_LOAD_PARAMETER_TYPE: String className = fieldrefConstant.getClassName(clazz); String fieldName = fieldrefConstant.getName(clazz); String fieldType = fieldrefConstant.getType(clazz); if (className.startsWith(ClassConstants.PACKAGE_JAVA_LANG) && fieldName.equals(ClassConstants.FIELD_NAME_TYPE) && fieldType.equals(ClassConstants.FIELD_TYPE_TYPE)) { char parameterType = ClassUtil.internalPrimitiveTypeFromNumericClassName(className); parameterTypes.append(parameterType); label = LABEL_STORE_PARAMETER; } else { reset(); } break; default: reset(); break; } } public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) { // Argument of invokevirtual instruction. String className = methodrefConstant.getClassName(clazz); if (className.equals(ClassConstants.NAME_JAVA_LANG_CLASS)) { String methodName = methodrefConstant.getName(clazz); String methodType = methodrefConstant.getType(clazz); if (label == LABEL_LOAD_CLASS_ARRAY_SIZE && methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_FIELD) && memberName != null) { if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_FIELD)) { resolveMemberString(clazz, true, false, false); } else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_FIELD)) { resolveMemberString(clazz, true, false, true); } else { reset(); } } else if (label == LABEL_GET_MEMBER && methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_CONSTRUCTOR)) { if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_CONSTRUCTOR)) { resolveMemberString(clazz, false, true, false); } else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_CONSTRUCTOR)) { resolveMemberString(clazz, false, true, true); } else { reset(); } } else if (label == LABEL_GET_MEMBER && methodType.equals(ClassConstants.METHOD_TYPE_CLASS_GET_METHOD) && memberName != null) { if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_METHOD)) { resolveMemberString(clazz, false, false, false); } else if (methodName.equals(ClassConstants.METHOD_NAME_CLASS_GET_DECLARED_METHOD)) { resolveMemberString(clazz, false, false, true); } else { reset(); } } else { reset(); } } else { reset(); } } /** * Links the referenced class member in the string, or prints out * notes about the possible alternatives. */ private void resolveMemberString(Clazz clazz, boolean isField, boolean isConstructor, boolean isDeclared) { String memberDescriptor = isField ? null : ClassConstants.METHOD_ARGUMENTS_OPEN + parameterTypes.toString() + ClassConstants.METHOD_ARGUMENTS_CLOSE + "L***;"; if (DEBUG) { System.out.println("DynamicMemberReferenceInitializer: found member access"); System.out.println(" isField = "+isField); System.out.println(" isConstructor = "+isConstructor); System.out.println(" isDeclared = "+isDeclared); System.out.println(" referenced class = "+(referencedClass == null ? "(none)" : "["+referencedClass.getName()+"]")); System.out.println(" member name = "+(memberName == null ? "(none)" : "["+memberName+"]")); System.out.println(" member descriptor = "+(memberDescriptor == null ? "(none)" : "["+memberDescriptor+"]")); } if (referencedClass != null) { if (isConstructor) { // We currently can't fill out some reference to a // constructor. Just print out notes instead. printDynamicConstructorAccessNote(clazz, referencedClass, memberDescriptor, isDeclared); } else { // Create a new string constant and update the instruction. initializeDynamicMemberReference(clazz, memberNameInstructionOffset, referencedClass, memberName, memberDescriptor, isField, isConstructor, isDeclared); } } else { // Print out notes about the method in some unknown class. printDynamicMemberAccessNote(clazz, isConstructor ? ClassConstants.METHOD_NAME_INIT : memberName, memberDescriptor, isField, isConstructor, isDeclared); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy