diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /build/annotationProcessors/utils | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'build/annotationProcessors/utils')
3 files changed, 636 insertions, 0 deletions
diff --git a/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java b/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java new file mode 100644 index 000000000..2ee2ae56a --- /dev/null +++ b/build/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/build/annotationProcessors/utils/GeneratableElementIterator.java b/build/annotationProcessors/utils/GeneratableElementIterator.java new file mode 100644 index 000000000..8f94c8afa --- /dev/null +++ b/build/annotationProcessors/utils/GeneratableElementIterator.java @@ -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."); + } +} diff --git a/build/annotationProcessors/utils/Utils.java b/build/annotationProcessors/utils/Utils.java new file mode 100644 index 000000000..aea88cfbf --- /dev/null +++ b/build/annotationProcessors/utils/Utils.java @@ -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()); + } +} |