bug 1019836 - Support narrow char strings in android bridge r=snorp
authorBrad Lassey <blassey@mozilla.com>
Wed, 04 Jun 2014 15:04:12 -0400
changeset 205808 8fac243930da445352fd91c5d254b7253ba06d54
parent 205807 52068b669c2af0023292655c64a568a63e94608f
child 205809 3c54b7fe8941da0dc6859029cba1dbeb251be5e3
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1019836
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1019836 - Support narrow char strings in android bridge r=snorp
build/annotationProcessors/AnnotationInfo.java
build/annotationProcessors/CodeGenerator.java
build/annotationProcessors/utils/GeneratableElementIterator.java
build/annotationProcessors/utils/Utils.java
mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
widget/android/AndroidJavaWrappers.cpp
widget/android/AndroidJavaWrappers.h
--- a/build/annotationProcessors/AnnotationInfo.java
+++ b/build/annotationProcessors/AnnotationInfo.java
@@ -7,17 +7,19 @@ package org.mozilla.gecko.annotationProc
 /**
  * Object holding annotation data. Used by GeneratableElementIterator.
  */
 public class AnnotationInfo {
     public final String wrapperName;
     public final boolean isStatic;
     public final boolean isMultithreaded;
     public final boolean noThrow;
+    public final boolean narrowChars;
 
     public AnnotationInfo(String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded,
-                          boolean aNoThrow) {
+                          boolean aNoThrow, boolean aNarrowChars) {
         wrapperName = aWrapperName;
         isStatic = aIsStatic;
         isMultithreaded = aIsMultithreaded;
         noThrow = aNoThrow;
+	narrowChars = aNarrowChars;
     }
 }
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -98,47 +98,48 @@ public class CodeGenerator {
 
         boolean isFieldStatic = Utils.isMemberStatic(theMethod);
         boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic;
 
         Class<?>[] parameterTypes = theMethod.getParameterTypes();
         Class<?> returnType = theMethod.getReturnType();
 
         // Get the C++ method signature for this method.
-        String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName);
-        String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic);
+        String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName, aMethodTuple.mAnnotationInfo.narrowChars);
+        String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic, aMethodTuple.mAnnotationInfo.narrowChars);
 
         // Add the header signature to the header file.
         writeSignatureToHeader(headerSignature);
 
         // Use the implementation signature to generate the method body...
         writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap,
             aMethodTuple.mAnnotationInfo.isStatic,
             aMethodTuple.mAnnotationInfo.isMultithreaded,
-            aMethodTuple.mAnnotationInfo.noThrow);
+            aMethodTuple.mAnnotationInfo.noThrow,
+            aMethodTuple.mAnnotationInfo.narrowChars);
     }
 
-    private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) {
+    private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter, boolean aNarrowChars) {
         StringBuilder argumentContent = null;
 
         if (isSetter) {
             Class<?>[] setterArguments = new Class<?>[]{aFieldType};
             // Marshall the argument..
             argumentContent = getArgumentMarshalling(setterArguments);
         }
 
         boolean isObjectReturningMethod = Utils.isObjectType(aFieldType);
         wrapperMethodBodies.append("    ");
         if (isSetter) {
             wrapperMethodBodies.append("env->Set");
         } else {
             wrapperMethodBodies.append("return ");
 
             if (isObjectReturningMethod) {
-                wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">(");
+                wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType, aNarrowChars)).append(">(");
             }
 
             wrapperMethodBodies.append("env->Get");
         }
 
         if (aIsFieldStatic) {
             wrapperMethodBodies.append("Static");
         }
@@ -179,51 +180,51 @@ public class CodeGenerator {
 
         generateMemberCommon(theField, CFieldName, mClassToWrap);
 
         boolean isFieldStatic = Utils.isMemberStatic(theField);
         boolean isFieldFinal = Utils.isMemberFinal(theField);
         boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic;
 
         String getterName = "get" + CFieldName;
-        String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName);
-        String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic);
+        String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars);
+        String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic, aFieldTuple.mAnnotationInfo.narrowChars);
 
         writeSignatureToHeader(getterHeaderSignature);
 
         writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true);
 
