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