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 }