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}