-        generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false);
+        generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false, aFieldTuple.mAnnotationInfo.narrowChars);
 
         // If field not final, also generate a setter function.
         if (!isFieldFinal) {
             String setterName = "set" + CFieldName;
 
             Class<?>[] setterArguments = new Class<?>[]{fieldType};
 
-            String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName);
-            String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic);
+            String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars);
+            String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic, aFieldTuple.mAnnotationInfo.narrowChars);
 
             writeSignatureToHeader(setterHeaderSignature);
 
             writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true);
 
-            generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true);
+            generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true, aFieldTuple.mAnnotationInfo.narrowChars);
         }
     }
 
     public void generateConstructor(AnnotatableEntity aCtorTuple) {
         // Unpack the tuple and extract some useful fields from the Method..
         Constructor theCtor = aCtorTuple.getConstructor();
         String CMethodName = mCClassName;
 
         generateMemberCommon(theCtor, mCClassName, mClassToWrap);
 
-        String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName);
-        String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false);
+        String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName, aCtorTuple.mAnnotationInfo.narrowChars);
+        String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false, aCtorTuple.mAnnotationInfo.narrowChars);
 
         // Slice off the "void " from the start of the constructor declaration.
         headerSignature = headerSignature.substring(5);
         implementationSignature = implementationSignature.substring(5);
 
         // Add the header signatures to the header file.
         writeSignatureToHeader(headerSignature);
 
@@ -421,17 +422,17 @@ public class CodeGenerator {
      * @param methodSignature The previously-generated C++ method signature for the method to be
      *                        generated.
      * @param aCMethodName    The C++ method name for the method to be generated.
      * @param aMethod         The Java method to be wrapped by the C++ method being generated.
      * @param aClass          The Java class to which the method belongs.
      */
     private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod,
             Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded,
-            boolean aNoThrow) {
+				 boolean aNoThrow, boolean aNarrowChars) {
         Class<?>[] argumentTypes = aMethod.getParameterTypes();
         Class<?> returnType = aMethod.getReturnType();
 
         writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded);
 
         boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType);
 
         writeFramePushBoilerplate(aMethod, isObjectReturningMethod, aNoThrow);
@@ -450,17 +451,17 @@ public class CodeGenerator {
         }
 
         // Allocate a temporary variable to hold the return type from Java.
         wrapperMethodBodies.append("    ");
         if (!returnType.getCanonicalName().equals("void")) {
             if (isObjectReturningMethod) {
                 wrapperMethodBodies.append("jobject");
             } else {
-                wrapperMethodBodies.append(Utils.getCReturnType(returnType));
+                wrapperMethodBodies.append(Utils.getCReturnType(returnType, aNarrowChars));
             }
             wrapperMethodBodies.append(" temp = ");
         }
 
         boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod);
 
         // The call into Java
         wrapperMethodBodies.append("env->")
