1 /*
   2  * Copyright (c) 2014, 2015, 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 package org.graalvm.compiler.replacements.verifier;
  24 
  25 import java.lang.annotation.Annotation;
  26 import java.util.ArrayList;
  27 import java.util.Arrays;
  28 import java.util.List;
  29 
  30 import javax.annotation.processing.ProcessingEnvironment;
  31 import javax.lang.model.element.AnnotationMirror;
  32 import javax.lang.model.element.Element;
  33 import javax.lang.model.element.ElementKind;
  34 import javax.lang.model.element.ExecutableElement;
  35 import javax.lang.model.element.Modifier;
  36 import javax.lang.model.element.TypeElement;
  37 import javax.lang.model.element.VariableElement;
  38 import javax.lang.model.type.ArrayType;
  39 import javax.lang.model.type.TypeKind;
  40 import javax.lang.model.type.TypeMirror;
  41 import javax.lang.model.type.TypeVariable;
  42 import javax.lang.model.util.ElementFilter;
  43 import javax.tools.Diagnostic.Kind;
  44 
  45 import org.graalvm.compiler.graph.Node.ConstantNodeParameter;
  46 import org.graalvm.compiler.graph.Node.InjectedNodeParameter;
  47 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
  48 import org.graalvm.compiler.nodeinfo.InputType;
  49 import org.graalvm.compiler.nodeinfo.NodeInfo;
  50 import org.graalvm.compiler.nodeinfo.StructuralInput.MarkerType;
  51 
  52 public final class NodeIntrinsicVerifier extends AbstractVerifier {
  53 
  54     private static final String NODE_CLASS_NAME = "value";
  55 
  56     private TypeMirror nodeType() {
  57         return env.getElementUtils().getTypeElement("org.graalvm.compiler.graph.Node").asType();
  58     }
  59 
  60     private TypeMirror valueNodeType() {
  61         return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodes.ValueNode").asType();
  62     }
  63 
  64     private TypeMirror classType() {
  65         return env.getElementUtils().getTypeElement("java.lang.Class").asType();
  66     }
  67 
  68     private TypeMirror resolvedJavaTypeType() {
  69         return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaType").asType();
  70     }
  71 
  72     private TypeMirror resolvedJavaMethodType() {
  73         return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaMethod").asType();
  74     }
  75 
  76     private TypeMirror structuralInputType() {
  77         return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodeinfo.StructuralInput").asType();
  78     }
  79 
  80     private TypeMirror graphBuilderContextType() {
  81         return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext").asType();
  82     }
  83 
  84     public NodeIntrinsicVerifier(ProcessingEnvironment env) {
  85         super(env);
  86     }
  87 
  88     @Override
  89     public Class<? extends Annotation> getAnnotationClass() {
  90         return NodeIntrinsic.class;
  91     }
  92 
  93     @Override
  94     public void verify(Element element, AnnotationMirror annotation, PluginGenerator generator) {
  95         if (element.getKind() != ElementKind.METHOD) {
  96             assert false : "Element is guaranteed to be a method.";
  97             return;
  98         }
  99 
 100         ExecutableElement intrinsicMethod = (ExecutableElement) element;
 101         if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) {
 102             env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be static.", NodeIntrinsic.class.getSimpleName()), element, annotation);
 103         }
 104         if (!intrinsicMethod.getModifiers().contains(Modifier.NATIVE)) {
 105             env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be native.", NodeIntrinsic.class.getSimpleName()), element, annotation);
 106         }
 107 
 108         TypeMirror nodeClassMirror = resolveAnnotationValue(TypeMirror.class, findAnnotationValue(annotation, NODE_CLASS_NAME));
 109         TypeElement nodeClass = (TypeElement) env.getTypeUtils().asElement(nodeClassMirror);
 110         if (nodeClass.getSimpleName().contentEquals(NodeIntrinsic.class.getSimpleName())) {
 111             // default value
 112             Element enclosingElement = intrinsicMethod.getEnclosingElement();
 113             while (enclosingElement != null && enclosingElement.getKind() != ElementKind.CLASS) {
 114                 enclosingElement = enclosingElement.getEnclosingElement();
 115             }
 116             if (enclosingElement != null) {
 117                 nodeClass = (TypeElement) enclosingElement;
 118             }
 119         }
 120 
 121         if (intrinsicMethod.getReturnType() instanceof TypeVariable) {
 122             env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic cannot have a generic return type.", element, annotation);
 123         }
 124 
 125         TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod);
 126         ExecutableElement custom = findCustomIntrinsifyMethod(nodeClass, constructorSignature);
 127         if (custom != null) {
 128             generator.addPlugin(new GeneratedNodeIntrinsicPlugin.CustomFactoryPlugin(intrinsicMethod, custom, constructorSignature));
 129         } else {
 130             if (isNodeType(nodeClass)) {
 131                 if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) {
 132                     env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation);
 133                 } else {
 134                     TypeMirror ret = intrinsicMethod.getReturnType();
 135                     if (env.getTypeUtils().isAssignable(ret, structuralInputType())) {
 136                         checkInputType(nodeClass, ret, element, annotation);
 137                     }
 138 
 139                     ExecutableElement constructor = findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation);
 140                     if (constructor != null) {
 141                         generator.addPlugin(new GeneratedNodeIntrinsicPlugin.ConstructorPlugin(intrinsicMethod, constructor, constructorSignature));
 142                     }
 143                 }
 144             } else {
 145                 env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation);
 146             }
 147         }
 148     }
 149 
 150     private void checkInputType(TypeElement nodeClass, TypeMirror returnType, Element element, AnnotationMirror annotation) {
 151         InputType inputType = getInputType(returnType, element, annotation);
 152         if (inputType != InputType.Value) {
 153             boolean allowed = false;
 154             InputType[] allowedTypes = nodeClass.getAnnotation(NodeInfo.class).allowedUsageTypes();
 155             for (InputType allowedType : allowedTypes) {
 156                 if (inputType == allowedType) {
 157                     allowed = true;
 158                     break;
 159                 }
 160             }
 161             if (!allowed) {
 162                 env.getMessager().printMessage(Kind.ERROR, String.format("@NodeIntrinsic returns input type %s, but only %s is allowed.", inputType, Arrays.toString(allowedTypes)), element,
 163                                 annotation);
 164             }
 165         }
 166     }
 167 
 168     private InputType getInputType(TypeMirror type, Element element, AnnotationMirror annotation) {
 169         TypeElement current = (TypeElement) env.getTypeUtils().asElement(type);
 170         while (current != null) {
 171             MarkerType markerType = current.getAnnotation(MarkerType.class);
 172             if (markerType != null) {
 173                 return markerType.value();
 174             }
 175 
 176             current = (TypeElement) env.getTypeUtils().asElement(current.getSuperclass());
 177         }
 178 
 179         env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is a subclass of StructuralInput, but isn't annotated with @MarkerType.", type), element, annotation);
 180         return InputType.Value;
 181     }
 182 
 183     private boolean isNodeType(TypeElement nodeClass) {
 184         return env.getTypeUtils().isSubtype(nodeClass.asType(), nodeType());
 185     }
 186 
 187     private TypeMirror[] constructorSignature(ExecutableElement method) {
 188         TypeMirror[] parameters = new TypeMirror[method.getParameters().size()];
 189         for (int i = 0; i < method.getParameters().size(); i++) {
 190             VariableElement parameter = method.getParameters().get(i);
 191             if (parameter.getAnnotation(ConstantNodeParameter.class) == null) {
 192                 parameters[i] = valueNodeType();
 193             } else {
 194                 TypeMirror type = parameter.asType();
 195                 if (isTypeCompatible(type, classType())) {
 196                     type = resolvedJavaTypeType();
 197                 }
 198                 parameters[i] = type;
 199             }
 200         }
 201         return parameters;
 202     }
 203 
 204     private ExecutableElement findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) {
 205         List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements());
 206         List<String> failureReasons = new ArrayList<>();
 207 
 208         for (ExecutableElement constructor : constructors) {
 209             String failureReason = matchSignature(0, constructor, signature);
 210             if (failureReason == null) {
 211                 // found
 212                 return constructor;
 213             }
 214 
 215             failureReasons.add(failureReason);
 216         }
 217 
 218         // not found
 219         if (failureReasons.isEmpty()) {
 220             env.getMessager().printMessage(Kind.ERROR, "Could not find matching constructor for node intrinsic.", intrinsicMethod, intrinsicAnnotation);
 221         } else {
 222             for (String reason : failureReasons) {
 223                 env.getMessager().printMessage(Kind.ERROR, reason, intrinsicMethod, intrinsicAnnotation);
 224             }
 225         }
 226 
 227         return null;
 228     }
 229 
 230     private ExecutableElement findCustomIntrinsifyMethod(TypeElement nodeClass, TypeMirror[] signature) {
 231         List<ExecutableElement> methods = ElementFilter.methodsIn(nodeClass.getEnclosedElements());
 232         for (ExecutableElement method : methods) {
 233             if (!method.getSimpleName().toString().equals("intrinsify")) {
 234                 continue;
 235             }
 236 
 237             if (method.getParameters().size() < 2) {
 238                 continue;
 239             }
 240 
 241             VariableElement firstArg = method.getParameters().get(0);
 242             if (!isTypeCompatible(firstArg.asType(), graphBuilderContextType())) {
 243                 continue;
 244             }
 245 
 246             VariableElement secondArg = method.getParameters().get(1);
 247             if (!isTypeCompatible(secondArg.asType(), resolvedJavaMethodType())) {
 248                 continue;
 249             }
 250 
 251             String failureReason = matchSignature(2, method, signature);
 252             if (failureReason == null) {
 253                 // found
 254                 return method;
 255             }
 256         }
 257 
 258         return null;
 259     }
 260 
 261     private String matchSignature(int numSkippedParameters, ExecutableElement method, TypeMirror[] signature) {
 262         int sIdx = 0;
 263         int cIdx = numSkippedParameters;
 264         while (cIdx < method.getParameters().size()) {
 265             VariableElement parameter = method.getParameters().get(cIdx++);
 266             if (parameter.getAnnotation(InjectedNodeParameter.class) != null) {
 267                 // skip injected parameters
 268                 continue;
 269             }
 270 
 271             TypeMirror paramType = parameter.asType();
 272             if (cIdx == method.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) {
 273                 // last argument of constructor is varargs, match remaining intrinsic arguments
 274                 TypeMirror varargsType = ((ArrayType) paramType).getComponentType();
 275                 while (sIdx < signature.length) {
 276                     if (!isTypeCompatible(varargsType, signature[sIdx++])) {
 277                         return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, varargsType, signature[sIdx - 1]);
 278                     }
 279                 }
 280             } else if (sIdx >= signature.length) {
 281                 // too many arguments in intrinsic method
 282                 return String.format("Too many arguments for %s", method);
 283             } else if (!isTypeCompatible(paramType, signature[sIdx++])) {
 284                 return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, paramType, signature[sIdx - 1]);
 285             }
 286         }
 287 
 288         if (sIdx == signature.length) {
 289             // found
 290             return null;
 291         }
 292 
 293         // too many arguments in constructor
 294         return String.format("Not enough arguments for %s", method);
 295     }
 296 
 297     private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) {
 298         TypeMirror original = originalType;
 299         TypeMirror substitution = substitutionType;
 300         if (needsErasure(original)) {
 301             original = env.getTypeUtils().erasure(original);
 302         }
 303         if (needsErasure(substitution)) {
 304             substitution = env.getTypeUtils().erasure(substitution);
 305         }
 306         return env.getTypeUtils().isSameType(original, substitution);
 307     }
 308 
 309     private static boolean needsErasure(TypeMirror typeMirror) {
 310         return typeMirror.getKind() != TypeKind.NONE && typeMirror.getKind() != TypeKind.VOID && !typeMirror.getKind().isPrimitive() && typeMirror.getKind() != TypeKind.OTHER &&
 311                         typeMirror.getKind() != TypeKind.NULL;
 312     }
 313 }