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 }