@@ -489,18 +490,18 @@ public class CodeGenerator {
         if (!aNoThrow) {
             wrapperMethodBodies.append("    AndroidBridge::HandleUncaughtException(env);\n");
         }
 
         // If we're returning an object, pop the callee's stack frame extracting our ref as the return
         // value.
         if (isObjectReturningMethod) {
             wrapperMethodBodies.append("    ")
-                               .append(Utils.getCReturnType(returnType))
-                               .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
+		.append(Utils.getCReturnType(returnType, aNarrowChars))
+		.append(" ret = static_cast<").append(Utils.getCReturnType(returnType, aNarrowChars)).append(">(env->PopLocalFrame(temp));\n" +
                                        "    return ret;\n");
         } else if (!returnType.getCanonicalName().equals("void")) {
             // If we're a primitive-returning function, just return the directly-obtained primative
             // from the call to Java.
             wrapperMethodBodies.append("    env->PopLocalFrame(nullptr);\n" +
                                        "    return temp;\n");
         } else {
             // If we don't return anything, just pop the stack frame and move on with life.
--- a/build/annotationProcessors/utils/GeneratableElementIterator.java
+++ b/build/annotationProcessors/utils/GeneratableElementIterator.java
@@ -71,16 +71,17 @@ public class GeneratableElementIterator 
                 // WrappedJNIMethod has parameters. Use Reflection to obtain them.
                 Class<? extends Annotation> annotationType = annotation.annotationType();
                 final String annotationTypeName = annotationType.getName();
                 if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
                     String stubName = null;
                     boolean isStaticStub = false;
                     boolean isMultithreadedStub = false;
                     boolean noThrow = false;
+		    boolean narrowChars = false;
                     try {
                         // 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);
 
                         // Detemine if the generated stub should be static.
                         final Method staticStubMethod = annotationType.getDeclaredMethod("generateStatic");
@@ -92,16 +93,21 @@ public class GeneratableElementIterator 
                         multithreadedStubMethod.setAccessible(true);
                         isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
 
                         // Determine if ignoring exceptions
                         final Method noThrowMethod = annotationType.getDeclaredMethod("noThrow");
                         noThrowMethod.setAccessible(true);
                         noThrow = (Boolean) noThrowMethod.invoke(annotation);
 
+                        // Determine if strings should be wide or narrow
+                        final Method narrowCharsMethod = annotationType.getDeclaredMethod("narrowChars");
+                        narrowCharsMethod.setAccessible(true);
+                        narrowChars = (Boolean) narrowCharsMethod.invoke(annotation);
+
                     } catch (NoSuchMethodException e) {
                         System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?");
                         e.printStackTrace(System.err);
                         System.exit(3);
                     } catch (IllegalAccessException e) {
                         System.err.println("IllegalAccessException reading fields on WrapElementForJNI annotation. Seems the semantics of Reflection have changed...");
                         e.printStackTrace(System.err);
                         System.exit(4);
@@ -113,27 +119,27 @@ public class GeneratableElementIterator 
 
                     // If the method name was not explicitly given in the annotation generate one...
                     if (stubName.isEmpty()) {
                         String aMethodName = candidateElement.getName();
                         stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
                     }
 
                     AnnotationInfo annotationInfo = new AnnotationInfo(
-                        stubName, isStaticStub, isMultithreadedStub, noThrow);
+		        stubName, isStaticStub, isMultithreadedStub, noThrow, narrowChars);
                     mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
                     return;
                 }
             }
 
             // 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(
-                    candidateElement.getName(), false, false, false);
+		    candidateElement.getName(), false, false, false, false);
                 mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
                 return;
             }
         }
         mNextReturnValue = null;
     }
 
     @Override
