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.IOException; 26 import java.io.PrintWriter; 27 import java.util.ArrayList; 28 import java.util.Comparator; 29 import java.util.HashMap; 30 import java.util.HashSet; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Map.Entry; 34 import java.util.function.Function; 35 36 import javax.annotation.processing.ProcessingEnvironment; 37 import javax.lang.model.element.Element; 38 import javax.lang.model.element.ElementKind; 39 import javax.lang.model.element.PackageElement; 40 import javax.lang.model.element.TypeElement; 41 import javax.lang.model.element.VariableElement; 42 import javax.lang.model.type.ArrayType; 43 import javax.lang.model.type.DeclaredType; 44 import javax.lang.model.type.TypeMirror; 45 import javax.lang.model.type.TypeVariable; 46 import javax.lang.model.type.WildcardType; 47 import javax.tools.Diagnostic; 48 import javax.tools.JavaFileObject; 49 50 public class PluginGenerator { 51 52 private final Map<Element, List<GeneratedPlugin>> plugins; 53 54 public PluginGenerator() { 55 this.plugins = new HashMap<>(); 56 } 57 58 public void addPlugin(GeneratedPlugin plugin) { 59 Element topLevel = getTopLevelClass(plugin.intrinsicMethod); 60 List<GeneratedPlugin> list = plugins.get(topLevel); 61 if (list == null) { 62 list = new ArrayList<>(); 63 plugins.put(topLevel, list); 64 } 65 list.add(plugin); 66 } 67 68 public void generateAll(ProcessingEnvironment env) { 69 for (Entry<Element, List<GeneratedPlugin>> entry : plugins.entrySet()) { 70 disambiguateNames(entry.getValue()); 71 createPluginFactory(env, entry.getKey(), entry.getValue()); 72 } 73 } 74 75 private static Element getTopLevelClass(Element element) { 76 Element prev = element; 77 Element enclosing = element.getEnclosingElement(); 78 while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) { 79 prev = enclosing; 80 enclosing = enclosing.getEnclosingElement(); 81 } 82 return prev; 83 } 84 85 private static void disambiguateWith(List<GeneratedPlugin> plugins, Function<GeneratedPlugin, String> genName) { 86 plugins.sort(Comparator.comparing(GeneratedPlugin::getPluginName)); 87 88 GeneratedPlugin current = plugins.get(0); 89 String currentName = current.getPluginName(); 90 91 for (int i = 1; i < plugins.size(); i++) { 92 GeneratedPlugin next = plugins.get(i); 93 if (currentName.equals(next.getPluginName())) { 94 if (current != null) { 95 current.setPluginName(genName.apply(current)); 96 current = null; 97 } 98 next.setPluginName(genName.apply(next)); 99 } else { 100 current = next; 101 currentName = current.getPluginName(); 102 } 103 } 104 } 105 106 private static void appendSimpleTypeName(StringBuilder ret, TypeMirror type) { 107 switch (type.getKind()) { 108 case DECLARED: 109 DeclaredType declared = (DeclaredType) type; 110 TypeElement element = (TypeElement) declared.asElement(); 111 ret.append(element.getSimpleName()); 112 break; 113 case TYPEVAR: 114 appendSimpleTypeName(ret, ((TypeVariable) type).getUpperBound()); 115 break; 116 case WILDCARD: 117 appendSimpleTypeName(ret, ((WildcardType) type).getExtendsBound()); 118 break; 119 case ARRAY: 120 appendSimpleTypeName(ret, ((ArrayType) type).getComponentType()); 121 ret.append("Array"); 122 break; 123 default: 124 ret.append(type); 125 } 126 } 127 128 private static void disambiguateNames(List<GeneratedPlugin> plugins) { 129 // if we have more than one method with the same name, disambiguate with the argument types 130 disambiguateWith(plugins, plugin -> { 131 StringBuilder ret = new StringBuilder(plugin.getPluginName()); 132 for (VariableElement param : plugin.intrinsicMethod.getParameters()) { 133 ret.append('_'); 134 appendSimpleTypeName(ret, param.asType()); 135 } 136 return ret.toString(); 137 }); 138 139 // since we're using simple names for argument types, we could still have a collision 140 disambiguateWith(plugins, new Function<GeneratedPlugin, String>() { 141 142 private int idx = 0; 143 144 @Override 145 public String apply(GeneratedPlugin plugin) { 146 return plugin.getPluginName() + "_" + (idx++); 147 } 148 }); 149 } 150 151 private static void createPluginFactory(ProcessingEnvironment env, Element topLevelClass, List<GeneratedPlugin> plugins) { 152 PackageElement pkg = (PackageElement) topLevelClass.getEnclosingElement(); 153 154 String genClassName = "PluginFactory_" + topLevelClass.getSimpleName(); 155 156 try { 157 JavaFileObject factory = env.getFiler().createSourceFile(pkg.getQualifiedName() + "." + genClassName, topLevelClass); 158 try (PrintWriter out = new PrintWriter(factory.openWriter())) { 159 out.printf("// CheckStyle: stop header check\n"); 160 out.printf("// CheckStyle: stop line length check\n"); 161 out.printf("// GENERATED CONTENT - DO NOT EDIT\n"); 162 out.printf("// GENERATORS: %s, %s\n", VerifierAnnotationProcessor.class.getName(), PluginGenerator.class.getName()); 163 out.printf("package %s;\n", pkg.getQualifiedName()); 164 out.printf("\n"); 165 createImports(out, plugins); 166 out.printf("\n"); 167 out.printf("@ServiceProvider(NodeIntrinsicPluginFactory.class)\n"); 168 out.printf("public class %s implements NodeIntrinsicPluginFactory {\n", genClassName); 169 for (GeneratedPlugin plugin : plugins) { 170 out.printf("\n"); 171 plugin.generate(env, out); 172 } 173 out.printf("\n"); 174 createPluginFactoryMethod(out, plugins); 175 out.printf("}\n"); 176 } 177 } catch (IOException e) { 178 env.getMessager().printMessage(Diagnostic.Kind.ERROR, e.getMessage()); 179 } 180 } 181 182 protected static void createImports(PrintWriter out, List<GeneratedPlugin> plugins) { 183 out.printf("import jdk.vm.ci.meta.ResolvedJavaMethod;\n"); 184 out.printf("import org.graalvm.compiler.serviceprovider.ServiceProvider;\n"); 185 out.printf("\n"); 186 out.printf("import org.graalvm.compiler.nodes.ValueNode;\n"); 187 out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;\n"); 188 out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;\n"); 189 out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;\n"); 190 out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;\n"); 191 out.printf("import org.graalvm.compiler.nodes.graphbuilderconf.NodeIntrinsicPluginFactory;\n"); 192 193 HashSet<String> extra = new HashSet<>(); 194 for (GeneratedPlugin plugin : plugins) { 195 plugin.extraImports(extra); 196 } 197 if (!extra.isEmpty()) { 198 out.printf("\n"); 199 for (String i : extra) { 200 out.printf("import %s;\n", i); 201 } 202 } 203 } 204 205 private static void createPluginFactoryMethod(PrintWriter out, List<GeneratedPlugin> plugins) { 206 out.printf(" @Override\n"); 207 out.printf(" public void registerPlugins(InvocationPlugins plugins, InjectionProvider injection) {\n"); 208 for (GeneratedPlugin plugin : plugins) { 209 plugin.register(out); 210 } 211 out.printf(" }\n"); 212 } 213 }