Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jruby.anno.AnnotationBinder Maven / Gradle / Ivy
package org.jruby.anno;
import org.jruby.CompatVersion;
import org.jruby.util.CodegenUtils;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import java.io.*;
import java.util.*;
import java.util.logging.Logger;
@SupportedAnnotationTypes({"org.jruby.anno.JRubyMethod" })
public class AnnotationBinder extends AbstractProcessor {
public static final String POPULATOR_SUFFIX = "$POPULATOR" ;
private static final Logger LOG = Logger.getLogger("AnnotationBinder" );
public static final String SRC_GEN_DIR = "target/generated-sources/org/jruby/gen/" ;
private final List classNames = new ArrayList();
private PrintStream out ;
private static final boolean DEBUG = false ;
@Override
public boolean process (Set typeElements, RoundEnvironment roundEnvironment ) {
for (TypeElement element : ElementFilter.typesIn(roundEnvironment.getRootElements())) {
processType(element);
}
try {
FileWriter fw = new FileWriter("target/generated-sources/annotated_classes.txt" );
for (CharSequence name : classNames) {
fw.write(name.toString());
fw.write('\n' );
}
fw.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
return true ;
}
@Override
public SourceVersion getSupportedSourceVersion ( ) {
return SourceVersion.latest();
}
public void processType (TypeElement cd ) {
for (TypeElement innerType : ElementFilter.typesIn(cd.getEnclosedElements())) {
processType(innerType);
}
try {
String qualifiedName = cd.getQualifiedName().toString().replace('.' , '$' );
if (!qualifiedName.contains("org$jruby" )) {
return ;
}
ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024 );
out = new PrintStream(bytes);
out .println("/* THIS FILE IS GENERATED. DO NOT EDIT */" );
out .println("package org.jruby.gen;" );
out .println("import org.jruby.Ruby;" );
out .println("import org.jruby.RubyModule;" );
out .println("import org.jruby.RubyClass;" );
out .println("import org.jruby.CompatVersion;" );
out .println("import org.jruby.anno.TypePopulator;" );
out .println("import org.jruby.internal.runtime.methods.CallConfiguration;" );
out .println("import org.jruby.internal.runtime.methods.JavaMethod;" );
out .println("import org.jruby.internal.runtime.methods.DynamicMethod;" );
out .println("import org.jruby.runtime.Arity;" );
out .println("import org.jruby.runtime.Visibility;" );
out .println("import org.jruby.compiler.ASTInspector;" );
out .println("import java.util.Arrays;" );
out .println("import java.util.List;" );
out .println("import javax.annotation.Generated;" );
out .println("@Generated(\"org.jruby.anno.AnnotationBinder\")" );
out .println("public class " + qualifiedName + POPULATOR_SUFFIX + " extends TypePopulator {" );
out .println(" public void populate(RubyModule cls, Class clazz) {" );
if (DEBUG) {
out .println(" System.out.println(\"Using pregenerated populator: \" + \"" + qualifiedName + POPULATOR_SUFFIX + "\");" );
}
boolean hasAnno = false ;
boolean hasMeta = false ;
boolean hasModule = false ;
boolean hasCompat = false ;
for (ExecutableElement method : ElementFilter.methodsIn(cd.getEnclosedElements())) {
JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
if (anno == null ) {
continue ;
}
hasAnno = true ;
hasMeta |= anno.meta();
hasModule |= anno.module();
hasCompat |= anno.compat() != CompatVersion.BOTH;
}
if (!hasAnno) return ;
out .println(" JavaMethod javaMethod;" );
out .println(" DynamicMethod moduleMethod;" );
if (hasMeta || hasModule) out .println(" RubyClass singletonClass = cls.getSingletonClass();" );
if (hasCompat)
out .println(" CompatVersion compatVersion = cls.getRuntime().getInstanceConfig().getCompatVersion();" );
out .println(" Ruby runtime = cls.getRuntime();" );
Map> annotatedMethods = new HashMap>();
Map> staticAnnotatedMethods = new HashMap>();
Map> annotatedMethods1_8 = new HashMap>();
Map> staticAnnotatedMethods1_8 = new HashMap>();
Map> annotatedMethods1_9 = new HashMap>();
Map> staticAnnotatedMethods1_9 = new HashMap>();
Map> annotatedMethods2_0 = new HashMap>();
Map> staticAnnotatedMethods2_0 = new HashMap>();
Set frameAwareMethods = new HashSet();
Set scopeAwareMethods = new HashSet();
int methodCount = 0 ;
for (ExecutableElement method : ElementFilter.methodsIn(cd.getEnclosedElements())) {
JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
if (anno == null ) {
continue ;
}
methodCount++;
if (method.getThrownTypes().size() != 0 ) {
System.err.print("Method " + cd.toString() + "." + method.toString() + " should not throw exceptions: " );
boolean comma = false ;
for (TypeMirror thrownType : method.getThrownTypes()) {
if (comma) System.err.print(", " );
System.err.print(thrownType);
comma = true ;
}
System.err.print("\n" );
}
CharSequence name = anno.name().length == 0 ? method.getSimpleName() : anno.name()[0 ];
List methodDescs;
Map> methodsHash = null ;
if (method.getModifiers().contains(Modifier.STATIC)) {
if (anno.compat() == CompatVersion.RUBY1_8) {
methodsHash = staticAnnotatedMethods1_8;
} else if (anno.compat() == CompatVersion.RUBY1_9) {
methodsHash = staticAnnotatedMethods1_9;
} else if (anno.compat() == CompatVersion.RUBY2_0) {
methodsHash = staticAnnotatedMethods2_0;
} else {
methodsHash = staticAnnotatedMethods;
}
} else {
if (anno.compat() == CompatVersion.RUBY1_8) {
methodsHash = annotatedMethods1_8;
} else if (anno.compat() == CompatVersion.RUBY1_9) {
methodsHash = annotatedMethods1_9;
} else if (anno.compat() == CompatVersion.RUBY2_0) {
methodsHash = annotatedMethods2_0;
} else {
methodsHash = annotatedMethods;
}
}
methodDescs = methodsHash.get (name);
if (methodDescs == null ) {
methodDescs = new ArrayList();
methodsHash.put(name, methodDescs);
}
methodDescs.add (method);
boolean frame = false ;
boolean scope = false ;
if (anno.frame()) {
if (DEBUG)
System.out .println("Method has frame = true: " + methodDescs.get (0 ).getEnclosingElement() + ":" + methodDescs);
frame = true ;
}
if (anno.scope()) {
if (DEBUG)
System.out .println("Method has frame = true: " + methodDescs.get (0 ).getEnclosingElement() + ":" + methodDescs);
scope = true ;
}
for (FrameField field : anno.reads()) {
frame |= field.needsFrame();
scope |= field.needsScope();
}
for (FrameField field : anno.writes()) {
frame |= field.needsFrame();
scope |= field.needsScope();
}
if (frame) AnnotationHelper.addMethodNamesToSet(frameAwareMethods, anno, method.getSimpleName().toString());
if (scope) AnnotationHelper.addMethodNamesToSet(scopeAwareMethods, anno, method.getSimpleName().toString());
}
if (methodCount == 0 ) {
return ;
}
classNames.add (getActualQualifiedName(cd));
processMethodDeclarations(staticAnnotatedMethods);
for (Map.Entry> entry : staticAnnotatedMethods.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
if (!staticAnnotatedMethods1_8.isEmpty()) {
out .println(" if (compatVersion == CompatVersion.RUBY1_8 || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(staticAnnotatedMethods1_8);
for (Map.Entry> entry : staticAnnotatedMethods1_8.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
if (!staticAnnotatedMethods1_9.isEmpty()) {
out .println(" if (compatVersion.is1_9() || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(staticAnnotatedMethods1_9);
for (Map.Entry> entry : staticAnnotatedMethods1_9.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
if (!staticAnnotatedMethods2_0.isEmpty()) {
out .println(" if (compatVersion.is2_0() || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(staticAnnotatedMethods2_0);
for (Map.Entry> entry : staticAnnotatedMethods2_0.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
processMethodDeclarations(annotatedMethods);
for (Map.Entry> entry : annotatedMethods.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
if (!annotatedMethods1_8.isEmpty()) {
out .println(" if (compatVersion == CompatVersion.RUBY1_8 || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(annotatedMethods1_8);
for (Map.Entry> entry : annotatedMethods1_8.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
if (!annotatedMethods1_9.isEmpty()) {
out .println(" if (compatVersion.is1_9() || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(annotatedMethods1_9);
for (Map.Entry> entry : annotatedMethods1_9.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
if (!annotatedMethods2_0.isEmpty()) {
out .println(" if (compatVersion.is2_0() || compatVersion == CompatVersion.BOTH) {" );
processMethodDeclarations(annotatedMethods2_0);
for (Map.Entry> entry : annotatedMethods2_0.entrySet()) {
ExecutableElement decl = entry.getValue().get (0 );
if (!decl.getAnnotation(JRubyMethod.class).omit()) addCoreMethodMapping(entry.getKey(), decl, out );
}
out .println(" }" );
}
out .println(" }" );
out .println(" static {" );
if (!frameAwareMethods.isEmpty()) {
StringBuffer frameMethodsString = new StringBuffer();
boolean first = true ;
for (CharSequence name : frameAwareMethods) {
if (!first) frameMethodsString.append(',' );
first = false ;
frameMethodsString.append('"' ).append(name).append('"' );
}
out .println(" ASTInspector.addFrameAwareMethods(" + frameMethodsString + ");" );
}
if (!scopeAwareMethods.isEmpty()) {
StringBuffer scopeMethodsString = new StringBuffer();
boolean first = true ;
for (CharSequence name : scopeAwareMethods) {
if (!first) scopeMethodsString.append(',' );
first = false ;
scopeMethodsString.append('"' ).append(name).append('"' );
}
out .println(" ASTInspector.addScopeAwareMethods(" + scopeMethodsString + ");" );
}
out .println(" }" );
out .println("}" );
out .close();
out = null ;
new File(SRC_GEN_DIR).mkdirs();
FileOutputStream fos = new FileOutputStream(SRC_GEN_DIR + qualifiedName + POPULATOR_SUFFIX + ".java" );
fos.write(bytes.toByteArray());
fos.close();
} catch (IOException ioe) {
LOG.severe("FAILED TO GENERATE: " + ioe);
System.exit(1 );
}
}
public void processMethodDeclarations (Map > declarations ) {
for (Map.Entry> entry : declarations.entrySet()) {
List list = entry.getValue();
if (list.size() == 1 ) {
processMethodDeclaration(list.get (0 ));
} else {
processMethodDeclarationMulti(list.get (0 ));
}
}
}
public void processMethodDeclaration (ExecutableElement method ) {
JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
if (anno != null && out != null ) {
boolean isStatic = method.getModifiers().contains(Modifier.STATIC);
CharSequence qualifiedName = getActualQualifiedName((TypeElement)method.getEnclosingElement());
boolean hasContext = false ;
boolean hasBlock = false ;
StringBuffer buffer = new StringBuffer();
boolean first = true ;
for (VariableElement parameter : method.getParameters()) {
if (!first) buffer.append(", " );
first = false ;
buffer.append(parameter.asType().toString());
buffer.append(".class" );
hasContext |= parameter.asType().toString().equals ("org.jruby.runtime.ThreadContext" );
hasBlock |= parameter.asType().toString().equals ("org.jruby.runtime.Block" );
}
int actualRequired = calculateActualRequired(method, method.getParameters().size(), anno.optional(), anno.rest(), isStatic, hasContext, hasBlock);
String annotatedBindingName = CodegenUtils.getAnnotatedBindingClassName(
method.getSimpleName(),
qualifiedName,
isStatic,
actualRequired,
anno.optional(),
false ,
anno.frame());
String implClass = anno.meta() ? "singletonClass" : "cls" ;
out .println(" javaMethod = new " + annotatedBindingName + "(" + implClass + ", Visibility." + anno.visibility() + ");" );
out .println(" populateMethod(javaMethod, " +
+AnnotationHelper.getArityValue(anno, actualRequired) + ", \""
+ method.getSimpleName() + "\", "
+ isStatic + ", "
+ "CallConfiguration." + AnnotationHelper.getCallConfigNameByAnno(anno) + ", "
+ anno.notImplemented() + ", "
+ ((TypeElement)method.getEnclosingElement()).getQualifiedName() + ".class, "
+ "\"" + method.getSimpleName() + "\", "
+ method.getReturnType().toString() + ".class, "
+ "new Class[] {" + buffer.toString() + "});" );
generateMethodAddCalls(method, anno);
}
}
public void processMethodDeclarationMulti (ExecutableElement method ) {
JRubyMethod anno = method.getAnnotation(JRubyMethod.class);
if (anno != null && out != null ) {
boolean isStatic = method.getModifiers().contains(Modifier.STATIC);
CharSequence qualifiedName = getActualQualifiedName((TypeElement)method.getEnclosingElement());
boolean hasContext = false ;
boolean hasBlock = false ;
StringBuffer buffer = new StringBuffer();
boolean first = true ;
for (VariableElement parameter : method.getParameters()) {
if (!first) buffer.append(", " );
first = false ;
buffer.append(parameter.asType().toString());
buffer.append(".class" );
hasContext |= parameter.asType().toString().equals ("org.jruby.runtime.ThreadContext" );
hasBlock |= parameter.asType().toString().equals ("org.jruby.runtime.Block" );
}
int actualRequired = calculateActualRequired(method, method.getParameters().size(), anno.optional(), anno.rest(), isStatic, hasContext, hasBlock);
String annotatedBindingName = CodegenUtils.getAnnotatedBindingClassName(
method.getSimpleName(),
qualifiedName,
isStatic,
actualRequired,
anno.optional(),
true ,
anno.frame());
String implClass = anno.meta() ? "singletonClass" : "cls" ;
out .println(" javaMethod = new " + annotatedBindingName + "(" + implClass + ", Visibility." + anno.visibility() + ");" );
out .println(" populateMethod(javaMethod, " +
"-1, \"" +
method.getSimpleName() + "\", " +
isStatic + ", " +
"CallConfiguration." + AnnotationHelper.getCallConfigNameByAnno(anno) + ", " +
anno.notImplemented() + ", "
+ ((TypeElement)method.getEnclosingElement()).getQualifiedName() + ".class, "
+ "\"" + method.getSimpleName() + "\", "
+ method.getReturnType().toString() + ".class, "
+ "new Class[] {" + buffer.toString() + "});" );
generateMethodAddCalls(method, anno);
}
}
private void addCoreMethodMapping (CharSequence rubyName, ExecutableElement decl, PrintStream out ) {
out .println(new StringBuilder(50 )
.append(" runtime.addBoundMethod(" )
.append('"' ).append(((TypeElement)decl.getEnclosingElement()).getQualifiedName()).append('"' )
.append(',' )
.append('"' ).append(decl.getSimpleName()).append('"' )
.append(',' )
.append('"' ).append(rubyName).append('"' )
.append(");" ).toString());
}
private CharSequence getActualQualifiedName (TypeElement td ) {
if (td.getNestingKind() == NestingKind.MEMBER) {
return getActualQualifiedName((TypeElement)td.getEnclosingElement()) + "$" + td.getSimpleName();
}
return td.getQualifiedName().toString();
}
private int calculateActualRequired (ExecutableElement md, int paramsLength, int optional, boolean rest, boolean isStatic, boolean hasContext, boolean hasBlock ) {
int actualRequired;
if (optional == 0 && !rest) {
int args = paramsLength;
if (args == 0 ) {
actualRequired = 0 ;
} else {
if (isStatic) {
args--;
}
if (hasContext) {
args--;
}
if (hasBlock) {
args--;
}
actualRequired = args;
}
} else {
int args = paramsLength;
if (args == 0 ) {
actualRequired = 0 ;
} else {
if (isStatic) {
args--;
}
if (hasContext) {
args--;
}
if (hasBlock) {
args--;
}
args--;
actualRequired = args;
}
if (actualRequired != 0 ) {
throw new RuntimeException("Combining specific args with IRubyObject[] is not yet supported: "
+ ((TypeElement)md.getEnclosingElement()).getQualifiedName() + "." + md.toString());
}
}
return actualRequired;
}
public void generateMethodAddCalls (ExecutableElement md, JRubyMethod jrubyMethod ) {
if (jrubyMethod.meta()) {
defineMethodOnClass("javaMethod" , "singletonClass" , jrubyMethod, md);
} else {
defineMethodOnClass("javaMethod" , "cls" , jrubyMethod, md);
if (jrubyMethod.module()) {
out .println(" moduleMethod = populateModuleMethod(cls, javaMethod);" );
defineMethodOnClass("moduleMethod" , "singletonClass" , jrubyMethod, md);
}
}
}
private void defineMethodOnClass (String methodVar, String classVar, JRubyMethod jrubyMethod, ExecutableElement md ) {
CharSequence baseName;
if (jrubyMethod.name().length == 0 ) {
baseName = md.getSimpleName();
out .println(" " + classVar + ".addMethodAtBootTimeOnly(\"" + baseName + "\", " + methodVar + ");" );
} else {
baseName = jrubyMethod.name()[0 ];
for (String name : jrubyMethod.name()) {
out .println(" " + classVar + ".addMethodAtBootTimeOnly(\"" + name + "\", " + methodVar + ");" );
}
}
if (jrubyMethod.alias ().length > 0 ) {
for (String alias : jrubyMethod.alias ()) {
out .println(" " + classVar + ".defineAlias(\"" + alias + "\", \"" + baseName + "\");" );
}
}
}
}