mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-06 07:17:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
54
build/annotationProcessors/AnnotationInfo.java
Normal file
54
build/annotationProcessors/AnnotationInfo.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/* 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;
|
||||
|
||||
/**
|
||||
* Object holding annotation data. Used by GeneratableElementIterator.
|
||||
*/
|
||||
public class AnnotationInfo {
|
||||
public enum ExceptionMode {
|
||||
ABORT,
|
||||
NSRESULT,
|
||||
IGNORE;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::ExceptionMode::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallingThread {
|
||||
GECKO,
|
||||
UI,
|
||||
ANY;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::CallingThread::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DispatchTarget {
|
||||
GECKO,
|
||||
PROXY,
|
||||
CURRENT;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::DispatchTarget::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public final String wrapperName;
|
||||
public final ExceptionMode exceptionMode;
|
||||
public final CallingThread callingThread;
|
||||
public final DispatchTarget dispatchTarget;
|
||||
|
||||
public AnnotationInfo(String wrapperName, ExceptionMode exceptionMode,
|
||||
CallingThread callingThread, DispatchTarget dispatchTarget) {
|
||||
|
||||
this.wrapperName = wrapperName;
|
||||
this.exceptionMode = exceptionMode;
|
||||
this.callingThread = callingThread;
|
||||
this.dispatchTarget = dispatchTarget;
|
||||
}
|
||||
}
|
||||
175
build/annotationProcessors/AnnotationProcessor.java
Normal file
175
build/annotationProcessors/AnnotationProcessor.java
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* 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;
|
||||
|
||||
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.GeneratableElementIterator;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class AnnotationProcessor {
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
||||
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
||||
"// corresponding Java methods and rerun the build. Manually updating this file\n" +
|
||||
"// will cause your build to fail.\n" +
|
||||
"\n";
|
||||
|
||||
private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
||||
private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length <= 2) {
|
||||
System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ...");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
final String OUTPUT_PREFIX = args[0];
|
||||
final String SOURCE_FILE = OUTPUT_PREFIX + "JNIWrappers.cpp";
|
||||
final String HEADER_FILE = OUTPUT_PREFIX + "JNIWrappers.h";
|
||||
final String NATIVES_FILE = OUTPUT_PREFIX + "JNINatives.h";
|
||||
|
||||
System.out.println("Processing annotations...");
|
||||
|
||||
// We want to produce the same output as last time as often as possible. Ordering of
|
||||
// generated statements, therefore, needs to be consistent.
|
||||
final String[] jars = Arrays.copyOfRange(args, 1, args.length);
|
||||
Arrays.sort(jars);
|
||||
|
||||
// Start the clock!
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
// Get an iterator over the classes in the jar files given...
|
||||
Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(jars);
|
||||
|
||||
headerFile.append(
|
||||
"#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"mozilla/jni/Refs.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
implementationFile.append(
|
||||
"#include \"" + HEADER_FILE + "\"\n" +
|
||||
"#include \"mozilla/jni/Accessors.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
nativesFile.append(
|
||||
"#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"" + HEADER_FILE + "\"\n" +
|
||||
"#include \"mozilla/jni/Natives.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
while (jarClassIterator.hasNext()) {
|
||||
generateClass(jarClassIterator.next());
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n");
|
||||
|
||||
headerFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // " + getHeaderGuardName(HEADER_FILE) + "\n");
|
||||
|
||||
nativesFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n");
|
||||
|
||||
writeOutputFile(SOURCE_FILE, implementationFile);
|
||||
writeOutputFile(HEADER_FILE, headerFile);
|
||||
writeOutputFile(NATIVES_FILE, nativesFile);
|
||||
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("Annotation processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static void generateClass(final ClassWithOptions annotatedClass) {
|
||||
// Get an iterator over the appropriately generated methods of this class
|
||||
final GeneratableElementIterator methodIterator
|
||||
= new GeneratableElementIterator(annotatedClass);
|
||||
final ClassWithOptions[] innerClasses = methodIterator.getInnerClasses();
|
||||
|
||||
if (!methodIterator.hasNext() && innerClasses.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final CodeGenerator generatorInstance = new CodeGenerator(annotatedClass);
|
||||
generatorInstance.generateClasses(innerClasses);
|
||||
|
||||
// Iterate all annotated members in this class..
|
||||
while (methodIterator.hasNext()) {
|
||||
AnnotatableEntity aElementTuple = methodIterator.next();
|
||||
switch (aElementTuple.mEntityType) {
|
||||
case METHOD:
|
||||
generatorInstance.generateMethod(aElementTuple);
|
||||
break;
|
||||
case NATIVE:
|
||||
generatorInstance.generateNative(aElementTuple);
|
||||
break;
|
||||
case FIELD:
|
||||
generatorInstance.generateField(aElementTuple);
|
||||
break;
|
||||
case CONSTRUCTOR:
|
||||
generatorInstance.generateConstructor(aElementTuple);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
headerFile.append(generatorInstance.getHeaderFileContents());
|
||||
implementationFile.append(generatorInstance.getWrapperFileContents());
|
||||
nativesFile.append(generatorInstance.getNativesFileContents());
|
||||
|
||||
for (ClassWithOptions innerClass : innerClasses) {
|
||||
generateClass(innerClass);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHeaderGuardName(final String name) {
|
||||
return name.replaceAll("\\W", "_");
|
||||
}
|
||||
|
||||
private static void writeOutputFile(final String name,
|
||||
final StringBuilder content) {
|
||||
FileOutputStream outStream = null;
|
||||
try {
|
||||
outStream = new FileOutputStream(name);
|
||||
outStream.write(content.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + name + ". 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
627
build/annotationProcessors/CodeGenerator.java
Normal file
627
build/annotationProcessors/CodeGenerator.java
Normal file
|
|
@ -0,0 +1,627 @@
|
|||
/* 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;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
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.HashSet;
|
||||
|
||||
public class CodeGenerator {
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
// Buffers holding the strings to ultimately be written to the output files.
|
||||
private final StringBuilder cpp = new StringBuilder();
|
||||
private final StringBuilder header = new StringBuilder();
|
||||
private final StringBuilder natives = new StringBuilder();
|
||||
private final StringBuilder nativesInits = new StringBuilder();
|
||||
|
||||
private final Class<?> cls;
|
||||
private final String clsName;
|
||||
private AnnotationInfo.CallingThread callingThread = null;
|
||||
private int numNativesInits;
|
||||
|
||||
private final HashSet<String> takenMethodNames = new HashSet<String>();
|
||||
|
||||
public CodeGenerator(ClassWithOptions annotatedClass) {
|
||||
this.cls = annotatedClass.wrappedClass;
|
||||
this.clsName = annotatedClass.generatedName;
|
||||
|
||||
final String unqualifiedName = Utils.getUnqualifiedName(clsName);
|
||||
header.append(
|
||||
"class " + clsName + " : public mozilla::jni::ObjectBase<" +
|
||||
unqualifiedName + ">\n" +
|
||||
"{\n" +
|
||||
"public:\n" +
|
||||
" static const char name[];\n" +
|
||||
"\n" +
|
||||
" explicit " + unqualifiedName + "(const Context& ctx) : ObjectBase<" +
|
||||
unqualifiedName + ">(ctx) {}\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"const char " + clsName + "::name[] =\n" +
|
||||
" \"" + cls.getName().replace('.', '/') + "\";\n" +
|
||||
"\n");
|
||||
|
||||
natives.append(
|
||||
"template<class Impl>\n" +
|
||||
"class " + clsName + "::Natives : " +
|
||||
"public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" +
|
||||
"{\n" +
|
||||
"public:\n");
|
||||
}
|
||||
|
||||
private String getTraitsName(String uniqueName, boolean includeScope) {
|
||||
return (includeScope ? clsName + "::" : "") + uniqueName + "_t";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the C++ type name for this class or any class within the chain
|
||||
* of declaring classes, if the target class matches the given type.
|
||||
*
|
||||
* Return null if the given type does not match any class searched.
|
||||
*/
|
||||
private String getMatchingClassType(final Class<?> type) {
|
||||
Class<?> cls = this.cls;
|
||||
String clsName = this.clsName;
|
||||
|
||||
while (cls != null) {
|
||||
if (type.equals(cls)) {
|
||||
return clsName;
|
||||
}
|
||||
cls = cls.getDeclaringClass();
|
||||
clsName = clsName.substring(0, Math.max(0, clsName.lastIndexOf("::")));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getNativeParameterType(Class<?> type, AnnotationInfo info) {
|
||||
final String clsName = getMatchingClassType(type);
|
||||
if (clsName != null) {
|
||||
return Utils.getUnqualifiedName(clsName) + "::Param";
|
||||
}
|
||||
return Utils.getNativeParameterType(type, info);
|
||||
}
|
||||
|
||||
private String getNativeReturnType(Class<?> type, AnnotationInfo info) {
|
||||
final String clsName = getMatchingClassType(type);
|
||||
if (clsName != null) {
|
||||
return Utils.getUnqualifiedName(clsName) + "::LocalRef";
|
||||
}
|
||||
return Utils.getNativeReturnType(type, info);
|
||||
}
|
||||
|
||||
private void generateMember(AnnotationInfo info, Member member,
|
||||
String uniqueName, Class<?> type, Class<?>[] argTypes) {
|
||||
final StringBuilder args = new StringBuilder();
|
||||
for (Class<?> argType : argTypes) {
|
||||
args.append("\n " + getNativeParameterType(argType, info) + ",");
|
||||
}
|
||||
if (args.length() > 0) {
|
||||
args.setLength(args.length() - 1);
|
||||
}
|
||||
|
||||
header.append(
|
||||
" struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" +
|
||||
" typedef " + Utils.getUnqualifiedName(clsName) + " Owner;\n" +
|
||||
" typedef " + getNativeReturnType(type, info) + " ReturnType;\n" +
|
||||
" typedef " + getNativeParameterType(type, info) + " SetterType;\n" +
|
||||
" typedef mozilla::jni::Args<" + args + "> Args;\n" +
|
||||
" static constexpr char name[] = \"" +
|
||||
Utils.getMemberName(member) + "\";\n" +
|
||||
" static constexpr char signature[] =\n" +
|
||||
" \"" + Utils.getSignature(member) + "\";\n" +
|
||||
" static const bool isStatic = " + Utils.isStatic(member) + ";\n" +
|
||||
" static const mozilla::jni::ExceptionMode exceptionMode =\n" +
|
||||
" " + info.exceptionMode.nativeValue() + ";\n" +
|
||||
" static const mozilla::jni::CallingThread callingThread =\n" +
|
||||
" " + info.callingThread.nativeValue() + ";\n" +
|
||||
" static const mozilla::jni::DispatchTarget dispatchTarget =\n" +
|
||||
" " + info.dispatchTarget.nativeValue() + ";\n" +
|
||||
" };\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) +
|
||||
"::name[];\n" +
|
||||
"constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) +
|
||||
"::signature[];\n" +
|
||||
"\n");
|
||||
|
||||
if (this.callingThread == null) {
|
||||
this.callingThread = info.callingThread;
|
||||
} else if (this.callingThread != info.callingThread) {
|
||||
// We have a mix of calling threads, so specify "any" for the whole class.
|
||||
this.callingThread = AnnotationInfo.CallingThread.ANY;
|
||||
}
|
||||
}
|
||||
|
||||
private String getUniqueMethodName(String basename) {
|
||||
String newName = basename;
|
||||
int index = 1;
|
||||
|
||||
while (takenMethodNames.contains(newName)) {
|
||||
newName = basename + (++index);
|
||||
}
|
||||
|
||||
takenMethodNames.add(newName);
|
||||
return newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method prototype that includes return and argument types,
|
||||
* without specifiers (static, const, etc.).
|
||||
*/
|
||||
private String generatePrototype(String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info,
|
||||
boolean includeScope, boolean includeArgName,
|
||||
boolean isConst) {
|
||||
|
||||
final StringBuilder proto = new StringBuilder();
|
||||
int argIndex = 0;
|
||||
|
||||
proto.append("auto ");
|
||||
|
||||
if (includeScope) {
|
||||
proto.append(clsName).append("::");
|
||||
}
|
||||
|
||||
proto.append(name).append('(');
|
||||
|
||||
for (Class<?> argType : argTypes) {
|
||||
proto.append(getNativeParameterType(argType, info));
|
||||
if (includeArgName) {
|
||||
proto.append(" a").append(argIndex++);
|
||||
}
|
||||
proto.append(", ");
|
||||
}
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT &&
|
||||
!returnType.equals(void.class)) {
|
||||
proto.append(getNativeReturnType(returnType, info)).append('*');
|
||||
if (includeArgName) {
|
||||
proto.append(" a").append(argIndex++);
|
||||
}
|
||||
proto.append(", ");
|
||||
}
|
||||
|
||||
if (proto.substring(proto.length() - 2).equals(", ")) {
|
||||
proto.setLength(proto.length() - 2);
|
||||
}
|
||||
|
||||
proto.append(')');
|
||||
|
||||
if (isConst) {
|
||||
proto.append(" const");
|
||||
}
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
proto.append(" -> nsresult");
|
||||
} else {
|
||||
proto.append(" -> ").append(getNativeReturnType(returnType, info));
|
||||
}
|
||||
return proto.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method declaration that includes the prototype with specifiers,
|
||||
* but without the method body.
|
||||
*/
|
||||
private String generateDeclaration(String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info,
|
||||
boolean isStatic) {
|
||||
|
||||
return (isStatic ? "static " : "") +
|
||||
generatePrototype(name, argTypes, returnType, info,
|
||||
/* includeScope */ false, /* includeArgName */ false,
|
||||
/* isConst */ !isStatic) + ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method definition that includes the prototype with specifiers,
|
||||
* and with the method body.
|
||||
*/
|
||||
private String generateDefinition(String accessorName, String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info, boolean isStatic) {
|
||||
|
||||
final StringBuilder def = new StringBuilder(
|
||||
generatePrototype(name, argTypes, returnType, info,
|
||||
/* includeScope */ true, /* includeArgName */ true,
|
||||
/* isConst */ !isStatic));
|
||||
def.append("\n{\n");
|
||||
|
||||
|
||||
// Generate code to handle the return value, if needed.
|
||||
// We initialize rv to NS_OK instead of NS_ERROR_* because loading NS_OK (0) uses
|
||||
// fewer instructions. We are guaranteed to set rv to the correct value later.
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT &&
|
||||
returnType.equals(void.class)) {
|
||||
def.append(
|
||||
" nsresult rv = NS_OK;\n" +
|
||||
" ");
|
||||
|
||||
} else if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
// Non-void return type
|
||||
final String resultArg = "a" + argTypes.length;
|
||||
def.append(
|
||||
" MOZ_ASSERT(" + resultArg + ");\n" +
|
||||
" nsresult rv = NS_OK;\n" +
|
||||
" *" + resultArg + " = ");
|
||||
|
||||
} else {
|
||||
def.append(
|
||||
" return ");
|
||||
}
|
||||
|
||||
|
||||
// Generate a call, e.g., Method<Traits>::Call(a0, a1, a2);
|
||||
|
||||
def.append(accessorName).append("(")
|
||||
.append(Utils.getUnqualifiedName(clsName) +
|
||||
(isStatic ? "::Context()" : "::mCtx"));
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
def.append(", &rv");
|
||||
} else {
|
||||
def.append(", nullptr");
|
||||
}
|
||||
|
||||
// Generate the call argument list.
|
||||
for (int argIndex = 0; argIndex < argTypes.length; argIndex++) {
|
||||
def.append(", a").append(argIndex);
|
||||
}
|
||||
|
||||
def.append(");\n");
|
||||
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
def.append(" return rv;\n");
|
||||
}
|
||||
|
||||
return def.append("}").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the method provided.
|
||||
*
|
||||
* @param annotatedMethod The Java method, plus annotation data.
|
||||
*/
|
||||
public void generateMethod(AnnotatableEntity annotatedMethod) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for non-native method " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final boolean isStatic = Utils.isStatic(method);
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, argTypes,
|
||||
returnType, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Method<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Call",
|
||||
info.wrapperName, argTypes, returnType, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the native method provided.
|
||||
*
|
||||
* @param annotatedMethod The Java native method, plus annotation data.
|
||||
*/
|
||||
public void generateNative(AnnotatableEntity annotatedMethod) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
// Sanity check
|
||||
if (info.exceptionMode != AnnotationInfo.ExceptionMode.ABORT &&
|
||||
info.exceptionMode != AnnotationInfo.ExceptionMode.IGNORE) {
|
||||
throw new IllegalStateException("Invalid exception mode \"" +
|
||||
info.exceptionMode.name().toLowerCase() +
|
||||
"\" for native method " + clsName + "::" + uniqueName);
|
||||
}
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT &&
|
||||
returnType != void.class) {
|
||||
throw new IllegalStateException(
|
||||
"Must return void when not dispatching to current thread for native method " +
|
||||
clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final String traits = getTraitsName(uniqueName, /* includeScope */ true);
|
||||
|
||||
if (nativesInits.length() > 0) {
|
||||
nativesInits.append(',');
|
||||
}
|
||||
|
||||
nativesInits.append(
|
||||
"\n" +
|
||||
"\n" +
|
||||
" mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
|
||||
" mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
|
||||
" ::template Wrap<&Impl::" + info.wrapperName + ">)");
|
||||
numNativesInits++;
|
||||
}
|
||||
|
||||
private String getLiteral(Object val, AnnotationInfo info) {
|
||||
final Class<?> type = val.getClass();
|
||||
|
||||
if (type.equals(char.class) || type.equals(Character.class)) {
|
||||
final char c = (char) val;
|
||||
if (c >= 0x20 && c < 0x7F) {
|
||||
return "'" + c + '\'';
|
||||
}
|
||||
return "u'\\u" + Integer.toHexString(0x10000 | (int) c).substring(1) + '\'';
|
||||
|
||||
} else if (type.equals(CharSequence.class) || type.equals(String.class)) {
|
||||
final CharSequence str = (CharSequence) val;
|
||||
final StringBuilder out = new StringBuilder("u\"");
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
final char c = str.charAt(i);
|
||||
if (c >= 0x20 && c < 0x7F) {
|
||||
out.append(c);
|
||||
} else {
|
||||
out.append("\\u").append(Integer.toHexString(0x10000 | (int) c).substring(1));
|
||||
}
|
||||
}
|
||||
return out.append('"').toString();
|
||||
}
|
||||
|
||||
return String.valueOf(val);
|
||||
}
|
||||
|
||||
public void generateField(AnnotatableEntity annotatedField) {
|
||||
final Field field = annotatedField.getField();
|
||||
final AnnotationInfo info = annotatedField.mAnnotationInfo;
|
||||
final String uniqueName = info.wrapperName;
|
||||
final Class<?> type = field.getType();
|
||||
|
||||
// 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 (field.isSynthetic() || field.getName().equals("$VALUES")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for field " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
final boolean isStatic = Utils.isStatic(field);
|
||||
final boolean isFinal = Utils.isFinal(field);
|
||||
|
||||
if (isStatic && isFinal && (type.isPrimitive() || type.equals(String.class))) {
|
||||
Object val = null;
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
val = field.get(null);
|
||||
} catch (final IllegalAccessException e) {
|
||||
}
|
||||
|
||||
if (val != null && type.isPrimitive()) {
|
||||
// For static final primitive fields, we can use a "static const" declaration.
|
||||
header.append(
|
||||
" static const " + Utils.getNativeReturnType(type, info) +
|
||||
' ' + info.wrapperName + " = " + getLiteral(val, info) + ";\n" +
|
||||
"\n");
|
||||
return;
|
||||
|
||||
} else if (val != null && type.equals(String.class)) {
|
||||
final String nativeType = "char16_t";
|
||||
|
||||
header.append(
|
||||
" static const " + nativeType + ' ' + info.wrapperName + "[];\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"const " + nativeType + ' ' + clsName + "::" + info.wrapperName +
|
||||
"[] = " + getLiteral(val, info) + ";\n" +
|
||||
"\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to using accessors if we encounter an exception.
|
||||
}
|
||||
|
||||
generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY);
|
||||
|
||||
final Class<?>[] getterArgs = EMPTY_CLASS_ARRAY;
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, getterArgs,
|
||||
type, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Field<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Get",
|
||||
info.wrapperName, getterArgs, type, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
if (isFinal) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Class<?>[] setterArgs = new Class<?>[] { type };
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, setterArgs,
|
||||
void.class, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Field<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Set",
|
||||
info.wrapperName, setterArgs, void.class, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
public void generateConstructor(AnnotatableEntity annotatedConstructor) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Constructor<?> method = annotatedConstructor.getConstructor();
|
||||
final AnnotationInfo info = annotatedConstructor.mAnnotationInfo;
|
||||
final String wrapperName = "New";
|
||||
final String uniqueName = getUniqueMethodName(wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = cls;
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for constructor " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(wrapperName, argTypes,
|
||||
returnType, info, /* isStatic */ true) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Constructor<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Call",
|
||||
wrapperName, argTypes, returnType, info, /* isStatic */ true) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
public void generateMembers(Member[] members) {
|
||||
for (Member m : members) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = Utils.getMemberName(m);
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
|
||||
// Default for SDK bindings.
|
||||
final AnnotationInfo info = new AnnotationInfo(name,
|
||||
AnnotationInfo.ExceptionMode.NSRESULT,
|
||||
AnnotationInfo.CallingThread.ANY,
|
||||
AnnotationInfo.DispatchTarget.CURRENT);
|
||||
final AnnotatableEntity entity = new AnnotatableEntity(m, info);
|
||||
|
||||
if (m instanceof Constructor) {
|
||||
generateConstructor(entity);
|
||||
} else if (m instanceof Method) {
|
||||
generateMethod(entity);
|
||||
} else if (m instanceof Field) {
|
||||
generateField(entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"expected member to be Constructor, Method, or Field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateClasses(final ClassWithOptions[] classes) {
|
||||
if (classes.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final ClassWithOptions cls : classes) {
|
||||
// Extract "Inner" from "Outer::Inner".
|
||||
header.append(
|
||||
" class " + Utils.getUnqualifiedName(cls.generatedName) + ";\n");
|
||||
}
|
||||
header.append('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated wrappers file.
|
||||
*
|
||||
* @return The bytes to be written to the wrappers file.
|
||||
*/
|
||||
public String getWrapperFileContents() {
|
||||
return cpp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated header file.
|
||||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getHeaderFileContents() {
|
||||
if (this.callingThread == null) {
|
||||
this.callingThread = AnnotationInfo.CallingThread.ANY;
|
||||
}
|
||||
|
||||
header.append(
|
||||
" static const mozilla::jni::CallingThread callingThread =\n" +
|
||||
" " + this.callingThread.nativeValue() + ";\n" +
|
||||
"\n");
|
||||
|
||||
if (nativesInits.length() > 0) {
|
||||
header.append(
|
||||
" template<class Impl> class Natives;\n");
|
||||
}
|
||||
header.append(
|
||||
"};\n" +
|
||||
"\n");
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated natives header file.
|
||||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getNativesFileContents() {
|
||||
if (nativesInits.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
natives.append(
|
||||
" static const JNINativeMethod methods[" + numNativesInits + "];\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"template<class Impl>\n" +
|
||||
"const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' +
|
||||
"};\n" +
|
||||
"\n");
|
||||
return natives.toString();
|
||||
}
|
||||
}
|
||||
10
build/annotationProcessors/Makefile.in
Normal file
10
build/annotationProcessors/Makefile.in
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# 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/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# Include Android specific java flags, instead of what's in rules.mk.
|
||||
include $(topsrcdir)/config/android-common.mk
|
||||
|
||||
export:: annotationProcessors.jar
|
||||
258
build/annotationProcessors/SDKProcessor.java
Normal file
258
build/annotationProcessors/SDKProcessor.java
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/* 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;
|
||||
|
||||
import com.android.tools.lint.checks.ApiLookup;
|
||||
import com.android.tools.lint.LintCliClient;
|
||||
|
||||
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.GeneratableElementIterator;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Scanner;
|
||||
import java.util.Vector;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
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;
|
||||
|
||||
public class SDKProcessor {
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
||||
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
||||
"// corresponding Javamethods and rerun the build. Manually updating this file\n" +
|
||||
"// will cause your build to fail.\n" +
|
||||
"\n";
|
||||
|
||||
private static ApiLookup sApiLookup;
|
||||
private static int sMaxSdkVersion;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length < 5) {
|
||||
System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println("Processing platform bindings...");
|
||||
|
||||
String sdkJar = args[0];
|
||||
Vector classes = getClassList(args[1]);
|
||||
String outdir = args[2];
|
||||
String generatedFilePrefix = args[3];
|
||||
sMaxSdkVersion = Integer.parseInt(args[4]);
|
||||
|
||||
LintCliClient lintClient = new LintCliClient();
|
||||
sApiLookup = ApiLookup.get(lintClient);
|
||||
|
||||
// Start the clock!
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
// Get an iterator over the classes in the jar files given...
|
||||
// Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
|
||||
|
||||
StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
headerFile.append(
|
||||
"#ifndef " + generatedFilePrefix + "_h__\n" +
|
||||
"#define " + generatedFilePrefix + "_h__\n" +
|
||||
"\n" +
|
||||
"#include \"mozilla/jni/Refs.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"namespace sdk {\n" +
|
||||
"\n");
|
||||
|
||||
StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
||||
implementationFile.append(
|
||||
"#include \"" + generatedFilePrefix + ".h\"\n" +
|
||||
"#include \"mozilla/jni/Accessors.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"namespace sdk {\n" +
|
||||
"\n");
|
||||
|
||||
// Used to track the calls to the various class-specific initialisation functions.
|
||||
ClassLoader loader = null;
|
||||
try {
|
||||
loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) },
|
||||
SDKProcessor.class.getClassLoader());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.toString());
|
||||
}
|
||||
|
||||
for (Iterator<String> i = classes.iterator(); i.hasNext(); ) {
|
||||
String className = i.next();
|
||||
System.out.println("Looking up: " + className);
|
||||
|
||||
generateClass(Class.forName(className, true, loader),
|
||||
implementationFile,
|
||||
headerFile);
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
"} /* sdk */\n" +
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n");
|
||||
|
||||
headerFile.append(
|
||||
"} /* sdk */\n" +
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif\n");
|
||||
|
||||
writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("SDK processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static int getAPIVersion(Class<?> cls, Member m) {
|
||||
if (m instanceof Method || m instanceof Constructor) {
|
||||
return sApiLookup.getCallVersion(
|
||||
cls.getName().replace('.', '/'),
|
||||
Utils.getMemberName(m),
|
||||
Utils.getSignature(m));
|
||||
} else if (m instanceof Field) {
|
||||
return sApiLookup.getFieldVersion(
|
||||
Utils.getClassDescriptor(m.getDeclaringClass()), m.getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("expected member to be Method, Constructor, or Field");
|
||||
}
|
||||
}
|
||||
|
||||
private static Member[] sortAndFilterMembers(Class<?> cls, Member[] members) {
|
||||
Arrays.sort(members, new Comparator<Member>() {
|
||||
@Override
|
||||
public int compare(Member a, Member b) {
|
||||
return a.getName().compareTo(b.getName());
|
||||
}
|
||||
});
|
||||
|
||||
ArrayList<Member> list = new ArrayList<>();
|
||||
for (Member m : members) {
|
||||
// Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK
|
||||
// version, so we check for both classes and see if we can find a minimum SDK version.
|
||||
int version = getAPIVersion(cls, m);
|
||||
final int version2 = getAPIVersion(m.getDeclaringClass(), m);
|
||||
if (version2 > 0 && version2 < version) {
|
||||
version = version2;
|
||||
}
|
||||
if (version > sMaxSdkVersion) {
|
||||
System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() +
|
||||
", version " + version + " > " + sMaxSdkVersion);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sometimes (e.g. KeyEvent) a field can appear in both a class and a superclass. In
|
||||
// that case we want to filter out the version that appears in the superclass, or
|
||||
// we'll have bindings with duplicate names.
|
||||
try {
|
||||
if (m instanceof Field && !m.equals(cls.getField(m.getName()))) {
|
||||
// m is a field in a superclass that has been hidden by
|
||||
// a field with the same name in a subclass.
|
||||
System.out.println("Skipping " + m.getName() +
|
||||
" from " + m.getDeclaringClass());
|
||||
continue;
|
||||
}
|
||||
} catch (final NoSuchFieldException e) {
|
||||
}
|
||||
|
||||
list.add(m);
|
||||
}
|
||||
|
||||
return list.toArray(new Member[list.size()]);
|
||||
}
|
||||
|
||||
private static void generateClass(Class<?> clazz,
|
||||
StringBuilder implementationFile,
|
||||
StringBuilder headerFile) {
|
||||
String generatedName = clazz.getSimpleName();
|
||||
|
||||
CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
|
||||
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields()));
|
||||
|
||||
headerFile.append(generator.getHeaderFileContents());
|
||||
implementationFile.append(generator.getWrapperFileContents());
|
||||
}
|
||||
|
||||
private static Vector<String> getClassList(String path) {
|
||||
Scanner scanner = null;
|
||||
try {
|
||||
scanner = new Scanner(new FileInputStream(path));
|
||||
|
||||
Vector lines = new Vector();
|
||||
while (scanner.hasNextLine()) {
|
||||
lines.add(scanner.nextLine());
|
||||
}
|
||||
return lines;
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.toString());
|
||||
return null;
|
||||
} finally {
|
||||
if (scanner != null) {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeOutputFiles(String aOutputDir, String aPrefix, StringBuilder aHeaderFile,
|
||||
StringBuilder aImplementationFile) {
|
||||
FileOutputStream implStream = null;
|
||||
try {
|
||||
implStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".cpp"));
|
||||
implStream.write(aImplementationFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (implStream != null) {
|
||||
try {
|
||||
implStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close implStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream headerStream = null;
|
||||
try {
|
||||
headerStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".h"));
|
||||
headerStream.write(aHeaderFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + aOutputDir + ". 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
/* 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;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* 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, NATIVE, 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) {
|
||||
if (Modifier.isNative(aObject.getModifiers())) {
|
||||
mEntityType = ENTITY_TYPE.NATIVE;
|
||||
} else {
|
||||
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 && mEntityType != ENTITY_TYPE.NATIVE) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
15
build/annotationProcessors/classloader/ClassWithOptions.java
Normal file
15
build/annotationProcessors/classloader/ClassWithOptions.java
Normal file
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/* 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 java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* A classloader which can be initialised with a list of jar files and which can provide an iterator
|
||||
* over the top level classes in the jar files it was initialised with.
|
||||
* 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.
|
||||
*/
|
||||
public class IterableJarLoadingURLClassLoader extends URLClassLoader {
|
||||
LinkedList<String> classNames = new LinkedList<String>();
|
||||
|
||||
/**
|
||||
* Create an instance and return its iterator. Provides an iterator over the classes in the jar
|
||||
* files provided as arguments.
|
||||
* Inner classes are not supported.
|
||||
*
|
||||
* @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<ClassWithOptions> getIteratorOverJars(String[] args) {
|
||||
URL[] urlArray = new URL[args.length];
|
||||
LinkedList<String> aClassNames = new LinkedList<String>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
try {
|
||||
urlArray[i] = (new File(args[i])).toURI().toURL();
|
||||
|
||||
Enumeration<JarEntry> entries = new JarFile(args[i]).entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry e = entries.nextElement();
|
||||
String fName = e.getName();
|
||||
if (!fName.endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
|
||||
|
||||
aClassNames.add(className);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error loading jar file \"" + args[i] + '"');
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
Collections.sort(aClassNames);
|
||||
return new JarClassIterator(new IterableJarLoadingURLClassLoader(urlArray, aClassNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a classloader capable of loading all classes given as URLs in urls. Used by static
|
||||
* method above.
|
||||
*
|
||||
* @param urls URLs for all classes the new instance shall be capable of loading.
|
||||
* @param aClassNames A list of names of the classes this instance shall be capable of loading.
|
||||
*/
|
||||
protected IterableJarLoadingURLClassLoader(URL[] urls, LinkedList<String> aClassNames) {// Array to populate with URLs for each class in the given jars.
|
||||
super(urls);
|
||||
classNames = aClassNames;
|
||||
}
|
||||
}
|
||||
84
build/annotationProcessors/classloader/JarClassIterator.java
Normal file
84
build/annotationProcessors/classloader/JarClassIterator.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* 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 java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Class for iterating over an IterableJarLoadingURLClassLoader's classes.
|
||||
*
|
||||
* This class is not thread safe: use it only from a single thread.
|
||||
*/
|
||||
public class JarClassIterator implements Iterator<ClassWithOptions> {
|
||||
private IterableJarLoadingURLClassLoader mTarget;
|
||||
private Iterator<String> mTargetClassListIterator;
|
||||
|
||||
private ClassWithOptions lookAhead;
|
||||
|
||||
public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) {
|
||||
mTarget = aTarget;
|
||||
mTargetClassListIterator = aTarget.classNames.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassWithOptions next() {
|
||||
if (!fillLookAheadIfPossible()) {
|
||||
throw new IllegalStateException("Failed to look ahead in next()!");
|
||||
}
|
||||
ClassWithOptions next = lookAhead;
|
||||
lookAhead = null;
|
||||
return next;
|
||||
}
|
||||
|
||||
private boolean fillLookAheadIfPossible() {
|
||||
if (lookAhead != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mTargetClassListIterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String className = mTargetClassListIterator.next();
|
||||
try {
|
||||
Class<?> ret = mTarget.loadClass(className);
|
||||
|
||||
// Incremental builds can leave stale classfiles in the jar. Such classfiles will cause
|
||||
// an exception at this point. We can safely ignore these classes - they cannot possibly
|
||||
// ever be loaded as they conflict with their parent class and will be killed by
|
||||
// Proguard later on anyway.
|
||||
final Class<?> enclosingClass;
|
||||
try {
|
||||
enclosingClass = ret.getEnclosingClass();
|
||||
} catch (IncompatibleClassChangeError e) {
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
if (enclosingClass != null) {
|
||||
// Anonymous inner class - unsupported.
|
||||
// Or named inner class, which will be processed when we process the outer class.
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
lookAhead = new ClassWithOptions(ret, ret.getSimpleName());
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Removal of classes from iterator not supported.");
|
||||
}
|
||||
}
|
||||
24
build/annotationProcessors/moz.build
Normal file
24
build/annotationProcessors/moz.build
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
jar = add_java_jar('annotationProcessors')
|
||||
jar.sources += [
|
||||
'AnnotationInfo.java',
|
||||
'AnnotationProcessor.java',
|
||||
'classloader/AnnotatableEntity.java',
|
||||
'classloader/ClassWithOptions.java',
|
||||
'classloader/IterableJarLoadingURLClassLoader.java',
|
||||
'classloader/JarClassIterator.java',
|
||||
'CodeGenerator.java',
|
||||
'SDKProcessor.java',
|
||||
'utils/AlphabeticAnnotatableEntityComparator.java',
|
||||
'utils/GeneratableElementIterator.java',
|
||||
'utils/Utils.java',
|
||||
]
|
||||
jar.extra_jars += [
|
||||
CONFIG['ANDROID_TOOLS'] + '/lib/lint.jar',
|
||||
CONFIG['ANDROID_TOOLS'] + '/lib/lint-checks.jar',
|
||||
]
|
||||
|
|
@ -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.getSignature(aLhs);
|
||||
rName = Utils.getSignature(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.getSignature(aLhs);
|
||||
String rName = Utils.getSignature(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);
|
||||
}
|
||||
}
|
||||
267
build/annotationProcessors/utils/GeneratableElementIterator.java
Normal file
267
build/annotationProcessors/utils/GeneratableElementIterator.java
Normal file
|
|
@ -0,0 +1,267 @@
|
|||
/* 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 org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
|
||||
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.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* 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 GeneratableElementIterator implements Iterator<AnnotatableEntity> {
|
||||
private final ClassWithOptions mClass;
|
||||
private final Member[] mObjects;
|
||||
private AnnotatableEntity mNextReturnValue;
|
||||
private int mElementIndex;
|
||||
private AnnotationInfo mClassInfo;
|
||||
|
||||
private boolean mIterateEveryEntry;
|
||||
private boolean mSkipCurrentEntry;
|
||||
|
||||
public GeneratableElementIterator(ClassWithOptions annotatedClass) {
|
||||
mClass = annotatedClass;
|
||||
|
||||
final Class<?> aClass = annotatedClass.wrappedClass;
|
||||
// Get all the elements of this class as AccessibleObjects.
|
||||
Member[] aMethods = aClass.getDeclaredMethods();
|
||||
Member[] aFields = aClass.getDeclaredFields();
|
||||
Member[] aCtors = aClass.getDeclaredConstructors();
|
||||
|
||||
// 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<Member>());
|
||||
mObjects = objs;
|
||||
|
||||
// Check for "Wrap ALL the things" flag.
|
||||
for (Annotation annotation : aClass.getDeclaredAnnotations()) {
|
||||
mClassInfo = buildAnnotationInfo(aClass, annotation);
|
||||
if (mClassInfo != null) {
|
||||
mIterateEveryEntry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSkipCurrentEntry) {
|
||||
throw new IllegalArgumentException("Cannot skip entire class");
|
||||
}
|
||||
|
||||
findNextValue();
|
||||
}
|
||||
|
||||
private Class<?>[] getFilteredInnerClasses() {
|
||||
// Go through all inner classes and see which ones we want to generate.
|
||||
final Class<?>[] candidates = mClass.wrappedClass.getDeclaredClasses();
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < candidates.length; ++i) {
|
||||
final GeneratableElementIterator testIterator
|
||||
= new GeneratableElementIterator(new ClassWithOptions(candidates[i], null));
|
||||
if (testIterator.hasNext()
|
||||
|| testIterator.getFilteredInnerClasses() != null) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
// Clear out ones that don't match.
|
||||
candidates[i] = null;
|
||||
}
|
||||
return count > 0 ? candidates : null;
|
||||
}
|
||||
|
||||
public ClassWithOptions[] getInnerClasses() {
|
||||
final Class<?>[] candidates = getFilteredInnerClasses();
|
||||
if (candidates == null) {
|
||||
return new ClassWithOptions[0];
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (Class<?> candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
final ClassWithOptions[] ret = new ClassWithOptions[count];
|
||||
count = 0;
|
||||
for (Class<?> candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
ret[count++] = new ClassWithOptions(
|
||||
candidate, mClass.generatedName + "::" + candidate.getSimpleName());
|
||||
}
|
||||
}
|
||||
assert ret.length == count;
|
||||
|
||||
Arrays.sort(ret, new Comparator<ClassWithOptions>() {
|
||||
@Override public int compare(ClassWithOptions lhs, ClassWithOptions rhs) {
|
||||
return lhs.generatedName.compareTo(rhs.generatedName);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static <T extends Enum<T>> T getEnumValue(Class<T> type, String name)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
try {
|
||||
return Enum.valueOf(type, name.toUpperCase());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object[] values = (Object[]) type.getDeclaredMethod("values").invoke(null);
|
||||
StringBuilder names = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i != 0) {
|
||||
names.append(", ");
|
||||
}
|
||||
names.append(values[i].toString().toLowerCase());
|
||||
}
|
||||
|
||||
System.err.println("***");
|
||||
System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName());
|
||||
System.err.println("*** Specify one of " + names.toString());
|
||||
System.err.println("***");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(6);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationInfo buildAnnotationInfo(AnnotatedElement element, Annotation annotation) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final String annotationTypeName = annotationType.getName();
|
||||
if (!annotationTypeName.equals("org.mozilla.gecko.annotation.WrapForJNI")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String stubName = null;
|
||||
AnnotationInfo.ExceptionMode exceptionMode = null;
|
||||
AnnotationInfo.CallingThread callingThread = null;
|
||||
AnnotationInfo.DispatchTarget dispatchTarget = null;
|
||||
|
||||
try {
|
||||
final Method skipMethod = annotationType.getDeclaredMethod("skip");
|
||||
skipMethod.setAccessible(true);
|
||||
if ((Boolean) skipMethod.invoke(annotation)) {
|
||||
mSkipCurrentEntry = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine the explicitly-given name of the stub to generate, if any.
|
||||
final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
|
||||
stubNameMethod.setAccessible(true);
|
||||
stubName = (String) stubNameMethod.invoke(annotation);
|
||||
|
||||
final Method exceptionModeMethod = annotationType.getDeclaredMethod("exceptionMode");
|
||||
exceptionModeMethod.setAccessible(true);
|
||||
exceptionMode = getEnumValue(
|
||||
AnnotationInfo.ExceptionMode.class,
|
||||
(String) exceptionModeMethod.invoke(annotation));
|
||||
|
||||
final Method calledFromMethod = annotationType.getDeclaredMethod("calledFrom");
|
||||
calledFromMethod.setAccessible(true);
|
||||
callingThread = getEnumValue(
|
||||
AnnotationInfo.CallingThread.class,
|
||||
(String) calledFromMethod.invoke(annotation));
|
||||
|
||||
final Method dispatchToMethod = annotationType.getDeclaredMethod("dispatchTo");
|
||||
dispatchToMethod.setAccessible(true);
|
||||
dispatchTarget = getEnumValue(
|
||||
AnnotationInfo.DispatchTarget.class,
|
||||
(String) dispatchToMethod.invoke(annotation));
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("Unable to find expected field on WrapForJNI annotation. Did the signature change?");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(3);
|
||||
} catch (IllegalAccessException e) {
|
||||
System.err.println("IllegalAccessException reading fields on WrapForJNI 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 WrapForJNI 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 (stubName.isEmpty()) {
|
||||
stubName = Utils.getNativeName(element);
|
||||
}
|
||||
|
||||
return new AnnotationInfo(stubName, exceptionMode, callingThread, dispatchTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and cache the next appropriately annotated method, plus the annotation parameter, if
|
||||
* one exists. Otherwise cache null, so hasNext returns false.
|
||||
*/
|
||||
private void findNextValue() {
|
||||
while (mElementIndex < mObjects.length) {
|
||||
Member candidateElement = mObjects[mElementIndex];
|
||||
mElementIndex++;
|
||||
for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) {
|
||||
AnnotationInfo info = buildAnnotationInfo((AnnotatedElement)candidateElement, annotation);
|
||||
if (info != null) {
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSkipCurrentEntry) {
|
||||
mSkipCurrentEntry = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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(
|
||||
Utils.getNativeName(candidateElement),
|
||||
mClassInfo.exceptionMode,
|
||||
mClassInfo.callingThread,
|
||||
mClassInfo.dispatchTarget);
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mNextReturnValue = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return mNextReturnValue != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotatableEntity next() {
|
||||
AnnotatableEntity ret = mNextReturnValue;
|
||||
findNextValue();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported.");
|
||||
}
|
||||
}
|
||||
288
build/annotationProcessors/utils/Utils.java
Normal file
288
build/annotationProcessors/utils/Utils.java
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* 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 org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
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.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A collection of utility methods used by CodeGenerator. Largely used for translating types.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
// A collection of lookup tables to simplify the functions to follow...
|
||||
private static final HashMap<String, String> NATIVE_TYPES = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
NATIVE_TYPES.put("void", "void");
|
||||
NATIVE_TYPES.put("boolean", "bool");
|
||||
NATIVE_TYPES.put("byte", "int8_t");
|
||||
NATIVE_TYPES.put("char", "char16_t");
|
||||
NATIVE_TYPES.put("short", "int16_t");
|
||||
NATIVE_TYPES.put("int", "int32_t");
|
||||
NATIVE_TYPES.put("long", "int64_t");
|
||||
NATIVE_TYPES.put("float", "float");
|
||||
NATIVE_TYPES.put("double", "double");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> NATIVE_ARRAY_TYPES = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
NATIVE_ARRAY_TYPES.put("boolean", "mozilla::jni::BooleanArray");
|
||||
NATIVE_ARRAY_TYPES.put("byte", "mozilla::jni::ByteArray");
|
||||
NATIVE_ARRAY_TYPES.put("char", "mozilla::jni::CharArray");
|
||||
NATIVE_ARRAY_TYPES.put("short", "mozilla::jni::ShortArray");
|
||||
NATIVE_ARRAY_TYPES.put("int", "mozilla::jni::IntArray");
|
||||
NATIVE_ARRAY_TYPES.put("long", "mozilla::jni::LongArray");
|
||||
NATIVE_ARRAY_TYPES.put("float", "mozilla::jni::FloatArray");
|
||||
NATIVE_ARRAY_TYPES.put("double", "mozilla::jni::DoubleArray");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> CLASS_DESCRIPTORS = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
CLASS_DESCRIPTORS.put("void", "V");
|
||||
CLASS_DESCRIPTORS.put("boolean", "Z");
|
||||
CLASS_DESCRIPTORS.put("byte", "B");
|
||||
CLASS_DESCRIPTORS.put("char", "C");
|
||||
CLASS_DESCRIPTORS.put("short", "S");
|
||||
CLASS_DESCRIPTORS.put("int", "I");
|
||||
CLASS_DESCRIPTORS.put("long", "J");
|
||||
CLASS_DESCRIPTORS.put("float", "F");
|
||||
CLASS_DESCRIPTORS.put("double", "D");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ type corresponding to the provided type parameter.
|
||||
*
|
||||
* @param type Class to determine the corresponding JNI type for.
|
||||
* @return C++ type as a String
|
||||
*/
|
||||
public static String getNativeParameterType(Class<?> type, AnnotationInfo info) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (NATIVE_TYPES.containsKey(name)) {
|
||||
return NATIVE_TYPES.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
final String compName = type.getComponentType().getName();
|
||||
if (NATIVE_ARRAY_TYPES.containsKey(compName)) {
|
||||
return NATIVE_ARRAY_TYPES.get(compName) + "::Param";
|
||||
}
|
||||
return "mozilla::jni::ObjectArray::Param";
|
||||
}
|
||||
|
||||
if (type.equals(String.class) || type.equals(CharSequence.class)) {
|
||||
return "mozilla::jni::String::Param";
|
||||
}
|
||||
|
||||
if (type.equals(Class.class)) {
|
||||
// You're doing reflection on Java objects from inside C, returning Class objects
|
||||
// to C, generating the corresponding code using this Java program. Really?!
|
||||
return "mozilla::jni::Class::Param";
|
||||
}
|
||||
|
||||
if (type.equals(Throwable.class)) {
|
||||
return "mozilla::jni::Throwable::Param";
|
||||
}
|
||||
|
||||
if (type.equals(ByteBuffer.class)) {
|
||||
return "mozilla::jni::ByteBuffer::Param";
|
||||
}
|
||||
|
||||
return "mozilla::jni::Object::Param";
|
||||
}
|
||||
|
||||
public static String getNativeReturnType(Class<?> type, AnnotationInfo info) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (NATIVE_TYPES.containsKey(name)) {
|
||||
return NATIVE_TYPES.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
final String compName = type.getComponentType().getName();
|
||||
if (NATIVE_ARRAY_TYPES.containsKey(compName)) {
|
||||
return NATIVE_ARRAY_TYPES.get(compName) + "::LocalRef";
|
||||
}
|
||||
return "mozilla::jni::ObjectArray::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(String.class)) {
|
||||
return "mozilla::jni::String::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(Class.class)) {
|
||||
// You're doing reflection on Java objects from inside C, returning Class objects
|
||||
// to C, generating the corresponding code using this Java program. Really?!
|
||||
return "mozilla::jni::Class::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(Throwable.class)) {
|
||||
return "mozilla::jni::Throwable::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(ByteBuffer.class)) {
|
||||
return "mozilla::jni::ByteBuffer::LocalRef";
|
||||
}
|
||||
|
||||
return "mozilla::jni::Object::LocalRef";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI class descriptor corresponding to the provided type parameter.
|
||||
*
|
||||
* @param type Class to determine the corresponding JNI descriptor for.
|
||||
* @return Class descripor as a String
|
||||
*/
|
||||
public static String getClassDescriptor(Class<?> type) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (CLASS_DESCRIPTORS.containsKey(name)) {
|
||||
return CLASS_DESCRIPTORS.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
// Array names are already in class descriptor form.
|
||||
return name;
|
||||
}
|
||||
|
||||
return "L" + name + ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a member.
|
||||
*
|
||||
* @param member Member to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Member member) {
|
||||
return member instanceof Field ? getSignature((Field) member) :
|
||||
member instanceof Method ? getSignature((Method) member) :
|
||||
getSignature((Constructor<?>) member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a field.
|
||||
*
|
||||
* @param member Field to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Field member) {
|
||||
return getClassDescriptor(member.getType());
|
||||
}
|
||||
|
||||
private static String getSignature(Class<?>[] args, Class<?> ret) {
|
||||
final StringBuilder sig = new StringBuilder("(");
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
sig.append(getClassDescriptor(args[i]));
|
||||
}
|
||||
return sig.append(')').append(getClassDescriptor(ret)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a method.
|
||||
*
|
||||
* @param member Method to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Method member) {
|
||||
return getSignature(member.getParameterTypes(), member.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a constructor.
|
||||
*
|
||||
* @param member Constructor to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Constructor<?> member) {
|
||||
return getSignature(member.getParameterTypes(), void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(Member member) {
|
||||
final String name = getMemberName(member);
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(Class<?> clz) {
|
||||
final String name = clz.getName();
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(AnnotatedElement element) {
|
||||
if (element instanceof Class<?>) {
|
||||
return getNativeName((Class<?>)element);
|
||||
} else if (element instanceof Member) {
|
||||
return getNativeName((Member)element);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getMemberName(Member member) {
|
||||
if (member instanceof Constructor) {
|
||||
return "<init>";
|
||||
}
|
||||
return member.getName();
|
||||
}
|
||||
|
||||
public static String getUnqualifiedName(String name) {
|
||||
return name.substring(name.lastIndexOf(':') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member is declared static.
|
||||
*
|
||||
* @param member The Member to check.
|
||||
* @return true if the member is declared static, false otherwise.
|
||||
*/
|
||||
public static boolean isStatic(final Member member) {
|
||||
return Modifier.isStatic(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member is declared final.
|
||||
*
|
||||
* @param member The Member to check.
|
||||
* @return true if the member is declared final, false otherwise.
|
||||
*/
|
||||
public static boolean isFinal(final Member member) {
|
||||
return Modifier.isFinal(member.getModifiers());
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue