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

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

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2023 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package proguard.classfile.util;

import static proguard.classfile.ClassConstants.NAME_JAVA_LANG_CLASS;
import static proguard.classfile.ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER;
import static proguard.classfile.ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER;
import static proguard.classfile.ClassConstants.NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER;

import proguard.classfile.ClassConstants;
import proguard.classfile.ClassPool;
import proguard.classfile.Clazz;
import proguard.classfile.JavaTypeConstants;
import proguard.classfile.LibraryClass;
import proguard.classfile.LibraryField;
import proguard.classfile.LibraryMethod;
import proguard.classfile.Member;
import proguard.classfile.Method;
import proguard.classfile.ProgramClass;
import proguard.classfile.ProgramField;
import proguard.classfile.ProgramMethod;
import proguard.classfile.TypeConstants;
import proguard.classfile.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.visitor.AllAttributeVisitor;
import proguard.classfile.attribute.visitor.AttributeNameFilter;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.FieldrefConstant;
import proguard.classfile.constant.MethodrefConstant;
import proguard.classfile.constant.StringConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.editor.ConstantPoolEditor;
import proguard.classfile.editor.InstructionSequenceBuilder;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.SimpleInstruction;
import proguard.classfile.instruction.VariableInstruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.visitor.AllFieldVisitor;
import proguard.classfile.visitor.AllMethodVisitor;
import proguard.classfile.visitor.ClassConstantClassFilter;
import proguard.classfile.visitor.ClassVisitor;
import proguard.classfile.visitor.MemberDescriptorFilter;
import proguard.classfile.visitor.MemberNameFilter;
import proguard.classfile.visitor.MemberVisitor;
import proguard.util.CollectionMatcher;
import proguard.util.StringMatcher;

/**
 * This {@link 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. * *

It's more efficient to use as a {@link ClassVisitor} than an {@link InstructionVisitor}. * * @see ClassSuperHierarchyInitializer * @see ClassReferenceInitializer * @author Eric Lafortune */ public class DynamicMemberReferenceInitializer implements ClassVisitor, AttributeVisitor, InstructionVisitor, ConstantVisitor, MemberVisitor { // * private static final boolean DEBUG = false; /*/ private static boolean DEBUG = System.getProperty("dmri") != null; //*/ 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 knownIntegerUpdaterMatcher; 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(); private final MemberVisitor extraMemberVisitor; // Used to prefilter classes to avoid visiting all instructions in all classes. private final ClassConstantClassFilter classConstantClassFilter = new ClassConstantClassFilter( new CollectionMatcher( NAME_JAVA_LANG_CLASS, NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_INTEGER_FIELD_UPDATER, NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_LONG_FIELD_UPDATER, NAME_JAVA_UTIL_CONCURRENT_ATOMIC_ATOMIC_REFERENCE_FIELD_UPDATER), new AllMethodVisitor( new AllAttributeVisitor(new AttributeNameFilter(Attribute.CODE, this)))); // 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, libraryClassPool, notePrinter, noteFieldExceptionMatcher, noteMethodExceptionMatcher, null); } /** Creates a new DynamicMemberReferenceInitializer. */ public DynamicMemberReferenceInitializer( ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter notePrinter, StringMatcher noteFieldExceptionMatcher, StringMatcher noteMethodExceptionMatcher, MemberVisitor extraMemberVisitor) { this.programClassPool = programClassPool; this.libraryClassPool = libraryClassPool; this.notePrinter = notePrinter; this.noteFieldExceptionMatcher = noteFieldExceptionMatcher; this.noteMethodExceptionMatcher = noteMethodExceptionMatcher; this.extraMemberVisitor = extraMemberVisitor; // Create the instruction sequences and matchers. InstructionSequenceBuilder builder = new InstructionSequenceBuilder(programClassPool, libraryClassPool); // AtomicIntegerFieldUpdater.newUpdater(A.class, "someField"). Instruction[] knownIntegerUpdaterInstructions = 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(); knownIntegerUpdaterMatcher = new InstructionSequenceMatcher(constants, knownIntegerUpdaterInstructions); 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); } @Override public void visitAnyClass(Clazz clazz) { // Only ProgramClasses contain code. } @Override public void visitProgramClass(ProgramClass programClass) { programClass.accept(classConstantClassFilter); } // Implementations for AttributeVisitor. @Override public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @Override 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. @Override 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, knownIntegerUpdaterMatcher, unknownIntegerUpdaterMatcher, true, false, false, "" + TypeConstants.INT); // Try to match the AtomicLongFieldUpdater.newUpdater( // SomeClass.class, "someField") construct. matchGetMember( clazz, method, codeAttribute, offset, instruction, knownLongUpdaterMatcher, unknownLongUpdaterMatcher, true, false, false, "" + TypeConstants.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(Instruction.OP_LDC, stringConstantIndex)); if (extraMemberVisitor != null) { referencedMember.accept(referencedClass, extraMemberVisitor); } } } /** 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 + JavaTypeConstants.METHOD_ARGUMENTS_OPEN + ClassUtil.externalMethodArguments(memberDescriptor) + JavaTypeConstants.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 += JavaTypeConstants.METHOD_ARGUMENTS_OPEN + ClassUtil.externalMethodArguments(memberDescriptor) + JavaTypeConstants.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) { notePrinter.note( programClass.getName(), " 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) { notePrinter.note( programClass.getName(), " 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) { notePrinter.note( libraryClass.getName(), " 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) { notePrinter.note( libraryClass.getName(), " 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 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 final 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(clazz, offset)); } reset(); } @Override public void visitVariableInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) { if (DEBUG) { System.out.println( "Label [" + label + "] V " + variableInstruction.toString(clazz, offset)); } switch (variableInstruction.canonicalOpcode()) { case Instruction.OP_ASTORE: case Instruction.OP_ALOAD: // Ignore astore/aload instructions. break; default: reset(); break; } } public void visitSimpleInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) { if (DEBUG) { System.out.println("Label [" + label + "] S " + simpleInstruction.toString(clazz, offset)); } int transition = label | simpleInstruction.canonicalOpcode() << 8; switch (transition) { case LABEL_START | Instruction.OP_ICONST_0 << 8: case LABEL_LOAD_MEMBER_NAME | Instruction.OP_ICONST_0 << 8: case LABEL_CREATE_CLASS_ARRAY | Instruction.OP_ICONST_0 << 8: case LABEL_DUP_CLASS_ARRAY | Instruction.OP_ICONST_0 << 8: case LABEL_LOAD_PARAMETER_TYPE | Instruction.OP_ICONST_0 << 8: case LABEL_STORE_PARAMETER | Instruction.OP_ICONST_0 << 8: case LABEL_GET_MEMBER | Instruction.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 | Instruction.OP_ICONST_0 << 8: parameterCount = simpleInstruction.constant; label = LABEL_CREATE_CLASS_ARRAY; break; case LABEL_LOAD_CLASS_ARRAY_SIZE | Instruction.OP_ACONST_NULL << 8: parameterCount = 0; label = LABEL_GET_MEMBER; break; case LABEL_DUP_CLASS_ARRAY | Instruction.OP_DUP << 8: label = LABEL_LOAD_PARAMETER_INDEX; break; case LABEL_LOAD_PARAMETER_INDEX | Instruction.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 | Instruction.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(clazz, offset)); } // Let the constant figure out the transition. switch (constantInstruction.canonicalOpcode()) { case Instruction.OP_LDC: instructionOffset = offset; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case Instruction.OP_CHECKCAST: // We simply ignore any casts, typically to Class[], // but maybe even to Class or String. break; case Instruction.OP_GETSTATIC: case Instruction.OP_INVOKEVIRTUAL: case Instruction.OP_INVOKESTATIC: clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case Instruction.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_START: // Also handle calls to Class.forName("className") that have // already been processed by the DynamicClassReferenceInitializer. referencedClass = stringConstant.referencedClass; break; 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/invokestatic 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 if (label == LABEL_LOAD_CLASS_ARRAY_SIZE && methodName.equals(ClassConstants.METHOD_NAME_CLASS_FOR_NAME) && methodType.equals(ClassConstants.METHOD_TYPE_CLASS_FOR_NAME)) { label = LABEL_LOAD_MEMBER_NAME; } 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 : TypeConstants.METHOD_ARGUMENTS_OPEN + parameterTypes.toString() + TypeConstants.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); } reset(); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy