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

proguard.classfile.util.ClassReferenceInitializer 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-2022 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.TypeConstants.INNER_CLASS_SEPARATOR;
import static proguard.classfile.kotlin.KotlinAnnotationArgument.EnumValue;
import static proguard.classfile.kotlin.KotlinAnnotationArgument.Value;
import static proguard.classfile.kotlin.KotlinConstants.DEFAULT_IMPLEMENTATIONS_SUFFIX;
import static proguard.classfile.kotlin.KotlinConstants.DEFAULT_METHOD_SUFFIX;
import static proguard.classfile.kotlin.KotlinConstants.FUNCTION_NAME_ANONYMOUS;
import static proguard.classfile.kotlin.KotlinConstants.METHOD_NAME_LAMBDA_INVOKE;

import java.util.Collections;
import java.util.stream.Collectors;
import proguard.classfile.AccessConstants;
import proguard.classfile.ClassConstants;
import proguard.classfile.ClassPool;
import proguard.classfile.Clazz;
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.attribute.Attribute;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.EnclosingMethodAttribute;
import proguard.classfile.attribute.InnerClassesAttribute;
import proguard.classfile.attribute.InnerClassesInfo;
import proguard.classfile.attribute.LocalVariableInfo;
import proguard.classfile.attribute.LocalVariableTableAttribute;
import proguard.classfile.attribute.LocalVariableTypeInfo;
import proguard.classfile.attribute.LocalVariableTypeTableAttribute;
import proguard.classfile.attribute.RecordAttribute;
import proguard.classfile.attribute.RecordComponentInfo;
import proguard.classfile.attribute.SignatureAttribute;
import proguard.classfile.attribute.annotation.Annotation;
import proguard.classfile.attribute.annotation.AnnotationDefaultAttribute;
import proguard.classfile.attribute.annotation.AnnotationElementValue;
import proguard.classfile.attribute.annotation.AnnotationsAttribute;
import proguard.classfile.attribute.annotation.ArrayElementValue;
import proguard.classfile.attribute.annotation.ClassElementValue;
import proguard.classfile.attribute.annotation.ConstantElementValue;
import proguard.classfile.attribute.annotation.ElementValue;
import proguard.classfile.attribute.annotation.EnumConstantElementValue;
import proguard.classfile.attribute.annotation.ParameterAnnotationsAttribute;
import proguard.classfile.attribute.annotation.visitor.AnnotationVisitor;
import proguard.classfile.attribute.annotation.visitor.ElementValueVisitor;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.attribute.visitor.InnerClassesInfoVisitor;
import proguard.classfile.attribute.visitor.LocalVariableInfoVisitor;
import proguard.classfile.attribute.visitor.LocalVariableTypeInfoVisitor;
import proguard.classfile.attribute.visitor.RecordComponentInfoVisitor;
import proguard.classfile.constant.AnyMethodrefConstant;
import proguard.classfile.constant.ClassConstant;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.DynamicConstant;
import proguard.classfile.constant.FieldrefConstant;
import proguard.classfile.constant.InvokeDynamicConstant;
import proguard.classfile.constant.MethodHandleConstant;
import proguard.classfile.constant.MethodTypeConstant;
import proguard.classfile.constant.StringConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.NamedAttributeDeleter;
import proguard.classfile.kotlin.KotlinAnnotatable;
import proguard.classfile.kotlin.KotlinAnnotation;
import proguard.classfile.kotlin.KotlinAnnotationArgument;
import proguard.classfile.kotlin.KotlinClassKindMetadata;
import proguard.classfile.kotlin.KotlinConstants;
import proguard.classfile.kotlin.KotlinConstructorMetadata;
import proguard.classfile.kotlin.KotlinDeclarationContainerMetadata;
import proguard.classfile.kotlin.KotlinFileFacadeKindMetadata;
import proguard.classfile.kotlin.KotlinFunctionMetadata;
import proguard.classfile.kotlin.KotlinMetadata;
import proguard.classfile.kotlin.KotlinMultiFileFacadeKindMetadata;
import proguard.classfile.kotlin.KotlinMultiFilePartKindMetadata;
import proguard.classfile.kotlin.KotlinPropertyMetadata;
import proguard.classfile.kotlin.KotlinSyntheticClassKindMetadata;
import proguard.classfile.kotlin.KotlinTypeAliasMetadata;
import proguard.classfile.kotlin.KotlinTypeMetadata;
import proguard.classfile.kotlin.KotlinTypeParameterMetadata;
import proguard.classfile.kotlin.KotlinValueParameterMetadata;
import proguard.classfile.kotlin.reflect.util.KotlinCallableReferenceInitializer;
import proguard.classfile.kotlin.visitor.AllPropertyVisitor;
import proguard.classfile.kotlin.visitor.AllTypeVisitor;
import proguard.classfile.kotlin.visitor.KotlinAnnotationArgumentVisitor;
import proguard.classfile.kotlin.visitor.KotlinAnnotationVisitor;
import proguard.classfile.kotlin.visitor.KotlinConstructorVisitor;
import proguard.classfile.kotlin.visitor.KotlinFunctionVisitor;
import proguard.classfile.kotlin.visitor.KotlinMetadataVisitor;
import proguard.classfile.kotlin.visitor.KotlinPropertyVisitor;
import proguard.classfile.kotlin.visitor.KotlinTypeAliasVisitor;
import proguard.classfile.kotlin.visitor.KotlinTypeParameterVisitor;
import proguard.classfile.kotlin.visitor.KotlinTypeVisitor;
import proguard.classfile.kotlin.visitor.KotlinValueParameterVisitor;
import proguard.classfile.kotlin.visitor.ReferencedKotlinMetadataVisitor;
import proguard.classfile.visitor.ClassVisitor;
import proguard.classfile.visitor.MemberVisitor;

/**
 * This {@link ClassVisitor} initializes the references of all classes that it visits.
 *
 * 

All class constant pool entries get direct references to the corresponding classes. These * references make it more convenient to travel up and across the class hierarchy. * *

All field and method reference constant pool entries get direct references to the * corresponding classes, fields, and methods. * *

All name and type constant pool entries get a list of direct references to the classes listed * in the type. * *

The classpools are searched by keys and not by Clazz.getName(), so after obfuscation make sure * to pass a ClassPool.refreshedCopy() to this class. * *

All Kotlin metadata elements get references to their corresponding Java implementation * elements. * *

The class hierarchy must be initialized before using this visitor. * * @author Eric Lafortune */ public class ClassReferenceInitializer implements ClassVisitor, // Implementation interfaces. MemberVisitor, ConstantVisitor, AttributeVisitor, RecordComponentInfoVisitor, LocalVariableInfoVisitor, LocalVariableTypeInfoVisitor, AnnotationVisitor, ElementValueVisitor { private final ClassPool programClassPool; private final ClassPool libraryClassPool; private final boolean checkAccessRules; private final InvalidReferenceVisitor invalidReferenceVisitor; private final MemberFinder memberFinder = new MemberFinder(); private final MemberFinder strictMemberFinder = new MemberFinder(false); private final KotlinReferenceInitializer kotlinReferenceInitializer; private final InvalidSignatureCleaner invalidSignatureCleaner = new InvalidSignatureCleaner(); /** * Creates a new ClassReferenceInitializer that initializes the references of all visited class * files. */ public ClassReferenceInitializer(ClassPool programClassPool, ClassPool libraryClassPool) { this(programClassPool, libraryClassPool, true); } /** * Creates a new ClassReferenceInitializer that initializes the references of all visited class * files. */ public ClassReferenceInitializer( ClassPool programClassPool, ClassPool libraryClassPool, boolean checkAccessRules) { this(programClassPool, libraryClassPool, checkAccessRules, null); } /** * Creates a new ClassReferenceInitializer that initializes the references of all visited class * files, optionally printing warnings if some classes or class members can't be found or if they * are in the program class pool. */ public ClassReferenceInitializer( ClassPool programClassPool, ClassPool libraryClassPool, WarningPrinter missingClassWarningPrinter, WarningPrinter missingProgramMemberWarningPrinter, WarningPrinter missingLibraryMemberWarningPrinter, WarningPrinter dependencyWarningPrinter) { this( programClassPool, libraryClassPool, true, missingClassWarningPrinter, missingProgramMemberWarningPrinter, missingLibraryMemberWarningPrinter, dependencyWarningPrinter); } /** * Creates a new ClassReferenceInitializer that initializes the references of all visited class * files, optionally printing warnings if some classes or class members can't be found or if they * are in the program class pool. */ public ClassReferenceInitializer( ClassPool programClassPool, ClassPool libraryClassPool, boolean checkAccessRules, WarningPrinter missingClassWarningPrinter, WarningPrinter missingProgramMemberWarningPrinter, WarningPrinter missingLibraryMemberWarningPrinter, WarningPrinter dependencyWarningPrinter) { this( programClassPool, libraryClassPool, checkAccessRules, new InvalidReferenceWarningVisitor( missingClassWarningPrinter, missingProgramMemberWarningPrinter, missingLibraryMemberWarningPrinter, dependencyWarningPrinter)); } /** * Creates a new ClassReferenceInitializer that initializes the references of all visited class * files, visiting the given {@link InvalidReferenceVisitor} for any broken references. */ public ClassReferenceInitializer( ClassPool programClassPool, ClassPool libraryClassPool, boolean checkAccessRules, InvalidReferenceVisitor invalidReferenceVisitor) { this.programClassPool = programClassPool; this.libraryClassPool = libraryClassPool; this.checkAccessRules = checkAccessRules; this.invalidReferenceVisitor = invalidReferenceVisitor; this.kotlinReferenceInitializer = new KotlinReferenceInitializer(); } // Implementations for ClassVisitor. @Override public void visitAnyClass(Clazz clazz) { throw new UnsupportedOperationException( this.getClass().getName() + " does not support " + clazz.getClass().getName()); } @Override public void visitProgramClass(ProgramClass programClass) { // Initialize the constant pool entries. programClass.constantPoolEntriesAccept(this); // Initialize all fields and methods. programClass.fieldsAccept(this); programClass.methodsAccept(this); // Clean up invalid signatures. We cannot do this while initializing the other attributes, since // that will result in possibly modifying the attributes while we are iterating over them. programClass.attributesAccept(invalidSignatureCleaner); // Initialize the attributes. programClass.attributesAccept(this); // Initialize the Kotlin metadata. programClass.kotlinMetadataAccept(kotlinReferenceInitializer); } @Override public void visitLibraryClass(LibraryClass libraryClass) { // Initialize all fields and methods. libraryClass.fieldsAccept(this); libraryClass.methodsAccept(this); // Initialize the Kotlin metadata. libraryClass.kotlinMetadataAccept(kotlinReferenceInitializer); } // Implementations for MemberVisitor. @Override public void visitProgramField(ProgramClass programClass, ProgramField programField) { programField.referencedClass = findReferencedClass(programClass, programField.getDescriptor(programClass)); // Clean up invalid signatures. We cannot do this while initializing the other attributes, since // that will result in possibly modifying the attributes while we are iterating over them. programField.attributesAccept(programClass, invalidSignatureCleaner); // Initialize the attributes. programField.attributesAccept(programClass, this); } @Override public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { programMethod.referencedClasses = findReferencedClasses(programClass, programMethod.getDescriptor(programClass)); // Clean up invalid signatures. We cannot do this while initializing the other attributes, since // that will result in possibly modifying the attributes while we are iterating over them. programMethod.attributesAccept(programClass, invalidSignatureCleaner); // Initialize the attributes. programMethod.attributesAccept(programClass, this); } @Override public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) { libraryField.referencedClass = findReferencedClass(libraryClass, libraryField.getDescriptor(libraryClass)); } @Override public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { libraryMethod.referencedClasses = findReferencedClasses(libraryClass, libraryMethod.getDescriptor(libraryClass)); } // Implementations for ConstantVisitor. @Override public void visitAnyConstant(Clazz clazz, Constant constant) {} @Override public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { // Fill out the String class. stringConstant.javaLangStringClass = findClass(clazz, ClassConstants.NAME_JAVA_LANG_STRING); } @Override public void visitDynamicConstant(Clazz clazz, DynamicConstant dynamicConstant) { dynamicConstant.referencedClasses = findReferencedClasses(clazz, dynamicConstant.getType(clazz)); } @Override public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { invokeDynamicConstant.referencedClasses = findReferencedClasses(clazz, invokeDynamicConstant.getType(clazz)); } @Override public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) { // Fill out the MethodHandle class. methodHandleConstant.javaLangInvokeMethodHandleClass = findClass(clazz, ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_HANDLE); } @Override public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { String className = fieldrefConstant.getClassName(clazz); // Methods for array types should be found in the Object class. if (ClassUtil.isInternalArrayType(className)) { className = ClassConstants.NAME_JAVA_LANG_OBJECT; } // See if we can find the referenced class. // Unresolved references are assumed to refer to library classes // that will not change anyway. Clazz referencedClass = findClass(clazz, className); if (referencedClass != null) { String name = fieldrefConstant.getName(clazz); String type = fieldrefConstant.getType(clazz); // See if we can find the referenced class member somewhere in the // hierarchy. Clazz referencingClass = checkAccessRules ? clazz : null; fieldrefConstant.referencedField = memberFinder.findField(referencingClass, referencedClass, name, type); fieldrefConstant.referencedClass = memberFinder.correspondingClass(); if (fieldrefConstant.referencedField == null && invalidReferenceVisitor != null) { // We haven't found the class member anywhere in the hierarchy. if (referencedClass instanceof ProgramClass) { invalidReferenceVisitor.visitMissingProgramField( clazz, ((ProgramClass) referencedClass), name, type); } else { invalidReferenceVisitor.visitMissingLibraryField( clazz, ((LibraryClass) referencedClass), name, type); } } } } @Override public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant) { String className = anyMethodrefConstant.getClassName(clazz); // Methods for array types should be found in the Object class. if (ClassUtil.isInternalArrayType(className)) { className = ClassConstants.NAME_JAVA_LANG_OBJECT; } // See if we can find the referenced class. // Unresolved references are assumed to refer to library classes // that will not change anyway. Clazz referencedClass = findClass(clazz, className); if (referencedClass != null) { String name = anyMethodrefConstant.getName(clazz); String type = anyMethodrefConstant.getType(clazz); // See if we can find the referenced class member somewhere in the // hierarchy. Clazz referencingClass = checkAccessRules ? clazz : null; anyMethodrefConstant.referencedMethod = memberFinder.findMethod(referencingClass, referencedClass, name, type); anyMethodrefConstant.referencedClass = memberFinder.correspondingClass(); if (anyMethodrefConstant.referencedMethod == null && invalidReferenceVisitor != null) { // We haven't found the class member anywhere in the hierarchy. if (referencedClass instanceof ProgramClass) { invalidReferenceVisitor.visitMissingProgramMethod( clazz, ((ProgramClass) referencedClass), name, type); } else { invalidReferenceVisitor.visitMissingLibraryMethod( clazz, ((LibraryClass) referencedClass), name, type); } } } } @Override public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { // Fill out the referenced class. classConstant.referencedClass = findClass(clazz, ClassUtil.internalClassNameFromClassType(classConstant.getName(clazz))); // Fill out the Class class. classConstant.javaLangClassClass = findClass(clazz, ClassConstants.NAME_JAVA_LANG_CLASS); } @Override public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) { // Fill out the MethodType class. methodTypeConstant.javaLangInvokeMethodTypeClass = findClass(clazz, ClassConstants.NAME_JAVA_LANG_INVOKE_METHOD_TYPE); methodTypeConstant.referencedClasses = findReferencedClasses(clazz, methodTypeConstant.getType(clazz)); } // Implementations for AttributeVisitor. @Override public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @Override public void visitRecordAttribute(Clazz clazz, RecordAttribute recordAttributeAttribute) { recordAttributeAttribute.componentsAccept(clazz, this); } @Override public void visitEnclosingMethodAttribute( Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) { String enclosingClassName = enclosingMethodAttribute.getClassName(clazz); // See if we can find the referenced class. enclosingMethodAttribute.referencedClass = findClass(clazz, enclosingClassName); if (enclosingMethodAttribute.referencedClass != null) { // Is there an enclosing method? Otherwise it's just initialization // code outside of the constructors. if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) { String name = enclosingMethodAttribute.getName(clazz); String type = enclosingMethodAttribute.getType(clazz); // See if we can find the method in the referenced class. enclosingMethodAttribute.referencedMethod = enclosingMethodAttribute.referencedClass.findMethod(name, type); if (enclosingMethodAttribute.referencedMethod == null && invalidReferenceVisitor != null) { // We couldn't find the enclosing method. invalidReferenceVisitor.visitMissingEnclosingMethod( clazz, enclosingMethodAttribute.referencedClass, name, type); } } } else { if (enclosingClassName != null && enclosingClassName.indexOf(INNER_CLASS_SEPARATOR) != -1) { String myEnclosingClassName = enclosingClassName; do { // [DGD-1462] Truncate the name until we find the first // non-anonymous class, i.e. the first class that exists in // a class pool. myEnclosingClassName = myEnclosingClassName.substring(0, myEnclosingClassName.lastIndexOf("$")); enclosingMethodAttribute.referencedClass = findClass(clazz, myEnclosingClassName, false); } while (enclosingMethodAttribute.referencedClass == null && myEnclosingClassName.indexOf(INNER_CLASS_SEPARATOR) != -1); if (enclosingMethodAttribute.referencedClass == null && clazz != null && invalidReferenceVisitor != null) { invalidReferenceVisitor.visitMissingClass(clazz, enclosingClassName); } } } } @Override public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // Initialize the nested attributes. codeAttribute.attributesAccept(clazz, method, this); } @Override public void visitLocalVariableTableAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) { // Initialize the local variables. localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); } @Override public void visitLocalVariableTypeTableAttribute( Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) { // Initialize the local variable types. localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); } @Override public void visitSignatureAttribute( Clazz clazz, Member member, SignatureAttribute signatureAttribute) { signatureAttribute.referencedClasses = findReferencedClasses(clazz, signatureAttribute.getSignature(clazz)); } @Override public void visitSignatureAttribute( Clazz clazz, RecordComponentInfo recordComponentInfo, SignatureAttribute signatureAttribute) { signatureAttribute.referencedClasses = findReferencedClasses(clazz, signatureAttribute.getSignature(clazz)); } @Override public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) { signatureAttribute.referencedClasses = findReferencedClasses(clazz, signatureAttribute.getSignature(clazz)); } @Override public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) { // Initialize the annotations. annotationsAttribute.annotationsAccept(clazz, this); } @Override public void visitAnyParameterAnnotationsAttribute( Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) { // Initialize the annotations. parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); } @Override public void visitAnnotationDefaultAttribute( Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) { // Initialize the annotation. annotationDefaultAttribute.defaultValueAccept(clazz, this); } // Implementations for RecordComponentInfoVisitor. @Override public void visitRecordComponentInfo(Clazz clazz, RecordComponentInfo recordComponentInfo) { String name = recordComponentInfo.getName(clazz); String descriptor = recordComponentInfo.getDescriptor(clazz); recordComponentInfo.referencedField = memberFinder.findField(clazz, name, descriptor); // Clean up invalid signatures. We cannot do this while initializing the other attributes, since // that will result in possibly modifying the attributes while we are iterating over them. recordComponentInfo.attributesAccept(clazz, invalidSignatureCleaner); // Initialize the attributes. recordComponentInfo.attributesAccept(clazz, this); } // Implementations for LocalVariableInfoVisitor. @Override public void visitLocalVariableInfo( Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) { localVariableInfo.referencedClass = findReferencedClass(clazz, localVariableInfo.getDescriptor(clazz)); } // Implementations for LocalVariableTypeInfoVisitor. @Override public void visitLocalVariableTypeInfo( Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) { localVariableTypeInfo.referencedClasses = findReferencedClasses(clazz, localVariableTypeInfo.getSignature(clazz)); } // Implementations for AnnotationVisitor. @Override public void visitAnnotation(Clazz clazz, Annotation annotation) { annotation.referencedClasses = findReferencedClasses(clazz, annotation.getType(clazz)); // Initialize the element values. annotation.elementValuesAccept(clazz, this); } // Implementations for ElementValueVisitor. @Override public void visitConstantElementValue( Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) { initializeElementValue(clazz, annotation, constantElementValue); } @Override public void visitEnumConstantElementValue( Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) { initializeElementValue(clazz, annotation, enumConstantElementValue); enumConstantElementValue.referencedClasses = findReferencedClasses(clazz, enumConstantElementValue.getTypeName(clazz)); } @Override public void visitClassElementValue( Clazz clazz, Annotation annotation, ClassElementValue classElementValue) { initializeElementValue(clazz, annotation, classElementValue); classElementValue.referencedClasses = findReferencedClasses(clazz, classElementValue.getClassName(clazz)); } @Override public void visitAnnotationElementValue( Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) { initializeElementValue(clazz, annotation, annotationElementValue); // Initialize the annotation. annotationElementValue.annotationAccept(clazz, this); } @Override public void visitArrayElementValue( Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) { initializeElementValue(clazz, annotation, arrayElementValue); // Initialize the element values. arrayElementValue.elementValuesAccept(clazz, annotation, this); } /** Initializes the referenced method of an element value, if any. */ private void initializeElementValue( Clazz clazz, Annotation annotation, ElementValue elementValue) { // See if we have a referenced class. if (annotation != null && annotation.referencedClasses != null && elementValue.u2elementNameIndex != 0) { // See if we can find the method in the referenced class // (ignoring the descriptor). String name = elementValue.getMethodName(clazz); Clazz referencedClass = annotation.referencedClasses[0]; elementValue.referencedClass = referencedClass; elementValue.referencedMethod = referencedClass.findMethod(name, null); } } // Kotlin initializer class, used to initialize Kotlin metadata references. private class KotlinReferenceInitializer implements KotlinMetadataVisitor, KotlinPropertyVisitor, KotlinFunctionVisitor, KotlinConstructorVisitor, KotlinTypeVisitor, KotlinTypeAliasVisitor, KotlinValueParameterVisitor, KotlinTypeParameterVisitor, KotlinAnnotationVisitor, KotlinAnnotationArgumentVisitor { private final KotlinDefaultImplsInitializer kotlinDefaultImplsInitializer = new KotlinDefaultImplsInitializer(); private final KotlinDefaultMethodInitializer kotlinDefaultMethodInitializer = new KotlinDefaultMethodInitializer(); private final KotlinInterClassSyntheticFunctionInitializer interClassSyntheticFunctionInitializer = new KotlinInterClassSyntheticFunctionInitializer(); private final KotlinCallableReferenceInitializer callableReferenceInitializer = new KotlinCallableReferenceInitializer(programClassPool, libraryClassPool); // Initialize lazily, since the copy will only be required if there are type aliases. private ClassPool programClassPoolCopy; private ClassPool getProgramClassPoolCopy() { if (programClassPoolCopy == null) { programClassPoolCopy = programClassPool.refreshedCopy(); } return programClassPoolCopy; } // Implementations for KotlinMetadataVisitor. @Override public void visitAnyKotlinMetadata(Clazz clazz, KotlinMetadata kotlinMetadata) {} @Override public void visitKotlinDeclarationContainerMetadata( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata) { kotlinDeclarationContainerMetadata.ownerReferencedClass = clazz; kotlinDeclarationContainerMetadata.propertiesAccept(clazz, this); kotlinDeclarationContainerMetadata.delegatedPropertiesAccept(clazz, this); kotlinDeclarationContainerMetadata.functionsAccept(clazz, this); kotlinDeclarationContainerMetadata.typeAliasesAccept(clazz, this); } @Override public void visitKotlinClassMetadata( Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata) { kotlinClassKindMetadata.referencedClass = findClass(clazz, kotlinClassKindMetadata.className); if (kotlinClassKindMetadata.anonymousObjectOriginName != null) { kotlinClassKindMetadata.anonymousObjectOriginClass = findClass(clazz, kotlinClassKindMetadata.anonymousObjectOriginName, false); if (kotlinClassKindMetadata.anonymousObjectOriginClass == null) { kotlinClassKindMetadata.anonymousObjectOriginName = null; } } if (kotlinClassKindMetadata.companionObjectName != null) { String name = clazz.getName() + "$" + kotlinClassKindMetadata.companionObjectName; kotlinClassKindMetadata.referencedCompanionClass = findClass(clazz, name); kotlinClassKindMetadata.referencedCompanionField = memberFinder.findField( clazz, kotlinClassKindMetadata.companionObjectName, ClassUtil.internalTypeFromClassName(name)); } kotlinClassKindMetadata.referencedEnumEntries = kotlinClassKindMetadata.enumEntryNames.stream() .map(enumEntry -> strictMemberFinder.findField(clazz, enumEntry, null)) .collect(Collectors.toList()); kotlinClassKindMetadata.referencedNestedClasses = kotlinClassKindMetadata.nestedClassNames.stream() .map(nestedName -> findClass(clazz, clazz.getName() + "$" + nestedName)) .collect(Collectors.toList()); kotlinClassKindMetadata.referencedSealedSubClasses = kotlinClassKindMetadata.sealedSubclassNames.stream() .map(sealedSubName -> findClass(clazz, sealedSubName)) .collect(Collectors.toList()); kotlinClassKindMetadata.typeParametersAccept(clazz, this); kotlinClassKindMetadata.contextReceiverTypesAccept(clazz, this); kotlinClassKindMetadata.superTypesAccept(clazz, this); kotlinClassKindMetadata.constructorsAccept(clazz, this); kotlinClassKindMetadata.inlineClassUnderlyingPropertyTypeAccept(clazz, this); visitKotlinDeclarationContainerMetadata(clazz, kotlinClassKindMetadata); if (kotlinClassKindMetadata.flags.isInterface || kotlinClassKindMetadata.flags.isAnnotationClass) { // Initialize the default implementations class of interfaces. // The class will be an inner class and have a name like MyInterface$DefaultImpls. kotlinClassKindMetadata.referencedDefaultImplsClass = findClass( clazz, kotlinClassKindMetadata.className + KotlinConstants.DEFAULT_IMPLEMENTATIONS_SUFFIX, false); if (kotlinClassKindMetadata.referencedDefaultImplsClass != null) { // Initialize references from interface functions to // their default implementations. kotlinDefaultImplsInitializer.defaultImplsClass = kotlinClassKindMetadata.referencedDefaultImplsClass; kotlinClassKindMetadata.functionsAccept(clazz, kotlinDefaultImplsInitializer); // Initialize missing references from interface properties // to their backing field. kotlinClassKindMetadata.accept( clazz, new AllPropertyVisitor( new KotlinInterClassPropertyReferenceInitializer( kotlinClassKindMetadata.referencedDefaultImplsClass))); } } // Initialize references from functions to their utility methods // for default parameter values. kotlinDefaultMethodInitializer.isInterface = kotlinClassKindMetadata.flags.isInterface; kotlinClassKindMetadata.functionsAccept(clazz, kotlinDefaultMethodInitializer); if (kotlinClassKindMetadata.flags.isCompanionObject) { // Initialize missing references from properties in the Companion class // that have their backing field on the parent class. clazz.accept(new KotlinCompanionParentPropertyInitializer()); } } @Override public void visitKotlinFileFacadeMetadata( Clazz clazz, KotlinFileFacadeKindMetadata kotlinFileFacadeKindMetadata) { visitKotlinDeclarationContainerMetadata(clazz, kotlinFileFacadeKindMetadata); // Initialize references from functions to their utility methods // for default parameter values. kotlinDefaultMethodInitializer.isInterface = false; kotlinFileFacadeKindMetadata.functionsAccept(clazz, kotlinDefaultMethodInitializer); } @Override public void visitKotlinSyntheticClassMetadata( Clazz clazz, KotlinSyntheticClassKindMetadata kotlinSyntheticClassKindMetadata) { kotlinSyntheticClassKindMetadata.functionsAccept(clazz, this); // Initialize references from functions to their utility methods // for default parameter values. kotlinDefaultMethodInitializer.isInterface = false; kotlinSyntheticClassKindMetadata.functionsAccept(clazz, kotlinDefaultMethodInitializer); // Initialize missing references from synthetic functions by // searching in the enclosing method attribute. kotlinSyntheticClassKindMetadata.functionsAccept( clazz, interClassSyntheticFunctionInitializer); // Initialize callable references. kotlinSyntheticClassKindMetadata.accept(clazz, callableReferenceInitializer); } @Override public void visitKotlinMultiFileFacadeMetadata( Clazz clazz, KotlinMultiFileFacadeKindMetadata kotlinMultiFileFacadeKindMetadata) { kotlinMultiFileFacadeKindMetadata.referencedPartClasses = kotlinMultiFileFacadeKindMetadata.partClassNames.stream() .map(partName -> findClass(clazz, partName)) .collect(Collectors.toList()); } @Override public void visitKotlinMultiFilePartMetadata( Clazz clazz, KotlinMultiFilePartKindMetadata kotlinMultiFilePartKindMetadata) { kotlinMultiFilePartKindMetadata.referencedFacadeClass = findClass(clazz, kotlinMultiFilePartKindMetadata.facadeName); visitKotlinDeclarationContainerMetadata(clazz, kotlinMultiFilePartKindMetadata); // Initialize references from functions to their utility methods // for default parameter values. kotlinDefaultMethodInitializer.isInterface = false; kotlinMultiFilePartKindMetadata.functionsAccept(clazz, kotlinDefaultMethodInitializer); if (kotlinMultiFilePartKindMetadata.referencedFacadeClass != null) { // Initialize missing references from properties in multi-file parts // that have their backing field on the multi-file facade class. kotlinMultiFilePartKindMetadata.accept( clazz, new AllPropertyVisitor( new KotlinInterClassPropertyReferenceInitializer( kotlinMultiFilePartKindMetadata.referencedFacadeClass))); } } // Implementations for KotlinPropertyVisitor. @Override public void visitAnyProperty( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinPropertyMetadata kotlinPropertyMetadata) { if (kotlinPropertyMetadata.backingFieldSignature != null) { kotlinPropertyMetadata.referencedBackingField = strictMemberFinder.findField( clazz, kotlinPropertyMetadata.backingFieldSignature.memberName, kotlinPropertyMetadata.backingFieldSignature.descriptor); kotlinPropertyMetadata.referencedBackingFieldClass = strictMemberFinder.correspondingClass(); } if (kotlinPropertyMetadata.getterSignature != null) { kotlinPropertyMetadata.referencedGetterMethod = strictMemberFinder.findMethod( clazz, kotlinPropertyMetadata.getterSignature.method, kotlinPropertyMetadata.getterSignature.descriptor.toString()); } if (kotlinPropertyMetadata.setterSignature != null) { kotlinPropertyMetadata.referencedSetterMethod = strictMemberFinder.findMethod( clazz, kotlinPropertyMetadata.setterSignature.method, kotlinPropertyMetadata.setterSignature.descriptor.toString()); } if (kotlinPropertyMetadata.syntheticMethodForAnnotations != null) { kotlinPropertyMetadata.referencedSyntheticMethodForAnnotations = strictMemberFinder.findMethod( clazz, kotlinPropertyMetadata.syntheticMethodForAnnotations.method, kotlinPropertyMetadata.syntheticMethodForAnnotations.descriptor.toString()); kotlinPropertyMetadata.referencedSyntheticMethodClass = strictMemberFinder.correspondingClass(); } if (kotlinPropertyMetadata.syntheticMethodForDelegate != null) { kotlinPropertyMetadata.referencedSyntheticMethodForDelegateMethod = strictMemberFinder.findMethod( clazz, kotlinPropertyMetadata.syntheticMethodForDelegate.method, kotlinPropertyMetadata.syntheticMethodForDelegate.descriptor.toString()); kotlinPropertyMetadata.referencedSyntheticMethodForDelegateClass = strictMemberFinder.correspondingClass(); } kotlinPropertyMetadata.typeParametersAccept(clazz, kotlinDeclarationContainerMetadata, this); kotlinPropertyMetadata.receiverTypeAccept(clazz, kotlinDeclarationContainerMetadata, this); kotlinPropertyMetadata.contextReceiverTypesAccept( clazz, kotlinDeclarationContainerMetadata, this); kotlinPropertyMetadata.typeAccept(clazz, kotlinDeclarationContainerMetadata, this); kotlinPropertyMetadata.setterParametersAccept( clazz, kotlinDeclarationContainerMetadata, this); } // Implementations for KotlinFunctionVisitor. @Override public void visitAnyFunction( Clazz clazz, KotlinMetadata kotlinMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) { kotlinFunctionMetadata.referencedMethodClass = clazz; if (kotlinFunctionMetadata.jvmSignature != null) { String method = !FUNCTION_NAME_ANONYMOUS.equals(kotlinFunctionMetadata.jvmSignature.method) ? kotlinFunctionMetadata.jvmSignature.method : // T16483: In some cases, the jvmSignature erroneously contains the name // instead of invoke METHOD_NAME_LAMBDA_INVOKE; kotlinFunctionMetadata.referencedMethod = strictMemberFinder.findMethod( kotlinFunctionMetadata.referencedMethodClass, method, kotlinFunctionMetadata.jvmSignature.descriptor.toString()); } if (kotlinFunctionMetadata.lambdaClassOriginName != null) { kotlinFunctionMetadata.referencedLambdaClassOrigin = findClass(clazz, kotlinFunctionMetadata.lambdaClassOriginName, false); if (kotlinFunctionMetadata.referencedLambdaClassOrigin == null) { kotlinFunctionMetadata.lambdaClassOriginName = null; } } kotlinFunctionMetadata.contractsAccept(clazz, kotlinMetadata, new AllTypeVisitor(this)); kotlinFunctionMetadata.typeParametersAccept(clazz, kotlinMetadata, this); kotlinFunctionMetadata.receiverTypeAccept(clazz, kotlinMetadata, this); kotlinFunctionMetadata.contextReceiverTypesAccept(clazz, kotlinMetadata, this); kotlinFunctionMetadata.valueParametersAccept(clazz, kotlinMetadata, this); kotlinFunctionMetadata.returnTypeAccept(clazz, kotlinMetadata, this); } // Implementations for KotlinConstructorVisitor. @Override public void visitConstructor( Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata, KotlinConstructorMetadata kotlinConstructorMetadata) { // Annotation constructors don't have a corresponding constructor method. if (kotlinConstructorMetadata.jvmSignature != null) { kotlinConstructorMetadata.referencedMethod = strictMemberFinder.findMethod( clazz, kotlinConstructorMetadata.jvmSignature.method, kotlinConstructorMetadata.jvmSignature.descriptor.toString()); } kotlinConstructorMetadata.valueParametersAccept(clazz, kotlinClassKindMetadata, this); } // Implementations for KotlinTypeVisitor. @Override public void visitAnyType(Clazz clazz, KotlinTypeMetadata kotlinTypeMetadata) { String className = kotlinTypeMetadata.className; if (className != null) { kotlinTypeMetadata.referencedClass = findKotlinClass(clazz, className); } else if (kotlinTypeMetadata.aliasName != null) { // This type is an alias, that refers to an actual type (or another alias). // We search for the alias definition to create a reference for this use. String aliasName = kotlinTypeMetadata.aliasName; int innerClassMarkerIndex = aliasName.lastIndexOf('.'); String classNameFilter, simpleName; if (innerClassMarkerIndex != -1) { // Declared inside a class - we know exactly which one. classNameFilter = aliasName.substring(0, innerClassMarkerIndex); simpleName = aliasName.substring(innerClassMarkerIndex + 1); } else { // Declared in a file facade - we know which package only. classNameFilter = ClassUtil.internalPackagePrefix(aliasName) + "*"; simpleName = ClassUtil.internalSimpleClassName(kotlinTypeMetadata.aliasName); } ClassVisitor typeAliasInitializer = new ReferencedKotlinMetadataVisitor( new KotlinTypeAliasReferenceInitializer(kotlinTypeMetadata, simpleName)); // Use a refreshed copy of the class pool, in case any classes have been renamed. ClassPool programClassPool = getProgramClassPoolCopy(); if (innerClassMarkerIndex != -1) { programClassPool.classAccept(classNameFilter, typeAliasInitializer); libraryClassPool.classAccept(classNameFilter, typeAliasInitializer); } else { programClassPool.classesAccept(classNameFilter, typeAliasInitializer); libraryClassPool.classesAccept(classNameFilter, typeAliasInitializer); } } kotlinTypeMetadata.annotationsAccept(clazz, this); kotlinTypeMetadata.typeArgumentsAccept(clazz, this); kotlinTypeMetadata.outerClassAccept(clazz, this); kotlinTypeMetadata.upperBoundsAccept(clazz, this); kotlinTypeMetadata.abbreviationAccept(clazz, this); } // Implementations for KotlinTypeAliasVisitor. @Override public void visitTypeAlias( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinTypeAliasMetadata kotlinTypeAliasMetadata) { kotlinTypeAliasMetadata.referencedDeclarationContainer = kotlinDeclarationContainerMetadata; kotlinTypeAliasMetadata.annotationsAccept(clazz, this); kotlinTypeAliasMetadata.underlyingTypeAccept(clazz, kotlinDeclarationContainerMetadata, this); kotlinTypeAliasMetadata.expandedTypeAccept(clazz, kotlinDeclarationContainerMetadata, this); kotlinTypeAliasMetadata.typeParametersAccept(clazz, kotlinDeclarationContainerMetadata, this); } // Implementations for KotlinValueParameterVisitor. @Override public void visitAnyValueParameter( Clazz clazz, KotlinValueParameterMetadata kotlinValueParameterMetadata) {} @Override public void visitFunctionValParameter( Clazz clazz, KotlinMetadata kotlinMetadata, KotlinFunctionMetadata kotlinFunctionMetadata, KotlinValueParameterMetadata kotlinValueParameterMetadata) { kotlinValueParameterMetadata.typeAccept(clazz, kotlinMetadata, kotlinFunctionMetadata, this); } @Override public void visitConstructorValParameter( Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata, KotlinConstructorMetadata kotlinConstructorMetadata, KotlinValueParameterMetadata kotlinValueParameterMetadata) { kotlinValueParameterMetadata.typeAccept( clazz, kotlinClassKindMetadata, kotlinConstructorMetadata, this); } @Override public void visitPropertyValParameter( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinPropertyMetadata kotlinPropertyMetadata, KotlinValueParameterMetadata kotlinValueParameterMetadata) { kotlinValueParameterMetadata.typeAccept( clazz, kotlinDeclarationContainerMetadata, kotlinPropertyMetadata, this); } // Implementations for KotlinTypeParameterVisitor. @Override public void visitAnyTypeParameter( Clazz clazz, KotlinTypeParameterMetadata kotlinTypeParameterMetadata) { kotlinTypeParameterMetadata.annotationsAccept(clazz, this); kotlinTypeParameterMetadata.upperBoundsAccept(clazz, this); } // Implementations for KotlinMetadataAnnotationVisitor @Override public void visitAnyAnnotation( Clazz clazz, KotlinAnnotatable annotatable, KotlinAnnotation annotation) { annotation.referencedAnnotationClass = findClass(clazz, annotation.className); if (annotation.referencedAnnotationClass != null) { annotation.argumentsAccept(clazz, annotatable, this); } } // Implementations for KotlinMetadataAnnotationArgumentVisitor @Override public void visitAnyArgument( Clazz clazz, KotlinAnnotatable annotatable, KotlinAnnotation annotation, KotlinAnnotationArgument argument, Value value) { argument.referencedAnnotationMethodClass = annotation.referencedAnnotationClass; argument.referencedAnnotationMethod = strictMemberFinder.findMethod(annotation.referencedAnnotationClass, argument.name, null); } @Override public void visitClassArgument( Clazz clazz, KotlinAnnotatable annotatable, KotlinAnnotation annotation, KotlinAnnotationArgument argument, KotlinAnnotationArgument.ClassValue value) { this.visitAnyArgument(clazz, annotatable, annotation, argument, value); value.referencedClass = findKotlinClass(clazz, value.className); } @Override public void visitEnumArgument( Clazz clazz, KotlinAnnotatable annotatable, KotlinAnnotation annotation, KotlinAnnotationArgument argument, EnumValue value) { this.visitAnyArgument(clazz, annotatable, annotation, argument, value); value.referencedClass = findClass(clazz, value.className); } } // Small utility methods. /** * Returns the single class referenced by the given descriptor, or null if there * isn't any useful reference. */ private Clazz findReferencedClass(Clazz referencingClass, String descriptor) { DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); enumeration.nextFluff(); if (enumeration.hasMoreClassNames()) { return findClass(referencingClass, enumeration.nextClassName()); } return null; } /** * Returns an array of classes referenced by the given descriptor, or null if there * aren't any useful references. */ private Clazz[] findReferencedClasses(Clazz referencingClass, String descriptor) { DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(descriptor); int classCount = enumeration.classCount(); if (classCount > 0) { Clazz[] referencedClasses = new Clazz[classCount]; boolean foundReferencedClasses = false; for (int index = 0; index < classCount; index++) { String fluff = enumeration.nextFluff(); String name = enumeration.nextClassName(); Clazz referencedClass = findClass(referencingClass, name); if (referencedClass != null) { referencedClasses[index] = referencedClass; foundReferencedClasses = true; } } if (foundReferencedClasses) { return referencedClasses; } } return null; } /** * Returns the class with the given name, either for the dummy Kotlin class pool, program class * pool or from the library class pool, or null if it can't be found. */ private Clazz findKotlinClass(Clazz referencingClass, String name) { // If this is a dummy Kotlin type, assign a dummy referenced class. Clazz clazz = KotlinConstants.dummyClassPool.getClass(name); if (clazz == null) { clazz = findClass(referencingClass, name); } return clazz; } /** * Returns the class with the given name, either for the program class pool or from the library * class pool, or null if it can't be found. */ private Clazz findClass(Clazz referencingClass, String name) { return findClass(referencingClass, name, true); } /** * @param report Report if the class is not found. Set to false if the class doesn't necessarily * exist. */ private Clazz findClass(Clazz referencingClass, String name, boolean report) { // Is it an array type? if (ClassUtil.isInternalArrayType(name)) { // Ignore any primitive array types. if (!ClassUtil.isInternalClassType(name)) { return null; } // Strip the array part. name = ClassUtil.internalClassNameFromClassType(name); } // First look for the class in the program class pool. Clazz clazz = programClassPool.getClass(name); // Otherwise look for the class in the library class pool. if (clazz == null) { clazz = libraryClassPool.getClass(name); if (report && clazz == null) { // We didn't find the superclass or interface. Print a warning. if (invalidReferenceVisitor != null) { invalidReferenceVisitor.visitMissingClass(referencingClass, name); } } } else if (referencingClass instanceof LibraryClass) { // The superclass or interface was found in the program class pool. if (invalidReferenceVisitor != null) { invalidReferenceVisitor.visitProgramDependency(referencingClass, clazz); } } return clazz; } /** A small utility class used to clean up invalid signatures. */ private class InvalidSignatureCleaner implements AttributeVisitor { /** * Perform some sanity checks on the Signature and whether it follows the JVM specification. * TODO: T4517 */ private boolean isValidClassSignature(Clazz clazz, String signature) { try { // Loop through the signature to if it can be parsed. new DescriptorClassEnumeration(signature).classCount(); // Then check whether the listed types are as expected. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(signature); if (!internalTypeEnumeration.hasMoreTypes()) { return false; } String superName = clazz.getSuperName(); String signSuperName = ClassUtil.internalClassNameFromClassSignature(internalTypeEnumeration.nextType()); if (superName != null && !signSuperName.startsWith(superName)) { return false; } // We are assuming interfaces in the descriptor occur in the same order as they are defined // on the class file level. While this is very likely in the vast majority of cases, it // doesn't seem like the JVM actually checks for this. TODO: see T4517. for (int i = 0; i < clazz.getInterfaceCount(); i++) { if (!internalTypeEnumeration.hasMoreTypes()) { return false; } String intfName = clazz.getInterfaceName(i); String signIntfName = ClassUtil.internalClassNameFromClassSignature(internalTypeEnumeration.nextType()); if (!signIntfName.startsWith(intfName)) { return false; } } return !internalTypeEnumeration.hasMoreTypes(); } catch (Exception corruptedSignature) { return false; } } /** Return false if the given signature is invalid. */ private boolean isValidSignature(String signature) { try { DescriptorClassEnumeration enumeration = new DescriptorClassEnumeration(signature); // Calling 'classCount()' will parse the entire signature. enumeration.classCount(); return true; } catch (Exception e) { return false; } } // Implementations for AttributeVisitor. @Override public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @Override public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) { if (!isValidClassSignature(clazz, signatureAttribute.getSignature(clazz))) { clazz.accept(new NamedAttributeDeleter(Attribute.SIGNATURE)); } } @Override public void visitSignatureAttribute( Clazz clazz, RecordComponentInfo recordComponentInfo, SignatureAttribute signatureAttribute) { if (!isValidSignature(signatureAttribute.getSignature(clazz))) { recordComponentInfo.attributesAccept(clazz, new NamedAttributeDeleter(Attribute.SIGNATURE)); } } @Override public void visitSignatureAttribute( Clazz clazz, Member member, SignatureAttribute signatureAttribute) { if (!isValidSignature(signatureAttribute.getSignature(clazz))) { member.accept(clazz, new NamedAttributeDeleter(Attribute.SIGNATURE)); } } } // Helper classes for KotlinReferenceInitializer. public static class KotlinTypeAliasReferenceInitializer implements KotlinMetadataVisitor, KotlinTypeAliasVisitor { private final KotlinTypeMetadata kotlinTypeMetadata; private final String simpleName; KotlinTypeAliasReferenceInitializer(KotlinTypeMetadata kotlinTypeMetadata, String simpleName) { this.simpleName = simpleName; this.kotlinTypeMetadata = kotlinTypeMetadata; } // Implementations for KotlinMetadataVisitor. @Override public void visitAnyKotlinMetadata(Clazz clazz, KotlinMetadata kotlinMetadata) {} @Override public void visitKotlinDeclarationContainerMetadata( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata) { kotlinDeclarationContainerMetadata.typeAliasesAccept(clazz, this); } @Override public void visitKotlinClassMetadata( Clazz clazz, KotlinClassKindMetadata kotlinClassKindMetadata) { // Only if the alias was declared inside this class. if (kotlinTypeMetadata.aliasName.equals(clazz.getName() + "." + simpleName)) { kotlinClassKindMetadata.typeAliasesAccept(clazz, this); } } // Implementations for KotlinTypeAliasVisitor. @Override public void visitTypeAlias( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinTypeAliasMetadata kotlinTypeAliasMetadata) { if (this.simpleName.equals(kotlinTypeAliasMetadata.name)) { this.kotlinTypeMetadata.referencedTypeAlias = kotlinTypeAliasMetadata; } } } /** * This KotlinFunctionVisitor initializes references to the default implementations of interface * functions. */ private class KotlinDefaultImplsInitializer implements KotlinFunctionVisitor { private Clazz defaultImplsClass; @Override public void visitAnyFunction( Clazz clazz, KotlinMetadata kotlinMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) {} @Override public void visitFunction( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) { if (defaultImplsClass != null && kotlinFunctionMetadata.jvmSignature != null && !kotlinFunctionMetadata.flags.modality.isAbstract) { kotlinFunctionMetadata.referencedDefaultImplementationMethod = strictMemberFinder.findMethod( defaultImplsClass, kotlinFunctionMetadata.jvmSignature.method, getDescriptor(kotlinDeclarationContainerMetadata, kotlinFunctionMetadata)); if (kotlinFunctionMetadata.referencedDefaultImplementationMethod != null) { kotlinFunctionMetadata.referencedDefaultImplementationMethodClass = defaultImplsClass; } } } // Small helper methods. /** Returns the descriptor for the default implementation method. */ private String getDescriptor( KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) { // Default implementation methods are static and have the interface // instance as its first parameter. return kotlinFunctionMetadata .jvmSignature .descriptor .toString() .replace("(", "(L" + kotlinDeclarationContainerMetadata.ownerClassName + ";"); } } /** * This KotlinFunctionVisitor initializes references to sibling methods that handle default * parameter values. */ private class KotlinDefaultMethodInitializer implements KotlinFunctionVisitor { boolean isInterface = false; boolean hasDefaults = false; // Implementations for KotlinFunctionVisitor. @Override public void visitAnyFunction( Clazz clazz, KotlinMetadata kotlinMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) { // During initialization this can be null because the // Asserter has not been run yet. if (kotlinFunctionMetadata.referencedMethod == null) { return; } // Use the jvm name here because the jvm name might not match // the metadata name. String methodName = kotlinFunctionMetadata.referencedMethod.getName( kotlinFunctionMetadata.referencedMethodClass); if (methodName.endsWith(DEFAULT_METHOD_SUFFIX)) { return; } // Check if there are parameters with default values. hasDefaults = false; kotlinFunctionMetadata.valueParametersAccept( clazz, kotlinMetadata, (_clazz, vp) -> hasDefaults |= vp.flags.hasDefaultValue); if (hasDefaults) { String defaultMethodName = methodName + DEFAULT_METHOD_SUFFIX; String descriptor = getDescriptor(kotlinFunctionMetadata); kotlinFunctionMetadata.referencedDefaultMethod = strictMemberFinder.findMethod( kotlinFunctionMetadata.referencedMethodClass, defaultMethodName, descriptor); kotlinFunctionMetadata.referencedDefaultMethodClass = strictMemberFinder.correspondingClass(); if (kotlinFunctionMetadata.referencedDefaultMethod == null && isInterface) { Clazz defaultImplsClass = findClass(clazz, clazz.getName() + DEFAULT_IMPLEMENTATIONS_SUFFIX, false); if (defaultImplsClass != null) { kotlinFunctionMetadata.referencedDefaultMethod = strictMemberFinder.findMethod(defaultImplsClass, defaultMethodName, descriptor); kotlinFunctionMetadata.referencedDefaultMethodClass = strictMemberFinder.correspondingClass(); } } } } // Small helper methods. /** * Return the descriptor for the utility method for default parameter values of the supplied * function. * *

