1 /* 2 * Copyright (C) 2022 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal.codegen.xprocessing; 18 19 import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv; 20 import static androidx.room.compiler.processing.compat.XConverters.toJavac; 21 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; 22 import static java.util.stream.Collectors.joining; 23 24 import androidx.room.compiler.codegen.XTypeNameKt; 25 import androidx.room.compiler.processing.XConstructorType; 26 import androidx.room.compiler.processing.XExecutableElement; 27 import androidx.room.compiler.processing.XExecutableType; 28 import androidx.room.compiler.processing.XMethodType; 29 import androidx.room.compiler.processing.XProcessingEnv; 30 import androidx.room.compiler.processing.XType; 31 import com.google.common.collect.ImmutableList; 32 import com.squareup.javapoet.TypeName; 33 34 /** A utility class for {@link XExecutableType} helper methods. */ 35 // TODO(bcorso): Consider moving these methods into XProcessing library. 36 public final class XExecutableTypes { 37 38 // TODO(b/271177465): Remove this method once XProcessing supports this feature. isSubsignature(XExecutableElement method1, XExecutableElement method2)39 public static boolean isSubsignature(XExecutableElement method1, XExecutableElement method2) { 40 XProcessingEnv processingEnv = getProcessingEnv(method1); 41 switch (processingEnv.getBackend()) { 42 case JAVAC: 43 return isSubsignatureJavac(method1, method2, processingEnv); 44 case KSP: 45 return isSubsignatureKsp(method1, method2); 46 } 47 throw new AssertionError("Unexpected backend: " + processingEnv.getBackend()); 48 } 49 isSubsignatureKsp(XExecutableElement method1, XExecutableElement method2)50 private static boolean isSubsignatureKsp(XExecutableElement method1, XExecutableElement method2) { 51 if (method1.getParameters().size() != method2.getParameters().size()) { 52 return false; 53 } 54 ImmutableList<TypeName> method1Parameters = getParameters(method1); 55 ImmutableList<TypeName> method1TypeParameters = getTypeParameters(method1); 56 ImmutableList<TypeName> method2TypeParameters = getTypeParameters(method2); 57 return (method1TypeParameters.equals(method2TypeParameters) 58 && method1Parameters.equals(getParameters(method2))) 59 || (method1TypeParameters 60 .isEmpty() // "The erasure of the signature of a generic method has no type 61 // parameters." 62 && method1Parameters.equals( 63 method2.getExecutableType().getParameterTypes().stream() 64 .map(XTypes::erasedTypeName) 65 .collect(toImmutableList()))); 66 } 67 getParameters(XExecutableElement method)68 private static ImmutableList<TypeName> getParameters(XExecutableElement method) { 69 return method.getExecutableType().getParameterTypes().stream() 70 .map(XType::asTypeName) 71 .map(XTypeNameKt::toJavaPoet) 72 .collect(toImmutableList()); 73 } 74 getTypeParameters(XExecutableElement method)75 private static ImmutableList<TypeName> getTypeParameters(XExecutableElement method) { 76 return method.getTypeParameters().stream() 77 .map(it -> it.getBounds().get(0)) 78 .map(XType::asTypeName) 79 .map(XTypeNameKt::toJavaPoet) 80 .collect(toImmutableList()); 81 } 82 isSubsignatureJavac( XExecutableElement method1, XExecutableElement method2, XProcessingEnv env)83 private static boolean isSubsignatureJavac( 84 XExecutableElement method1, XExecutableElement method2, XProcessingEnv env) { 85 return toJavac(env) 86 .getTypeUtils() // ALLOW_TYPES_ELEMENTS 87 .isSubsignature(toJavac(method1.getExecutableType()), toJavac(method2.getExecutableType())); 88 } 89 isConstructorType(XExecutableType executableType)90 public static boolean isConstructorType(XExecutableType executableType) { 91 return executableType instanceof XConstructorType; 92 } 93 isMethodType(XExecutableType executableType)94 public static boolean isMethodType(XExecutableType executableType) { 95 return executableType instanceof XMethodType; 96 } 97 asMethodType(XExecutableType executableType)98 public static XMethodType asMethodType(XExecutableType executableType) { 99 return (XMethodType) executableType; 100 } 101 getKindName(XExecutableType executableType)102 public static String getKindName(XExecutableType executableType) { 103 if (isMethodType(executableType)) { 104 return "METHOD"; 105 } else if (isConstructorType(executableType)) { 106 return "CONSTRUCTOR"; 107 } 108 return "UNKNOWN"; 109 } 110 111 /** 112 * Returns a string representation of {@link XExecutableType} that is independent of the backend 113 * (javac/ksp). 114 */ toStableString(XExecutableType executableType)115 public static String toStableString(XExecutableType executableType) { 116 try { 117 return String.format( 118 "(%s)%s", 119 executableType.getParameterTypes().stream() 120 .map(XTypes::toStableString) 121 .collect(joining(",")), 122 isMethodType(executableType) 123 ? XTypes.toStableString(asMethodType(executableType).getReturnType()) 124 : TypeName.VOID); 125 } catch (TypeNotPresentException e) { 126 return e.typeName(); 127 } 128 } 129 XExecutableTypes()130 private XExecutableTypes() {} 131 } 132