/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.tools.apt; import com.sun.mirror.apt.AnnotationProcessor; import com.sun.mirror.apt.AnnotationProcessorEnvironment; import com.sun.mirror.apt.Filer; import com.sun.mirror.apt.Messager; import com.sun.mirror.declaration.AnnotationMirror; import com.sun.mirror.declaration.AnnotationTypeElementDeclaration; import com.sun.mirror.declaration.AnnotationValue; import com.sun.mirror.declaration.ClassDeclaration; import com.sun.mirror.declaration.Declaration; import com.sun.mirror.declaration.MethodDeclaration; import com.sun.mirror.declaration.Modifier; import com.sun.mirror.declaration.ParameterDeclaration; import com.sun.mirror.declaration.TypeDeclaration; import com.sun.mirror.type.AnnotationType; import com.sun.mirror.type.TypeMirror; import com.sun.mirror.util.DeclarationVisitors; import com.sun.mirror.util.SimpleDeclarationVisitor; import java.io.IOException; import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; /** * Process the '@SysCallTemplate' annotation. Generates an implementation * class for methods annotated with this, that provides an indirection to * a native method annotated with @SysCall. */ public class SysCallProcessor implements AnnotationProcessor { public static final String GEN_IMPL_ANNOTATION = "org.jikesrvm.apt.annotations.GenerateImplementation"; public static final String SYSCALL_TEMPLATE_ANNOTATION = "org.jikesrvm.apt.annotations.SysCallTemplate"; private final AnnotationProcessorEnvironment env; public SysCallProcessor(AnnotationProcessorEnvironment env) { this.env = env; } /** * @see AnnotationProcessor#process() */ public void process() { for (TypeDeclaration typeDecl : env.getSpecifiedTypeDeclarations()) typeDecl.accept(DeclarationVisitors.getSourceOrderDeclarationScanner( /* pre-processing */ new SysCallVisitor(), /* post-processing */ DeclarationVisitors.NO_OP)); } /** * Visit a class that has SysCall annotations */ private class SysCallVisitor extends SimpleDeclarationVisitor { private PrintWriter out; private final Messager mess = env.getMessager(); private final Filer filer = env.getFiler(); SysCallVisitor() {} /** * Process a class declaration. We want to produce a new java class, * so process all the declarations in the class explicitly from here. * * A class that uses the SysCallTemplate annotation should itself * be annotated with @GenerateImplementation, and it is from this * annotation that we get the name of the generated class. */ @Override public void visitClassDeclaration(ClassDeclaration classDecl) { /* * Get the GeneratedImplementation annotation and find out * what class we should be generating */ String generatedClass = derivedClassName(classDecl); /* * Break the generated class name into package and class names */ int lastDot = generatedClass.lastIndexOf("."); String generatedPackage; String generatedClassName; if (lastDot > 0) { generatedPackage = generatedClass.substring(0,lastDot); generatedClassName = generatedClass.substring(lastDot+1); } else { generatedPackage = classDecl.getPackage().toString(); generatedClassName = generatedClass; } /* * Create the output file */ try { out = filer.createSourceFile(generatedClass); } catch (IOException e) { mess.printError("Error creating source file "+generatedClass); mess.printError(e.getMessage()); } mess.printNotice("Creating "+generatedClass); out.println(asComment("Auto-generated from "+classDecl.getQualifiedName())); printDocComment(classDecl.getPackage()); out.println("package "+generatedPackage+";"); /* Need a decent way to pass imports to the generated file */ out.println(); out.println("import org.vmmagic.pragma.*;"); out.println("import org.vmmagic.unboxed.*;"); out.println(); /* * Preserve other annotations */ for (AnnotationMirror ann : classDecl.getAnnotationMirrors()) { AnnotationType type = ann.getAnnotationType(); if (!type.toString().equals(GEN_IMPL_ANNOTATION)) out.println(" "+ann); } out.println("public final class "+generatedClassName+" extends "+classDecl.getQualifiedName()+" {"); for (MethodDeclaration m : classDecl.getMethods()) if (getAnnotation(m,SYSCALL_TEMPLATE_ANNOTATION) != null) doMethodDeclaration(m); out.println("}"); } private String derivedClassName(ClassDeclaration d) { String generatedClass; AnnotationMirror ann = getAnnotation(d,GEN_IMPL_ANNOTATION); if (ann != null) { generatedClass = getAnnotationElementValue("value()", ann); } else { generatedClass = d.getQualifiedName()+"Impl"; } return generatedClass; } /** * Process a method declaration, annotated with a @SysCallTemplate annotation. * * Generate the public instance method that dispatches to the native method, * and the native method that is the SysCall itself. * * @param methodDecl */ public void doMethodDeclaration(MethodDeclaration methodDecl) { String methodName = methodDecl.getSimpleName(); printDocComment(methodDecl); /* * Presevre annotations that we don't process */ for (AnnotationMirror ann : methodDecl.getAnnotationMirrors()) { AnnotationType type = ann.getAnnotationType(); if (!type.toString().equals(SYSCALL_TEMPLATE_ANNOTATION)) out.println(" "+ann); } /* * Create the concrete method body */ out.print(" "); boolean isAbstract = false; for (Modifier m : methodDecl.getModifiers()) { switch(m) { case NATIVE: mess.printError("Cannot implement @SysCallTemplate on a native method"); break; case STATIC: mess.printError("Cannot implement @SysCallTemplate on a static method"); break; case ABSTRACT: isAbstract = true; break; default: out.print(m+" "); } } if (!isAbstract) { mess.printError("Can only implement @SysCallTemplate on an abstract method"); } final TypeMirror returnType = methodDecl.getReturnType(); boolean isVoid = returnType.equals(env.getTypeUtils().getVoidType()); out.print(returnType+" "+methodName); /* * Formal parameters */ out.print("("); Iterator<ParameterDeclaration> params = methodDecl.getParameters().iterator(); while (params.hasNext()) { ParameterDeclaration p = params.next(); out.print(p); if (params.hasNext()) out.print(","); } out.println(") {"); /* * Method body */ out.print(" "); if (!isVoid) out.print("return "); out.print(methodName+"(BootRecord.the_boot_record."); out.print(methodName+"IP"); if (!methodDecl.getParameters().isEmpty()) { params = methodDecl.getParameters().iterator(); while (params.hasNext()) { out.print(", "); ParameterDeclaration p = params.next(); out.print(p.getSimpleName()); } } out.println(");"); out.println(" }"); /* * Generate the private native stub */ out.println(); out.println(" @SysCallNative"); out.print(" private static native "+returnType+" "+methodName+"(Address nativeIP"); params = methodDecl.getParameters().iterator(); while (params.hasNext()) { out.print(", "); ParameterDeclaration p = params.next(); out.print(p.getType()+" "+p.getSimpleName()); } out.println(");"); out.println(); out.println(); } private void printDocComment(Declaration d) { if (d.getDocComment() != null) out.println(asJavadoc(d.getDocComment())); } } private static String asJavadoc(String content) { return asComment(content,true); } private static String asComment(String content) { return asComment(content,false); } private static String asComment(String content, boolean javadoc) { String result = javadoc ? "/**\n" : "/*\n"; for (String line : content.split("\n")) result += " * "+line+"\n"; result += " */"; return result; } /* utility methods for dealing with type mirrors */ /** * Return an annotation from a declaration, given its fq name * * @param d The declaration to look at * @param annotationName Name of the annotation */ private static AnnotationMirror getAnnotation(Declaration d, String annotationName) { for (AnnotationMirror ann : d.getAnnotationMirrors()) { AnnotationType type = ann.getAnnotationType(); if (type.toString().equals(annotationName)) { return ann; } } return null; } /** * Get the value of an element of an annotation * * @param elementName * @param ann * @return */ private static String getAnnotationElementValue(String elementName, AnnotationMirror ann) { Map<AnnotationTypeElementDeclaration,AnnotationValue> values = ann.getElementValues(); for (AnnotationTypeElementDeclaration element : values.keySet()) { if (element.toString().equals(elementName)) { AnnotationValue val = values.get(element); return (String)val.getValue(); } } return null; } }