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.IOException;
  28 import java.io.PrintWriter;
  29 import java.util.ArrayList;
  30 import java.util.Comparator;
  31 import java.util.HashMap;
  32 import java.util.HashSet;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Map.Entry;
  36 import java.util.function.Function;
  37 
  38 import javax.lang.model.element.Element;
  39 import javax.lang.model.element.ElementKind;
  40 import javax.lang.model.element.PackageElement;
  41 import javax.tools.Diagnostic;
  42 import javax.tools.JavaFileObject;
  43 
  44 import org.graalvm.compiler.processor.AbstractProcessor;
  45 
  46 public class PluginGenerator {
  47 
  48     private final Map<Element, List<GeneratedPlugin>> plugins;
  49 
  50     public PluginGenerator() {
  51         this.plugins = new HashMap<>();
  52     }
  53 
  54     public void addPlugin(GeneratedPlugin plugin) {
  55         Element topLevel = getTopLevelClass(plugin.intrinsicMethod);
  56         List<GeneratedPlugin> list = plugins.get(topLevel);
  57         if (list == null) {
  58             list = new ArrayList<>();
  59             plugins.put(topLevel, list);
  60         }
  61         list.add(plugin);
  62     }
  63 
  64     public void generateAll(AbstractProcessor processor) {
  65         for (Entry<Element, List<GeneratedPlugin>> entry : plugins.entrySet()) {
  66             disambiguateNames(entry.getValue());
  67             createPluginFactory(processor, entry.getKey(), entry.getValue());
  68         }
  69     }
  70 
  71     private static Element getTopLevelClass(Element element) {
  72         Element prev = element;
  73         Element enclosing = element.getEnclosingElement();
  74         while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) {
  75             prev = enclosing;
  76             enclosing = enclosing.getEnclosingElement();
  77         }
  78         return prev;
  79     }
  80 
  81     private static void disambiguateWith(List<GeneratedPlugin> plugins, Function<GeneratedPlugin, String> genName) {
  82         plugins.sort(Comparator.comparing(GeneratedPlugin::getPluginName));
  83 
  84         GeneratedPlugin current = plugins.get(0);
  85         String currentName = current.getPluginName();
  86 
  87         for (int i = 1; i < plugins.size(); i++) {
  88             GeneratedPlugin next = plugins.get(i);
  89             if (currentName.equals(next.getPluginName())) {
  90                 if (current != null) {
  91                     current.setPluginName(genName.apply(current));
  92                     current = null;
  93                 }
  94                 next.setPluginName(genName.apply(next));
  95             } else {
  96                 current = next;
  97                 currentName = current.getPluginName();
  98             }
  99         }
 100     }
 101 
 102     private static void disambiguateNames(List<GeneratedPlugin> plugins) {
 103         // If we have more than one method with the same name, disambiguate with a numeric suffix.
 104         // We use this instead of a suffix based on argument types to mitigate hitting file name
 105         // length limits. We start the suffix with "__" to make it visually stick out.
 106         int[] nextId = {0};
 107         disambiguateWith(plugins, plugin -> plugin.getPluginName() + "__" + nextId[0]++);
 108     }
 109 
 110     private static void createPluginFactory(AbstractProcessor processor, Element topLevelClass, List<GeneratedPlugin> plugins) {
 111         PackageElement pkg = (PackageElement) topLevelClass.getEnclosingElement();
 112 
 113         String genClassName = "PluginFactory_" + topLevelClass.getSimpleName();
 114 
 115         String qualifiedGenClassName = pkg.getQualifiedName() + "." + genClassName;
 116         try {
 117             JavaFileObject factory = processor.env().getFiler().createSourceFile(qualifiedGenClassName, topLevelClass);
 118             try (PrintWriter out = new PrintWriter(factory.openWriter())) {
 119                 out.printf("// CheckStyle: stop header check\n");
 120                 out.printf("// CheckStyle: stop line length check\n");
 121                 out.printf("// GENERATED CONTENT - DO NOT EDIT\n");
 122                 out.printf("// GENERATORS: %s, %s\n", ReplacementsAnnotationProcessor.class.getName(), PluginGenerator.class.getName());
 123                 out.printf("package %s;\n", pkg.getQualifiedName());
 124                 out.printf("\n");
 125                 createImports(out, plugins);
 126                 out.printf("\n");
 127                 out.printf("public class %s implements NodeIntrinsicPluginFactory {\n", genClassName);
 128                 for (GeneratedPlugin plugin : plugins) {
 129                     out.printf("\n");
 130                     plugin.generate(processor, out);
 131                 }
 132                 out.printf("\n");
 133                 createPluginFactoryMethod(out, plugins);
 134                 out.printf("}\n");
 135             }
 136         } catch (IOException e) {
 137             processor.env().getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage());
 138         }
 139         processor.createProviderFile(qualifiedGenClassName, "org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory", topLevelClass);
 140     }
 141 
 142     protected static void createImports(PrintWriter out, List<GeneratedPlugin> plugins) {
 143         out.printf("import jdk.vm.ci.meta.ResolvedJavaMethod;\n");
 144         out.printf("\n");
 145         out.printf("import java.lang.annotation.Annotation;\n");
 146         out.printf("import org.graalvm.compiler.nodes.ValueNode;\n");
 147         out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;\n");
 148         out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;\n");
 149         out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;\n");
 150         out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;\n");
 151         out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;\n");
 152 
 153         HashSet<String> extra = new HashSet<>();
 154         for (GeneratedPlugin plugin : plugins) {
 155             plugin.extraImports(extra);
 156         }
 157         if (!extra.isEmpty()) {
 158             out.printf("\n");
 159             for (String i : extra) {
 160                 out.printf("import %s;\n", i);
 161             }
 162         }
 163     }
 164 
 165     private static void createPluginFactoryMethod(PrintWriter out, List<GeneratedPlugin> plugins) {
 166         out.printf("    @Override\n");
 167         out.printf("    public void registerPlugins(InvocationPlugins plugins, InjectionProvider injection) {\n");
 168         for (GeneratedPlugin plugin : plugins) {
 169             plugin.register(out);
 170         }
 171         out.printf("    }\n");
 172     }
 173 }