001/* 002 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements.verifier; 024 025import java.lang.annotation.*; 026import java.util.*; 027 028import javax.annotation.processing.*; 029import javax.lang.model.element.*; 030import javax.lang.model.type.*; 031import javax.lang.model.util.*; 032import javax.tools.Diagnostic.Kind; 033 034import com.oracle.graal.graph.Node.ConstantNodeParameter; 035import com.oracle.graal.graph.Node.InjectedNodeParameter; 036import com.oracle.graal.graph.Node.NodeIntrinsic; 037import com.oracle.graal.nodeinfo.*; 038import com.oracle.graal.nodeinfo.StructuralInput.MarkerType; 039 040public final class NodeIntrinsicVerifier extends AbstractVerifier { 041 042 private static final String NODE_CLASS_NAME = "value"; 043 044 private TypeMirror nodeType() { 045 return env.getElementUtils().getTypeElement("com.oracle.graal.graph.Node").asType(); 046 } 047 048 private TypeMirror valueNodeType() { 049 return env.getElementUtils().getTypeElement("com.oracle.graal.nodes.ValueNode").asType(); 050 } 051 052 private TypeMirror classType() { 053 return env.getElementUtils().getTypeElement("java.lang.Class").asType(); 054 } 055 056 private TypeMirror resolvedJavaTypeType() { 057 return env.getElementUtils().getTypeElement("jdk.internal.jvmci.meta.ResolvedJavaType").asType(); 058 } 059 060 private TypeMirror structuralInputType() { 061 return env.getElementUtils().getTypeElement("com.oracle.graal.nodeinfo.StructuralInput").asType(); 062 } 063 064 public NodeIntrinsicVerifier(ProcessingEnvironment env) { 065 super(env); 066 } 067 068 @Override 069 public Class<? extends Annotation> getAnnotationClass() { 070 return NodeIntrinsic.class; 071 } 072 073 @Override 074 public void verify(Element element, AnnotationMirror annotation) { 075 if (element.getKind() != ElementKind.METHOD) { 076 assert false : "Element is guaranteed to be a method."; 077 return; 078 } 079 080 ExecutableElement intrinsicMethod = (ExecutableElement) element; 081 if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) { 082 env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be static.", NodeIntrinsic.class.getSimpleName()), element, annotation); 083 } 084 if (!intrinsicMethod.getModifiers().contains(Modifier.NATIVE)) { 085 env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be native.", NodeIntrinsic.class.getSimpleName()), element, annotation); 086 } 087 088 TypeMirror nodeClassMirror = resolveAnnotationValue(TypeMirror.class, findAnnotationValue(annotation, NODE_CLASS_NAME)); 089 TypeElement nodeClass = (TypeElement) env.getTypeUtils().asElement(nodeClassMirror); 090 if (nodeClass.getSimpleName().contentEquals(NodeIntrinsic.class.getSimpleName())) { 091 // default value 092 Element enclosingElement = intrinsicMethod.getEnclosingElement(); 093 while (enclosingElement != null && enclosingElement.getKind() != ElementKind.CLASS) { 094 enclosingElement = enclosingElement.getEnclosingElement(); 095 } 096 if (enclosingElement != null) { 097 nodeClass = (TypeElement) enclosingElement; 098 } 099 } 100 101 if (intrinsicMethod.getReturnType() instanceof TypeVariable) { 102 env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic cannot have a generic return type.", element, annotation); 103 } 104 105 if (isNodeType(nodeClass)) { 106 if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) { 107 env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation); 108 } else { 109 TypeMirror ret = intrinsicMethod.getReturnType(); 110 if (env.getTypeUtils().isAssignable(ret, structuralInputType())) { 111 checkInputType(nodeClass, ret, element, annotation); 112 } 113 114 TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod); 115 findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation); 116 } 117 } else { 118 env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation); 119 } 120 } 121 122 private void checkInputType(TypeElement nodeClass, TypeMirror returnType, Element element, AnnotationMirror annotation) { 123 InputType inputType = getInputType(returnType, element, annotation); 124 if (inputType != InputType.Value) { 125 boolean allowed = false; 126 InputType[] allowedTypes = nodeClass.getAnnotation(NodeInfo.class).allowedUsageTypes(); 127 for (InputType allowedType : allowedTypes) { 128 if (inputType == allowedType) { 129 allowed = true; 130 break; 131 } 132 } 133 if (!allowed) { 134 env.getMessager().printMessage(Kind.ERROR, String.format("@NodeIntrinsic returns input type %s, but only %s is allowed.", inputType, Arrays.toString(allowedTypes)), element, 135 annotation); 136 } 137 } 138 } 139 140 private InputType getInputType(TypeMirror type, Element element, AnnotationMirror annotation) { 141 TypeElement current = (TypeElement) env.getTypeUtils().asElement(type); 142 while (current != null) { 143 MarkerType markerType = current.getAnnotation(MarkerType.class); 144 if (markerType != null) { 145 return markerType.value(); 146 } 147 148 current = (TypeElement) env.getTypeUtils().asElement(current.getSuperclass()); 149 } 150 151 env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is a subclass of StructuralInput, but isn't annotated with @MarkerType.", type), element, annotation); 152 return InputType.Value; 153 } 154 155 private boolean isNodeType(TypeElement nodeClass) { 156 return env.getTypeUtils().isSubtype(nodeClass.asType(), nodeType()); 157 } 158 159 private TypeMirror[] constructorSignature(ExecutableElement method) { 160 TypeMirror[] parameters = new TypeMirror[method.getParameters().size()]; 161 for (int i = 0; i < method.getParameters().size(); i++) { 162 VariableElement parameter = method.getParameters().get(i); 163 if (parameter.getAnnotation(ConstantNodeParameter.class) == null) { 164 parameters[i] = valueNodeType(); 165 } else { 166 TypeMirror type = parameter.asType(); 167 if (isTypeCompatible(type, classType())) { 168 type = resolvedJavaTypeType(); 169 } 170 parameters[i] = type; 171 } 172 } 173 return parameters; 174 } 175 176 private void findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) { 177 List<ExecutableElement> constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements()); 178 List<String> failureReasons = new ArrayList<>(); 179 180 nextConstructor: for (ExecutableElement constructor : constructors) { 181 int sIdx = 0; 182 int cIdx = 0; 183 while (cIdx < constructor.getParameters().size()) { 184 VariableElement parameter = constructor.getParameters().get(cIdx++); 185 if (parameter.getAnnotation(InjectedNodeParameter.class) != null) { 186 // skip injected parameters 187 continue; 188 } 189 190 TypeMirror paramType = parameter.asType(); 191 if (cIdx == constructor.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) { 192 // last argument of constructor is varargs, match remaining intrinsic arguments 193 TypeMirror varargsType = ((ArrayType) paramType).getComponentType(); 194 while (sIdx < signature.length) { 195 if (!isTypeCompatible(varargsType, signature[sIdx++])) { 196 failureReasons.add(String.format("Constructor %s failed because the types of argument %d are incompatible: %s != %s", constructor, sIdx, varargsType, signature[sIdx - 1])); 197 continue nextConstructor; 198 } 199 } 200 } else if (sIdx >= signature.length) { 201 // too many arguments in intrinsic method 202 failureReasons.add(String.format("Too many arguments for %s", constructor)); 203 continue nextConstructor; 204 } else if (!isTypeCompatible(paramType, signature[sIdx++])) { 205 failureReasons.add(String.format("Constructor %s failed because the types of argument %d are incompatible: %s != %s", constructor, sIdx, paramType, signature[sIdx - 1])); 206 continue nextConstructor; 207 } 208 } 209 210 if (sIdx == signature.length) { 211 // found 212 return; 213 } 214 215 // too many arguments in constructor 216 failureReasons.add(String.format("Not enough arguments for %s", constructor)); 217 } 218 219 // not found 220 if (failureReasons.isEmpty()) { 221 env.getMessager().printMessage(Kind.ERROR, "Could not find matching constructor for node intrinsic.", intrinsicMethod, intrinsicAnnotation); 222 } else { 223 for (String reason : failureReasons) { 224 env.getMessager().printMessage(Kind.ERROR, reason, intrinsicMethod, intrinsicAnnotation); 225 } 226 } 227 } 228 229 private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) { 230 TypeMirror original = originalType; 231 TypeMirror substitution = substitutionType; 232 if (needsErasure(original)) { 233 original = env.getTypeUtils().erasure(original); 234 } 235 if (needsErasure(substitution)) { 236 substitution = env.getTypeUtils().erasure(substitution); 237 } 238 return env.getTypeUtils().isSameType(original, substitution); 239 } 240 241 private static boolean needsErasure(TypeMirror typeMirror) { 242 return typeMirror.getKind() != TypeKind.NONE && typeMirror.getKind() != TypeKind.VOID && !typeMirror.getKind().isPrimitive() && typeMirror.getKind() != TypeKind.OTHER && 243 typeMirror.getKind() != TypeKind.NULL; 244 } 245}