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 jdk.vm.ci.options.processor;
24
25 import java.io.*;
26 import java.util.*;
27
28 import javax.annotation.processing.*;
29 import javax.lang.model.*;
30 import javax.lang.model.element.*;
31 import javax.lang.model.type.*;
32 import javax.lang.model.util.*;
33 import javax.tools.Diagnostic.Kind;
34
35 import jdk.vm.ci.options.*;
36
37 import javax.tools.*;
38
39 /**
40 * Processes static fields annotated with {@link Option}. An {@link OptionDescriptors}
41 * implementation is generated for each top level class containing at least one such field. The name
42 * of the generated class for top level class {@code com.foo.Bar} is
43 * {@code com.foo.Bar_OptionDescriptors}.
44 */
45 @SupportedAnnotationTypes({"jdk.vm.ci.options.Option"})
46 public class OptionProcessor extends AbstractProcessor {
47
48 @Override
49 public SourceVersion getSupportedSourceVersion() {
50 return SourceVersion.latest();
51 }
52
53 private final Set<Element> processed = new HashSet<>();
54
55 private void processElement(Element element, OptionsInfo info) {
56
57 if (!element.getModifiers().contains(Modifier.STATIC)) {
88 return;
89 }
90
91 String help = annotation.help();
92 if (help.length() != 0) {
93 char firstChar = help.charAt(0);
94 if (!Character.isUpperCase(firstChar)) {
95 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element);
96 return;
97 }
98 }
99
100 String optionName = annotation.name();
101 if (optionName.equals("")) {
102 optionName = fieldName;
103 }
104
105 DeclaredType declaredOptionValueType = declaredFieldType;
106 while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) {
107 List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
108 assert!directSupertypes.isEmpty();
109 declaredOptionValueType = (DeclaredType) directSupertypes.get(0);
110 }
111
112 assert!declaredOptionValueType.getTypeArguments().isEmpty();
113 String optionType = declaredOptionValueType.getTypeArguments().get(0).toString();
114 if (optionType.startsWith("java.lang.")) {
115 optionType = optionType.substring("java.lang.".length());
116 }
117
118 Element enclosing = element.getEnclosingElement();
119 String declaringClass = "";
120 String separator = "";
121 Set<Element> originatingElementsList = info.originatingElements;
122 originatingElementsList.add(field);
123 while (enclosing != null) {
124 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
125 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
126 String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
127 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
128 return;
129 }
130 originatingElementsList.add(enclosing);
131 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
132 separator = ".";
177 out.println(" if (value.equals(\"" + info.options.get(0).name + "\")) {");
178 } else {
179 out.println(" switch (value) {");
180 }
181 for (OptionInfo option : info.options) {
182 String name = option.name;
183 String optionValue;
184 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
185 needPrivateFieldAccessor = true;
186 optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
187 } else {
188 optionValue = option.declaringClass + "." + option.field.getSimpleName();
189 }
190 String type = option.type;
191 String help = option.help;
192 String declaringClass = option.declaringClass;
193 Name fieldName = option.field.getSimpleName();
194 if (info.options.size() == 1) {
195 out.printf(" return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);
196 } else {
197 out.printf(" case \"" + name + "\": return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName,
198 optionValue);
199 }
200 }
201 out.println(" }");
202 out.println(" // CheckStyle: resume line length check");
203 out.println(" return null;");
204 out.println(" }");
205 out.println();
206 out.println(" @Override");
207 out.println(" public Iterator<" + desc + "> iterator() {");
208 out.println(" // CheckStyle: stop line length check");
209 out.println(" List<" + desc + "> options = Arrays.asList(");
210 for (OptionInfo option : info.options) {
211 String optionValue;
212 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
213 needPrivateFieldAccessor = true;
214 optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
215 } else {
216 optionValue = option.declaringClass + "." + option.field.getSimpleName();
217 }
218 String name = option.name;
224 out.printf(" %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma);
225 i++;
226 }
227 out.println(" );");
228 out.println(" // CheckStyle: resume line length check");
229 out.println(" return options.iterator();");
230 out.println(" }");
231 if (needPrivateFieldAccessor) {
232 out.println(" private static " + OptionValue.class.getSimpleName() + "<?> field(Class<?> declaringClass, String fieldName) {");
233 out.println(" try {");
234 out.println(" java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);");
235 out.println(" field.setAccessible(true);");
236 out.println(" return (" + OptionValue.class.getSimpleName() + "<?>) field.get(null);");
237 out.println(" } catch (Exception e) {");
238 out.println(" throw (InternalError) new InternalError().initCause(e);");
239 out.println(" }");
240 out.println(" }");
241 }
242 out.println("}");
243 }
244
245 try {
246 createOptionsFile(pkg, topDeclaringClass.toString(), originatingElements);
247 } catch (IOException e) {
248 processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), info.topDeclaringType);
249 }
250 }
251
252 private void createOptionsFile(String pkg, String relativeName, Element... originatingElements) throws IOException {
253 String filename = "META-INF/jvmci.options/" + pkg + "." + relativeName;
254 FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements);
255 PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
256 writer.close();
257 }
258
259 protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
260 try {
261 // Ensure Unix line endings to comply with code style guide checked by Checkstyle
262 JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
263 return new PrintWriter(sourceFile.openWriter()) {
264
265 @Override
266 public void println() {
267 print("\n");
268 }
269 };
270 } catch (IOException e) {
271 throw new RuntimeException(e);
272 }
273 }
274
275 static class OptionInfo implements Comparable<OptionInfo> {
276
|
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 jdk.vm.ci.options.processor;
24
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import javax.annotation.processing.AbstractProcessor;
36 import javax.annotation.processing.Filer;
37 import javax.annotation.processing.RoundEnvironment;
38 import javax.annotation.processing.SupportedAnnotationTypes;
39 import javax.lang.model.SourceVersion;
40 import javax.lang.model.element.Element;
41 import javax.lang.model.element.ElementKind;
42 import javax.lang.model.element.Modifier;
43 import javax.lang.model.element.Name;
44 import javax.lang.model.element.PackageElement;
45 import javax.lang.model.element.TypeElement;
46 import javax.lang.model.element.VariableElement;
47 import javax.lang.model.type.DeclaredType;
48 import javax.lang.model.type.TypeKind;
49 import javax.lang.model.type.TypeMirror;
50 import javax.lang.model.util.Elements;
51 import javax.lang.model.util.Types;
52 import javax.tools.Diagnostic.Kind;
53 import javax.tools.JavaFileObject;
54
55 import jdk.vm.ci.options.Option;
56 import jdk.vm.ci.options.OptionDescriptor;
57 import jdk.vm.ci.options.OptionDescriptors;
58 import jdk.vm.ci.options.OptionValue;
59
60 /**
61 * Processes static fields annotated with {@link Option}. An {@link OptionDescriptors}
62 * implementation is generated for each top level class containing at least one such field. The name
63 * of the generated class for top level class {@code com.foo.Bar} is
64 * {@code com.foo.Bar_OptionDescriptors}.
65 */
66 @SupportedAnnotationTypes({"jdk.vm.ci.options.Option"})
67 public class OptionProcessor extends AbstractProcessor {
68
69 @Override
70 public SourceVersion getSupportedSourceVersion() {
71 return SourceVersion.latest();
72 }
73
74 private final Set<Element> processed = new HashSet<>();
75
76 private void processElement(Element element, OptionsInfo info) {
77
78 if (!element.getModifiers().contains(Modifier.STATIC)) {
109 return;
110 }
111
112 String help = annotation.help();
113 if (help.length() != 0) {
114 char firstChar = help.charAt(0);
115 if (!Character.isUpperCase(firstChar)) {
116 processingEnv.getMessager().printMessage(Kind.ERROR, "Option help text must start with upper case letter", element);
117 return;
118 }
119 }
120
121 String optionName = annotation.name();
122 if (optionName.equals("")) {
123 optionName = fieldName;
124 }
125
126 DeclaredType declaredOptionValueType = declaredFieldType;
127 while (!types.isSameType(types.erasure(declaredOptionValueType), types.erasure(optionValueType))) {
128 List<? extends TypeMirror> directSupertypes = types.directSupertypes(declaredFieldType);
129 assert !directSupertypes.isEmpty();
130 declaredOptionValueType = (DeclaredType) directSupertypes.get(0);
131 }
132
133 assert !declaredOptionValueType.getTypeArguments().isEmpty();
134 String optionType = declaredOptionValueType.getTypeArguments().get(0).toString();
135 if (optionType.startsWith("java.lang.")) {
136 optionType = optionType.substring("java.lang.".length());
137 }
138
139 Element enclosing = element.getEnclosingElement();
140 String declaringClass = "";
141 String separator = "";
142 Set<Element> originatingElementsList = info.originatingElements;
143 originatingElementsList.add(field);
144 while (enclosing != null) {
145 if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
146 if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
147 String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
148 processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
149 return;
150 }
151 originatingElementsList.add(enclosing);
152 declaringClass = enclosing.getSimpleName() + separator + declaringClass;
153 separator = ".";
198 out.println(" if (value.equals(\"" + info.options.get(0).name + "\")) {");
199 } else {
200 out.println(" switch (value) {");
201 }
202 for (OptionInfo option : info.options) {
203 String name = option.name;
204 String optionValue;
205 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
206 needPrivateFieldAccessor = true;
207 optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
208 } else {
209 optionValue = option.declaringClass + "." + option.field.getSimpleName();
210 }
211 String type = option.type;
212 String help = option.help;
213 String declaringClass = option.declaringClass;
214 Name fieldName = option.field.getSimpleName();
215 if (info.options.size() == 1) {
216 out.printf(" return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);
217 } else {
218 out.printf(" case \"" + name + "\": return %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s);\n", desc, name, type, help, declaringClass, fieldName, optionValue);
219 }
220 }
221 out.println(" }");
222 out.println(" // CheckStyle: resume line length check");
223 out.println(" return null;");
224 out.println(" }");
225 out.println();
226 out.println(" @Override");
227 out.println(" public Iterator<" + desc + "> iterator() {");
228 out.println(" // CheckStyle: stop line length check");
229 out.println(" List<" + desc + "> options = Arrays.asList(");
230 for (OptionInfo option : info.options) {
231 String optionValue;
232 if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
233 needPrivateFieldAccessor = true;
234 optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
235 } else {
236 optionValue = option.declaringClass + "." + option.field.getSimpleName();
237 }
238 String name = option.name;
244 out.printf(" %s.create(\"%s\", %s.class, \"%s\", %s.class, \"%s\", %s)%s\n", desc, name, type, help, declaringClass, fieldName, optionValue, comma);
245 i++;
246 }
247 out.println(" );");
248 out.println(" // CheckStyle: resume line length check");
249 out.println(" return options.iterator();");
250 out.println(" }");
251 if (needPrivateFieldAccessor) {
252 out.println(" private static " + OptionValue.class.getSimpleName() + "<?> field(Class<?> declaringClass, String fieldName) {");
253 out.println(" try {");
254 out.println(" java.lang.reflect.Field field = declaringClass.getDeclaredField(fieldName);");
255 out.println(" field.setAccessible(true);");
256 out.println(" return (" + OptionValue.class.getSimpleName() + "<?>) field.get(null);");
257 out.println(" } catch (Exception e) {");
258 out.println(" throw (InternalError) new InternalError().initCause(e);");
259 out.println(" }");
260 out.println(" }");
261 }
262 out.println("}");
263 }
264 }
265
266 protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
267 try {
268 // Ensure Unix line endings to comply with code style guide checked by Checkstyle
269 JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
270 return new PrintWriter(sourceFile.openWriter()) {
271
272 @Override
273 public void println() {
274 print("\n");
275 }
276 };
277 } catch (IOException e) {
278 throw new RuntimeException(e);
279 }
280 }
281
282 static class OptionInfo implements Comparable<OptionInfo> {
283
|