Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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/. */
5 package org.mozilla.gecko.annotationProcessors.utils;
7 import java.lang.annotation.Annotation;
8 import java.lang.reflect.Constructor;
9 import java.lang.reflect.Field;
10 import java.lang.reflect.Member;
11 import java.lang.reflect.Method;
12 import java.lang.reflect.Modifier;
13 import java.util.HashMap;
15 /**
16 * A collection of utility methods used by CodeGenerator. Largely used for translating types.
17 */
18 public class Utils {
20 // A collection of lookup tables to simplify the functions to follow...
21 private static final HashMap<String, String> sBasicCTypes = new HashMap<String, String>();
23 static {
24 sBasicCTypes.put("void", "void");
25 sBasicCTypes.put("int", "int32_t");
26 sBasicCTypes.put("boolean", "bool");
27 sBasicCTypes.put("long", "int64_t");
28 sBasicCTypes.put("double", "jdouble");
29 sBasicCTypes.put("float", "jfloat");
30 sBasicCTypes.put("char", "uint16_t");
31 sBasicCTypes.put("byte", "int8_t");
32 sBasicCTypes.put("short", "int16_t");
33 }
35 private static final HashMap<String, String> sArrayCTypes = new HashMap<String, String>();
37 static {
38 sArrayCTypes.put("int", "jintArray");
39 sArrayCTypes.put("boolean", "jbooleanArray");
40 sArrayCTypes.put("long", "jlongArray");
41 sArrayCTypes.put("double", "jdoubleArray");
42 sArrayCTypes.put("float", "jfloatArray");
43 sArrayCTypes.put("char", "jcharArray");
44 sArrayCTypes.put("byte", "jbyteArray");
45 sArrayCTypes.put("short", "jshortArray");
46 }
48 private static final HashMap<String, String> sStaticCallTypes = new HashMap<String, String>();
50 static {
51 sStaticCallTypes.put("void", "CallStaticVoidMethod");
52 sStaticCallTypes.put("int", "CallStaticIntMethod");
53 sStaticCallTypes.put("boolean", "CallStaticBooleanMethod");
54 sStaticCallTypes.put("long", "CallStaticLongMethod");
55 sStaticCallTypes.put("double", "CallStaticDoubleMethod");
56 sStaticCallTypes.put("float", "CallStaticFloatMethod");
57 sStaticCallTypes.put("char", "CallStaticCharMethod");
58 sStaticCallTypes.put("byte", "CallStaticByteMethod");
59 sStaticCallTypes.put("short", "CallStaticShortMethod");
60 }
62 private static final HashMap<String, String> sInstanceCallTypes = new HashMap<String, String>();
64 static {
65 sInstanceCallTypes.put("void", "CallVoidMethod");
66 sInstanceCallTypes.put("int", "CallIntMethod");
67 sInstanceCallTypes.put("boolean", "CallBooleanMethod");
68 sInstanceCallTypes.put("long", "CallLongMethod");
69 sInstanceCallTypes.put("double", "CallDoubleMethod");
70 sInstanceCallTypes.put("float", "CallFloatMethod");
71 sInstanceCallTypes.put("char", "CallCharMethod");
72 sInstanceCallTypes.put("byte", "CallByteMethod");
73 sInstanceCallTypes.put("short", "CallShortMethod");
74 }
76 private static final HashMap<String, String> sFieldTypes = new HashMap<String, String>();
78 static {
79 sFieldTypes.put("int", "Int");
80 sFieldTypes.put("boolean", "Boolean");
81 sFieldTypes.put("long", "Long");
82 sFieldTypes.put("double", "Double");
83 sFieldTypes.put("float", "Float");
84 sFieldTypes.put("char", "Char");
85 sFieldTypes.put("byte", "Byte");
86 sFieldTypes.put("short", "Short");
87 }
89 private static final HashMap<String, String> sFailureReturns = new HashMap<String, String>();
91 static {
92 sFailureReturns.put("java.lang.Void", "");
93 sFailureReturns.put("void", "");
94 sFailureReturns.put("int", " 0");
95 sFailureReturns.put("boolean", " false");
96 sFailureReturns.put("long", " 0");
97 sFailureReturns.put("double", " 0.0");
98 sFailureReturns.put("float", " 0.0");
99 sFailureReturns.put("char", " 0");
100 sFailureReturns.put("byte", " 0");
101 sFailureReturns.put("short", " 0");
102 }
104 private static final HashMap<String, String> sCanonicalSignatureParts = new HashMap<String, String>();
106 static {
107 sCanonicalSignatureParts.put("java/lang/Void", "V");
108 sCanonicalSignatureParts.put("void", "V");
109 sCanonicalSignatureParts.put("int", "I");
110 sCanonicalSignatureParts.put("boolean", "Z");
111 sCanonicalSignatureParts.put("long", "J");
112 sCanonicalSignatureParts.put("double", "D");
113 sCanonicalSignatureParts.put("float", "F");
114 sCanonicalSignatureParts.put("char", "C");
115 sCanonicalSignatureParts.put("byte", "B");
116 sCanonicalSignatureParts.put("short", "S");
117 }
120 private static final HashMap<String, String> sDefaultParameterValues = new HashMap<String, String>();
122 static {
123 sDefaultParameterValues.put("int", "0");
124 sDefaultParameterValues.put("boolean", "false");
125 sDefaultParameterValues.put("long", "0");
126 sDefaultParameterValues.put("double", "0");
127 sDefaultParameterValues.put("float", "0.0");
128 sDefaultParameterValues.put("char", "0");
129 sDefaultParameterValues.put("byte", "0");
130 sDefaultParameterValues.put("short", "0");
131 }
133 /**
134 * Get the C type corresponding to the provided type parameter. Used for generating argument
135 * types for the wrapper method.
136 *
137 * @param type Class to determine the corresponding JNI type for.
138 * @return true if the type an object type, false otherwise.
139 */
140 public static String getCParameterType(Class<?> type) {
141 String name = type.getCanonicalName();
142 if (sBasicCTypes.containsKey(name)) {
143 return sBasicCTypes.get(name);
144 }
145 // Are we dealing with an array type?
146 int len = name.length();
147 if (name.endsWith("[]")) {
148 // Determine if it is a 2D array - these map to jobjectArrays
149 name = name.substring(0, len - 2);
150 if (name.endsWith("[]")) {
151 return "jobjectArray";
152 } else {
153 // Which flavour of Array is it?
154 if (sArrayCTypes.containsKey(name)) {
155 return sArrayCTypes.get(name);
156 }
157 return "jobjectArray";
158 }
159 }
160 // Not an array type, check the remaining possibilities before we fall back to jobject
162 // Check for CharSequences (Strings and things that are string-like)
163 if (isCharSequence(type)) {
164 return "const nsAString&";
165 }
167 if (name.equals("java.lang.Class")) {
168 // You're doing reflection on Java objects from inside C, returning Class objects
169 // to C, generating the corresponding code using this Java program. Really?!
170 return "jclass";
171 }
172 if (name.equals("java.lang.Throwable")) {
173 return "jthrowable";
174 }
175 return "jobject";
176 }
178 /**
179 * For a given Java type, get the corresponding C++ type if we're returning it from a function.
180 *
181 * @param type The Java return type.
182 * @return A string representation of the C++ return type.
183 */
184 public static String getCReturnType(Class<?> type) {
185 if (type.getCanonicalName().equals("java.lang.Void")) {
186 return "void";
187 }
188 String cParameterType = getCParameterType(type);
189 if (cParameterType.equals("const nsAString&")) {
190 return "jstring";
191 } else {
192 return cParameterType;
193 }
194 }
196 /**
197 * Gets the type-specific part of the JNI function to use to get or set a field of a given type.
198 *
199 * @param aFieldType The Java type of the field.
200 * @return A string representation of the JNI call function substring to use.
201 */
202 public static String getFieldType(Class<?> aFieldType) {
203 String name = aFieldType.getCanonicalName();
205 if (sFieldTypes.containsKey(name)) {
206 return sFieldTypes.get(name);
207 }
208 return "Object";
209 }
211 /**
212 * Gets the appropriate JNI call function to use to invoke a Java method with the given return
213 * type. This, plus a call postfix (Such as "A") forms a complete JNI call function name.
214 *
215 * @param aReturnType The Java return type of the method being generated.
216 * @param isStatic Boolean indicating if the underlying Java method is declared static.
217 * @return A string representation of the JNI call function prefix to use.
218 */
219 public static String getCallPrefix(Class<?> aReturnType, boolean isStatic) {
220 String name = aReturnType.getCanonicalName();
221 if (isStatic) {
222 if (sStaticCallTypes.containsKey(name)) {
223 return sStaticCallTypes.get(name);
224 }
225 return "CallStaticObjectMethod";
226 } else {
227 if (sInstanceCallTypes.containsKey(name)) {
228 return sInstanceCallTypes.get(name);
229 }
230 return "CallObjectMethod";
231 }
232 }
234 /**
235 * On failure, the generated method returns a null-esque value. This helper method gets the
236 * appropriate failure return value for a given Java return type, plus a leading space.
237 *
238 * @param type Java return type of method being generated
239 * @return String representation of the failure return value to be used in the generated code.
240 */
241 public static String getFailureReturnForType(Class<?> type) {
242 String name = type.getCanonicalName();
243 if (sFailureReturns.containsKey(name)) {
244 return sFailureReturns.get(name);
245 }
246 return " nullptr";
247 }
249 /**
250 * Helper method to get the type signature for methods, given argument and return type.
251 * Allows for the near-identical logic needed for constructors and methods to be shared.
252 * (Alas, constructor does not extend method)
253 *
254 * @param arguments Argument types of the underlying method.
255 * @param returnType Return type of the underlying method.
256 * @return The canonical Java type string for the method. eg. (IIIIFZ)Lorg/mozilla/gecko/gfx/ViewTransform;
257 */
258 private static String getTypeSignatureInternal(Class<?>[] arguments, Class<?> returnType) {
259 StringBuilder sb = new StringBuilder();
260 sb.append('(');
262 // For each argument, write its signature component to the buffer..
263 for (int i = 0; i < arguments.length; i++) {
264 writeTypeSignature(sb, arguments[i]);
265 }
266 sb.append(')');
268 // Write the return value's signature..
269 writeTypeSignature(sb, returnType);
270 return sb.toString();
271 }
273 /**
274 * Get the canonical JNI type signature for a Field.
275 *
276 * @param aField The field to generate a signature for.
277 * @return The canonical JNI type signature for this method.
278 */
279 protected static String getTypeSignatureStringForField(Field aField) {
280 StringBuilder sb = new StringBuilder();
281 writeTypeSignature(sb, aField.getType());
282 return sb.toString();
283 }
285 /**
286 * Get the canonical JNI type signature for a method.
287 *
288 * @param aMethod The method to generate a signature for.
289 * @return The canonical JNI type signature for this method.
290 */
291 protected static String getTypeSignatureStringForMethod(Method aMethod) {
292 Class<?>[] arguments = aMethod.getParameterTypes();
293 Class<?> returnType = aMethod.getReturnType();
294 return getTypeSignatureInternal(arguments, returnType);
295 }
297 /**
298 * Get the canonical JNI type signature for a Constructor.
299 *
300 * @param aConstructor The Constructor to generate a signature for.
301 * @return The canonical JNI type signature for this method.
302 */
303 protected static String getTypeSignatureStringForConstructor(Constructor aConstructor) {
304 Class<?>[] arguments = aConstructor.getParameterTypes();
305 return getTypeSignatureInternal(arguments, Void.class);
306 }
308 public static String getTypeSignatureStringForMember(Member aMember) {
309 if (aMember instanceof Method) {
310 return getTypeSignatureStringForMethod((Method) aMember);
311 } else if (aMember instanceof Field) {
312 return getTypeSignatureStringForField((Field) aMember);
313 } else {
314 return getTypeSignatureStringForConstructor((Constructor) aMember);
315 }
316 }
318 public static String getTypeSignatureString(Constructor aConstructor) {
319 Class<?>[] arguments = aConstructor.getParameterTypes();
320 StringBuilder sb = new StringBuilder();
321 sb.append('(');
323 // For each argument, write its signature component to the buffer..
324 for (int i = 0; i < arguments.length; i++) {
325 writeTypeSignature(sb, arguments[i]);
326 }
328 // Constructors always return Void.
329 sb.append(")V");
330 return sb.toString();
331 }
333 /**
334 * Helper method used by getTypeSignatureStringForMethod to build the signature. Write the subsignature
335 * of a given type into the buffer.
336 *
337 * @param sb The buffer to write into.
338 * @param c The type of the element to write the subsignature of.
339 */
340 private static void writeTypeSignature(StringBuilder sb, Class<?> c) {
341 String name = c.getCanonicalName().replaceAll("\\.", "/");
343 // Determine if this is an array type and, if so, peel away the array operators..
344 int len = name.length();
345 while (name.endsWith("[]")) {
346 sb.append('[');
347 name = name.substring(0, len - 2);
348 len = len - 2;
349 }
351 if (c.isArray()) {
352 c = c.getComponentType();
353 }
355 Class<?> containerClass = c.getDeclaringClass();
356 if (containerClass != null) {
357 // Is an inner class. Add the $ symbol.
358 final int lastSlash = name.lastIndexOf('/');
359 name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
360 }
362 // Look in the hashmap for the remainder...
363 if (sCanonicalSignatureParts.containsKey(name)) {
364 // It was a primitive type, so lookup was a success.
365 sb.append(sCanonicalSignatureParts.get(name));
366 } else {
367 // It was a reference type - generate.
368 sb.append('L');
369 sb.append(name);
370 sb.append(';');
371 }
372 }
374 /**
375 * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
376 * generating header files and method bodies.
377 *
378 * @param aArgumentTypes Argument types of the Java method being wrapped.
379 * @param aReturnType Return type of the Java method being wrapped.
380 * @param aCMethodName Name of the method to generate in the C++ class.
381 * @param aCClassName Name of the C++ class into which the method is declared.
382 * @return The C++ method implementation signature for the method described.
383 */
384 public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName) {
385 StringBuilder retBuffer = new StringBuilder();
387 retBuffer.append(getCReturnType(aReturnType));
388 retBuffer.append(' ');
389 retBuffer.append(aCClassName);
390 retBuffer.append("::");
391 retBuffer.append(aCMethodName);
392 retBuffer.append('(');
394 // Write argument types...
395 for (int aT = 0; aT < aArgumentTypes.length; aT++) {
396 retBuffer.append(getCParameterType(aArgumentTypes[aT]));
397 retBuffer.append(" a");
398 // We, imaginatively, call our arguments a1, a2, a3...
399 // The only way to preserve the names from Java would be to parse the
400 // Java source, which would be computationally hard.
401 retBuffer.append(aT);
402 if (aT != aArgumentTypes.length - 1) {
403 retBuffer.append(", ");
404 }
405 }
406 retBuffer.append(')');
407 return retBuffer.toString();
408 }
410 /**
411 * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
412 * generating header files and method bodies.
413 *
414 * @param aArgumentTypes Argument types of the Java method being wrapped.
415 * @param aArgumentAnnotations The annotations on the Java method arguments. Used to specify
416 * default values etc.
417 * @param aReturnType Return type of the Java method being wrapped.
418 * @param aCMethodName Name of the method to generate in the C++ class.
419 * @param aCClassName Name of the C++ class into which the method is declared.e
420 * @param aIsStaticStub true if the generated C++ method should be static, false otherwise.
421 * @return The generated C++ header method signature for the method described.
422 */
423 public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub) {
424 StringBuilder retBuffer = new StringBuilder();
426 // Add the static keyword, if applicable.
427 if (aIsStaticStub) {
428 retBuffer.append("static ");
429 }
431 // Write return type..
432 retBuffer.append(getCReturnType(aReturnType));
433 retBuffer.append(' ');
434 retBuffer.append(aCMethodName);
435 retBuffer.append('(');
437 // Write argument types...
438 for (int aT = 0; aT < aArgumentTypes.length; aT++) {
439 retBuffer.append(getCParameterType(aArgumentTypes[aT]));
440 retBuffer.append(" a");
441 // We, imaginatively, call our arguments a1, a2, a3...
442 // The only way to preserve the names from Java would be to parse the
443 // Java source, which would be computationally hard.
444 retBuffer.append(aT);
446 // Append the default value, if there is one..
447 retBuffer.append(getDefaultValueString(aArgumentTypes[aT], aArgumentAnnotations[aT]));
449 if (aT != aArgumentTypes.length - 1) {
450 retBuffer.append(", ");
451 }
452 }
453 retBuffer.append(')');
454 return retBuffer.toString();
455 }
457 /**
458 * If the given Annotation[] contains an OptionalGeneratedParameter annotation then return a
459 * string assigning an argument of type aArgumentType to the default value for that type.
460 * Otherwise, return the empty string.
461 *
462 * @param aArgumentType The type of the argument to consider.
463 * @param aArgumentAnnotations The annotations on the argument to consider.
464 * @return An appropriate string to append to the signature of this argument assigning it to a
465 * default value (Or not, as applicable).
466 */
467 public static String getDefaultValueString(Class<?> aArgumentType, Annotation[] aArgumentAnnotations) {
468 for (int i = 0; i < aArgumentAnnotations.length; i++) {
469 Class<? extends Annotation> annotationType = aArgumentAnnotations[i].annotationType();
470 final String annotationTypeName = annotationType.getName();
471 if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.OptionalGeneratedParameter")) {
472 return " = " + getDefaultParameterValueForType(aArgumentType);
473 }
474 }
475 return "";
476 }
478 /**
479 * Helper method to return an appropriate default parameter value for an argument of a given type.
480 * The lookup table contains values for primitive types and strings. All other object types default
481 * to null pointers.
482 *
483 * @param aArgumentType The parameter type for which a default value is desired.
484 * @return An appropriate string representation of the default value selected, for use in generated
485 * C++ code.
486 */
487 private static String getDefaultParameterValueForType(Class<?> aArgumentType) {
488 String typeName = aArgumentType.getCanonicalName();
489 if (sDefaultParameterValues.containsKey(typeName)) {
490 return sDefaultParameterValues.get(typeName);
491 } else if (isCharSequence(aArgumentType)) {
492 return "EmptyString()";
493 } else {
494 return "nullptr";
495 }
496 }
498 /**
499 * Helper method that returns the number of reference types in the arguments of m.
500 *
501 * @param aArgs The method arguments to consider.
502 * @return How many of the arguments of m are nonprimitive.
503 */
504 public static int enumerateReferenceArguments(Class<?>[] aArgs) {
505 int ret = 0;
506 for (int i = 0; i < aArgs.length; i++) {
507 String name = aArgs[i].getCanonicalName();
508 if (!sBasicCTypes.containsKey(name)) {
509 ret++;
510 }
511 }
512 return ret;
513 }
515 /**
516 * Helper method that returns true iff the given method has a string argument.
517 *
518 * @param m The method to consider.
519 * @return True if the given method has a string argument, false otherwise.
520 */
521 public static boolean hasStringArgument(Method m) {
522 Class<?>[] args = m.getParameterTypes();
523 for (int i = 0; i < args.length; i++) {
524 if (isCharSequence(args[i])) {
525 return true;
526 }
527 }
528 return false;
529 }
531 /**
532 * Write the argument array assignment line for the given argument type. Does not support array
533 * types.
534 *
535 * @param type Type of this argument according to the target Java method's signature.
536 * @param argName Wrapper function argument name corresponding to this argument.
537 */
538 public static String getArrayArgumentMashallingLine(Class<?> type, String argName) {
539 StringBuilder sb = new StringBuilder();
541 String name = type.getCanonicalName();
542 if (sCanonicalSignatureParts.containsKey(name)) {
543 sb.append(sCanonicalSignatureParts.get(name).toLowerCase());
544 sb.append(" = ").append(argName).append(";\n");
545 } else {
546 if (isCharSequence(type)) {
547 sb.append("l = AndroidBridge::NewJavaString(env, ").append(argName).append(");\n");
548 } else {
549 sb.append("l = ").append(argName).append(";\n");
550 }
551 }
553 return sb.toString();
554 }
556 /**
557 * Returns true if the type provided is an object type. Returns false otherwise
558 *
559 * @param aType The type to consider.
560 * @return true if the method provided is an object type, false otherwise.
561 */
562 public static boolean isObjectType(Class<?> aType) {
563 return !sBasicCTypes.containsKey(aType.getCanonicalName());
564 }
566 /**
567 * For a given Java class, get the name of the value in C++ which holds a reference to it.
568 *
569 * @param aClass Target Java class.
570 * @return The name of the C++ jclass entity referencing the given class.
571 */
572 public static String getClassReferenceName(Class<?> aClass) {
573 String className = aClass.getSimpleName();
574 return 'm' + className + "Class";
575 }
577 /**
578 * Generate a line to get a global reference to the Java class given.
579 *
580 * @param aClass The target Java class.
581 * @return The generated code to populate the reference to the class.
582 */
583 public static String getStartupLineForClass(Class<?> aClass) {
584 StringBuilder sb = new StringBuilder();
585 sb.append(" ");
586 sb.append(getClassReferenceName(aClass));
587 sb.append(" = getClassGlobalRef(\"");
589 String name = aClass.getCanonicalName().replaceAll("\\.", "/");
590 Class<?> containerClass = aClass.getDeclaringClass();
591 if (containerClass != null) {
592 // Is an inner class. Add the $ symbol.
593 final int lastSlash = name.lastIndexOf('/');
594 name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
595 }
597 sb.append(name);
598 sb.append("\");\n");
599 return sb.toString();
600 }
602 /**
603 * Helper method to determine if this object implements CharSequence
604 * @param aClass Class to check for CharSequence-esqueness
605 * @return True if the given class implements CharSequence, false otherwise.
606 */
607 public static boolean isCharSequence(Class<?> aClass) {
608 if (aClass.getCanonicalName().equals("java.lang.CharSequence")) {
609 return true;
610 }
611 Class<?>[] interfaces = aClass.getInterfaces();
612 for (Class<?> c : interfaces) {
613 if (c.getCanonicalName().equals("java.lang.CharSequence")) {
614 return true;
615 }
616 }
617 return false;
618 }
620 /**
621 * Helper method to read the modifier bits of the given method to determine if it is static.
622 * @param aMember The Member to check.
623 * @return true of the method is declared static, false otherwise.
624 */
625 public static boolean isMemberStatic(Member aMember) {
626 int aMethodModifiers = aMember.getModifiers();
627 return Modifier.isStatic(aMethodModifiers);
628 }
630 /**
631 * Helper method to read the modifier bits of the given method to determine if it is static.
632 * @param aMember The Member to check.
633 * @return true of the method is declared static, false otherwise.
634 */
635 public static boolean isMemberFinal(Member aMember) {
636 int aMethodModifiers = aMember.getModifiers();
637 return Modifier.isFinal(aMethodModifiers);
638 }
639 }