From 4f5b79570918b2775fba782e1237acfa1019c1d3 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Thu, 13 Nov 2014 12:47:24 -0600 Subject: [PATCH] Bug 1086693 - Part 5: Add a 'catchException' mode to JNI generator r=ckitching --- .../annotationProcessors/AnnotationInfo.java | 9 ++- build/annotationProcessors/CodeGenerator.java | 61 ++++++++++++++----- .../utils/GeneratableElementIterator.java | 10 ++- build/annotationProcessors/utils/Utils.java | 22 ++++++- .../WrapElementForJNI.java | 9 +++ 5 files changed, 90 insertions(+), 21 deletions(-) diff --git a/build/annotationProcessors/AnnotationInfo.java b/build/annotationProcessors/AnnotationInfo.java index 98fdb17d96fb..0f2da147d6a7 100644 --- a/build/annotationProcessors/AnnotationInfo.java +++ b/build/annotationProcessors/AnnotationInfo.java @@ -12,12 +12,19 @@ public class AnnotationInfo { public final boolean isMultithreaded; public final boolean noThrow; public final boolean narrowChars; + public final boolean catchException; public AnnotationInfo(String aWrapperName, boolean aIsMultithreaded, - boolean aNoThrow, boolean aNarrowChars) { + boolean aNoThrow, boolean aNarrowChars, boolean aCatchException) { wrapperName = aWrapperName; isMultithreaded = aIsMultithreaded; noThrow = aNoThrow; narrowChars = aNarrowChars; + catchException = aCatchException; + + if (!noThrow && catchException) { + // It doesn't make sense to have these together + throw new IllegalArgumentException("noThrow and catchException are not allowed together"); + } } } diff --git a/build/annotationProcessors/CodeGenerator.java b/build/annotationProcessors/CodeGenerator.java index b172f33e8f30..215b0009e8dc 100644 --- a/build/annotationProcessors/CodeGenerator.java +++ b/build/annotationProcessors/CodeGenerator.java @@ -111,8 +111,10 @@ public class CodeGenerator { Class returnType = theMethod.getReturnType(); // Get the C++ method signature for this method. - String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName, aMethodTuple.mAnnotationInfo.narrowChars); - String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, isFieldStatic, aMethodTuple.mAnnotationInfo.narrowChars); + String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, + mCClassName, aMethodTuple.mAnnotationInfo.narrowChars, aMethodTuple.mAnnotationInfo.catchException); + String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, + CMethodName, mCClassName, isFieldStatic, aMethodTuple.mAnnotationInfo.narrowChars, aMethodTuple.mAnnotationInfo.catchException); // Add the header signature to the header file. writeSignatureToHeader(headerSignature); @@ -121,7 +123,8 @@ public class CodeGenerator { writeMethodBody(implementationSignature, theMethod, mClassToWrap, aMethodTuple.mAnnotationInfo.isMultithreaded, aMethodTuple.mAnnotationInfo.noThrow, - aMethodTuple.mAnnotationInfo.narrowChars); + aMethodTuple.mAnnotationInfo.narrowChars, + aMethodTuple.mAnnotationInfo.catchException); } private void generateGetterOrSetterBody(Field aField, String aFieldName, boolean aIsFieldStatic, boolean isSetter, boolean aNarrowChars) { @@ -196,8 +199,8 @@ public class CodeGenerator { boolean isFieldFinal = Utils.isMemberFinal(theField); String getterName = "get" + CFieldName; - 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, isFieldStatic, aFieldTuple.mAnnotationInfo.narrowChars); + String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars, false); + String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, isFieldStatic, aFieldTuple.mAnnotationInfo.narrowChars, false); writeSignatureToHeader(getterHeaderSignature); @@ -211,8 +214,8 @@ public class CodeGenerator { Class[] setterArguments = new Class[]{fieldType}; - String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars); - String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, isFieldStatic, aFieldTuple.mAnnotationInfo.narrowChars); + String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName, aFieldTuple.mAnnotationInfo.narrowChars, false); + String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, isFieldStatic, aFieldTuple.mAnnotationInfo.narrowChars, false); writeSignatureToHeader(setterHeaderSignature); @@ -229,8 +232,10 @@ public class CodeGenerator { generateMemberCommon(theCtor, mCClassName, mClassToWrap); - 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); + String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, + mCClassName, aCtorTuple.mAnnotationInfo.narrowChars, aCtorTuple.mAnnotationInfo.catchException); + String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, + mCClassName, false, aCtorTuple.mAnnotationInfo.narrowChars, aCtorTuple.mAnnotationInfo.catchException); // Slice off the "void " from the start of the constructor declaration. headerSignature = headerSignature.substring(5); @@ -242,7 +247,8 @@ public class CodeGenerator { // Use the implementation signature to generate the method body... writeCtorBody(implementationSignature, theCtor, aCtorTuple.mAnnotationInfo.isMultithreaded, - aCtorTuple.mAnnotationInfo.noThrow); + aCtorTuple.mAnnotationInfo.noThrow, + aCtorTuple.mAnnotationInfo.catchException); if (theCtor.getParameterTypes().length == 0) { mHasEncounteredDefaultConstructor = true; @@ -258,7 +264,7 @@ public class CodeGenerator { String name = m.getName(); name = name.substring(0, 1).toUpperCase() + name.substring(1); - AnnotationInfo info = new AnnotationInfo(name, true, true, true); + AnnotationInfo info = new AnnotationInfo(name, true, true, true, true); AnnotatableEntity entity = new AnnotatableEntity(m, info); if (m instanceof Constructor) { generateConstructor(entity); @@ -408,8 +414,20 @@ public class CodeGenerator { return argumentContent; } + private void writeCatchException() { + wrapperMethodBodies.append( + " if (env->ExceptionCheck()) {\n" + + " env->ExceptionClear();\n" + + " if (aResult) {\n" + + " *aResult = NS_ERROR_FAILURE;\n" + + " }\n" + + " } else if (aResult) {\n" + + " *aResult = NS_OK;\n" + + " }\n\n"); + } + private void writeCtorBody(String implementationSignature, Constructor theCtor, - boolean aIsThreaded, boolean aNoThrow) { + boolean aIsThreaded, boolean aNoThrow, boolean aCatchException) { Class[] argumentTypes = theCtor.getParameterTypes(); writeFunctionStartupBoilerPlate(implementationSignature, aIsThreaded); @@ -443,9 +461,14 @@ public class CodeGenerator { wrapperMethodBodies.append(mMembersToIds.get(theCtor)) // Tack on the arguments, if any.. .append(argumentContent) - .append("), env);\n" + - " env->PopLocalFrame(nullptr);\n" + - "}\n"); + .append("), env);\n"); + + // Check for exception and set aResult + if (aCatchException) { + writeCatchException(); + } + + wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n}\n"); } /** @@ -458,7 +481,8 @@ public class CodeGenerator { */ private void writeMethodBody(String methodSignature, Method aMethod, Class aClass, boolean aIsMultithreaded, - boolean aNoThrow, boolean aNarrowChars) { + boolean aNoThrow, boolean aNarrowChars, + boolean aCatchException) { Class[] argumentTypes = aMethod.getParameterTypes(); Class returnType = aMethod.getReturnType(); @@ -526,6 +550,11 @@ public class CodeGenerator { wrapperMethodBodies.append(" AndroidBridge::HandleUncaughtException(env);\n"); } + // Check for exception and set aResult + if (aCatchException) { + writeCatchException(); + } + // If we're returning an object, pop the callee's stack frame extracting our ref as the return // value. if (isObjectReturningMethod) { diff --git a/build/annotationProcessors/utils/GeneratableElementIterator.java b/build/annotationProcessors/utils/GeneratableElementIterator.java index 6e7b1349fbb1..32dd2b963e05 100644 --- a/build/annotationProcessors/utils/GeneratableElementIterator.java +++ b/build/annotationProcessors/utils/GeneratableElementIterator.java @@ -76,6 +76,7 @@ public class GeneratableElementIterator implements Iterator { boolean isMultithreadedStub = false; boolean noThrow = false; boolean narrowChars = false; + boolean catchException = false; try { // Determine the explicitly-given name of the stub to generate, if any. final Method stubNameMethod = annotationType.getDeclaredMethod("stubName"); @@ -97,6 +98,11 @@ public class GeneratableElementIterator implements Iterator { narrowCharsMethod.setAccessible(true); narrowChars = (Boolean) narrowCharsMethod.invoke(annotation); + // Determine if we should catch exceptions + final Method catchExceptionMethod = annotationType.getDeclaredMethod("catchException"); + catchExceptionMethod.setAccessible(true); + catchException = (Boolean) catchExceptionMethod.invoke(annotation); + } catch (NoSuchMethodException e) { System.err.println("Unable to find expected field on WrapElementForJNI annotation. Did the signature change?"); e.printStackTrace(System.err); @@ -118,7 +124,7 @@ public class GeneratableElementIterator implements Iterator { } AnnotationInfo annotationInfo = new AnnotationInfo( - stubName, isMultithreadedStub, noThrow, narrowChars); + stubName, isMultithreadedStub, noThrow, narrowChars, catchException); mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); return; } @@ -128,7 +134,7 @@ public class GeneratableElementIterator implements Iterator { // 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; } diff --git a/build/annotationProcessors/utils/Utils.java b/build/annotationProcessors/utils/Utils.java index 89f70dc8b5fb..44876ebb4b99 100644 --- a/build/annotationProcessors/utils/Utils.java +++ b/build/annotationProcessors/utils/Utils.java @@ -388,7 +388,8 @@ public class Utils { * @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, boolean aNarrowChars) { + public static String getCImplementationMethodSignature(Class[] aArgumentTypes, Class aReturnType, + String aCMethodName, String aCClassName, boolean aNarrowChars, boolean aCatchException) { StringBuilder retBuffer = new StringBuilder(); retBuffer.append(getCReturnType(aReturnType, aNarrowChars)); @@ -410,6 +411,14 @@ public class Utils { retBuffer.append(", "); } } + + if (aCatchException) { + if (aArgumentTypes.length > 0) { + retBuffer.append(", "); + } + retBuffer.append("nsresult* aResult"); + } + retBuffer.append(')'); return retBuffer.toString(); } @@ -427,7 +436,8 @@ public class Utils { * @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, boolean aNarrowChars) { + public static String getCHeaderMethodSignature(Class[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class aReturnType, + String aCMethodName, String aCClassName, boolean aIsStaticStub, boolean aNarrowChars, boolean aCatchException) { StringBuilder retBuffer = new StringBuilder(); // Add the static keyword, if applicable. @@ -457,6 +467,14 @@ public class Utils { retBuffer.append(", "); } } + + if (aCatchException) { + if (aArgumentTypes.length > 0) { + retBuffer.append(", "); + } + retBuffer.append("nsresult* aResult = nullptr"); + } + retBuffer.append(')'); return retBuffer.toString(); } diff --git a/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java b/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java index 3036ecbf4954..a73d2d91b35f 100644 --- a/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java +++ b/mobile/android/base/mozglue/generatorannotations/WrapElementForJNI.java @@ -44,5 +44,14 @@ public @interface WrapElementForJNI { */ boolean noThrow() default false; + /** + * If set, uses UTF-8 strings + */ boolean narrowChars() default false; + + /** + * If set, the generated stub will catch any exception thrown and + * set a passed-in nsresult to NS_ERROR_FAILURE + */ + boolean catchException() default false; }