summaryrefslogtreecommitdiffstats
path: root/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils')
-rw-r--r--mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java81
-rw-r--r--mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/GeneratableElementIterator.java291
-rw-r--r--mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/Utils.java480
3 files changed, 852 insertions, 0 deletions
diff --git a/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java
new file mode 100644
index 0000000000..47d8b82fba
--- /dev/null
+++ b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java
@@ -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);
+ }
+}
diff --git a/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/GeneratableElementIterator.java b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/GeneratableElementIterator.java
new file mode 100644
index 0000000000..0ef25cab52
--- /dev/null
+++ b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/GeneratableElementIterator.java
@@ -0,0 +1,291 @@
+/* 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.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+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;
+import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
+import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
+import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
+
+/**
+ * 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 mIterateEnumValues;
+ 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) {
+ if (aClass.isEnum()) {
+ // We treat "Wrap ALL the things" differently for enums. See the javadoc for
+ // isAnnotatedEnumField for more information.
+ mIterateEnumValues = true;
+ } else {
+ 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, /* ifdef */ ""));
+ 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(), /* ifdef */ "");
+ }
+ }
+ 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 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;
+ boolean noLiteral = false;
+
+ 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 =
+ Utils.getEnumValue(
+ AnnotationInfo.ExceptionMode.class, (String) exceptionModeMethod.invoke(annotation));
+
+ final Method calledFromMethod = annotationType.getDeclaredMethod("calledFrom");
+ calledFromMethod.setAccessible(true);
+ callingThread =
+ Utils.getEnumValue(
+ AnnotationInfo.CallingThread.class, (String) calledFromMethod.invoke(annotation));
+
+ final Method dispatchToMethod = annotationType.getDeclaredMethod("dispatchTo");
+ dispatchToMethod.setAccessible(true);
+ dispatchTarget =
+ Utils.getEnumValue(
+ AnnotationInfo.DispatchTarget.class, (String) dispatchToMethod.invoke(annotation));
+
+ final Method noLiteralMethod = annotationType.getDeclaredMethod("noLiteral");
+ noLiteralMethod.setAccessible(true);
+ noLiteral = (Boolean) noLiteralMethod.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, noLiteral);
+ }
+
+ /**
+ * 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 || isAnnotatedEnumField(candidateElement)) {
+ AnnotationInfo annotationInfo =
+ new AnnotationInfo(
+ Utils.getNativeName(candidateElement),
+ mClassInfo.exceptionMode,
+ mClassInfo.callingThread,
+ mClassInfo.dispatchTarget,
+ mClassInfo.noLiteral);
+ mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
+ return;
+ }
+ }
+ mNextReturnValue = null;
+ }
+
+ /**
+ * For enums that are annotated in their entirety, we typically only need to generate the
+ * enumerated values, but not other members. This method determines whether the given member is
+ * likely to be one of the enumerated values: We look for public, static, final fields that share
+ * the same class as the declaring enum's class.
+ *
+ * <p>Note that any additional members that should be wrapped may be explicitly annotated on a
+ * case-by-case basis.
+ */
+ private boolean isAnnotatedEnumField(final Member member) {
+ if (!mIterateEnumValues) {
+ return false;
+ }
+
+ if (!Utils.isPublic(member)
+ || !Utils.isStatic(member)
+ || !Utils.isFinal(member)
+ || !(member instanceof Field)) {
+ return false;
+ }
+
+ final Class<?> enumClass = mClass.wrappedClass;
+
+ final Field field = (Field) member;
+ final Class<?> fieldClass = field.getType();
+
+ return enumClass.equals(fieldClass);
+ }
+
+ @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.");
+ }
+}
diff --git a/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/Utils.java b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/Utils.java
new file mode 100644
index 0000000000..3ed9546223
--- /dev/null
+++ b/mobile/android/annotations/src/main/java/org/mozilla/gecko/annotationProcessors/utils/Utils.java
@@ -0,0 +1,480 @@
+/* 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.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
+
+/** 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");
+ }
+
+ private static boolean isMozClass(final Class<?> type) {
+ return type.getName().startsWith("org.mozilla.");
+ }
+
+ private static boolean useObjectForType(final Class<?> type, final boolean isHint) {
+ // Essentially we want to know whether we can use generated wrappers or not:
+ // If |type| is not ours, then it most likely doesn't have generated C++ wrappers.
+ // Furthermore, we skip interfaces as we generally do not wrap those.
+ return !isHint || type.equals(Object.class) || !isMozClass(type) || type.isInterface();
+ }
+
+ /**
+ * Returns the simplified name of a class that includes any outer classes but excludes
+ * package/namespace qualifiers.
+ *
+ * @param genScope The current scope of the class containing the current declaration. @Param type
+ * The class whose simplified name is to be generated. @Param connector String to be used for
+ * concatenating scopes.
+ * @return String containing the result
+ */
+ private static String getSimplifiedClassName(
+ final Class<?> genScope, final Class<?> type, final String connector) {
+ final ArrayList<String> names = new ArrayList<>();
+
+ // Starting with |type|, walk up our enclosing classes until we either reach genScope or we
+ // have reached the outermost scope. We save them to a list because we need to reverse them
+ // during output.
+ Class<?> c = type;
+ do {
+ names.add(c.getSimpleName());
+ c = c.getEnclosingClass();
+ } while (c != null && (genScope == null || !genScope.equals(c)));
+
+ // Walk through names in reverse order, joining them using |connector|
+ final StringBuilder builder = new StringBuilder();
+ for (int i = names.size() - 1; i >= 0; --i) {
+ builder.append(names.get(i));
+ if (i > 0) {
+ builder.append(connector);
+ }
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Returns the simplified name of a Java class that includes any outer classes but excludes
+ * package qualifiers. Used for Java signature hints.
+ *
+ * @param genScope The current scope of the class containing the current declaration. @Param type
+ * The class whose simplified name is to be generated.
+ * @return String containing the result
+ */
+ public static String getSimplifiedJavaClassName(final Class<?> genScope, final Class<?> type) {
+ return getSimplifiedClassName(genScope, type, ".");
+ }
+
+ /** Returns the fully-qualified name of the native class wrapper for the given type. */
+ public static String getWrappedNativeClassName(final Class<?> type) {
+ return "mozilla::java::" + getSimplifiedClassName(null, type, "::");
+ }
+
+ /**
+ * Get the C++ parameter 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) {
+ return getNativeParameterType(type, info, false);
+ }
+
+ /**
+ * Get the C++ hint type corresponding to the provided type parameter. The returned type may be
+ * more specific than the type returned by getNativeParameterType, as this method is used for
+ * generating comments instead of machine-readable code.
+ *
+ * @param type Class to determine the corresponding JNI type for.
+ * @return C++ type as a String
+ */
+ public static String getNativeParameterTypeHint(Class<?> type, AnnotationInfo info) {
+ return getNativeParameterType(type, info, true);
+ }
+
+ private static String getNativeParameterType(
+ final Class<?> type, final AnnotationInfo info, final boolean isHint) {
+ final String name = type.getName().replace('.', '/');
+
+ String value = NATIVE_TYPES.get(name);
+ if (value != null) {
+ return value;
+ }
+
+ if (type.isArray()) {
+ final String compName = type.getComponentType().getName();
+ value = NATIVE_ARRAY_TYPES.get(compName);
+ if (value != null) {
+ return value + "::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";
+ }
+
+ if (useObjectForType(type, isHint)) {
+ return "mozilla::jni::Object::Param";
+ }
+
+ return getWrappedNativeClassName(type) + "::Param";
+ }
+
+ /**
+ * Get the C++ return 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 getNativeReturnType(Class<?> type, AnnotationInfo info) {
+ return getNativeReturnType(type, info, false);
+ }
+
+ /**
+ * Get the C++ hint return type corresponding to the provided type parameter. The returned type
+ * may be more specific than the type returned by getNativeReturnType, as this method is used for
+ * generating comments instead of machine-readable code.
+ *
+ * @param type Class to determine the corresponding JNI type for.
+ * @return C++ type as a String
+ */
+ public static String getNativeReturnTypeHint(Class<?> type, AnnotationInfo info) {
+ return getNativeReturnType(type, info, true);
+ }
+
+ private static String getNativeReturnType(
+ final Class<?> type, final AnnotationInfo info, final boolean isHint) {
+ final String name = type.getName().replace('.', '/');
+
+ String value = NATIVE_TYPES.get(name);
+ if (value != null) {
+ return value;
+ }
+
+ if (type.isArray()) {
+ final String compName = type.getComponentType().getName();
+ value = NATIVE_ARRAY_TYPES.get(compName);
+ if (value != null) {
+ return value + "::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";
+ }
+
+ if (useObjectForType(type, isHint)) {
+ return "mozilla::jni::Object::LocalRef";
+ }
+
+ return getWrappedNativeClassName(type) + "::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('.', '/');
+
+ final String classDescriptor = CLASS_DESCRIPTORS.get(name);
+ if (classDescriptor != null) {
+ return classDescriptor;
+ }
+
+ 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(Locale.ROOT) + 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(Locale.ROOT) + 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());
+ }
+
+ /**
+ * Determine if a member is declared public.
+ *
+ * @param member The Member to check.
+ * @return true if the member is declared public, false otherwise.
+ */
+ public static boolean isPublic(final Member member) {
+ return Modifier.isPublic(member.getModifiers());
+ }
+
+ /**
+ * Return an enum value with the given name.
+ *
+ * @param type Enum class type.
+ * @param name Enum value name.
+ * @return Enum value with the given name.
+ */
+ public static <T extends Enum<T>> T getEnumValue(Class<T> type, String name) {
+ try {
+ return Enum.valueOf(type, name.toUpperCase(Locale.ROOT));
+
+ } catch (IllegalArgumentException e) {
+ final Object[] values;
+ try {
+ values = (Object[]) type.getDeclaredMethod("values").invoke(null);
+ } catch (final NoSuchMethodException
+ | IllegalAccessException
+ | InvocationTargetException exception) {
+ throw new RuntimeException("Cannot access enum: " + type, exception);
+ }
+
+ StringBuilder names = new StringBuilder();
+
+ for (int i = 0; i < values.length; i++) {
+ if (i != 0) {
+ names.append(", ");
+ }
+ names.append(values[i].toString().toLowerCase(Locale.ROOT));
+ }
+
+ 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(1);
+ return null;
+ }
+ }
+
+ public static String getIfdefHeader(String ifdef) {
+ if (ifdef.isEmpty()) {
+ return "";
+ } else if (ifdef.startsWith("!")) {
+ return "#ifndef " + ifdef.substring(1) + "\n";
+ }
+ return "#ifdef " + ifdef + "\n";
+ }
+
+ public static String getIfdefFooter(String ifdef) {
+ if (ifdef.isEmpty()) {
+ return "";
+ }
+ return "#endif // " + ifdef + "\n";
+ }
+
+ public static boolean isJNIObject(Class<?> cls) {
+ for (; cls != null; cls = cls.getSuperclass()) {
+ if (cls.getName().equals("org.mozilla.gecko.mozglue.JNIObject")) {
+ return true;
+ }
+ }
+ return false;
+ }
+}