зеркало из https://github.com/mozilla/gecko-dev.git
Bug 913985: Part 3 - Update the annotation processor to generate wrapper classes. r=kats
--HG-- rename : build/annotationProcessors/MethodWithAnnotationInfo.java => build/annotationProcessors/AnnotationInfo.java rename : build/annotationProcessors/utils/AlphabeticMethodComparator.java => build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java rename : build/annotationProcessors/utils/GeneratableEntryPointIterator.java => build/annotationProcessors/utils/GeneratableElementIterator.java
This commit is contained in:
Родитель
243a3f5a34
Коммит
5c178045aa
|
@ -4,19 +4,15 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Object holding method and annotation. Used by GeneratableEntryPointIterator.
|
||||
* Object holding annotation data. Used by GeneratableElementIterator.
|
||||
*/
|
||||
public class MethodWithAnnotationInfo {
|
||||
public final Method method;
|
||||
public class AnnotationInfo {
|
||||
public final String wrapperName;
|
||||
public final boolean isStatic;
|
||||
public final boolean isMultithreaded;
|
||||
|
||||
public MethodWithAnnotationInfo(Method aMethod, String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
|
||||
method = aMethod;
|
||||
public AnnotationInfo(String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
|
||||
wrapperName = aWrapperName;
|
||||
isStatic = aIsStatic;
|
||||
isMultithreaded = aIsMultithreaded;
|
|
@ -4,8 +4,10 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.GeneratableEntryPointIterator;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -16,6 +18,12 @@ public class AnnotationProcessor {
|
|||
public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
|
||||
public static final String HEADERFILE = "GeneratedJNIWrappers.h";
|
||||
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
|
||||
"// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
|
||||
"// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
|
||||
|
||||
public static void main(String[] args) {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length <= 1) {
|
||||
|
@ -33,44 +41,121 @@ public class AnnotationProcessor {
|
|||
long s = System.currentTimeMillis();
|
||||
|
||||
// Get an iterator over the classes in the jar files given...
|
||||
Iterator<Class<?>> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
|
||||
Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
|
||||
|
||||
CodeGenerator generatorInstance = new CodeGenerator();
|
||||
StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
headerFile.append("#ifndef GeneratedJNIWrappers_h__\n" +
|
||||
"#define GeneratedJNIWrappers_h__\n\n" +
|
||||
"#include \"nsXPCOMStrings.h\"\n" +
|
||||
"#include \"AndroidJavaWrappers.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace widget {\n" +
|
||||
"namespace android {\n" +
|
||||
"void InitStubs(JNIEnv *jEnv);\n\n");
|
||||
|
||||
StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
||||
implementationFile.append("#include \"GeneratedJNIWrappers.h\"\n" +
|
||||
"#include \"AndroidBridgeUtilities.h\"\n" +
|
||||
"#include \"nsXPCOMStrings.h\"\n" +
|
||||
"#include \"AndroidBridge.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace widget {\n" +
|
||||
"namespace android {\n");
|
||||
|
||||
// Used to track the calls to the various class-specific initialisation functions.
|
||||
StringBuilder stubInitialiser = new StringBuilder();
|
||||
stubInitialiser.append("void InitStubs(JNIEnv *jEnv) {\n");
|
||||
|
||||
while (jarClassIterator.hasNext()) {
|
||||
Class<?> aClass = jarClassIterator.next();
|
||||
ClassWithOptions aClassTuple = jarClassIterator.next();
|
||||
|
||||
CodeGenerator generatorInstance;
|
||||
|
||||
// Get an iterator over the appropriately generated methods of this class
|
||||
Iterator<MethodWithAnnotationInfo> methodIterator = new GeneratableEntryPointIterator(aClass.getDeclaredMethods());
|
||||
Iterator<AnnotatableEntity> methodIterator = new GeneratableElementIterator(aClassTuple.wrappedClass);
|
||||
|
||||
// Iterate all annotated methods in this class..
|
||||
if (!methodIterator.hasNext()) {
|
||||
continue;
|
||||
}
|
||||
generatorInstance = new CodeGenerator(aClassTuple.wrappedClass, aClassTuple.generatedName);
|
||||
|
||||
stubInitialiser.append(" ").append(aClassTuple.generatedName).append("::InitStubs(jEnv);\n");
|
||||
|
||||
// Iterate all annotated members in this class..
|
||||
while (methodIterator.hasNext()) {
|
||||
MethodWithAnnotationInfo aMethodTuple = methodIterator.next();
|
||||
generatorInstance.generateMethod(aMethodTuple, aClass);
|
||||
AnnotatableEntity aElementTuple = methodIterator.next();
|
||||
switch (aElementTuple.mEntityType) {
|
||||
case METHOD:
|
||||
generatorInstance.generateMethod(aElementTuple);
|
||||
break;
|
||||
case FIELD:
|
||||
generatorInstance.generateField(aElementTuple);
|
||||
break;
|
||||
case CONSTRUCTOR:
|
||||
generatorInstance.generateConstructor(aElementTuple);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
headerFile.append(generatorInstance.getHeaderFileContents());
|
||||
implementationFile.append(generatorInstance.getWrapperFileContents());
|
||||
}
|
||||
|
||||
writeOutputFiles(generatorInstance);
|
||||
implementationFile.append('\n');
|
||||
stubInitialiser.append("}");
|
||||
implementationFile.append(stubInitialiser);
|
||||
|
||||
implementationFile.append("\n} /* android */\n" +
|
||||
"} /* widget */\n" +
|
||||
"} /* mozilla */\n");
|
||||
|
||||
headerFile.append("\n} /* android */\n" +
|
||||
"} /* widget */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif\n");
|
||||
|
||||
writeOutputFiles(headerFile, implementationFile);
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("Annotation processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static void writeOutputFiles(CodeGenerator aGenerator) {
|
||||
private static void writeOutputFiles(StringBuilder aHeaderFile, StringBuilder aImplementationFile) {
|
||||
FileOutputStream headerStream = null;
|
||||
try {
|
||||
FileOutputStream outStream = new FileOutputStream(OUTFILE);
|
||||
outStream.write(aGenerator.getWrapperFileContents());
|
||||
headerStream = new FileOutputStream(OUTFILE);
|
||||
headerStream.write(aImplementationFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (headerStream != null) {
|
||||
try {
|
||||
headerStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close headerStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream outStream = null;
|
||||
try {
|
||||
FileOutputStream headerStream = new FileOutputStream(HEADERFILE);
|
||||
headerStream.write(aGenerator.getHeaderFileContents());
|
||||
outStream = new FileOutputStream(HEADERFILE);
|
||||
outStream.write(aHeaderFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
|
||||
System.err.println("Unable to write " + HEADERFILE + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (outStream != null) {
|
||||
try {
|
||||
outStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close outStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,82 +4,236 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class CodeGenerator {
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
private static final Annotation[][] GETTER_ARGUMENT_ANNOTATIONS = new Annotation[0][0];
|
||||
private static final Annotation[][] SETTER_ARGUMENT_ANNOTATIONS = new Annotation[1][0];
|
||||
|
||||
// Buffers holding the strings to ultimately be written to the output files.
|
||||
private final StringBuilder zeroingCode = new StringBuilder();
|
||||
private final StringBuilder wrapperStartupCode = new StringBuilder();
|
||||
private final StringBuilder wrapperMethodBodies = new StringBuilder();
|
||||
private final StringBuilder headerFields = new StringBuilder();
|
||||
private final StringBuilder headerMethods = new StringBuilder();
|
||||
private final StringBuilder headerPublic = new StringBuilder();
|
||||
private final StringBuilder headerProtected = new StringBuilder();
|
||||
|
||||
private final HashSet<String> seenClasses = new HashSet<String>();
|
||||
|
||||
private final String GENERATED_COMMENT = "// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
|
||||
"// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
|
||||
"// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
|
||||
private final String mCClassName;
|
||||
|
||||
private final Class<?> mClassToWrap;
|
||||
|
||||
private boolean mHasEncounteredDefaultConstructor;
|
||||
|
||||
// Used for creating unique names for method ID fields in the face of
|
||||
private final HashMap<Member, String> mMembersToIds = new HashMap<Member, String>();
|
||||
private final HashSet<String> mTakenMemberNames = new HashSet<String>();
|
||||
private int mNameMunger;
|
||||
|
||||
public CodeGenerator(Class<?> aClass, String aGeneratedName) {
|
||||
mClassToWrap = aClass;
|
||||
mCClassName = aGeneratedName;
|
||||
|
||||
public CodeGenerator() {
|
||||
// Write the file header things. Includes and so forth.
|
||||
// GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with
|
||||
// wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerFields
|
||||
// with headerMethods.
|
||||
wrapperStartupCode.append(GENERATED_COMMENT);
|
||||
wrapperStartupCode.append(
|
||||
"#include \"nsXPCOMStrings.h\"\n" +
|
||||
"#include \"AndroidBridge.h\"\n" +
|
||||
"#include \"AndroidBridgeUtilities.h\"\n" +
|
||||
"\n" +
|
||||
"#ifdef DEBUG\n" +
|
||||
"#define ALOG_BRIDGE(args...) ALOG(args)\n" +
|
||||
"#else\n" +
|
||||
"#define ALOG_BRIDGE(args...) ((void)0)\n" +
|
||||
"#endif\n" +
|
||||
"\n" +
|
||||
"using namespace mozilla;\n" +
|
||||
"void AndroidBridge::InitStubs(JNIEnv *jEnv) {\n" +
|
||||
" initInit();\n");
|
||||
// Now we write the various GetStaticMethodID calls here...
|
||||
// wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerPublic
|
||||
// with headerProtected.
|
||||
wrapperStartupCode.append("void ").append(mCClassName).append("::InitStubs(JNIEnv *jEnv) {\n" +
|
||||
" initInit();\n");
|
||||
|
||||
headerFields.append("protected:\n\n");
|
||||
headerMethods.append(GENERATED_COMMENT);
|
||||
headerMethods.append("public:\n\n");
|
||||
// Now we write the various GetStaticMethodID calls here...
|
||||
headerPublic.append("class ").append(mCClassName).append(" : public AutoGlobalWrappedJavaObject {\n" +
|
||||
"public:\n" +
|
||||
" static void InitStubs(JNIEnv *jEnv);\n");
|
||||
headerProtected.append("protected:");
|
||||
|
||||
generateWrapperMethod();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a static method which takes an instance of the class being wrapped and returns an instance
|
||||
* of the C++ wrapper class backed by that object.
|
||||
*/
|
||||
private void generateWrapperMethod() {
|
||||
headerPublic.append(" static ").append(mCClassName).append("* Wrap(jobject obj);\n" +
|
||||
" ").append(mCClassName).append("(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};\n");
|
||||
|
||||
wrapperMethodBodies.append("\n").append(mCClassName).append("* ").append(mCClassName).append("::Wrap(jobject obj) {\n" +
|
||||
" JNIEnv *env = GetJNIForThread();\n\n" +
|
||||
" if (!env) {\n" +
|
||||
" ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" return NULL;\n" +
|
||||
" }\n\n" +
|
||||
" ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" +
|
||||
" env->DeleteLocalRef(obj);\n" +
|
||||
" return ret;\n" +
|
||||
"}\n");
|
||||
}
|
||||
|
||||
private void generateMemberCommon(Member theMethod, String aCMethodName, Class<?> aClass) {
|
||||
ensureClassHeaderAndStartup(aClass);
|
||||
writeMemberIdField(theMethod, aCMethodName);
|
||||
writeStartupCode(theMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the method provided.
|
||||
*
|
||||
* @param aMethodTuple The Java method, plus the name for the generated method.
|
||||
* @param aClass The class to which the Java method belongs.
|
||||
* @param aMethodTuple The Java method, plus annotation data.
|
||||
*/
|
||||
public void generateMethod(MethodWithAnnotationInfo aMethodTuple, Class<?> aClass) {
|
||||
public void generateMethod(AnnotatableEntity aMethodTuple) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
Method aMethod = aMethodTuple.method;
|
||||
String CMethodName = aMethodTuple.wrapperName;
|
||||
Method theMethod = aMethodTuple.getMethod();
|
||||
|
||||
String javaMethodName = aMethod.getName();
|
||||
String CMethodName = aMethodTuple.mAnnotationInfo.wrapperName;
|
||||
|
||||
ensureClassHeaderAndStartup(aClass);
|
||||
generateMemberCommon(theMethod, CMethodName, mClassToWrap);
|
||||
|
||||
writeHeaderField(CMethodName);
|
||||
writeStartupCode(CMethodName, javaMethodName, aMethod, aClass);
|
||||
boolean isFieldStatic = Utils.isMemberStatic(theMethod);
|
||||
boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic;
|
||||
|
||||
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
||||
Class<?> returnType = theMethod.getReturnType();
|
||||
|
||||
// Get the C++ method signature for this method.
|
||||
String implementationSignature = Utils.getCImplementationMethodSignature(aMethod, CMethodName);
|
||||
String headerSignature = Utils.getCHeaderMethodSignature(aMethod, CMethodName, aMethodTuple.isStatic);
|
||||
String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName);
|
||||
String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic);
|
||||
|
||||
// Add the header signature to the header file.
|
||||
headerMethods.append(headerSignature);
|
||||
headerMethods.append(";\n");
|
||||
writeSignatureToHeader(headerSignature);
|
||||
|
||||
// Use the implementation signature to generate the method body...
|
||||
writeMethodBody(implementationSignature, CMethodName, aMethod, aClass, aMethodTuple.isStatic, aMethodTuple.isMultithreaded);
|
||||
writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap, aMethodTuple.mAnnotationInfo.isStatic, aMethodTuple.mAnnotationInfo.isMultithreaded);
|
||||
}
|
||||
|
||||
private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) {
|
||||
StringBuilder argumentContent = null;
|
||||
|
||||
if (isSetter) {
|
||||
Class<?>[] setterArguments = new Class<?>[]{aFieldType};
|
||||
// Marshall the argument..
|
||||
argumentContent = getArgumentMarshalling(setterArguments);
|
||||
}
|
||||
|
||||
boolean isObjectReturningMethod = Utils.isObjectType(aFieldType);
|
||||
wrapperMethodBodies.append(" ");
|
||||
if (isSetter) {
|
||||
wrapperMethodBodies.append("env->Set");
|
||||
} else {
|
||||
wrapperMethodBodies.append("return ");
|
||||
|
||||
if (isObjectReturningMethod) {
|
||||
wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">(");
|
||||
}
|
||||
|
||||
wrapperMethodBodies.append("env->Get");
|
||||
}
|
||||
|
||||
if (aIsFieldStatic) {
|
||||
wrapperMethodBodies.append("Static");
|
||||
}
|
||||
wrapperMethodBodies.append(Utils.getFieldType(aFieldType))
|
||||
.append("Field(");
|
||||
|
||||
// Static will require the class and the field id. Nonstatic, the object and the field id.
|
||||
if (aIsFieldStatic) {
|
||||
wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap));
|
||||
} else {
|
||||
wrapperMethodBodies.append("wrapped_obj");
|
||||
}
|
||||
wrapperMethodBodies.append(", j")
|
||||
.append(aFieldName);
|
||||
if (isSetter) {
|
||||
wrapperMethodBodies.append(argumentContent);
|
||||
}
|
||||
|
||||
if (!isSetter && isObjectReturningMethod) {
|
||||
wrapperMethodBodies.append(')');
|
||||
}
|
||||
wrapperMethodBodies.append(");\n" +
|
||||
"}\n");
|
||||
}
|
||||
|
||||
public void generateField(AnnotatableEntity aFieldTuple) {
|
||||
Field theField = aFieldTuple.getField();
|
||||
|
||||
// Handles a peculiar case when dealing with enum types. We don't care about this field.
|
||||
// It just gets in the way and stops our code from compiling.
|
||||
if (theField.getName().equals("$VALUES")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String CFieldName = aFieldTuple.mAnnotationInfo.wrapperName;
|
||||
|
||||
Class<?> fieldType = theField.getType();
|
||||
|
||||
generateMemberCommon(theField, CFieldName, mClassToWrap);
|
||||
|
||||
boolean isFieldStatic = Utils.isMemberStatic(theField);
|
||||
boolean isFieldFinal = Utils.isMemberFinal(theField);
|
||||
boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic;
|
||||
|
||||
String getterName = "get" + CFieldName;
|
||||
String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName);
|
||||
String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic);
|
||||
|
||||
writeSignatureToHeader(getterHeaderSignature);
|
||||
|
||||
writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true);
|
||||
|
||||
generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false);
|
||||
|
||||
// If field not final, also generate a setter function.
|
||||
if (!isFieldFinal) {
|
||||
String setterName = "set" + CFieldName;
|
||||
|
||||
Class<?>[] setterArguments = new Class<?>[]{fieldType};
|
||||
|
||||
String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName);
|
||||
String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic);
|
||||
|
||||
writeSignatureToHeader(setterHeaderSignature);
|
||||
|
||||
writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true);
|
||||
|
||||
generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void generateConstructor(AnnotatableEntity aCtorTuple) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
Constructor theCtor = aCtorTuple.getConstructor();
|
||||
String CMethodName = mCClassName;
|
||||
|
||||
generateMemberCommon(theCtor, mCClassName, mClassToWrap);
|
||||
|
||||
String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName);
|
||||
String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false);
|
||||
|
||||
// Slice off the "void " from the start of the constructor declaration.
|
||||
headerSignature = headerSignature.substring(5);
|
||||
implementationSignature = implementationSignature.substring(5);
|
||||
|
||||
// Add the header signatures to the header file.
|
||||
writeSignatureToHeader(headerSignature);
|
||||
|
||||
// Use the implementation signature to generate the method body...
|
||||
writeCtorBody(implementationSignature, theCtor, aCtorTuple.mAnnotationInfo.isMultithreaded);
|
||||
|
||||
if (theCtor.getParameterTypes().length == 0) {
|
||||
mHasEncounteredDefaultConstructor = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,18 +248,169 @@ public class CodeGenerator {
|
|||
return;
|
||||
}
|
||||
|
||||
zeroingCode.append("jclass ")
|
||||
.append(mCClassName)
|
||||
.append("::")
|
||||
.append(Utils.getClassReferenceName(aClass))
|
||||
.append(" = 0;\n");
|
||||
|
||||
// Add a field to hold the reference...
|
||||
headerFields.append("\njclass ");
|
||||
headerFields.append(Utils.getClassReferenceName(aClass));
|
||||
headerFields.append(";\n");
|
||||
headerProtected.append("\n static jclass ")
|
||||
.append(Utils.getClassReferenceName(aClass))
|
||||
.append(";\n");
|
||||
|
||||
// Add startup code to populate it..
|
||||
wrapperStartupCode.append('\n');
|
||||
wrapperStartupCode.append(Utils.getStartupLineForClass(aClass));
|
||||
wrapperStartupCode.append('\n')
|
||||
.append(Utils.getStartupLineForClass(aClass));
|
||||
|
||||
seenClasses.add(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the function startup boilerplate for the method described. Check for environment
|
||||
* existence,
|
||||
* @param methodSignature
|
||||
* @param returnType
|
||||
* @param aIsStatic
|
||||
* @param aIsThreaded
|
||||
*/
|
||||
private void writeFunctionStartupBoilerPlate(String methodSignature, Class<?> returnType, boolean aIsStatic, boolean aIsThreaded) {
|
||||
// The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
|
||||
wrapperMethodBodies.append('\n')
|
||||
.append(methodSignature)
|
||||
.append(" {\n");
|
||||
|
||||
wrapperMethodBodies.append(" JNIEnv *env = ");
|
||||
if (!aIsThreaded) {
|
||||
wrapperMethodBodies.append("AndroidBridge::GetJNIEnv();\n");
|
||||
} else {
|
||||
wrapperMethodBodies.append("GetJNIForThread();\n");
|
||||
}
|
||||
wrapperMethodBodies.append(" if (!env) {\n" +
|
||||
" ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the appropriate JNI frame pushing boilerplate for a call to the member provided (
|
||||
* which must be a constructor or method).
|
||||
*
|
||||
* @param aMethod A constructor/method being wrapped.
|
||||
* @param aIsObjectReturningMethod Does the method being wrapped return an object?
|
||||
*/
|
||||
private void writeFramePushBoilerplate(Member aMethod, boolean aIsObjectReturningMethod) {
|
||||
if (aMethod instanceof Field) {
|
||||
throw new IllegalArgumentException("Tried to push frame for a FIELD?!");
|
||||
}
|
||||
|
||||
Method m;
|
||||
Constructor c;
|
||||
|
||||
Class<?> returnType;
|
||||
|
||||
int localReferencesNeeded;
|
||||
if (aMethod instanceof Method) {
|
||||
m = (Method) aMethod;
|
||||
returnType = m.getReturnType();
|
||||
localReferencesNeeded = Utils.enumerateReferenceArguments(m.getParameterTypes());
|
||||
} else {
|
||||
c = (Constructor) aMethod;
|
||||
returnType = Void.class;
|
||||
localReferencesNeeded = Utils.enumerateReferenceArguments(c.getParameterTypes());
|
||||
}
|
||||
|
||||
// Determine the number of local refs required for our local frame..
|
||||
// AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
|
||||
if (aIsObjectReturningMethod) {
|
||||
localReferencesNeeded++;
|
||||
}
|
||||
wrapperMethodBodies.append(" if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
|
||||
" ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" env->ExceptionDescribe();\n"+
|
||||
" env->ExceptionClear();\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n\n");
|
||||
|
||||
}
|
||||
|
||||
private StringBuilder getArgumentMarshalling(Class<?>[] argumentTypes) {
|
||||
StringBuilder argumentContent = new StringBuilder();
|
||||
|
||||
// If we have >2 arguments, use the jvalue[] calling approach.
|
||||
argumentContent.append(", ");
|
||||
if (argumentTypes.length > 2) {
|
||||
wrapperMethodBodies.append(" jvalue args[").append(argumentTypes.length).append("];\n");
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
wrapperMethodBodies.append(" args[").append(aT).append("].")
|
||||
.append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
|
||||
}
|
||||
|
||||
// The only argument is the array of arguments.
|
||||
argumentContent.append("args");
|
||||
wrapperMethodBodies.append('\n');
|
||||
} else {
|
||||
// Otherwise, use the vanilla calling approach.
|
||||
boolean needsNewline = false;
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
// If the argument is a string-esque type, create a jstring from it, otherwise
|
||||
// it can be passed directly.
|
||||
if (Utils.isCharSequence(argumentTypes[aT])) {
|
||||
wrapperMethodBodies.append(" jstring j").append(aT).append(" = AndroidBridge::NewJavaString(env, a").append(aT).append(");\n");
|
||||
needsNewline = true;
|
||||
// Ensure we refer to the newly constructed Java string - not to the original
|
||||
// parameter to the wrapper function.
|
||||
argumentContent.append('j').append(aT);
|
||||
} else {
|
||||
argumentContent.append('a').append(aT);
|
||||
}
|
||||
if (aT != argumentTypes.length - 1) {
|
||||
argumentContent.append(", ");
|
||||
}
|
||||
}
|
||||
if (needsNewline) {
|
||||
wrapperMethodBodies.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return argumentContent;
|
||||
}
|
||||
|
||||
private void writeCtorBody(String implementationSignature, Constructor theCtor, boolean aIsThreaded) {
|
||||
Class<?>[] argumentTypes = theCtor.getParameterTypes();
|
||||
|
||||
writeFunctionStartupBoilerPlate(implementationSignature, Void.class, false, aIsThreaded);
|
||||
|
||||
writeFramePushBoilerplate(theCtor, false);
|
||||
|
||||
// Marshall arguments for this constructor, if any...
|
||||
boolean hasArguments = argumentTypes.length != 0;
|
||||
|
||||
StringBuilder argumentContent = new StringBuilder();
|
||||
if (hasArguments) {
|
||||
argumentContent = getArgumentMarshalling(argumentTypes);
|
||||
}
|
||||
|
||||
// The call into Java
|
||||
wrapperMethodBodies.append(" Init(env->NewObject");
|
||||
if (argumentTypes.length > 2) {
|
||||
wrapperMethodBodies.append('A');
|
||||
}
|
||||
|
||||
wrapperMethodBodies.append('(');
|
||||
|
||||
|
||||
// Call takes class id, method id of constructor method, then arguments.
|
||||
wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)).append(", ");
|
||||
|
||||
wrapperMethodBodies.append(mMembersToIds.get(theCtor))
|
||||
// Tack on the arguments, if any..
|
||||
.append(argumentContent)
|
||||
.append("), env);\n" +
|
||||
" env->PopLocalFrame(NULL);\n" +
|
||||
"}\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the method body of the C++ wrapper function for the Java method indicated.
|
||||
*
|
||||
|
@ -119,44 +424,11 @@ public class CodeGenerator {
|
|||
Class<?>[] argumentTypes = aMethod.getParameterTypes();
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
|
||||
// The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
|
||||
wrapperMethodBodies.append('\n');
|
||||
wrapperMethodBodies.append(methodSignature);
|
||||
writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded);
|
||||
|
||||
wrapperMethodBodies.append(" {\n");
|
||||
boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType);
|
||||
|
||||
// Static stubs check the bridge instance has been created before trying to run.
|
||||
if (aIsStaticBridgeMethod) {
|
||||
wrapperMethodBodies.append(" if (!sBridge) {\n" +
|
||||
" ALOG_BRIDGE(\"Aborted: No sBridge - %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n\n");
|
||||
}
|
||||
wrapperMethodBodies.append(" JNIEnv *env = ");
|
||||
if (!aIsMultithreaded) {
|
||||
wrapperMethodBodies.append("GetJNIEnv();\n");
|
||||
} else {
|
||||
wrapperMethodBodies.append("GetJNIForThread();\n");
|
||||
}
|
||||
wrapperMethodBodies.append(" if (!env) {\n" +
|
||||
" ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n\n");
|
||||
|
||||
boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.doesReturnObjectType(aMethod);
|
||||
|
||||
// Determine the number of local refs required for our local frame..
|
||||
// AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
|
||||
int localReferencesNeeded = Utils.enumerateReferenceArguments(aMethod);
|
||||
if (isObjectReturningMethod) {
|
||||
localReferencesNeeded++;
|
||||
}
|
||||
wrapperMethodBodies.append(" if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
|
||||
" ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
|
||||
" env->ExceptionDescribe();\n"+
|
||||
" env->ExceptionClear();\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n\n");
|
||||
writeFramePushBoilerplate(aMethod, isObjectReturningMethod);
|
||||
|
||||
// Marshall arguments, if we have any.
|
||||
boolean hasArguments = argumentTypes.length != 0;
|
||||
|
@ -168,43 +440,10 @@ public class CodeGenerator {
|
|||
// argumentContent).
|
||||
StringBuilder argumentContent = new StringBuilder();
|
||||
if (hasArguments) {
|
||||
argumentContent.append(", ");
|
||||
// If we have >2 arguments, use the jvalue[] calling approach.
|
||||
if (argumentTypes.length > 2) {
|
||||
wrapperMethodBodies.append(" jvalue args[").append(argumentTypes.length).append("];\n");
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
wrapperMethodBodies.append(" args[").append(aT).append("].");
|
||||
wrapperMethodBodies.append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
|
||||
}
|
||||
|
||||
// The only argument is the array of arguments.
|
||||
argumentContent.append("args");
|
||||
wrapperMethodBodies.append('\n');
|
||||
} else {
|
||||
// Otherwise, use the vanilla calling approach.
|
||||
boolean needsNewline = false;
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
// If the argument is a string-esque type, create a jstring from it, otherwise
|
||||
// it can be passed directly.
|
||||
if (Utils.isCharSequence(argumentTypes[aT])) {
|
||||
wrapperMethodBodies.append(" jstring j").append(aT).append(" = NewJavaString(env, a").append(aT).append(");\n");
|
||||
needsNewline = true;
|
||||
// Ensure we refer to the newly constructed Java string - not to the original
|
||||
// parameter to the wrapper function.
|
||||
argumentContent.append('j').append(aT);
|
||||
} else {
|
||||
argumentContent.append('a').append(aT);
|
||||
}
|
||||
if (aT != argumentTypes.length - 1) {
|
||||
argumentContent.append(", ");
|
||||
}
|
||||
}
|
||||
if (needsNewline) {
|
||||
wrapperMethodBodies.append('\n');
|
||||
}
|
||||
}
|
||||
argumentContent = getArgumentMarshalling(argumentTypes);
|
||||
}
|
||||
|
||||
// Allocate a temporary variable to hold the return type from Java.
|
||||
wrapperMethodBodies.append(" ");
|
||||
if (!returnType.getCanonicalName().equals("void")) {
|
||||
if (isObjectReturningMethod) {
|
||||
|
@ -215,11 +454,11 @@ public class CodeGenerator {
|
|||
wrapperMethodBodies.append(" temp = ");
|
||||
}
|
||||
|
||||
boolean isStaticJavaMethod = Utils.isMethodStatic(aMethod);
|
||||
boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod);
|
||||
|
||||
// The call into Java
|
||||
wrapperMethodBodies.append("env->");
|
||||
wrapperMethodBodies.append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
|
||||
wrapperMethodBodies.append("env->")
|
||||
.append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
|
||||
if (argumentTypes.length > 2) {
|
||||
wrapperMethodBodies.append('A');
|
||||
}
|
||||
|
@ -227,28 +466,18 @@ public class CodeGenerator {
|
|||
wrapperMethodBodies.append('(');
|
||||
// If the underlying Java method is nonstatic, we provide the target object to the JNI.
|
||||
if (!isStaticJavaMethod) {
|
||||
wrapperMethodBodies.append("aTarget, ");
|
||||
wrapperMethodBodies.append("wrapped_obj, ");
|
||||
} else {
|
||||
// If the stub to be generated is static, we need to use the singleton to access the class
|
||||
// reference.
|
||||
if (aIsStaticBridgeMethod) {
|
||||
wrapperMethodBodies.append("sBridge->");
|
||||
}
|
||||
// If this is a static underlyin Java method, we need to use the class reference in our
|
||||
// If this is a static underlying Java method, we need to use the class reference in our
|
||||
// call.
|
||||
wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", ");
|
||||
}
|
||||
|
||||
// Write the method id out..
|
||||
if (aIsStaticBridgeMethod) {
|
||||
wrapperMethodBodies.append("sBridge->");
|
||||
}
|
||||
wrapperMethodBodies.append('j');
|
||||
wrapperMethodBodies.append(aCMethodName);
|
||||
wrapperMethodBodies.append(mMembersToIds.get(aMethod));
|
||||
|
||||
// Tack on the arguments, if any..
|
||||
wrapperMethodBodies.append(argumentContent);
|
||||
wrapperMethodBodies.append(");\n\n");
|
||||
wrapperMethodBodies.append(argumentContent)
|
||||
.append(");\n\n");
|
||||
|
||||
// Check for exception and return the failure value..
|
||||
wrapperMethodBodies.append(" if (env->ExceptionCheck()) {\n" +
|
||||
|
@ -257,14 +486,14 @@ public class CodeGenerator {
|
|||
" env->ExceptionClear();\n" +
|
||||
" env->PopLocalFrame(NULL);\n" +
|
||||
" return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
|
||||
" }\n");
|
||||
" }\n\n");
|
||||
|
||||
// If we're returning an object, pop the callee's stack frame extracting our ref as the return
|
||||
// value.
|
||||
if (isObjectReturningMethod) {
|
||||
wrapperMethodBodies.append(" ");
|
||||
wrapperMethodBodies.append(Utils.getCReturnType(returnType));
|
||||
wrapperMethodBodies.append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
|
||||
wrapperMethodBodies.append(" ")
|
||||
.append(Utils.getCReturnType(returnType))
|
||||
.append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
|
||||
" return ret;\n");
|
||||
} else if (!returnType.getCanonicalName().equals("void")) {
|
||||
// If we're a primitive-returning function, just return the directly-obtained primative
|
||||
|
@ -279,35 +508,83 @@ public class CodeGenerator {
|
|||
}
|
||||
|
||||
/**
|
||||
* Generates the code to get the method id of the given method on startup.
|
||||
* Generates the code to get the id of the given member on startup.
|
||||
*
|
||||
* @param aCMethodName The C method name of the method being generated.
|
||||
* @param aJavaMethodName The name of the Java method being wrapped.
|
||||
* @param aMethod The Java method being wrapped.
|
||||
* @param aMember The Java member being wrapped.
|
||||
*/
|
||||
private void writeStartupCode(String aCMethodName, String aJavaMethodName, Method aMethod, Class<?> aClass) {
|
||||
wrapperStartupCode.append(" j");
|
||||
wrapperStartupCode.append(aCMethodName);
|
||||
wrapperStartupCode.append(" = get");
|
||||
if (Utils.isMethodStatic(aMethod)) {
|
||||
private void writeStartupCode(Member aMember) {
|
||||
wrapperStartupCode.append(" ")
|
||||
.append(mMembersToIds.get(aMember))
|
||||
.append(" = get");
|
||||
if (Utils.isMemberStatic(aMember)) {
|
||||
wrapperStartupCode.append("Static");
|
||||
}
|
||||
wrapperStartupCode.append("Method(\"");
|
||||
wrapperStartupCode.append(aJavaMethodName);
|
||||
wrapperStartupCode.append("\", \"");
|
||||
wrapperStartupCode.append(Utils.getTypeSignatureString(aMethod));
|
||||
wrapperStartupCode.append("\");\n");
|
||||
|
||||
boolean isField = aMember instanceof Field;
|
||||
if (isField) {
|
||||
wrapperStartupCode.append("Field(\"");
|
||||
} else {
|
||||
wrapperStartupCode.append("Method(\"");
|
||||
}
|
||||
if (aMember instanceof Constructor) {
|
||||
wrapperStartupCode.append("<init>");
|
||||
} else {
|
||||
wrapperStartupCode.append(aMember.getName());
|
||||
}
|
||||
|
||||
wrapperStartupCode.append("\", \"")
|
||||
.append(Utils.getTypeSignatureStringForMember(aMember))
|
||||
.append("\");\n");
|
||||
}
|
||||
|
||||
private void writeZeroingFor(Member aMember, final String aMemberName) {
|
||||
if (aMember instanceof Field) {
|
||||
zeroingCode.append("jfieldID ");
|
||||
} else {
|
||||
zeroingCode.append("jmethodID ");
|
||||
}
|
||||
zeroingCode.append(mCClassName)
|
||||
.append("::")
|
||||
.append(aMemberName)
|
||||
.append(" = 0;\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a method id field in the header file for the C method name provided.
|
||||
* Write the field declaration for the C++ id field of the given member.
|
||||
*
|
||||
* @param aMethodName C method name to generate a method id field for.
|
||||
* @param aMember Member for which an id field needs to be generated.
|
||||
*/
|
||||
private void writeHeaderField(String aMethodName) {
|
||||
headerFields.append("jmethodID j");
|
||||
headerFields.append(aMethodName);
|
||||
headerFields.append(";\n");
|
||||
private void writeMemberIdField(Member aMember, final String aCMethodName) {
|
||||
String memberName = 'j'+ aCMethodName;
|
||||
|
||||
if (aMember instanceof Field) {
|
||||
headerProtected.append(" static jfieldID ");
|
||||
} else {
|
||||
headerProtected.append(" static jmethodID ");
|
||||
}
|
||||
|
||||
while(mTakenMemberNames.contains(memberName)) {
|
||||
memberName = 'j' + aCMethodName + mNameMunger;
|
||||
mNameMunger++;
|
||||
}
|
||||
|
||||
writeZeroingFor(aMember, memberName);
|
||||
mMembersToIds.put(aMember, memberName);
|
||||
mTakenMemberNames.add(memberName);
|
||||
|
||||
headerProtected.append(memberName)
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add a provided method signature to the public section of the generated header.
|
||||
*
|
||||
* @param aSignature The header to add.
|
||||
*/
|
||||
private void writeSignatureToHeader(String aSignature) {
|
||||
headerPublic.append(" ")
|
||||
.append(aSignature)
|
||||
.append(";\n");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -315,11 +592,13 @@ public class CodeGenerator {
|
|||
*
|
||||
* @return The bytes to be written to the wrappers file.
|
||||
*/
|
||||
public byte[] getWrapperFileContents() {
|
||||
public String getWrapperFileContents() {
|
||||
wrapperStartupCode.append("}\n");
|
||||
wrapperStartupCode.append(wrapperMethodBodies);
|
||||
zeroingCode.append(wrapperStartupCode)
|
||||
.append(wrapperMethodBodies);
|
||||
wrapperMethodBodies.setLength(0);
|
||||
return wrapperStartupCode.toString().getBytes();
|
||||
wrapperStartupCode.setLength(0);
|
||||
return zeroingCode.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -327,10 +606,13 @@ public class CodeGenerator {
|
|||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public byte[] getHeaderFileContents() {
|
||||
headerFields.append('\n');
|
||||
headerFields.append(headerMethods);
|
||||
headerMethods.setLength(0);
|
||||
return headerFields.toString().getBytes();
|
||||
public String getHeaderFileContents() {
|
||||
if (!mHasEncounteredDefaultConstructor) {
|
||||
headerPublic.append(" ").append(mCClassName).append("() : AutoGlobalWrappedJavaObject() {};\n");
|
||||
}
|
||||
headerProtected.append("};\n\n");
|
||||
headerPublic.append(headerProtected);
|
||||
headerProtected.setLength(0);
|
||||
return headerPublic.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite
|
||||
* the fact that such things can be of either flavour.
|
||||
*/
|
||||
public class AnnotatableEntity {
|
||||
public enum ENTITY_TYPE {METHOD, FIELD, CONSTRUCTOR}
|
||||
|
||||
private final Member mMember;
|
||||
public final ENTITY_TYPE mEntityType;
|
||||
|
||||
public final AnnotationInfo mAnnotationInfo;
|
||||
|
||||
public AnnotatableEntity(Member aObject, AnnotationInfo aAnnotationInfo) {
|
||||
mMember = aObject;
|
||||
mAnnotationInfo = aAnnotationInfo;
|
||||
|
||||
if (aObject instanceof Method) {
|
||||
mEntityType = ENTITY_TYPE.METHOD;
|
||||
} else if (aObject instanceof Field) {
|
||||
mEntityType = ENTITY_TYPE.FIELD;
|
||||
} else {
|
||||
mEntityType = ENTITY_TYPE.CONSTRUCTOR;
|
||||
}
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
if (mEntityType != ENTITY_TYPE.METHOD) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Method) mMember;
|
||||
}
|
||||
public Field getField() {
|
||||
if (mEntityType != ENTITY_TYPE.FIELD) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Field) mMember;
|
||||
}
|
||||
public Constructor getConstructor() {
|
||||
if (mEntityType != ENTITY_TYPE.CONSTRUCTOR) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Constructor) mMember;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
public class ClassWithOptions {
|
||||
public final Class<?> wrappedClass;
|
||||
public final String generatedName;
|
||||
|
||||
public ClassWithOptions(Class<?> someClass, String name) {
|
||||
wrappedClass = someClass;
|
||||
generatedName = name;
|
||||
}
|
||||
}
|
|
@ -21,8 +21,6 @@ import java.util.jar.JarFile;
|
|||
* classNames is kept sorted to ensure iteration order is consistent across program invocations.
|
||||
* Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its
|
||||
* contents.
|
||||
* This classloader does not support inner classes. (You probably shouldn't be putting JNI entry
|
||||
* points in inner classes anyway)
|
||||
*/
|
||||
public class IterableJarLoadingURLClassLoader extends URLClassLoader {
|
||||
LinkedList<String> classNames = new LinkedList<String>();
|
||||
|
@ -35,7 +33,7 @@ public class IterableJarLoadingURLClassLoader extends URLClassLoader {
|
|||
* @param args A list of jar file names an iterator over the classes of which is desired.
|
||||
* @return An iterator over the top level classes in the jar files provided, in arbitrary order.
|
||||
*/
|
||||
public static Iterator<Class<?>> getIteratorOverJars(String[] args) {
|
||||
public static Iterator<ClassWithOptions> getIteratorOverJars(String[] args) {
|
||||
URL[] urlArray = new URL[args.length];
|
||||
LinkedList<String> aClassNames = new LinkedList<String>();
|
||||
|
||||
|
@ -51,10 +49,7 @@ public class IterableJarLoadingURLClassLoader extends URLClassLoader {
|
|||
continue;
|
||||
}
|
||||
final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
|
||||
// Inner classes are not supported.
|
||||
if (className.contains("$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aClassNames.add(className);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -4,12 +4,15 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Class for iterating over an IterableJarLoadingURLClassLoader's classes.
|
||||
*/
|
||||
public class JarClassIterator implements Iterator<Class<?>> {
|
||||
public class JarClassIterator implements Iterator<ClassWithOptions> {
|
||||
private IterableJarLoadingURLClassLoader mTarget;
|
||||
private Iterator<String> mTargetClassListIterator;
|
||||
|
||||
|
@ -24,10 +27,46 @@ public class JarClassIterator implements Iterator<Class<?>> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Class<?> next() {
|
||||
public ClassWithOptions next() {
|
||||
String className = mTargetClassListIterator.next();
|
||||
try {
|
||||
return mTarget.loadClass(className);
|
||||
Class<?> ret = mTarget.loadClass(className);
|
||||
if (ret.getCanonicalName() == null || "null".equals(ret.getCanonicalName())) {
|
||||
// Anonymous inner class - unsupported.
|
||||
return next();
|
||||
} else {
|
||||
String generateName = null;
|
||||
for (Annotation annotation : ret.getAnnotations()) {
|
||||
Class<?> annotationType = annotation.annotationType();
|
||||
if (annotationType.getCanonicalName().equals("org.mozilla.gecko.mozglue.generatorannotations.GeneratorOptions")) {
|
||||
try {
|
||||
// Determine the explicitly-given name of the stub to generate, if any.
|
||||
final Method generateNameMethod = annotationType.getDeclaredMethod("generatedClassName");
|
||||
generateNameMethod.setAccessible(true);
|
||||
generateName = (String) generateNameMethod.invoke(annotation);
|
||||
break;
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("Unable to find expected field on GeneratorOptions annotation. Did the signature change?");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(3);
|
||||
} catch (IllegalAccessException e) {
|
||||
System.err.println("IllegalAccessException reading fields on GeneratorOptions annotation. Seems the semantics of Reflection have changed...");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(4);
|
||||
} catch (InvocationTargetException e) {
|
||||
System.err.println("InvocationTargetException reading fields on GeneratorOptions annotation. This really shouldn't happen.");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (generateName == null) {
|
||||
generateName = ret.getSimpleName();
|
||||
}
|
||||
|
||||
return new ClassWithOptions(ret, generateName);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class AlphabeticAnnotatableEntityComparator<T extends Member> implements Comparator<T> {
|
||||
@Override
|
||||
public int compare(T aLhs, T aRhs) {
|
||||
// Constructors, Methods, Fields.
|
||||
boolean lIsConstructor = aLhs instanceof Constructor;
|
||||
boolean rIsConstructor = aRhs instanceof Constructor;
|
||||
boolean lIsMethod = aLhs instanceof Method;
|
||||
boolean rIsField = aRhs instanceof Field;
|
||||
|
||||
if (lIsConstructor) {
|
||||
if (!rIsConstructor) {
|
||||
return -1;
|
||||
}
|
||||
} else if (lIsMethod) {
|
||||
if (rIsConstructor) {
|
||||
return 1;
|
||||
} else if (rIsField) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!rIsField) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify these objects are the same type and cast them.
|
||||
if (aLhs instanceof Method) {
|
||||
return compare((Method) aLhs, (Method) aRhs);
|
||||
} else if (aLhs instanceof Field) {
|
||||
return compare((Field) aLhs, (Field) aRhs);
|
||||
} else {
|
||||
return compare((Constructor) aLhs, (Constructor) aRhs);
|
||||
}
|
||||
}
|
||||
|
||||
// Alas, the type system fails us.
|
||||
private static int compare(Method aLhs, Method aRhs) {
|
||||
// Initially, attempt to differentiate the methods be name alone..
|
||||
String lName = aLhs.getName();
|
||||
String rName = aRhs.getName();
|
||||
|
||||
int ret = lName.compareTo(rName);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The names were the same, so we need to compare signatures to find their uniqueness..
|
||||
lName = Utils.getTypeSignatureStringForMethod(aLhs);
|
||||
rName = Utils.getTypeSignatureStringForMethod(aRhs);
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
|
||||
private static int compare(Constructor aLhs, Constructor aRhs) {
|
||||
// The names will be the same, so we need to compare signatures to find their uniqueness..
|
||||
String lName = Utils.getTypeSignatureString(aLhs);
|
||||
String rName = Utils.getTypeSignatureString(aRhs);
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
|
||||
private static int compare(Field aLhs, Field aRhs) {
|
||||
// Compare field names..
|
||||
String lName = aLhs.getName();
|
||||
String rName = aRhs.getName();
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class AlphabeticMethodComparator implements Comparator<Method> {
|
||||
@Override
|
||||
public int compare(Method lhs, Method rhs) {
|
||||
// Initially, attempt to differentiate the methods be name alone..
|
||||
String lName = lhs.getName();
|
||||
String rName = rhs.getName();
|
||||
|
||||
int ret = lName.compareTo(rName);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The names were the same, so we need to compare signatures to find their uniqueness..
|
||||
lName = Utils.getTypeSignatureString(lhs);
|
||||
rName = Utils.getTypeSignatureString(rhs);
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
}
|
|
@ -4,31 +4,58 @@
|
|||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.MethodWithAnnotationInfo;
|
||||
import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Iterator over the methods in a given method list which have the GeneratableAndroidBridgeTarget
|
||||
* Iterator over the methods in a given method list which have the WrappedJNIMethod
|
||||
* annotation. Returns an object containing both the annotation (Which may contain interesting
|
||||
* parameters) and the argument.
|
||||
*/
|
||||
public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotationInfo> {
|
||||
private final Method[] mMethods;
|
||||
private MethodWithAnnotationInfo mNextReturnValue;
|
||||
private int mMethodIndex;
|
||||
public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
|
||||
private final Member[] mObjects;
|
||||
private AnnotatableEntity mNextReturnValue;
|
||||
private int mElementIndex;
|
||||
|
||||
public GeneratableEntryPointIterator(Method[] aMethods) {
|
||||
// Sort the methods into alphabetical order by name, to ensure we always iterate methods
|
||||
// in the same order..
|
||||
Arrays.sort(aMethods, new AlphabeticMethodComparator());
|
||||
mMethods = aMethods;
|
||||
private boolean mIterateEveryEntry;
|
||||
|
||||
public GeneratableElementIterator(Class<?> aClass) {
|
||||
// Get all the elements of this class as AccessibleObjects.
|
||||
Member[] aMethods = aClass.getDeclaredMethods();
|
||||
Member[] aFields = aClass.getDeclaredFields();
|
||||
Member[] aCtors = aClass.getConstructors();
|
||||
|
||||
// Shove them all into one buffer.
|
||||
Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length];
|
||||
int offset = 0;
|
||||
System.arraycopy(aMethods, 0, objs, 0, aMethods.length);
|
||||
offset += aMethods.length;
|
||||
System.arraycopy(aFields, 0, objs, offset, aFields.length);
|
||||
offset += aFields.length;
|
||||
System.arraycopy(aCtors, 0, objs, offset, aCtors.length);
|
||||
|
||||
// Sort the elements to ensure determinism.
|
||||
Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator());
|
||||
|
||||
mObjects = objs;
|
||||
|
||||
// Check for "Wrap ALL the things" flag.
|
||||
for (Annotation annotation : aClass.getDeclaredAnnotations()) {
|
||||
final String annotationTypeName = annotation.annotationType().getName();
|
||||
|
||||
if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI")) {
|
||||
mIterateEveryEntry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
findNextValue();
|
||||
}
|
||||
|
@ -38,15 +65,15 @@ public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotat
|
|||
* one exists. Otherwise cache null, so hasNext returns false.
|
||||
*/
|
||||
private void findNextValue() {
|
||||
while (mMethodIndex < mMethods.length) {
|
||||
Method candidateMethod = mMethods[mMethodIndex];
|
||||
mMethodIndex++;
|
||||
for (Annotation annotation : candidateMethod.getDeclaredAnnotations()) {
|
||||
// GeneratableAndroidBridgeTarget has a parameter. Use Reflection to obtain it.
|
||||
while (mElementIndex < mObjects.length) {
|
||||
Member candidateElement = mObjects[mElementIndex];
|
||||
mElementIndex++;
|
||||
for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) {
|
||||
// WrappedJNIMethod has a parameter. Use Reflection to obtain it.
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final String annotationTypeName = annotationType.getName();
|
||||
|
||||
if (annotationTypeName.equals("org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget")) {
|
||||
if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
|
||||
String stubName = null;
|
||||
boolean isStaticStub = false;
|
||||
boolean isMultithreadedStub = false;
|
||||
|
@ -66,28 +93,38 @@ public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotat
|
|||
multithreadedStubMethod.setAccessible(true);
|
||||
isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("Unable to find expected field on GeneratableAndroidBridgeTarget annotation. Did the signature change?");
|
||||
System.err.println("Unable to find expected field on WrappedJNIMethod annotation. Did the signature change?");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(3);
|
||||
} catch (IllegalAccessException e) {
|
||||
System.err.println("IllegalAccessException reading fields on GeneratableAndroidBridgeTarget annotation. Seems the semantics of Reflection have changed...");
|
||||
System.err.println("IllegalAccessException reading fields on WrappedJNIMethod annotation. Seems the semantics of Reflection have changed...");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(4);
|
||||
} catch (InvocationTargetException e) {
|
||||
System.err.println("InvocationTargetException reading fields on GeneratableAndroidBridgeTarget annotation. This really shouldn't happen.");
|
||||
System.err.println("InvocationTargetException reading fields on WrappedJNIMethod annotation. This really shouldn't happen.");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(5);
|
||||
}
|
||||
// If the method name was not explicitly given in the annotation generate one...
|
||||
|
||||
// If the element name was not explicitly given in the annotation generate one...
|
||||
if (stubName.isEmpty()) {
|
||||
String aMethodName = candidateMethod.getName();
|
||||
stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
|
||||
String elementName = candidateElement.getName();
|
||||
stubName = elementName.substring(0, 1).toUpperCase() + elementName.substring(1);
|
||||
}
|
||||
|
||||
mNextReturnValue = new MethodWithAnnotationInfo(candidateMethod, stubName, isStaticStub, isMultithreadedStub);
|
||||
AnnotationInfo annotationInfo = new AnnotationInfo(stubName, isStaticStub, isMultithreadedStub);
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If no annotation found, we might be expected to generate anyway using default arguments,
|
||||
// thanks to the "Generate everything" annotation.
|
||||
if (mIterateEveryEntry) {
|
||||
AnnotationInfo annotationInfo = new AnnotationInfo(candidateElement.getName(), false, false);
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mNextReturnValue = null;
|
||||
}
|
||||
|
@ -98,14 +135,14 @@ public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotat
|
|||
}
|
||||
|
||||
@Override
|
||||
public MethodWithAnnotationInfo next() {
|
||||
MethodWithAnnotationInfo ret = mNextReturnValue;
|
||||
public AnnotatableEntity next() {
|
||||
AnnotatableEntity ret = mNextReturnValue;
|
||||
findNextValue();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Removal of methods from GeneratableEntryPointIterator not supported.");
|
||||
throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported.");
|
||||
}
|
||||
}
|
|
@ -5,6 +5,9 @@
|
|||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
|
@ -70,9 +73,23 @@ public class Utils {
|
|||
sInstanceCallTypes.put("short", "CallShortMethod");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> sFieldTypes = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
sFieldTypes.put("int", "Int");
|
||||
sFieldTypes.put("boolean", "Boolean");
|
||||
sFieldTypes.put("long", "Long");
|
||||
sFieldTypes.put("double", "Double");
|
||||
sFieldTypes.put("float", "Float");
|
||||
sFieldTypes.put("char", "Char");
|
||||
sFieldTypes.put("byte", "Byte");
|
||||
sFieldTypes.put("short", "Short");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> sFailureReturns = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
sFailureReturns.put("java.lang.Void", "");
|
||||
sFailureReturns.put("void", "");
|
||||
sFailureReturns.put("int", " 0");
|
||||
sFailureReturns.put("boolean", " false");
|
||||
|
@ -87,6 +104,7 @@ public class Utils {
|
|||
private static final HashMap<String, String> sCanonicalSignatureParts = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
sCanonicalSignatureParts.put("java/lang/Void", "V");
|
||||
sCanonicalSignatureParts.put("void", "V");
|
||||
sCanonicalSignatureParts.put("int", "I");
|
||||
sCanonicalSignatureParts.put("boolean", "Z");
|
||||
|
@ -164,7 +182,9 @@ public class Utils {
|
|||
* @return A string representation of the C++ return type.
|
||||
*/
|
||||
public static String getCReturnType(Class<?> type) {
|
||||
// Since there's only one thing we want to do differently...
|
||||
if (type.getCanonicalName().equals("java.lang.Void")) {
|
||||
return "void";
|
||||
}
|
||||
String cParameterType = getCParameterType(type);
|
||||
if (cParameterType.equals("const nsAString&")) {
|
||||
return "jstring";
|
||||
|
@ -173,6 +193,21 @@ public class Utils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type-specific part of the JNI function to use to get or set a field of a given type.
|
||||
*
|
||||
* @param aFieldType The Java type of the field.
|
||||
* @return A string representation of the JNI call function substring to use.
|
||||
*/
|
||||
public static String getFieldType(Class<?> aFieldType) {
|
||||
String name = aFieldType.getCanonicalName();
|
||||
|
||||
if (sFieldTypes.containsKey(name)) {
|
||||
return sFieldTypes.get(name);
|
||||
}
|
||||
return "Object";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the appropriate JNI call function to use to invoke a Java method with the given return
|
||||
* type. This, plus a call postfix (Such as "A") forms a complete JNI call function name.
|
||||
|
@ -212,15 +247,15 @@ public class Utils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the canonical JNI type signature for a method.
|
||||
* Helper method to get the type signature for methods, given argument and return type.
|
||||
* Allows for the near-identical logic needed for constructors and methods to be shared.
|
||||
* (Alas, constructor does not extend method)
|
||||
*
|
||||
* @param aMethod The method to generate a signature for.
|
||||
* @return The canonical JNI type signature for this method.
|
||||
* @param arguments Argument types of the underlying method.
|
||||
* @param returnType Return type of the underlying method.
|
||||
* @return The canonical Java type string for the method. eg. (IIIIFZ)Lorg/mozilla/gecko/gfx/ViewTransform;
|
||||
*/
|
||||
public static String getTypeSignatureString(Method aMethod) {
|
||||
Class<?>[] arguments = aMethod.getParameterTypes();
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
|
||||
private static String getTypeSignatureInternal(Class<?>[] arguments, Class<?> returnType) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
// For each argument, write its signature component to the buffer..
|
||||
|
@ -234,7 +269,69 @@ public class Utils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Helper method used by getTypeSignatureString to build the signature. Write the subsignature
|
||||
* Get the canonical JNI type signature for a Field.
|
||||
*
|
||||
* @param aField The field to generate a signature for.
|
||||
* @return The canonical JNI type signature for this method.
|
||||
*/
|
||||
protected static String getTypeSignatureStringForField(Field aField) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
writeTypeSignature(sb, aField.getType());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonical JNI type signature for a method.
|
||||
*
|
||||
* @param aMethod The method to generate a signature for.
|
||||
* @return The canonical JNI type signature for this method.
|
||||
*/
|
||||
protected static String getTypeSignatureStringForMethod(Method aMethod) {
|
||||
Class<?>[] arguments = aMethod.getParameterTypes();
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
|
||||
return getTypeSignatureInternal(arguments, returnType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the canonical JNI type signature for a Constructor.
|
||||
*
|
||||
* @param aConstructor The Constructor to generate a signature for.
|
||||
* @return The canonical JNI type signature for this method.
|
||||
*/
|
||||
protected static String getTypeSignatureStringForConstructor(Constructor aConstructor) {
|
||||
Class<?>[] arguments = aConstructor.getParameterTypes();
|
||||
|
||||
return getTypeSignatureInternal(arguments, Void.class);
|
||||
}
|
||||
|
||||
public static String getTypeSignatureStringForMember(Member aMember) {
|
||||
if (aMember instanceof Method) {
|
||||
return getTypeSignatureStringForMethod((Method) aMember);
|
||||
} else if (aMember instanceof Field) {
|
||||
return getTypeSignatureStringForField((Field) aMember);
|
||||
} else {
|
||||
return getTypeSignatureStringForConstructor((Constructor) aMember);
|
||||
}
|
||||
}
|
||||
|
||||
public static String getTypeSignatureString(Constructor aConstructor) {
|
||||
Class<?>[] arguments = aConstructor.getParameterTypes();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('(');
|
||||
// For each argument, write its signature component to the buffer..
|
||||
for (int i = 0; i < arguments.length; i++) {
|
||||
writeTypeSignature(sb, arguments[i]);
|
||||
}
|
||||
|
||||
// Constructors always return Void.
|
||||
sb.append(")V");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by getTypeSignatureStringForMethod to build the signature. Write the subsignature
|
||||
* of a given type into the buffer.
|
||||
*
|
||||
* @param sb The buffer to write into.
|
||||
|
@ -242,6 +339,7 @@ public class Utils {
|
|||
*/
|
||||
private static void writeTypeSignature(StringBuilder sb, Class<?> c) {
|
||||
String name = c.getCanonicalName().replaceAll("\\.", "/");
|
||||
|
||||
// Determine if this is an array type and, if so, peel away the array operators..
|
||||
int len = name.length();
|
||||
while (name.endsWith("[]")) {
|
||||
|
@ -249,6 +347,17 @@ public class Utils {
|
|||
name = name.substring(0, len - 2);
|
||||
}
|
||||
|
||||
if (c.isArray()) {
|
||||
c = c.getComponentType();
|
||||
}
|
||||
|
||||
Class<?> containerClass = c.getDeclaringClass();
|
||||
if (containerClass != null) {
|
||||
// Is an inner class. Add the $ symbol.
|
||||
final int lastSlash = name.lastIndexOf('/');
|
||||
name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
|
||||
}
|
||||
|
||||
// Look in the hashmap for the remainder...
|
||||
if (sCanonicalSignatureParts.containsKey(name)) {
|
||||
// It was a primitive type, so lookup was a success.
|
||||
|
@ -265,38 +374,31 @@ public class Utils {
|
|||
* Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
|
||||
* generating header files and method bodies.
|
||||
*
|
||||
* @param aMethod The Java method to generate the corresponding wrapper signature for.
|
||||
* @param aCMethodName The name of the generated method this is to be the signatgure for.
|
||||
* @return The generated method signature.
|
||||
* @param aArgumentTypes Argument types of the Java method being wrapped.
|
||||
* @param aReturnType Return type of the Java method being wrapped.
|
||||
* @param aCMethodName Name of the method to generate in the C++ class.
|
||||
* @param aCClassName Name of the C++ class into which the method is declared.
|
||||
* @return The C++ method implementation signature for the method described.
|
||||
*/
|
||||
public static String getCImplementationMethodSignature(Method aMethod, String aCMethodName) {
|
||||
Class<?>[] argumentTypes = aMethod.getParameterTypes();
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
|
||||
public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName) {
|
||||
StringBuilder retBuffer = new StringBuilder();
|
||||
// Write return type..
|
||||
retBuffer.append(getCReturnType(returnType));
|
||||
retBuffer.append(" AndroidBridge::");
|
||||
|
||||
retBuffer.append(getCReturnType(aReturnType));
|
||||
retBuffer.append(' ');
|
||||
retBuffer.append(aCClassName);
|
||||
retBuffer.append("::");
|
||||
retBuffer.append(aCMethodName);
|
||||
retBuffer.append('(');
|
||||
|
||||
// For an instance method, the first argument is the target object.
|
||||
if (!isMethodStatic(aMethod)) {
|
||||
retBuffer.append("jobject aTarget");
|
||||
if (argumentTypes.length > 0) {
|
||||
retBuffer.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// Write argument types...
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
retBuffer.append(getCParameterType(argumentTypes[aT]));
|
||||
for (int aT = 0; aT < aArgumentTypes.length; aT++) {
|
||||
retBuffer.append(getCParameterType(aArgumentTypes[aT]));
|
||||
retBuffer.append(" a");
|
||||
// We, imaginatively, call our arguments a1, a2, a3...
|
||||
// The only way to preserve the names from Java would be to parse the
|
||||
// Java source, which would be computationally hard.
|
||||
retBuffer.append(aT);
|
||||
if (aT != argumentTypes.length - 1) {
|
||||
if (aT != aArgumentTypes.length - 1) {
|
||||
retBuffer.append(", ");
|
||||
}
|
||||
}
|
||||
|
@ -312,14 +414,18 @@ public class Utils {
|
|||
* @param aCMethodName The name of the generated method this is to be the signatgure for.
|
||||
* @return The generated method signature.
|
||||
*/
|
||||
public static String getCHeaderMethodSignature(Method aMethod, String aCMethodName, boolean aIsStaticStub) {
|
||||
Class<?>[] argumentTypes = aMethod.getParameterTypes();
|
||||
|
||||
// The annotations on the parameters of this method, in declaration order.
|
||||
// Importantly - the same order as those in argumentTypes.
|
||||
Annotation[][] argumentAnnotations = aMethod.getParameterAnnotations();
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param aArgumentTypes Argument types of the Java method being wrapped.
|
||||
* @param aArgumentAnnotations The annotations on the Java method arguments. Used to specify
|
||||
* default values etc.
|
||||
* @param aReturnType Return type of the Java method being wrapped.
|
||||
* @param aCMethodName Name of the method to generate in the C++ class.
|
||||
* @param aCClassName Name of the C++ class into which the method is declared.e
|
||||
* @param aIsStaticStub true if the generated C++ method should be static, false otherwise.
|
||||
* @return The generated C++ header method signature for the method described.
|
||||
*/
|
||||
public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub) {
|
||||
StringBuilder retBuffer = new StringBuilder();
|
||||
|
||||
// Add the static keyword, if applicable.
|
||||
|
@ -328,22 +434,14 @@ public class Utils {
|
|||
}
|
||||
|
||||
// Write return type..
|
||||
retBuffer.append(getCReturnType(returnType));
|
||||
retBuffer.append(getCReturnType(aReturnType));
|
||||
retBuffer.append(' ');
|
||||
retBuffer.append(aCMethodName);
|
||||
retBuffer.append('(');
|
||||
|
||||
// For an instance method, the first argument is the target object.
|
||||
if (!isMethodStatic(aMethod)) {
|
||||
retBuffer.append("jobject aTarget");
|
||||
if (argumentTypes.length > 0) {
|
||||
retBuffer.append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// Write argument types...
|
||||
for (int aT = 0; aT < argumentTypes.length; aT++) {
|
||||
retBuffer.append(getCParameterType(argumentTypes[aT]));
|
||||
for (int aT = 0; aT < aArgumentTypes.length; aT++) {
|
||||
retBuffer.append(getCParameterType(aArgumentTypes[aT]));
|
||||
retBuffer.append(" a");
|
||||
// We, imaginatively, call our arguments a1, a2, a3...
|
||||
// The only way to preserve the names from Java would be to parse the
|
||||
|
@ -351,9 +449,9 @@ public class Utils {
|
|||
retBuffer.append(aT);
|
||||
|
||||
// Append the default value, if there is one..
|
||||
retBuffer.append(getDefaultValueString(argumentTypes[aT], argumentAnnotations[aT]));
|
||||
retBuffer.append(getDefaultValueString(aArgumentTypes[aT], aArgumentAnnotations[aT]));
|
||||
|
||||
if (aT != argumentTypes.length - 1) {
|
||||
if (aT != aArgumentTypes.length - 1) {
|
||||
retBuffer.append(", ");
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +473,7 @@ public class Utils {
|
|||
for (int i = 0; i < aArgumentAnnotations.length; i++) {
|
||||
Class<? extends Annotation> annotationType = aArgumentAnnotations[i].annotationType();
|
||||
final String annotationTypeName = annotationType.getName();
|
||||
if (annotationTypeName.equals("org.mozilla.gecko.mozglue.OptionalGeneratedParameter")) {
|
||||
if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.OptionalGeneratedParameter")) {
|
||||
return " = " + getDefaultParameterValueForType(aArgumentType);
|
||||
}
|
||||
}
|
||||
|
@ -405,14 +503,13 @@ public class Utils {
|
|||
/**
|
||||
* Helper method that returns the number of reference types in the arguments of m.
|
||||
*
|
||||
* @param m The method to consider.
|
||||
* @param aArgs The method arguments to consider.
|
||||
* @return How many of the arguments of m are nonprimitive.
|
||||
*/
|
||||
public static int enumerateReferenceArguments(Method m) {
|
||||
public static int enumerateReferenceArguments(Class<?>[] aArgs) {
|
||||
int ret = 0;
|
||||
Class<?>[] args = m.getParameterTypes();
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
String name = args[i].getCanonicalName();
|
||||
for (int i = 0; i < aArgs.length; i++) {
|
||||
String name = aArgs[i].getCanonicalName();
|
||||
if (!sBasicCTypes.containsKey(name)) {
|
||||
ret++;
|
||||
}
|
||||
|
@ -452,7 +549,7 @@ public class Utils {
|
|||
sb.append(" = ").append(argName).append(";\n");
|
||||
} else {
|
||||
if (isCharSequence(type)) {
|
||||
sb.append("l = NewJavaString(env, ").append(argName).append(");\n");
|
||||
sb.append("l = AndroidBridge::NewJavaString(env, ").append(argName).append(");\n");
|
||||
} else {
|
||||
sb.append("l = ").append(argName).append(";\n");
|
||||
}
|
||||
|
@ -462,15 +559,13 @@ public class Utils {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if the method provided returns an object type. Returns false if it returns a
|
||||
* primitive type.
|
||||
* Returns true if the type provided is an object type. Returns false otherwise
|
||||
*
|
||||
* @param aMethod The method to consider.
|
||||
* @return true if the method provided returns an object type, false otherwise.
|
||||
* @param aType The type to consider.
|
||||
* @return true if the method provided is an object type, false otherwise.
|
||||
*/
|
||||
public static boolean doesReturnObjectType(Method aMethod) {
|
||||
Class<?> returnType = aMethod.getReturnType();
|
||||
return !sBasicCTypes.containsKey(returnType.getCanonicalName());
|
||||
public static boolean isObjectType(Class<?> aType) {
|
||||
return !sBasicCTypes.containsKey(aType.getCanonicalName());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -495,7 +590,16 @@ public class Utils {
|
|||
sb.append(" ");
|
||||
sb.append(getClassReferenceName(aClass));
|
||||
sb.append(" = getClassGlobalRef(\"");
|
||||
sb.append(aClass.getCanonicalName().replaceAll("\\.", "/"));
|
||||
|
||||
String name = aClass.getCanonicalName().replaceAll("\\.", "/");
|
||||
Class<?> containerClass = aClass.getDeclaringClass();
|
||||
if (containerClass != null) {
|
||||
// Is an inner class. Add the $ symbol.
|
||||
final int lastSlash = name.lastIndexOf('/');
|
||||
name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
|
||||
}
|
||||
|
||||
sb.append(name);
|
||||
sb.append("\");\n");
|
||||
return sb.toString();
|
||||
}
|
||||
|
@ -520,11 +624,21 @@ public class Utils {
|
|||
|
||||
/**
|
||||
* Helper method to read the modifier bits of the given method to determine if it is static.
|
||||
* @param aMethod The Method to check.
|
||||
* @param aMember The Member to check.
|
||||
* @return true of the method is declared static, false otherwise.
|
||||
*/
|
||||
public static boolean isMethodStatic(Method aMethod) {
|
||||
int aMethodModifiers = aMethod.getModifiers();
|
||||
public static boolean isMemberStatic(Member aMember) {
|
||||
int aMethodModifiers = aMember.getModifiers();
|
||||
return Modifier.isStatic(aMethodModifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to read the modifier bits of the given method to determine if it is static.
|
||||
* @param aMember The Member to check.
|
||||
* @return true of the method is declared static, false otherwise.
|
||||
*/
|
||||
public static boolean isMemberFinal(Member aMember) {
|
||||
int aMethodModifiers = aMember.getModifiers();
|
||||
return Modifier.isFinal(aMethodModifiers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1311,11 +1311,13 @@ jni-stubs.inc: jars/gecko-browser.jar jars/gecko-mozglue.jar jars/gecko-util.jar
|
|||
ANNOTATION_PROCESSOR_JAVA_FILES = \
|
||||
build/annotationProcessors/AnnotationProcessor.java \
|
||||
build/annotationProcessors/CodeGenerator.java \
|
||||
build/annotationProcessors/MethodWithAnnotationInfo.java \
|
||||
build/annotationProcessors/AnnotationInfo.java \
|
||||
build/annotationProcessors/classloader/AnnotatableEntity.java \
|
||||
build/annotationProcessors/classloader/ClassWithOptions.java \
|
||||
build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java \
|
||||
build/annotationProcessors/classloader/JarClassIterator.java \
|
||||
build/annotationProcessors/utils/AlphabeticMethodComparator.java \
|
||||
build/annotationProcessors/utils/GeneratableEntryPointIterator.java \
|
||||
build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java \
|
||||
build/annotationProcessors/utils/GeneratableElementIterator.java \
|
||||
build/annotationProcessors/utils/Utils.java \
|
||||
$(NULL)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче