1 /* 2 * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.replacements.processor; 26 27 import static org.graalvm.compiler.processor.AbstractProcessor.getAnnotationValue; 28 import static org.graalvm.compiler.processor.AbstractProcessor.getSimpleName; 29 import static org.graalvm.compiler.replacements.processor.ClassSubstitutionHandler.CLASS_SUBSTITUTION_CLASS_NAME; 30 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.List; 34 35 import javax.annotation.processing.Messager; 36 import javax.lang.model.element.AnnotationMirror; 37 import javax.lang.model.element.Element; 38 import javax.lang.model.element.ElementKind; 39 import javax.lang.model.element.ExecutableElement; 40 import javax.lang.model.element.Modifier; 41 import javax.lang.model.element.Name; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.element.VariableElement; 44 import javax.lang.model.type.TypeKind; 45 import javax.lang.model.type.TypeMirror; 46 import javax.lang.model.util.ElementFilter; 47 import javax.tools.Diagnostic.Kind; 48 49 import org.graalvm.compiler.processor.AbstractProcessor; 50 51 /** 52 * Handler for the {@value #METHOD_SUBSTITUTION_CLASS_NAME} annotation. 53 */ 54 public final class MethodSubstitutionHandler extends AnnotationHandler { 55 56 private static final String METHOD_SUBSTITUTION_CLASS_NAME = "org.graalvm.compiler.api.replacements.MethodSubstitution"; 57 58 private static final boolean DEBUG = false; 59 60 private static final String ORIGINAL_METHOD_NAME = "value"; 61 private static final String ORIGINAL_IS_STATIC = "isStatic"; 62 private static final String ORIGINAL_SIGNATURE = "signature"; 63 64 private static final String ORIGINAL_METHOD_NAME_DEFAULT = ""; 65 private static final String ORIGINAL_SIGNATURE_DEFAULT = ""; 66 MethodSubstitutionHandler(AbstractProcessor processor)67 public MethodSubstitutionHandler(AbstractProcessor processor) { 68 super(processor, METHOD_SUBSTITUTION_CLASS_NAME); 69 } 70 71 @SuppressWarnings("unused") 72 @Override process(Element element, AnnotationMirror annotation, PluginGenerator generator)73 public void process(Element element, AnnotationMirror annotation, PluginGenerator generator) { 74 if (element.getKind() != ElementKind.METHOD) { 75 assert false : "Element is guaranteed to be a method."; 76 return; 77 } 78 ExecutableElement substitutionMethod = (ExecutableElement) element; 79 TypeElement substitutionType = findEnclosingClass(substitutionMethod); 80 assert substitutionType != null; 81 82 Messager messager = processor.env().getMessager(); 83 AnnotationMirror substitutionClassAnnotation = processor.getAnnotation(substitutionType, processor.getType(CLASS_SUBSTITUTION_CLASS_NAME)); 84 if (substitutionClassAnnotation == null) { 85 messager.printMessage(Kind.ERROR, String.format("A @%s annotation is required on the enclosing class.", getSimpleName(CLASS_SUBSTITUTION_CLASS_NAME)), element, annotation); 86 return; 87 } 88 boolean optional = getAnnotationValue(substitutionClassAnnotation, "optional", Boolean.class); 89 if (optional) { 90 return; 91 } 92 93 TypeElement originalType = ClassSubstitutionHandler.resolveOriginalType(processor, substitutionType, substitutionClassAnnotation); 94 if (originalType == null) { 95 messager.printMessage(Kind.ERROR, String.format("The @%s annotation is invalid on the enclosing class.", getSimpleName(CLASS_SUBSTITUTION_CLASS_NAME)), element, annotation); 96 return; 97 } 98 99 if (!substitutionMethod.getModifiers().contains(Modifier.STATIC)) { 100 messager.printMessage(Kind.ERROR, String.format("A @%s method must be static.", getSimpleName(METHOD_SUBSTITUTION_CLASS_NAME)), element, annotation); 101 } 102 103 if (substitutionMethod.getModifiers().contains(Modifier.ABSTRACT) || substitutionMethod.getModifiers().contains(Modifier.NATIVE)) { 104 messager.printMessage(Kind.ERROR, String.format("A @%s method must not be native or abstract.", getSimpleName(METHOD_SUBSTITUTION_CLASS_NAME)), element, annotation); 105 } 106 107 String originalName = originalName(substitutionMethod, annotation); 108 boolean isStatic = getAnnotationValue(annotation, ORIGINAL_IS_STATIC, Boolean.class); 109 TypeMirror[] originalSignature = originalSignature(originalType, substitutionMethod, annotation, isStatic); 110 if (originalSignature == null) { 111 return; 112 } 113 ExecutableElement originalMethod = originalMethod(substitutionMethod, annotation, originalType, originalName, originalSignature, isStatic); 114 if (DEBUG && originalMethod != null) { 115 messager.printMessage(Kind.NOTE, String.format("Found original method %s in type %s.", originalMethod, findEnclosingClass(originalMethod))); 116 } 117 } 118 originalSignature(TypeElement originalType, ExecutableElement method, AnnotationMirror annotation, boolean isStatic)119 private TypeMirror[] originalSignature(TypeElement originalType, ExecutableElement method, AnnotationMirror annotation, boolean isStatic) { 120 String signatureString = getAnnotationValue(annotation, ORIGINAL_SIGNATURE, String.class); 121 List<TypeMirror> parameters = new ArrayList<>(); 122 Messager messager = processor.env().getMessager(); 123 if (signatureString.equals(ORIGINAL_SIGNATURE_DEFAULT)) { 124 for (int i = 0; i < method.getParameters().size(); i++) { 125 parameters.add(method.getParameters().get(i).asType()); 126 } 127 if (!isStatic) { 128 if (parameters.isEmpty()) { 129 messager.printMessage(Kind.ERROR, "Method signature must be a static method with the 'this' object as its first parameter", method, annotation); 130 return null; 131 } else { 132 TypeMirror thisParam = parameters.remove(0); 133 if (!isSubtype(originalType.asType(), thisParam)) { 134 Name thisName = method.getParameters().get(0).getSimpleName(); 135 messager.printMessage(Kind.ERROR, String.format("The type of %s must assignable from %s", thisName, originalType), method, annotation); 136 } 137 } 138 } 139 parameters.add(0, method.getReturnType()); 140 } else { 141 try { 142 APHotSpotSignature signature = new APHotSpotSignature(signatureString); 143 parameters.add(signature.getReturnType(processor.env())); 144 for (int i = 0; i < signature.getParameterCount(false); i++) { 145 parameters.add(signature.getParameterType(processor.env(), i)); 146 } 147 } catch (Exception e) { 148 /* 149 * That's not good practice and should be changed after APHotSpotSignature has 150 * received a cleanup. 151 */ 152 messager.printMessage(Kind.ERROR, String.format("Parsing the signature failed: %s", e.getMessage() != null ? e.getMessage() : e.toString()), method, annotation); 153 return null; 154 } 155 } 156 return parameters.toArray(new TypeMirror[parameters.size()]); 157 } 158 originalName(ExecutableElement substituteMethod, AnnotationMirror substitution)159 private static String originalName(ExecutableElement substituteMethod, AnnotationMirror substitution) { 160 String originalMethodName = getAnnotationValue(substitution, ORIGINAL_METHOD_NAME, String.class); 161 if (originalMethodName.equals(ORIGINAL_METHOD_NAME_DEFAULT)) { 162 originalMethodName = substituteMethod.getSimpleName().toString(); 163 } 164 return originalMethodName; 165 } 166 originalMethod(ExecutableElement substitutionMethod, AnnotationMirror substitutionAnnotation, TypeElement originalType, String originalName, TypeMirror[] originalSignature, boolean isStatic)167 private ExecutableElement originalMethod(ExecutableElement substitutionMethod, AnnotationMirror substitutionAnnotation, TypeElement originalType, String originalName, 168 TypeMirror[] originalSignature, boolean isStatic) { 169 TypeMirror signatureReturnType = originalSignature[0]; 170 TypeMirror[] signatureParameters = Arrays.copyOfRange(originalSignature, 1, originalSignature.length); 171 List<ExecutableElement> searchElements; 172 if (originalName.equals("<init>")) { 173 searchElements = ElementFilter.constructorsIn(originalType.getEnclosedElements()); 174 } else { 175 searchElements = ElementFilter.methodsIn(originalType.getEnclosedElements()); 176 } 177 178 Messager messager = processor.env().getMessager(); 179 ExecutableElement originalMethod = null; 180 outer: for (ExecutableElement searchElement : searchElements) { 181 if (searchElement.getSimpleName().toString().equals(originalName) && searchElement.getParameters().size() == signatureParameters.length) { 182 for (int i = 0; i < signatureParameters.length; i++) { 183 VariableElement parameter = searchElement.getParameters().get(i); 184 if (!isTypeCompatible(parameter.asType(), signatureParameters[i])) { 185 continue outer; 186 } 187 } 188 originalMethod = searchElement; 189 break; 190 } 191 } 192 if (originalMethod == null) { 193 boolean optional = getAnnotationValue(substitutionAnnotation, "optional", Boolean.class); 194 if (!optional) { 195 messager.printMessage(Kind.ERROR, 196 String.format("Could not find the original method with name '%s' and parameters '%s'.", originalName, Arrays.toString(signatureParameters)), 197 substitutionMethod, substitutionAnnotation); 198 } 199 return null; 200 } 201 202 if (originalMethod.getModifiers().contains(Modifier.STATIC) != isStatic) { 203 boolean optional = getAnnotationValue(substitutionAnnotation, "optional", Boolean.class); 204 if (!optional) { 205 messager.printMessage(Kind.ERROR, String.format("The %s element must be set to %s.", ORIGINAL_IS_STATIC, !isStatic), substitutionMethod, substitutionAnnotation); 206 } 207 return null; 208 } 209 210 if (!isTypeCompatible(originalMethod.getReturnType(), signatureReturnType)) { 211 messager.printMessage( 212 Kind.ERROR, 213 String.format("The return type of the substitution method '%s' must match with the return type of the original method '%s'.", signatureReturnType, 214 originalMethod.getReturnType()), 215 substitutionMethod, substitutionAnnotation); 216 return null; 217 } 218 219 return originalMethod; 220 } 221 isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType)222 private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) { 223 TypeMirror original = originalType; 224 TypeMirror substitution = substitutionType; 225 if (needsErasure(original)) { 226 original = processor.env().getTypeUtils().erasure(original); 227 } 228 if (needsErasure(substitution)) { 229 substitution = processor.env().getTypeUtils().erasure(substitution); 230 } 231 return processor.env().getTypeUtils().isSameType(original, substitution); 232 } 233 234 /** 235 * Tests whether one type is a subtype of another. Any type is considered to be a subtype of 236 * itself. 237 * 238 * @param t1 the first type 239 * @param t2 the second type 240 * @return {@code true} if and only if the first type is a subtype of the second 241 */ isSubtype(TypeMirror t1, TypeMirror t2)242 private boolean isSubtype(TypeMirror t1, TypeMirror t2) { 243 TypeMirror t1Erased = t1; 244 TypeMirror t2Erased = t2; 245 if (needsErasure(t1Erased)) { 246 t1Erased = processor.env().getTypeUtils().erasure(t1Erased); 247 } 248 if (needsErasure(t2Erased)) { 249 t2Erased = processor.env().getTypeUtils().erasure(t2Erased); 250 } 251 return processor.env().getTypeUtils().isSubtype(t1Erased, t2Erased); 252 } 253 needsErasure(TypeMirror typeMirror)254 private static boolean needsErasure(TypeMirror typeMirror) { 255 return typeMirror.getKind() != TypeKind.NONE && typeMirror.getKind() != TypeKind.VOID && !typeMirror.getKind().isPrimitive() && typeMirror.getKind() != TypeKind.OTHER && 256 typeMirror.getKind() != TypeKind.NULL; 257 } 258 findEnclosingClass(Element element)259 private static TypeElement findEnclosingClass(Element element) { 260 if (element.getKind().isClass()) { 261 return (TypeElement) element; 262 } 263 264 Element enclosing = element.getEnclosingElement(); 265 while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { 266 if (enclosing.getKind().isClass()) { 267 return (TypeElement) enclosing; 268 } 269 enclosing = enclosing.getEnclosingElement(); 270 } 271 return null; 272 } 273 274 } 275