Bug 1086693 - Part 5: Add a 'catchException' mode to JNI generator r=ckitching

This commit is contained in:
James Willcox 2014-11-13 12:47:24 -06:00
parent f793c98f19
commit 4f5b795709
5 changed files with 90 additions and 21 deletions

View File

@ -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");
}
}
}

View File

@ -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) {

View File

@ -76,6 +76,7 @@ public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
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<AnnotatableEntity> {
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<AnnotatableEntity> {
}
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<AnnotatableEntity> {
// 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;
}

View File

@ -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();
}

View File

@ -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;
}