|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.annotationProcessors.utils; |
|
6 |
|
7 import org.mozilla.gecko.annotationProcessors.AnnotationInfo; |
|
8 import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; |
|
9 |
|
10 import java.lang.annotation.Annotation; |
|
11 import java.lang.reflect.AnnotatedElement; |
|
12 import java.lang.reflect.InvocationTargetException; |
|
13 import java.lang.reflect.Member; |
|
14 import java.lang.reflect.Method; |
|
15 import java.util.Arrays; |
|
16 import java.util.Iterator; |
|
17 |
|
18 /** |
|
19 * Iterator over the methods in a given method list which have the WrappedJNIMethod |
|
20 * annotation. Returns an object containing both the annotation (Which may contain interesting |
|
21 * parameters) and the argument. |
|
22 */ |
|
23 public class GeneratableElementIterator implements Iterator<AnnotatableEntity> { |
|
24 private final Member[] mObjects; |
|
25 private AnnotatableEntity mNextReturnValue; |
|
26 private int mElementIndex; |
|
27 |
|
28 private boolean mIterateEveryEntry; |
|
29 |
|
30 public GeneratableElementIterator(Class<?> aClass) { |
|
31 // Get all the elements of this class as AccessibleObjects. |
|
32 Member[] aMethods = aClass.getDeclaredMethods(); |
|
33 Member[] aFields = aClass.getDeclaredFields(); |
|
34 Member[] aCtors = aClass.getConstructors(); |
|
35 |
|
36 // Shove them all into one buffer. |
|
37 Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length]; |
|
38 |
|
39 int offset = 0; |
|
40 System.arraycopy(aMethods, 0, objs, 0, aMethods.length); |
|
41 offset += aMethods.length; |
|
42 System.arraycopy(aFields, 0, objs, offset, aFields.length); |
|
43 offset += aFields.length; |
|
44 System.arraycopy(aCtors, 0, objs, offset, aCtors.length); |
|
45 |
|
46 // Sort the elements to ensure determinism. |
|
47 Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator()); |
|
48 mObjects = objs; |
|
49 |
|
50 // Check for "Wrap ALL the things" flag. |
|
51 for (Annotation annotation : aClass.getDeclaredAnnotations()) { |
|
52 final String annotationTypeName = annotation.annotationType().getName(); |
|
53 if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI")) { |
|
54 mIterateEveryEntry = true; |
|
55 break; |
|
56 } |
|
57 } |
|
58 |
|
59 findNextValue(); |
|
60 } |
|
61 |
|
62 /** |
|
63 * Find and cache the next appropriately annotated method, plus the annotation parameter, if |
|
64 * one exists. Otherwise cache null, so hasNext returns false. |
|
65 */ |
|
66 private void findNextValue() { |
|
67 while (mElementIndex < mObjects.length) { |
|
68 Member candidateElement = mObjects[mElementIndex]; |
|
69 mElementIndex++; |
|
70 for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) { |
|
71 // WrappedJNIMethod has parameters. Use Reflection to obtain them. |
|
72 Class<? extends Annotation> annotationType = annotation.annotationType(); |
|
73 final String annotationTypeName = annotationType.getName(); |
|
74 if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) { |
|
75 String stubName = null; |
|
76 boolean isStaticStub = false; |
|
77 boolean isMultithreadedStub = false; |
|
78 boolean noThrow = false; |
|
79 try { |
|
80 // Determine the explicitly-given name of the stub to generate, if any. |
|
81 final Method stubNameMethod = annotationType.getDeclaredMethod("stubName"); |
|
82 stubNameMethod.setAccessible(true); |
|
83 stubName = (String) stubNameMethod.invoke(annotation); |
|
84 |
|
85 // Detemine if the generated stub should be static. |
|
86 final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic"); |
|
87 staticStubMethod.setAccessible(true); |
|
88 isStaticStub = (Boolean) staticStubMethod.invoke(annotation); |
|
89 |
|
90 // Determine if the generated stub is to allow calls from multiple threads. |
|
91 final Method multithreadedStubMethod = annotationType.getDeclaredMethod("allowMultithread"); |
|
92 multithreadedStubMethod.setAccessible(true); |
|
93 isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation); |
|
94 |
|
95 // Determine if ignoring exceptions |
|
96 final Method noThrowMethod = annotationType.getDeclaredMethod("noThrow"); |
|
97 noThrowMethod.setAccessible(true); |
|
98 noThrow = (Boolean) noThrowMethod.invoke(annotation); |
|
99 |
|
100 } catch (NoSuchMethodException e) { |
|
101 System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?"); |
|
102 e.printStackTrace(System.err); |
|
103 System.exit(3); |
|
104 } catch (IllegalAccessException e) { |
|
105 System.err.println("IllegalAccessException reading fields on WrapElementForJNI annotation. Seems the semantics of Reflection have changed..."); |
|
106 e.printStackTrace(System.err); |
|
107 System.exit(4); |
|
108 } catch (InvocationTargetException e) { |
|
109 System.err.println("InvocationTargetException reading fields on WrapElementForJNI annotation. This really shouldn't happen."); |
|
110 e.printStackTrace(System.err); |
|
111 System.exit(5); |
|
112 } |
|
113 |
|
114 // If the method name was not explicitly given in the annotation generate one... |
|
115 if (stubName.isEmpty()) { |
|
116 String aMethodName = candidateElement.getName(); |
|
117 stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1); |
|
118 } |
|
119 |
|
120 AnnotationInfo annotationInfo = new AnnotationInfo( |
|
121 stubName, isStaticStub, isMultithreadedStub, noThrow); |
|
122 mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); |
|
123 return; |
|
124 } |
|
125 } |
|
126 |
|
127 // If no annotation found, we might be expected to generate anyway using default arguments, |
|
128 // thanks to the "Generate everything" annotation. |
|
129 if (mIterateEveryEntry) { |
|
130 AnnotationInfo annotationInfo = new AnnotationInfo( |
|
131 candidateElement.getName(), false, false, false); |
|
132 mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); |
|
133 return; |
|
134 } |
|
135 } |
|
136 mNextReturnValue = null; |
|
137 } |
|
138 |
|
139 @Override |
|
140 public boolean hasNext() { |
|
141 return mNextReturnValue != null; |
|
142 } |
|
143 |
|
144 @Override |
|
145 public AnnotatableEntity next() { |
|
146 AnnotatableEntity ret = mNextReturnValue; |
|
147 findNextValue(); |
|
148 return ret; |
|
149 } |
|
150 |
|
151 @Override |
|
152 public void remove() { |
|
153 throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported."); |
|
154 } |
|
155 } |