1 /*
   2  * Copyright (c) 2015, 2018, 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 
  24 
  25 package org.graalvm.compiler.replacements.processor;
  26 
  27 import java.io.PrintWriter;
  28 import java.util.Set;
  29 
  30 import javax.lang.model.element.ExecutableElement;
  31 import javax.lang.model.element.Modifier;
  32 import javax.lang.model.element.TypeElement;
  33 import javax.lang.model.element.VariableElement;
  34 import javax.lang.model.type.ArrayType;
  35 import javax.lang.model.type.DeclaredType;
  36 import javax.lang.model.type.TypeMirror;
  37 import javax.lang.model.type.TypeVariable;
  38 import javax.lang.model.type.WildcardType;
  39 
  40 import org.graalvm.compiler.processor.AbstractProcessor;
  41 import org.graalvm.compiler.replacements.processor.InjectedDependencies.Dependency;
  42 import org.graalvm.compiler.replacements.processor.InjectedDependencies.WellKnownDependency;
  43 
  44 public abstract class GeneratedPlugin {
  45 
  46     protected final ExecutableElement intrinsicMethod;
  47     private boolean needInjectionProvider;
  48 
  49     private String pluginName;
  50 
  51     public GeneratedPlugin(ExecutableElement intrinsicMethod) {
  52         this.intrinsicMethod = intrinsicMethod;
  53         this.needInjectionProvider = false;
  54         // Lets keep generated class names short to mitigate hitting file name length limits.
  55         this.pluginName = intrinsicMethod.getSimpleName().toString();
  56     }
  57 
  58     protected abstract TypeElement getAnnotationClass(AbstractProcessor processor);
  59 
  60     public String getPluginName() {
  61         return pluginName;
  62     }
  63 
  64     public void setPluginName(String pluginName) {
  65         this.pluginName = pluginName;
  66     }
  67 
  68     public void generate(AbstractProcessor processor, PrintWriter out) {
  69         out.printf("    //        class: %s\n", intrinsicMethod.getEnclosingElement());
  70         out.printf("    //       method: %s\n", intrinsicMethod);
  71         out.printf("    // generated-by: %s\n", getClass().getName());
  72         out.printf("    private static final class %s extends GeneratedInvocationPlugin {\n", pluginName);
  73         out.printf("\n");
  74         out.printf("        @Override\n");
  75         out.printf("        public boolean execute(GraphBuilderContext b, ResolvedJavaMethod targetMethod, InvocationPlugin.Receiver receiver, ValueNode[] args) {\n");
  76         InjectedDependencies deps = createExecute(processor, out);
  77         out.printf("        }\n");
  78         out.printf("        @Override\n");
  79         out.printf("        public Class<? extends Annotation> getSource() {\n");
  80         out.printf("            return %s.class;\n", getAnnotationClass(processor).getQualifiedName().toString().replace('$', '.'));
  81         out.printf("        }\n");
  82 
  83         createPrivateMembers(processor, out, deps);
  84 
  85         out.printf("    }\n");
  86     }
  87 
  88     public void register(PrintWriter out) {
  89         out.printf("        plugins.register(new %s(", pluginName);
  90         if (needInjectionProvider) {
  91             out.printf("injection");
  92         }
  93         out.printf("), %s.class, \"%s\"", intrinsicMethod.getEnclosingElement(), intrinsicMethod.getSimpleName());
  94         if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) {
  95             out.printf(", InvocationPlugin.Receiver.class");
  96         }
  97         for (VariableElement arg : intrinsicMethod.getParameters()) {
  98             out.printf(", %s.class", getErasedType(arg.asType()));
  99         }
 100         out.printf(");\n");
 101     }
 102 
 103     public abstract void extraImports(Set<String> imports);
 104 
 105     protected abstract InjectedDependencies createExecute(AbstractProcessor processor, PrintWriter out);
 106 
 107     static String getErasedType(TypeMirror type) {
 108         switch (type.getKind()) {
 109             case DECLARED:
 110                 DeclaredType declared = (DeclaredType) type;
 111                 TypeElement element = (TypeElement) declared.asElement();
 112                 return element.getQualifiedName().toString();
 113             case TYPEVAR:
 114                 return getErasedType(((TypeVariable) type).getUpperBound());
 115             case WILDCARD:
 116                 return getErasedType(((WildcardType) type).getExtendsBound());
 117             case ARRAY:
 118                 return getErasedType(((ArrayType) type).getComponentType()) + "[]";
 119             default:
 120                 return type.toString();
 121         }
 122     }
 123 
 124     static boolean hasRawtypeWarning(TypeMirror type) {
 125         switch (type.getKind()) {
 126             case DECLARED:
 127                 DeclaredType declared = (DeclaredType) type;
 128                 return declared.getTypeArguments().size() > 0;
 129             case TYPEVAR:
 130                 return false;
 131             case WILDCARD:
 132                 return false;
 133             case ARRAY:
 134                 return hasRawtypeWarning(((ArrayType) type).getComponentType());
 135             default:
 136                 return false;
 137         }
 138     }
 139 
 140     static boolean hasUncheckedWarning(TypeMirror type) {
 141         switch (type.getKind()) {
 142             case DECLARED:
 143                 DeclaredType declared = (DeclaredType) type;
 144                 for (TypeMirror typeParam : declared.getTypeArguments()) {
 145                     if (hasUncheckedWarning(typeParam)) {
 146                         return true;
 147                     }
 148                 }
 149                 return false;
 150             case TYPEVAR:
 151                 return true;
 152             case WILDCARD:
 153                 return ((WildcardType) type).getExtendsBound() != null;
 154             case ARRAY:
 155                 return hasUncheckedWarning(((ArrayType) type).getComponentType());
 156             default:
 157                 return false;
 158         }
 159     }
 160 
 161     private void createPrivateMembers(AbstractProcessor processor, PrintWriter out, InjectedDependencies deps) {
 162         if (!deps.isEmpty()) {
 163             out.printf("\n");
 164             for (Dependency dep : deps) {
 165                 out.printf("        private final %s %s;\n", dep.type, dep.name);
 166             }
 167 
 168             out.printf("\n");
 169             out.printf("        private %s(InjectionProvider injection) {\n", pluginName);
 170             for (Dependency dep : deps) {
 171                 out.printf("            this.%s = %s;\n", dep.name, dep.inject(processor, intrinsicMethod));
 172             }
 173             out.printf("        }\n");
 174 
 175             needInjectionProvider = true;
 176         }
 177     }
 178 
 179     protected static String getReturnKind(ExecutableElement method) {
 180         switch (method.getReturnType().getKind()) {
 181             case BOOLEAN:
 182             case BYTE:
 183             case SHORT:
 184             case CHAR:
 185             case INT:
 186                 return "Int";
 187             case LONG:
 188                 return "Long";
 189             case FLOAT:
 190                 return "Float";
 191             case DOUBLE:
 192                 return "Double";
 193             case VOID:
 194                 return "Void";
 195             case ARRAY:
 196             case TYPEVAR:
 197             case DECLARED:
 198                 return "Object";
 199             default:
 200                 throw new IllegalArgumentException(method.getReturnType().toString());
 201         }
 202     }
 203 
 204     protected static void constantArgument(AbstractProcessor processor, PrintWriter out, InjectedDependencies deps, int argIdx, TypeMirror type, int nodeIdx) {
 205         if (hasRawtypeWarning(type)) {
 206             out.printf("            @SuppressWarnings({\"rawtypes\"})\n");
 207         }
 208         out.printf("            %s arg%d;\n", getErasedType(type), argIdx);
 209         out.printf("            if (args[%d].isConstant()) {\n", nodeIdx);
 210         if (type.equals(processor.getType("jdk.vm.ci.meta.ResolvedJavaType"))) {
 211             out.printf("                arg%d = %s.asJavaType(args[%d].asConstant());\n", argIdx, deps.use(WellKnownDependency.CONSTANT_REFLECTION), nodeIdx);
 212         } else {
 213             switch (type.getKind()) {
 214                 case BOOLEAN:
 215                     out.printf("                arg%d = args[%d].asJavaConstant().asInt() != 0;\n", argIdx, nodeIdx);
 216                     break;
 217                 case BYTE:
 218                     out.printf("                arg%d = (byte) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 219                     break;
 220                 case CHAR:
 221                     out.printf("                arg%d = (char) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 222                     break;
 223                 case SHORT:
 224                     out.printf("                arg%d = (short) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 225                     break;
 226                 case INT:
 227                     out.printf("                arg%d = args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 228                     break;
 229                 case LONG:
 230                     out.printf("                arg%d = args[%d].asJavaConstant().asLong();\n", argIdx, nodeIdx);
 231                     break;
 232                 case FLOAT:
 233                     out.printf("                arg%d = args[%d].asJavaConstant().asFloat();\n", argIdx, nodeIdx);
 234                     break;
 235                 case DOUBLE:
 236                     out.printf("                arg%d = args[%d].asJavaConstant().asDouble();\n", argIdx, nodeIdx);
 237                     break;
 238                 case ARRAY:
 239                 case DECLARED:
 240                     out.printf("                arg%d = %s.asObject(%s.class, args[%d].asJavaConstant());\n", argIdx, deps.use(WellKnownDependency.SNIPPET_REFLECTION), getErasedType(type), nodeIdx);
 241                     break;
 242                 default:
 243                     throw new IllegalArgumentException(type.toString());
 244             }
 245         }
 246         out.printf("            } else {\n");
 247         out.printf("                assert b.canDeferPlugin(this) : b.getClass().toString();\n");
 248         out.printf("                return false;\n");
 249         out.printf("            }\n");
 250     }
 251 }