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 }