1 /*
   2  * Copyright (c) 2015, 2019, 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 = "Plugin_" + intrinsicMethod.getEnclosingElement().getSimpleName() + "_" + 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("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         out.printf("        if (!b.isPluginEnabled(this)) {\n");
  77         out.printf("            return false;\n");
  78         out.printf("        }\n");
  79         InjectedDependencies deps = createExecute(processor, out);
  80         out.printf("    }\n");
  81         out.printf("    @Override\n");
  82         out.printf("    public Class<? extends Annotation> getSource() {\n");
  83         out.printf("        return %s.class;\n", getAnnotationClass(processor).getQualifiedName().toString().replace('$', '.'));
  84         out.printf("    }\n");
  85 
  86         createPrivateMembers(processor, out, deps);
  87 
  88         out.printf("}\n");
  89     }
  90 
  91     public void register(PrintWriter out) {
  92         out.printf("        plugins.register(new %s(", pluginName);
  93         if (needInjectionProvider) {
  94             out.printf("injection");
  95         }
  96         out.printf("), %s.class, \"%s\"", intrinsicMethod.getEnclosingElement(), intrinsicMethod.getSimpleName());
  97         if (!intrinsicMethod.getModifiers().contains(Modifier.STATIC)) {
  98             out.printf(", InvocationPlugin.Receiver.class");
  99         }
 100         for (VariableElement arg : intrinsicMethod.getParameters()) {
 101             out.printf(", %s.class", getErasedType(arg.asType()));
 102         }
 103         out.printf(");\n");
 104     }
 105 
 106     public abstract void extraImports(Set<String> imports);
 107 
 108     protected abstract InjectedDependencies createExecute(AbstractProcessor processor, PrintWriter out);
 109 
 110     static String getErasedType(TypeMirror type) {
 111         switch (type.getKind()) {
 112             case DECLARED:
 113                 DeclaredType declared = (DeclaredType) type;
 114                 TypeElement element = (TypeElement) declared.asElement();
 115                 return element.getQualifiedName().toString();
 116             case TYPEVAR:
 117                 return getErasedType(((TypeVariable) type).getUpperBound());
 118             case WILDCARD:
 119                 return getErasedType(((WildcardType) type).getExtendsBound());
 120             case ARRAY:
 121                 return getErasedType(((ArrayType) type).getComponentType()) + "[]";
 122             default:
 123                 return type.toString();
 124         }
 125     }
 126 
 127     static boolean hasRawtypeWarning(TypeMirror type) {
 128         switch (type.getKind()) {
 129             case DECLARED:
 130                 DeclaredType declared = (DeclaredType) type;
 131                 return declared.getTypeArguments().size() > 0;
 132             case TYPEVAR:
 133                 return false;
 134             case WILDCARD:
 135                 return false;
 136             case ARRAY:
 137                 return hasRawtypeWarning(((ArrayType) type).getComponentType());
 138             default:
 139                 return false;
 140         }
 141     }
 142 
 143     static boolean hasUncheckedWarning(TypeMirror type) {
 144         switch (type.getKind()) {
 145             case DECLARED:
 146                 DeclaredType declared = (DeclaredType) type;
 147                 for (TypeMirror typeParam : declared.getTypeArguments()) {
 148                     if (hasUncheckedWarning(typeParam)) {
 149                         return true;
 150                     }
 151                 }
 152                 return false;
 153             case TYPEVAR:
 154                 return true;
 155             case WILDCARD:
 156                 return ((WildcardType) type).getExtendsBound() != null;
 157             case ARRAY:
 158                 return hasUncheckedWarning(((ArrayType) type).getComponentType());
 159             default:
 160                 return false;
 161         }
 162     }
 163 
 164     private void createPrivateMembers(AbstractProcessor processor, PrintWriter out, InjectedDependencies deps) {
 165         if (!deps.isEmpty()) {
 166             out.printf("\n");
 167             for (Dependency dep : deps) {
 168                 out.printf("    private final %s %s;\n", dep.type, dep.name);
 169             }
 170 
 171             out.printf("\n");
 172             out.printf("    %s(NodeIntrinsicPluginFactory.InjectionProvider injection) {\n", pluginName);
 173             for (Dependency dep : deps) {
 174                 out.printf("        this.%s = %s;\n", dep.name, dep.inject(processor, intrinsicMethod));
 175             }
 176             out.printf("    }\n");
 177 
 178             needInjectionProvider = true;
 179         }
 180     }
 181 
 182     protected static String getReturnKind(ExecutableElement method) {
 183         switch (method.getReturnType().getKind()) {
 184             case BOOLEAN:
 185             case BYTE:
 186             case SHORT:
 187             case CHAR:
 188             case INT:
 189                 return "Int";
 190             case LONG:
 191                 return "Long";
 192             case FLOAT:
 193                 return "Float";
 194             case DOUBLE:
 195                 return "Double";
 196             case VOID:
 197                 return "Void";
 198             case ARRAY:
 199             case TYPEVAR:
 200             case DECLARED:
 201                 return "Object";
 202             default:
 203                 throw new IllegalArgumentException(method.getReturnType().toString());
 204         }
 205     }
 206 
 207     protected static void constantArgument(AbstractProcessor processor, PrintWriter out, InjectedDependencies deps, int argIdx, TypeMirror type, int nodeIdx) {
 208         if (hasRawtypeWarning(type)) {
 209             out.printf("        @SuppressWarnings({\"rawtypes\"})\n");
 210         }
 211         out.printf("        %s arg%d;\n", getErasedType(type), argIdx);
 212         out.printf("        if (args[%d].isConstant()) {\n", nodeIdx);
 213         if (type.equals(processor.getType("jdk.vm.ci.meta.ResolvedJavaType"))) {
 214             out.printf("            jdk.vm.ci.meta.JavaConstant cst = args[%d].asJavaConstant();\n", nodeIdx);
 215             out.printf("            arg%d = %s.asJavaType(cst);\n", argIdx, deps.use(WellKnownDependency.CONSTANT_REFLECTION));
 216             out.printf("            if (arg%d == null) {\n", argIdx);
 217             out.printf("                arg%d = %s.asObject(jdk.vm.ci.meta.ResolvedJavaType.class, cst);\n", argIdx, deps.use(WellKnownDependency.SNIPPET_REFLECTION));
 218             out.printf("            }\n");
 219         } else {
 220             switch (type.getKind()) {
 221                 case BOOLEAN:
 222                     out.printf("            arg%d = args[%d].asJavaConstant().asInt() != 0;\n", argIdx, nodeIdx);
 223                     break;
 224                 case BYTE:
 225                     out.printf("            arg%d = (byte) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 226                     break;
 227                 case CHAR:
 228                     out.printf("            arg%d = (char) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 229                     break;
 230                 case SHORT:
 231                     out.printf("            arg%d = (short) args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 232                     break;
 233                 case INT:
 234                     out.printf("            arg%d = args[%d].asJavaConstant().asInt();\n", argIdx, nodeIdx);
 235                     break;
 236                 case LONG:
 237                     out.printf("            arg%d = args[%d].asJavaConstant().asLong();\n", argIdx, nodeIdx);
 238                     break;
 239                 case FLOAT:
 240                     out.printf("            arg%d = args[%d].asJavaConstant().asFloat();\n", argIdx, nodeIdx);
 241                     break;
 242                 case DOUBLE:
 243                     out.printf("            arg%d = args[%d].asJavaConstant().asDouble();\n", argIdx, nodeIdx);
 244                     break;
 245                 case ARRAY:
 246                 case DECLARED:
 247                     out.printf("            arg%d = %s.asObject(%s.class, args[%d].asJavaConstant());\n", argIdx, deps.use(WellKnownDependency.SNIPPET_REFLECTION), getErasedType(type), nodeIdx);
 248                     break;
 249                 default:
 250                     throw new IllegalArgumentException(type.toString());
 251             }
 252         }
 253         out.printf("        } else {\n");
 254         out.printf("            assert b.canDeferPlugin(this) : b.getClass().toString();\n");
 255         out.printf("            return false;\n");
 256         out.printf("        }\n");
 257     }
 258 }