--- a/build/annotationProcessors/utils/Utils.java
+++ b/build/annotationProcessors/utils/Utils.java
@@ -132,17 +132,17 @@ public class Utils {
 
     /**
      * Get the C type corresponding to the provided type parameter. Used for generating argument
      * types for the wrapper method.
      *
      * @param type Class to determine the corresponding JNI type for.
      * @return true if the type an object type, false otherwise.
      */
-    public static String getCParameterType(Class<?> type) {
+    public static String getCParameterType(Class<?> type, boolean aNarrowChars) {
         String name = type.getCanonicalName();
         if (sBasicCTypes.containsKey(name)) {
             return sBasicCTypes.get(name);
         }
         // Are we dealing with an array type?
         int len = name.length();
         if (name.endsWith("[]")) {
             // Determine if it is a 2D array - these map to jobjectArrays
@@ -156,16 +156,19 @@ public class Utils {
                 }
                 return "jobjectArray";
             }
         }
         // Not an array type, check the remaining possibilities before we fall back to jobject
 
         // Check for CharSequences (Strings and things that are string-like)
         if (isCharSequence(type)) {
+	    if (aNarrowChars) {
+		return "const nsACString&";
+	    }
             return "const nsAString&";
         }
 
         if (name.equals("java.lang.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 "jclass";
         }
@@ -176,22 +179,22 @@ public class Utils {
     }
 
     /**
      * For a given Java type, get the corresponding C++ type if we're returning it from a function.
      *
      * @param type The Java return type.
      * @return A string representation of the C++ return type.
      */
-    public static String getCReturnType(Class<?> type) {
+     public static String getCReturnType(Class<?> type, boolean aNarrowChars) {
         if (type.getCanonicalName().equals("java.lang.Void")) {
             return "void";
         }
-        String cParameterType = getCParameterType(type);
-        if (cParameterType.equals("const nsAString&")) {
+        String cParameterType = getCParameterType(type, aNarrowChars);
+        if (cParameterType.equals("const nsAString&") || cParameterType.equals("const nsACString&")) {
             return "jstring";
         } else {
             return cParameterType;
         }
     }
 
     /**
      * Gets the type-specific part of the  JNI function to use to get or set a field of a given type.
@@ -376,29 +379,29 @@ public class Utils {
      * generating header files and method bodies.
      *
      * @param aArgumentTypes Argument types of the Java method being wrapped.
      * @param aReturnType Return type of the Java method being wrapped.
      * @param aCMethodName Name of the method to generate in the C++ class.
      * @param aCClassName Name of the C++ class into which the method is declared.
      * @return The C++ method implementation signature for the method described.
      */
-    public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName) {
+    public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aNarrowChars) {
         StringBuilder retBuffer = new StringBuilder();
 
-        retBuffer.append(getCReturnType(aReturnType));
+        retBuffer.append(getCReturnType(aReturnType, aNarrowChars));
         retBuffer.append(' ');
         retBuffer.append(aCClassName);
         retBuffer.append("::");
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
         // Write argument types...
         for (int aT = 0; aT < aArgumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
+            retBuffer.append(getCParameterType(aArgumentTypes[aT], aNarrowChars));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
             if (aT != aArgumentTypes.length - 1) {
                 retBuffer.append(", ");
             }
@@ -415,33 +418,33 @@ public class Utils {
      * @param aArgumentAnnotations The annotations on the Java method arguments. Used to specify
      *                             default values etc.
      * @param aReturnType Return type of the Java method being wrapped.
      * @param aCMethodName Name of the method to generate in the C++ class.
      * @param aCClassName Name of the C++ class into which the method is declared.e
      * @param aIsStaticStub true if the generated C++ method should be static, false otherwise.
      * @return The generated C++ header method signature for the method described.
      */
-    public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub) {
+    public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub, boolean aNarrowChars) {
         StringBuilder retBuffer = new StringBuilder();
 
         // Add the static keyword, if applicable.
         if (aIsStaticStub) {
             retBuffer.append("static ");
         }
 
         // Write return type..
-        retBuffer.append(getCReturnType(aReturnType));
+        retBuffer.append(getCReturnType(aReturnType, aNarrowChars));
         retBuffer.append(' ');
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
         // Write argument types...
         for (int aT = 0; aT < aArgumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
+            retBuffer.append(getCParameterType(aArgumentTypes[aT], aNarrowChars));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
 
             // Append the default value, if there is one..
             retBuffer.append(getDefaultValueString(aArgumentTypes[aT], aArgumentAnnotations[aT]));
--- a/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
+++ b/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java
@@ -39,9 +39,11 @@ public @interface WrapElementForJNI {
      */
     boolean allowMultithread() default false;
 
     /**
      * If set, the generated stub will not handle uncaught exceptions.
      * Any exception must be handled or cleared by the code calling the stub.
      */
     boolean noThrow() default false;
+
+    boolean narrowChars() default false;
 }
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -1006,8 +1006,35 @@ nsJNIString::nsJNIString(jstring jstr, J
 
     if (len <= 0) {
         SetIsVoid(true);
     } else {
         Assign(reinterpret_cast<const char16_t*>(jCharPtr), len);
     }
     jni->ReleaseStringChars(jstr, jCharPtr);
 }
+
+nsJNICString::nsJNICString(jstring jstr, JNIEnv *jenv)
+{
+    if (!jstr) {
+        SetIsVoid(true);
+        return;
+    }
+    JNIEnv *jni = jenv;
+    if (!jni) {
+        jni = AndroidBridge::GetJNIEnv();
+    }
+    const char* jCharPtr = jni->GetStringUTFChars(jstr, nullptr);
+
+    if (!jCharPtr) {
+        SetIsVoid(true);
+        return;
+    }
+
+    jsize len = jni->GetStringUTFLength(jstr);
+
+    if (len <= 0) {
+        SetIsVoid(true);
+    } else {
+        Assign(jCharPtr, len);
+    }
+    jni->ReleaseStringUTFChars(jstr, jCharPtr);
+}
--- a/widget/android/AndroidJavaWrappers.h
+++ b/widget/android/AndroidJavaWrappers.h
@@ -745,11 +745,17 @@ public:
 };
 
 class nsJNIString : public nsString
 {
 public:
     nsJNIString(jstring jstr, JNIEnv *jenv);
 };
 
+class nsJNICString : public nsCString
+{
+public:
+    nsJNICString(jstring jstr, JNIEnv *jenv);
+};
+
 }
 
 #endif