The descriptor matches the original method but with 2 or more extra parameters: * *

- the first parameter is an instance reference if the original method was not static. - * the last 2+ parameters - int masks for every 32 parameters that encode which parameter values * were passed - an object for future use? seems to be always null for now? */ private String getDescriptor(KotlinFunctionMetadata kotlinFunctionMetadata) { String originalDescriptor = kotlinFunctionMetadata.referencedMethod.getDescriptor( kotlinFunctionMetadata.referencedMethodClass); // Each int param encodes up to 32 parameters as being present or not. int requiredIntParams = 1 + (ClassUtil.internalMethodParameterCount(originalDescriptor) / 32); String descriptor = originalDescriptor.replace( ")", String.join("", Collections.nCopies(requiredIntParams, "I")) + "Ljava/lang/Object;)"); // Instance methods will have been made static and have the class // instance as its first parameter. if ((kotlinFunctionMetadata.referencedMethod.getAccessFlags() & AccessConstants.STATIC) == 0) { descriptor = descriptor.replace( "(", "(L" + kotlinFunctionMetadata.referencedMethodClass.getName() + ";"); } return descriptor; } } /** * This KotlinPropertyVisitor tries to initialize missing property references by looking into the * supplied class. */ private class KotlinInterClassPropertyReferenceInitializer implements KotlinPropertyVisitor { private final Clazz clazz; KotlinInterClassPropertyReferenceInitializer(Clazz clazz) { this.clazz = clazz; } // Implementations for KotlinPropertyVisitor. @Override public void visitAnyProperty( Clazz clazz, KotlinDeclarationContainerMetadata kotlinDeclarationContainerMetadata, KotlinPropertyMetadata kotlinPropertyMetadata) { if (kotlinPropertyMetadata.backingFieldSignature != null && kotlinPropertyMetadata.referencedBackingField == null) { kotlinPropertyMetadata.referencedBackingField = strictMemberFinder.findField( this.clazz, kotlinPropertyMetadata.backingFieldSignature.memberName, kotlinPropertyMetadata.backingFieldSignature.descriptor); kotlinPropertyMetadata.referencedBackingFieldClass = strictMemberFinder.correspondingClass(); } /* Default implementations of methods are stored in the $DefaultImpls class not in the * interface class. * * We fix any synthetic methods for annotations that were not already set (because * they weren't found the in interface class) - these will be found in the $DefaultImpls class. * * Not necessary for getters/setters as they are stored on the interface class itself. */ if (kotlinPropertyMetadata.syntheticMethodForAnnotations != null && kotlinPropertyMetadata.referencedSyntheticMethodForAnnotations == null) { kotlinPropertyMetadata.referencedSyntheticMethodForAnnotations = strictMemberFinder.findMethod( this.clazz, kotlinPropertyMetadata.syntheticMethodForAnnotations.method, kotlinPropertyMetadata.syntheticMethodForAnnotations.descriptor.toString()); kotlinPropertyMetadata.referencedSyntheticMethodClass = strictMemberFinder.correspondingClass(); } } } /** This KotlinFunctionVisitor tries to initialize missing function references. */ private static class KotlinInterClassSyntheticFunctionInitializer implements KotlinFunctionVisitor, ClassVisitor, AttributeVisitor { private KotlinFunctionMetadata currentFunction; @Override public void visitAnyFunction( Clazz clazz, KotlinMetadata kotlinMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) {} @Override public void visitSyntheticFunction( Clazz clazz, KotlinSyntheticClassKindMetadata kotlinSyntheticClassKindMetadata, KotlinFunctionMetadata kotlinFunctionMetadata) { if (kotlinFunctionMetadata.referencedMethod == null) { this.currentFunction = kotlinFunctionMetadata; clazz.accept(this); } } @Override public void visitAnyClass(Clazz clazz) {} @Override public void visitProgramClass(ProgramClass programClass) { programClass.attributeAccept(Attribute.ENCLOSING_METHOD, this); } @Override public void visitLibraryClass(LibraryClass libraryClass) { // TODO: Not supported } @Override public void visitEnclosingMethodAttribute( Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) { Method enclosingMethod = enclosingMethodAttribute.referencedMethod; Clazz enclosingMethodClass = enclosingMethodAttribute.referencedClass; if (enclosingMethod != null && currentFunction.jvmSignature != null && enclosingMethod .getName(enclosingMethodClass) .equals(currentFunction.jvmSignature.method)) { currentFunction.referencedMethod = enclosingMethod; currentFunction.referencedMethodClass = enclosingMethodClass; } } } /** * This AttributeVisitor finds the parent of the visited companion class and passes it to a * KotlinInterClassPropertyReferenceInitializer. */ private class KotlinCompanionParentPropertyInitializer implements ClassVisitor, AttributeVisitor, InnerClassesInfoVisitor, ConstantVisitor { // Implements for ClassVisitor @Override public void visitAnyClass(Clazz clazz) {} @Override public void visitProgramClass(ProgramClass programClass) { programClass.attributeAccept(Attribute.INNER_CLASSES, this); } @Override public void visitLibraryClass(LibraryClass libraryClass) { // Since `LibraryClass` doesn't have attributes, use a heuristic shortcut int dollarIndex = libraryClass.getName().lastIndexOf('$'); if (dollarIndex != -1) { Clazz outerClass = findClass(libraryClass, libraryClass.getName().substring(0, dollarIndex)); initProperty(libraryClass, outerClass); } } // Implementations for AttributeVisitor. @Override public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @Override public void visitInnerClassesAttribute( Clazz clazz, InnerClassesAttribute innerClassesAttribute) { innerClassesAttribute.innerClassEntriesAccept(clazz, this); } // Implementations for InnerClassesInfoVisitor. @Override public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) { innerClassesInfo.outerClassConstantAccept(clazz, this); } // Implementations for ConstantVisitor. @Override public void visitAnyConstant(Clazz clazz, Constant constant) {} @Override public void visitClassConstant(Clazz clazz, ClassConstant classConstant) { initProperty(clazz, classConstant.referencedClass); } private void initProperty(Clazz thisClazz, Clazz parentClazz) { if (parentClazz != null) { thisClazz.kotlinMetadataAccept( new AllPropertyVisitor(new KotlinInterClassPropertyReferenceInitializer(parentClazz))); } } } /** * This {@link InvalidReferenceVisitor} will print out missing references to the supplied warning * printers. */ private static class InvalidReferenceWarningVisitor implements InvalidReferenceVisitor { private final WarningPrinter missingClassWarningPrinter; private final WarningPrinter missingProgramMemberWarningPrinter; private final WarningPrinter missingLibraryMemberWarningPrinter; private final WarningPrinter dependencyWarningPrinter; public InvalidReferenceWarningVisitor( WarningPrinter missingClassWarningPrinter, WarningPrinter missingProgramMemberWarningPrinter, WarningPrinter missingLibraryMemberWarningPrinter, WarningPrinter dependencyWarningPrinter) { this.missingClassWarningPrinter = missingClassWarningPrinter; this.missingProgramMemberWarningPrinter = missingProgramMemberWarningPrinter; this.missingLibraryMemberWarningPrinter = missingLibraryMemberWarningPrinter; this.dependencyWarningPrinter = dependencyWarningPrinter; } // Implementations for InvalidReferenceVisitor. @Override public void visitMissingClass(Clazz referencingClazz, String reference) { if (missingClassWarningPrinter != null) { String className = referencingClazz.getName(); missingClassWarningPrinter.print( className, reference, "Warning: " + ClassUtil.externalClassName(className) + ": can't find referenced class " + ClassUtil.externalClassName(reference)); } } @Override public void visitProgramDependency(Clazz referencingClazz, Clazz dependency) { if (dependencyWarningPrinter != null) { dependencyWarningPrinter.print( referencingClazz.getName(), dependency.getName(), "Warning: library class " + ClassUtil.externalClassName(referencingClazz.getName()) + " depends on program class " + ClassUtil.externalClassName(dependency.getName())); } } @Override public void visitAnyMissingMember( Clazz referencingClazz, Clazz reference, String name, String type) {} @Override public void visitAnyMissingField( Clazz referencingClazz, Clazz reference, String name, String type) { boolean isProgramClass = reference instanceof ProgramClass; WarningPrinter missingMemberWarningPrinter = isProgramClass ? missingProgramMemberWarningPrinter : missingLibraryMemberWarningPrinter; if (missingMemberWarningPrinter != null) { missingMemberWarningPrinter.print( referencingClazz.getName(), reference.getName(), "Warning: " + ClassUtil.externalClassName(referencingClazz.getName()) + ": can't find referenced field '" + ClassUtil.externalFullFieldDescription(0, name, type) + "' in " + (isProgramClass ? "program" : "library") + " class " + ClassUtil.externalClassName(reference.getName())); } } @Override public void visitAnyMissingMethod( Clazz referencingClazz, Clazz reference, String name, String type) { boolean isProgramClass = reference instanceof ProgramClass; WarningPrinter missingMemberWarningPrinter = isProgramClass ? missingProgramMemberWarningPrinter : missingLibraryMemberWarningPrinter; if (missingMemberWarningPrinter != null) { missingMemberWarningPrinter.print( referencingClazz.getName(), reference.getName(), "Warning: " + ClassUtil.externalClassName(referencingClazz.getName()) + ": can't find referenced method '" + ClassUtil.externalFullMethodDescription(reference.getName(), 0, name, type) + "' in " + (isProgramClass ? "program" : "library") + " class " + ClassUtil.externalClassName(reference.getName())); } } @Override public void visitMissingEnclosingMethod( Clazz enclosingClazz, Clazz reference, String name, String type) { if (missingProgramMemberWarningPrinter != null) { String className = enclosingClazz.getName(); missingProgramMemberWarningPrinter.print( className, reference.getName(), "Warning: " + ClassUtil.externalClassName(className) + ": can't find enclosing method '" + ClassUtil.externalFullMethodDescription(reference.getName(), 0, name, type) + "' in program class " + ClassUtil.externalClassName(reference.getName())); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy