--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.replacements.verifier/src/org/graalvm/compiler/replacements/verifier/NodeIntrinsicVerifier.java 2016-12-09 00:56:56.961045110 -0800 @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.replacements.verifier; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.util.ElementFilter; +import javax.tools.Diagnostic.Kind; + +import org.graalvm.compiler.graph.Node.ConstantNodeParameter; +import org.graalvm.compiler.graph.Node.InjectedNodeParameter; +import org.graalvm.compiler.graph.Node.NodeIntrinsic; +import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodeinfo.NodeInfo; +import org.graalvm.compiler.nodeinfo.StructuralInput.MarkerType; + +public final class NodeIntrinsicVerifier extends AbstractVerifier { + + private static final String NODE_CLASS_NAME = "value"; + + private TypeMirror nodeType() { + return env.getElementUtils().getTypeElement("org.graalvm.compiler.graph.Node").asType(); + } + + private TypeMirror valueNodeType() { + return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodes.ValueNode").asType(); + } + + private TypeMirror classType() { + return env.getElementUtils().getTypeElement("java.lang.Class").asType(); + } + + private TypeMirror resolvedJavaTypeType() { + return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaType").asType(); + } + + private TypeMirror resolvedJavaMethodType() { + return env.getElementUtils().getTypeElement("jdk.vm.ci.meta.ResolvedJavaMethod").asType(); + } + + private TypeMirror structuralInputType() { + return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodeinfo.StructuralInput").asType(); + } + + private TypeMirror graphBuilderContextType() { + return env.getElementUtils().getTypeElement("org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext").asType(); + } + + public NodeIntrinsicVerifier(ProcessingEnvironment env) { + super(env); + } + + @Override + public Class getAnnotationClass() { + return NodeIntrinsic.class; + } + + @Override + public void verify(Element element, AnnotationMirror annotation, PluginGenerator generator) { + if (element.getKind() != ElementKind.METHOD) { + assert false : "Element is guaranteed to be a method."; + return; + } + + ExecutableElement intrinsicMethod = (ExecutableElement) element; + if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) { + env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be static.", NodeIntrinsic.class.getSimpleName()), element, annotation); + } + if (!intrinsicMethod.getModifiers().contains(Modifier.NATIVE)) { + env.getMessager().printMessage(Kind.ERROR, String.format("A @%s method must be native.", NodeIntrinsic.class.getSimpleName()), element, annotation); + } + + TypeMirror nodeClassMirror = resolveAnnotationValue(TypeMirror.class, findAnnotationValue(annotation, NODE_CLASS_NAME)); + TypeElement nodeClass = (TypeElement) env.getTypeUtils().asElement(nodeClassMirror); + if (nodeClass.getSimpleName().contentEquals(NodeIntrinsic.class.getSimpleName())) { + // default value + Element enclosingElement = intrinsicMethod.getEnclosingElement(); + while (enclosingElement != null && enclosingElement.getKind() != ElementKind.CLASS) { + enclosingElement = enclosingElement.getEnclosingElement(); + } + if (enclosingElement != null) { + nodeClass = (TypeElement) enclosingElement; + } + } + + if (intrinsicMethod.getReturnType() instanceof TypeVariable) { + env.getMessager().printMessage(Kind.ERROR, "@NodeIntrinsic cannot have a generic return type.", element, annotation); + } + + TypeMirror[] constructorSignature = constructorSignature(intrinsicMethod); + ExecutableElement custom = findCustomIntrinsifyMethod(nodeClass, constructorSignature); + if (custom != null) { + generator.addPlugin(new GeneratedNodeIntrinsicPlugin.CustomFactoryPlugin(intrinsicMethod, custom, constructorSignature)); + } else { + if (isNodeType(nodeClass)) { + if (nodeClass.getModifiers().contains(Modifier.ABSTRACT)) { + env.getMessager().printMessage(Kind.ERROR, String.format("Cannot make @NodeIntrinsic for abstract node class %s.", nodeClass.getSimpleName()), element, annotation); + } else { + TypeMirror ret = intrinsicMethod.getReturnType(); + if (env.getTypeUtils().isAssignable(ret, structuralInputType())) { + checkInputType(nodeClass, ret, element, annotation); + } + + ExecutableElement constructor = findConstructor(nodeClass, constructorSignature, intrinsicMethod, annotation); + if (constructor != null) { + generator.addPlugin(new GeneratedNodeIntrinsicPlugin.ConstructorPlugin(intrinsicMethod, constructor, constructorSignature)); + } + } + } else { + env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is not a Node subclass.", nodeClass.getSimpleName()), element, annotation); + } + } + } + + private void checkInputType(TypeElement nodeClass, TypeMirror returnType, Element element, AnnotationMirror annotation) { + InputType inputType = getInputType(returnType, element, annotation); + if (inputType != InputType.Value) { + boolean allowed = false; + InputType[] allowedTypes = nodeClass.getAnnotation(NodeInfo.class).allowedUsageTypes(); + for (InputType allowedType : allowedTypes) { + if (inputType == allowedType) { + allowed = true; + break; + } + } + if (!allowed) { + env.getMessager().printMessage(Kind.ERROR, String.format("@NodeIntrinsic returns input type %s, but only %s is allowed.", inputType, Arrays.toString(allowedTypes)), element, + annotation); + } + } + } + + private InputType getInputType(TypeMirror type, Element element, AnnotationMirror annotation) { + TypeElement current = (TypeElement) env.getTypeUtils().asElement(type); + while (current != null) { + MarkerType markerType = current.getAnnotation(MarkerType.class); + if (markerType != null) { + return markerType.value(); + } + + current = (TypeElement) env.getTypeUtils().asElement(current.getSuperclass()); + } + + env.getMessager().printMessage(Kind.ERROR, String.format("The class %s is a subclass of StructuralInput, but isn't annotated with @MarkerType.", type), element, annotation); + return InputType.Value; + } + + private boolean isNodeType(TypeElement nodeClass) { + return env.getTypeUtils().isSubtype(nodeClass.asType(), nodeType()); + } + + private TypeMirror[] constructorSignature(ExecutableElement method) { + TypeMirror[] parameters = new TypeMirror[method.getParameters().size()]; + for (int i = 0; i < method.getParameters().size(); i++) { + VariableElement parameter = method.getParameters().get(i); + if (parameter.getAnnotation(ConstantNodeParameter.class) == null) { + parameters[i] = valueNodeType(); + } else { + TypeMirror type = parameter.asType(); + if (isTypeCompatible(type, classType())) { + type = resolvedJavaTypeType(); + } + parameters[i] = type; + } + } + return parameters; + } + + private ExecutableElement findConstructor(TypeElement nodeClass, TypeMirror[] signature, ExecutableElement intrinsicMethod, AnnotationMirror intrinsicAnnotation) { + List constructors = ElementFilter.constructorsIn(nodeClass.getEnclosedElements()); + List failureReasons = new ArrayList<>(); + + for (ExecutableElement constructor : constructors) { + String failureReason = matchSignature(0, constructor, signature); + if (failureReason == null) { + // found + return constructor; + } + + failureReasons.add(failureReason); + } + + // not found + if (failureReasons.isEmpty()) { + env.getMessager().printMessage(Kind.ERROR, "Could not find matching constructor for node intrinsic.", intrinsicMethod, intrinsicAnnotation); + } else { + for (String reason : failureReasons) { + env.getMessager().printMessage(Kind.ERROR, reason, intrinsicMethod, intrinsicAnnotation); + } + } + + return null; + } + + private ExecutableElement findCustomIntrinsifyMethod(TypeElement nodeClass, TypeMirror[] signature) { + List methods = ElementFilter.methodsIn(nodeClass.getEnclosedElements()); + for (ExecutableElement method : methods) { + if (!method.getSimpleName().toString().equals("intrinsify")) { + continue; + } + + if (method.getParameters().size() < 2) { + continue; + } + + VariableElement firstArg = method.getParameters().get(0); + if (!isTypeCompatible(firstArg.asType(), graphBuilderContextType())) { + continue; + } + + VariableElement secondArg = method.getParameters().get(1); + if (!isTypeCompatible(secondArg.asType(), resolvedJavaMethodType())) { + continue; + } + + String failureReason = matchSignature(2, method, signature); + if (failureReason == null) { + // found + return method; + } + } + + return null; + } + + private String matchSignature(int numSkippedParameters, ExecutableElement method, TypeMirror[] signature) { + int sIdx = 0; + int cIdx = numSkippedParameters; + while (cIdx < method.getParameters().size()) { + VariableElement parameter = method.getParameters().get(cIdx++); + if (parameter.getAnnotation(InjectedNodeParameter.class) != null) { + // skip injected parameters + continue; + } + + TypeMirror paramType = parameter.asType(); + if (cIdx == method.getParameters().size() && paramType.getKind() == TypeKind.ARRAY) { + // last argument of constructor is varargs, match remaining intrinsic arguments + TypeMirror varargsType = ((ArrayType) paramType).getComponentType(); + while (sIdx < signature.length) { + if (!isTypeCompatible(varargsType, signature[sIdx++])) { + return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, varargsType, signature[sIdx - 1]); + } + } + } else if (sIdx >= signature.length) { + // too many arguments in intrinsic method + return String.format("Too many arguments for %s", method); + } else if (!isTypeCompatible(paramType, signature[sIdx++])) { + return String.format("%s failed because the types of argument %d are incompatible: %s != %s", method, sIdx, paramType, signature[sIdx - 1]); + } + } + + if (sIdx == signature.length) { + // found + return null; + } + + // too many arguments in constructor + return String.format("Not enough arguments for %s", method); + } + + private boolean isTypeCompatible(TypeMirror originalType, TypeMirror substitutionType) { + TypeMirror original = originalType; + TypeMirror substitution = substitutionType; + if (needsErasure(original)) { + original = env.getTypeUtils().erasure(original); + } + if (needsErasure(substitution)) { + substitution = env.getTypeUtils().erasure(substitution); + } + return env.getTypeUtils().isSameType(original, substitution); + } + + private static boolean needsErasure(TypeMirror typeMirror) { + return typeMirror.getKind() != TypeKind.NONE && typeMirror.getKind() != TypeKind.VOID && !typeMirror.getKind().isPrimitive() && typeMirror.getKind() != TypeKind.OTHER && + typeMirror.getKind() != TypeKind.NULL; + } +}