1 package oracle.code.samples;
2
3 import jdk.incubator.code.CodeElement;
4 import jdk.incubator.code.CodeReflection;
5 import jdk.incubator.code.Op;
6 import jdk.incubator.code.dialect.core.CoreOp.FuncOp;
7 import jdk.incubator.code.dialect.java.JavaOp.InvokeOp;
8 import jdk.incubator.code.dialect.java.JavaOp.ThrowOp;
9 import jdk.incubator.code.dialect.java.JavaOp.TryOp;
10 import jdk.incubator.code.dialect.java.JavaType;
11
12 import javax.annotation.processing.AbstractProcessor;
13 import javax.annotation.processing.Messager;
14 import javax.annotation.processing.ProcessingEnvironment;
15 import javax.annotation.processing.RoundEnvironment;
16 import javax.annotation.processing.SupportedAnnotationTypes;
17 import javax.annotation.processing.SupportedSourceVersion;
18 import javax.lang.model.SourceVersion;
19 import javax.lang.model.element.Element;
20 import javax.lang.model.element.ExecutableElement;
21 import javax.lang.model.element.TypeElement;
22 import javax.lang.model.util.ElementScannerPreview;
23 import javax.tools.Diagnostic.Kind;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Optional;
27 import java.util.Set;
28
29 /**
30 * This annotation processor can be used to inspect methods annotated with {@code CodeReflection},
31 * and check if they conform to specific programming model restrictions. This provides an example on how
32 * clients might control the contents of a code model via compile-time checks, which can be useful e.g. if said
33 * models are meant for an <em>foreign</em> execution environment that might not support all the features of
34 * a full-blown JVM.
35 * <p>
36 * More specifically,this annotation processor issues error messages when it detects calls to <em>complex</em>
37 * system methods (such as {@link System#gc()}, or {@link Runtime#loadLibrary(String)}). It also issues errors
38 * when encountering <em>unsupported</em> languages features, such as {@code try/catch} or {@code throw}.
39 */
40 @SupportedAnnotationTypes("jdk.incubator.code.CodeReflection")
41 @SupportedSourceVersion(SourceVersion.RELEASE_26)
42 public class CodeReflectionProcessor extends AbstractProcessor {
43
44 @Override
45 public synchronized void init(ProcessingEnvironment processingEnv) {
46 super.init(processingEnv);
47 }
48
49 @Override
50 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
51 for (Element e : roundEnv.getElementsAnnotatedWith(CodeReflection.class)) {
52 ReflectableMethodScanner scanner = new ReflectableMethodScanner();
53 scanner.scan(e);
54 }
55 return true;
56 }
57
58 class ReflectableMethodScanner extends ElementScannerPreview<Void, Void> {
59
60 final Messager logger = processingEnv.getMessager();
61
62 @Override
63 public Void scan(Element e, Void unused) {
64 return super.scan(e, unused);
65 }
66
67 @Override
68 public Void visitExecutable(ExecutableElement e, Void unused) {
69 if (e.getAnnotationsByType(CodeReflection.class) != null) {
70 Optional<FuncOp> funcOp = Op.ofElement(processingEnv, e);
71 funcOp.ifPresent(f -> processMethodModel(e, f));
72 }
73 return null;
74 }
75
76 void processMethodModel(ExecutableElement element, FuncOp funcOp) {
77 funcOp.elements().forEach(ce -> processOp(element, ce));
78 }
79
80 void processOp(Element element, CodeElement<?, ?> codeElement) {
81 switch (codeElement) {
82 case InvokeOp invokeOp -> {
83 var desc = invokeOp.invokeDescriptor();
84 var receiverType = (JavaType) desc.refType();
85 String methodName = desc.name();
86 List<String> unsupportedMethods = UNSUPPORTED_METHODS.getOrDefault(receiverType, List.of());
87 for (String unsupportedMethod : unsupportedMethods) {
88 if (unsupportedMethod.equals(methodName)) {
89 String methErrString = receiverType.toNominalDescriptor().displayName() + "." + methodName;
90 logger.printMessage(Kind.ERROR, methErrString + " not supported in reflectable methods", element);
91 break;
92 }
93 }
94 }
95 case Op op -> {
96 String unsupportedOp = UNSUPPORTED_OPS.get(op.getClass());
97 if (unsupportedOp != null) {
98 logger.printMessage(Kind.ERROR, unsupportedOp + " not supported in reflectable methods", element);
99 }
100 }
101 default -> {
102 // do nothing
103 }
104 }
105 }
106 }
107
108 static final Map<JavaType, List<String>> UNSUPPORTED_METHODS = Map.of(
109 JavaType.type(System.class), List.of("exit", "gc", "load", "loadLibrary"),
110 JavaType.type(Runtime.class), List.of("load", "loadLibrary"));
111
112 static final Map<Class<?>, String> UNSUPPORTED_OPS = Map.of(
113 ThrowOp.class, "throw statement",
114 TryOp.class, "try/catch statement"
115 );
116 }