1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/build/annotationProcessors/CodeGenerator.java Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,620 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +package org.mozilla.gecko.annotationProcessors; 1.9 + 1.10 +import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; 1.11 +import org.mozilla.gecko.annotationProcessors.utils.Utils; 1.12 + 1.13 +import java.lang.annotation.Annotation; 1.14 +import java.lang.reflect.Constructor; 1.15 +import java.lang.reflect.Field; 1.16 +import java.lang.reflect.Member; 1.17 +import java.lang.reflect.Method; 1.18 +import java.lang.reflect.Modifier; 1.19 +import java.util.HashMap; 1.20 +import java.util.HashSet; 1.21 + 1.22 +public class CodeGenerator { 1.23 + private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; 1.24 + private static final Annotation[][] GETTER_ARGUMENT_ANNOTATIONS = new Annotation[0][0]; 1.25 + private static final Annotation[][] SETTER_ARGUMENT_ANNOTATIONS = new Annotation[1][0]; 1.26 + 1.27 + // Buffers holding the strings to ultimately be written to the output files. 1.28 + private final StringBuilder zeroingCode = new StringBuilder(); 1.29 + private final StringBuilder wrapperStartupCode = new StringBuilder(); 1.30 + private final StringBuilder wrapperMethodBodies = new StringBuilder(); 1.31 + private final StringBuilder headerPublic = new StringBuilder(); 1.32 + private final StringBuilder headerProtected = new StringBuilder(); 1.33 + 1.34 + private final HashSet<String> seenClasses = new HashSet<String>(); 1.35 + 1.36 + private final String mCClassName; 1.37 + 1.38 + private final Class<?> mClassToWrap; 1.39 + 1.40 + private boolean mHasEncounteredDefaultConstructor; 1.41 + 1.42 + // Used for creating unique names for method ID fields in the face of 1.43 + private final HashMap<Member, String> mMembersToIds = new HashMap<Member, String>(); 1.44 + private final HashSet<String> mTakenMemberNames = new HashSet<String>(); 1.45 + private int mNameMunger; 1.46 + 1.47 + public CodeGenerator(Class<?> aClass, String aGeneratedName) { 1.48 + mClassToWrap = aClass; 1.49 + mCClassName = aGeneratedName; 1.50 + 1.51 + // Write the file header things. Includes and so forth. 1.52 + // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with 1.53 + // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerPublic 1.54 + // with headerProtected. 1.55 + wrapperStartupCode.append("void ").append(mCClassName).append("::InitStubs(JNIEnv *jEnv) {\n" + 1.56 + " initInit();\n"); 1.57 + 1.58 + // Now we write the various GetStaticMethodID calls here... 1.59 + headerPublic.append("class ").append(mCClassName).append(" : public AutoGlobalWrappedJavaObject {\n" + 1.60 + "public:\n" + 1.61 + " static void InitStubs(JNIEnv *jEnv);\n"); 1.62 + headerProtected.append("protected:"); 1.63 + 1.64 + generateWrapperMethod(); 1.65 + } 1.66 + 1.67 + /** 1.68 + * Emit a static method which takes an instance of the class being wrapped and returns an instance 1.69 + * of the C++ wrapper class backed by that object. 1.70 + */ 1.71 + private void generateWrapperMethod() { 1.72 + headerPublic.append(" static ").append(mCClassName).append("* Wrap(jobject obj);\n" + 1.73 + " ").append(mCClassName).append("(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};\n"); 1.74 + 1.75 + wrapperMethodBodies.append("\n").append(mCClassName).append("* ").append(mCClassName).append("::Wrap(jobject obj) {\n" + 1.76 + " JNIEnv *env = GetJNIForThread();\n" + 1.77 + " ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" + 1.78 + " env->DeleteLocalRef(obj);\n" + 1.79 + " return ret;\n" + 1.80 + "}\n"); 1.81 + } 1.82 + 1.83 + private void generateMemberCommon(Member theMethod, String aCMethodName, Class<?> aClass) { 1.84 + ensureClassHeaderAndStartup(aClass); 1.85 + writeMemberIdField(theMethod, aCMethodName); 1.86 + writeStartupCode(theMethod); 1.87 + } 1.88 + 1.89 + /** 1.90 + * Append the appropriate generated code to the buffers for the method provided. 1.91 + * 1.92 + * @param aMethodTuple The Java method, plus annotation data. 1.93 + */ 1.94 + public void generateMethod(AnnotatableEntity aMethodTuple) { 1.95 + // Unpack the tuple and extract some useful fields from the Method.. 1.96 + Method theMethod = aMethodTuple.getMethod(); 1.97 + 1.98 + String CMethodName = aMethodTuple.mAnnotationInfo.wrapperName; 1.99 + 1.100 + generateMemberCommon(theMethod, CMethodName, mClassToWrap); 1.101 + 1.102 + boolean isFieldStatic = Utils.isMemberStatic(theMethod); 1.103 + boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic; 1.104 + 1.105 + Class<?>[] parameterTypes = theMethod.getParameterTypes(); 1.106 + Class<?> returnType = theMethod.getReturnType(); 1.107 + 1.108 + // Get the C++ method signature for this method. 1.109 + String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName); 1.110 + String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic); 1.111 + 1.112 + // Add the header signature to the header file. 1.113 + writeSignatureToHeader(headerSignature); 1.114 + 1.115 + // Use the implementation signature to generate the method body... 1.116 + writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap, 1.117 + aMethodTuple.mAnnotationInfo.isStatic, 1.118 + aMethodTuple.mAnnotationInfo.isMultithreaded, 1.119 + aMethodTuple.mAnnotationInfo.noThrow); 1.120 + } 1.121 + 1.122 + private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) { 1.123 + StringBuilder argumentContent = null; 1.124 + 1.125 + if (isSetter) { 1.126 + Class<?>[] setterArguments = new Class<?>[]{aFieldType}; 1.127 + // Marshall the argument.. 1.128 + argumentContent = getArgumentMarshalling(setterArguments); 1.129 + } 1.130 + 1.131 + boolean isObjectReturningMethod = Utils.isObjectType(aFieldType); 1.132 + wrapperMethodBodies.append(" "); 1.133 + if (isSetter) { 1.134 + wrapperMethodBodies.append("env->Set"); 1.135 + } else { 1.136 + wrapperMethodBodies.append("return "); 1.137 + 1.138 + if (isObjectReturningMethod) { 1.139 + wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">("); 1.140 + } 1.141 + 1.142 + wrapperMethodBodies.append("env->Get"); 1.143 + } 1.144 + 1.145 + if (aIsFieldStatic) { 1.146 + wrapperMethodBodies.append("Static"); 1.147 + } 1.148 + wrapperMethodBodies.append(Utils.getFieldType(aFieldType)) 1.149 + .append("Field("); 1.150 + 1.151 + // Static will require the class and the field id. Nonstatic, the object and the field id. 1.152 + if (aIsFieldStatic) { 1.153 + wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)); 1.154 + } else { 1.155 + wrapperMethodBodies.append("wrapped_obj"); 1.156 + } 1.157 + wrapperMethodBodies.append(", j") 1.158 + .append(aFieldName); 1.159 + if (isSetter) { 1.160 + wrapperMethodBodies.append(argumentContent); 1.161 + } 1.162 + 1.163 + if (!isSetter && isObjectReturningMethod) { 1.164 + wrapperMethodBodies.append(')'); 1.165 + } 1.166 + wrapperMethodBodies.append(");\n" + 1.167 + "}\n"); 1.168 + } 1.169 + 1.170 + public void generateField(AnnotatableEntity aFieldTuple) { 1.171 + Field theField = aFieldTuple.getField(); 1.172 + 1.173 + // Handles a peculiar case when dealing with enum types. We don't care about this field. 1.174 + // It just gets in the way and stops our code from compiling. 1.175 + if (theField.getName().equals("$VALUES")) { 1.176 + return; 1.177 + } 1.178 + 1.179 + String CFieldName = aFieldTuple.mAnnotationInfo.wrapperName; 1.180 + 1.181 + Class<?> fieldType = theField.getType(); 1.182 + 1.183 + generateMemberCommon(theField, CFieldName, mClassToWrap); 1.184 + 1.185 + boolean isFieldStatic = Utils.isMemberStatic(theField); 1.186 + boolean isFieldFinal = Utils.isMemberFinal(theField); 1.187 + boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic; 1.188 + 1.189 + String getterName = "get" + CFieldName; 1.190 + String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName); 1.191 + String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic); 1.192 + 1.193 + writeSignatureToHeader(getterHeaderSignature); 1.194 + 1.195 + writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true); 1.196 + 1.197 + generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false); 1.198 + 1.199 + // If field not final, also generate a setter function. 1.200 + if (!isFieldFinal) { 1.201 + String setterName = "set" + CFieldName; 1.202 + 1.203 + Class<?>[] setterArguments = new Class<?>[]{fieldType}; 1.204 + 1.205 + String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName); 1.206 + String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic); 1.207 + 1.208 + writeSignatureToHeader(setterHeaderSignature); 1.209 + 1.210 + writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true); 1.211 + 1.212 + generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true); 1.213 + } 1.214 + } 1.215 + 1.216 + public void generateConstructor(AnnotatableEntity aCtorTuple) { 1.217 + // Unpack the tuple and extract some useful fields from the Method.. 1.218 + Constructor theCtor = aCtorTuple.getConstructor(); 1.219 + String CMethodName = mCClassName; 1.220 + 1.221 + generateMemberCommon(theCtor, mCClassName, mClassToWrap); 1.222 + 1.223 + String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName); 1.224 + String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false); 1.225 + 1.226 + // Slice off the "void " from the start of the constructor declaration. 1.227 + headerSignature = headerSignature.substring(5); 1.228 + implementationSignature = implementationSignature.substring(5); 1.229 + 1.230 + // Add the header signatures to the header file. 1.231 + writeSignatureToHeader(headerSignature); 1.232 + 1.233 + // Use the implementation signature to generate the method body... 1.234 + writeCtorBody(implementationSignature, theCtor, 1.235 + aCtorTuple.mAnnotationInfo.isMultithreaded, 1.236 + aCtorTuple.mAnnotationInfo.noThrow); 1.237 + 1.238 + if (theCtor.getParameterTypes().length == 0) { 1.239 + mHasEncounteredDefaultConstructor = true; 1.240 + } 1.241 + } 1.242 + 1.243 + /** 1.244 + * Writes the appropriate header and startup code to ensure the existence of a reference to the 1.245 + * class specified. If this is already done, does nothing. 1.246 + * 1.247 + * @param aClass The target class. 1.248 + */ 1.249 + private void ensureClassHeaderAndStartup(Class<?> aClass) { 1.250 + String className = aClass.getCanonicalName(); 1.251 + if (seenClasses.contains(className)) { 1.252 + return; 1.253 + } 1.254 + 1.255 + zeroingCode.append("jclass ") 1.256 + .append(mCClassName) 1.257 + .append("::") 1.258 + .append(Utils.getClassReferenceName(aClass)) 1.259 + .append(" = 0;\n"); 1.260 + 1.261 + // Add a field to hold the reference... 1.262 + headerProtected.append("\n static jclass ") 1.263 + .append(Utils.getClassReferenceName(aClass)) 1.264 + .append(";\n"); 1.265 + 1.266 + // Add startup code to populate it.. 1.267 + wrapperStartupCode.append('\n') 1.268 + .append(Utils.getStartupLineForClass(aClass)); 1.269 + 1.270 + seenClasses.add(className); 1.271 + } 1.272 + 1.273 + /** 1.274 + * Write out the function startup boilerplate for the method described. Check for environment 1.275 + * existence, 1.276 + * @param methodSignature 1.277 + * @param returnType 1.278 + * @param aIsStatic 1.279 + * @param aIsThreaded 1.280 + */ 1.281 + private void writeFunctionStartupBoilerPlate(String methodSignature, Class<?> returnType, boolean aIsStatic, boolean aIsThreaded) { 1.282 + // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc. 1.283 + wrapperMethodBodies.append('\n') 1.284 + .append(methodSignature) 1.285 + .append(" {\n"); 1.286 + 1.287 + wrapperMethodBodies.append(" JNIEnv *env = "); 1.288 + if (!aIsThreaded) { 1.289 + wrapperMethodBodies.append("AndroidBridge::GetJNIEnv();\n"); 1.290 + } else { 1.291 + wrapperMethodBodies.append("GetJNIForThread();\n"); 1.292 + } 1.293 + } 1.294 + 1.295 + /** 1.296 + * Write out the appropriate JNI frame pushing boilerplate for a call to the member provided ( 1.297 + * which must be a constructor or method). 1.298 + * 1.299 + * @param aMethod A constructor/method being wrapped. 1.300 + * @param aIsObjectReturningMethod Does the method being wrapped return an object? 1.301 + */ 1.302 + private void writeFramePushBoilerplate(Member aMethod, 1.303 + boolean aIsObjectReturningMethod, boolean aNoThrow) { 1.304 + if (aMethod instanceof Field) { 1.305 + throw new IllegalArgumentException("Tried to push frame for a FIELD?!"); 1.306 + } 1.307 + 1.308 + Method m; 1.309 + Constructor c; 1.310 + 1.311 + Class<?> returnType; 1.312 + 1.313 + int localReferencesNeeded; 1.314 + if (aMethod instanceof Method) { 1.315 + m = (Method) aMethod; 1.316 + returnType = m.getReturnType(); 1.317 + localReferencesNeeded = Utils.enumerateReferenceArguments(m.getParameterTypes()); 1.318 + } else { 1.319 + c = (Constructor) aMethod; 1.320 + returnType = Void.class; 1.321 + localReferencesNeeded = Utils.enumerateReferenceArguments(c.getParameterTypes()); 1.322 + } 1.323 + 1.324 + // Determine the number of local refs required for our local frame.. 1.325 + // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values. 1.326 + if (aIsObjectReturningMethod) { 1.327 + localReferencesNeeded++; 1.328 + } 1.329 + wrapperMethodBodies.append( 1.330 + " if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n"); 1.331 + if (!aNoThrow) { 1.332 + wrapperMethodBodies.append( 1.333 + " AndroidBridge::HandleUncaughtException(env);\n" + 1.334 + " MOZ_ASSUME_UNREACHABLE(\"Exception should have caused crash.\");\n"); 1.335 + } else { 1.336 + wrapperMethodBodies.append( 1.337 + " return").append(Utils.getFailureReturnForType(returnType)).append(";\n"); 1.338 + } 1.339 + wrapperMethodBodies.append( 1.340 + " }\n\n"); 1.341 + } 1.342 + 1.343 + private StringBuilder getArgumentMarshalling(Class<?>[] argumentTypes) { 1.344 + StringBuilder argumentContent = new StringBuilder(); 1.345 + 1.346 + // If we have >2 arguments, use the jvalue[] calling approach. 1.347 + argumentContent.append(", "); 1.348 + if (argumentTypes.length > 2) { 1.349 + wrapperMethodBodies.append(" jvalue args[").append(argumentTypes.length).append("];\n"); 1.350 + for (int aT = 0; aT < argumentTypes.length; aT++) { 1.351 + wrapperMethodBodies.append(" args[").append(aT).append("].") 1.352 + .append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT)); 1.353 + } 1.354 + 1.355 + // The only argument is the array of arguments. 1.356 + argumentContent.append("args"); 1.357 + wrapperMethodBodies.append('\n'); 1.358 + } else { 1.359 + // Otherwise, use the vanilla calling approach. 1.360 + boolean needsNewline = false; 1.361 + for (int aT = 0; aT < argumentTypes.length; aT++) { 1.362 + // If the argument is a string-esque type, create a jstring from it, otherwise 1.363 + // it can be passed directly. 1.364 + if (Utils.isCharSequence(argumentTypes[aT])) { 1.365 + wrapperMethodBodies.append(" jstring j").append(aT).append(" = AndroidBridge::NewJavaString(env, a").append(aT).append(");\n"); 1.366 + needsNewline = true; 1.367 + // Ensure we refer to the newly constructed Java string - not to the original 1.368 + // parameter to the wrapper function. 1.369 + argumentContent.append('j').append(aT); 1.370 + } else { 1.371 + argumentContent.append('a').append(aT); 1.372 + } 1.373 + if (aT != argumentTypes.length - 1) { 1.374 + argumentContent.append(", "); 1.375 + } 1.376 + } 1.377 + if (needsNewline) { 1.378 + wrapperMethodBodies.append('\n'); 1.379 + } 1.380 + } 1.381 + 1.382 + return argumentContent; 1.383 + } 1.384 + 1.385 + private void writeCtorBody(String implementationSignature, Constructor theCtor, 1.386 + boolean aIsThreaded, boolean aNoThrow) { 1.387 + Class<?>[] argumentTypes = theCtor.getParameterTypes(); 1.388 + 1.389 + writeFunctionStartupBoilerPlate(implementationSignature, Void.class, false, aIsThreaded); 1.390 + 1.391 + writeFramePushBoilerplate(theCtor, false, aNoThrow); 1.392 + 1.393 + // Marshall arguments for this constructor, if any... 1.394 + boolean hasArguments = argumentTypes.length != 0; 1.395 + 1.396 + StringBuilder argumentContent = new StringBuilder(); 1.397 + if (hasArguments) { 1.398 + argumentContent = getArgumentMarshalling(argumentTypes); 1.399 + } 1.400 + 1.401 + // The call into Java 1.402 + wrapperMethodBodies.append(" Init(env->NewObject"); 1.403 + if (argumentTypes.length > 2) { 1.404 + wrapperMethodBodies.append('A'); 1.405 + } 1.406 + 1.407 + wrapperMethodBodies.append('('); 1.408 + 1.409 + 1.410 + // Call takes class id, method id of constructor method, then arguments. 1.411 + wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)).append(", "); 1.412 + 1.413 + wrapperMethodBodies.append(mMembersToIds.get(theCtor)) 1.414 + // Tack on the arguments, if any.. 1.415 + .append(argumentContent) 1.416 + .append("), env);\n" + 1.417 + " env->PopLocalFrame(nullptr);\n" + 1.418 + "}\n"); 1.419 + } 1.420 + 1.421 + /** 1.422 + * Generates the method body of the C++ wrapper function for the Java method indicated. 1.423 + * 1.424 + * @param methodSignature The previously-generated C++ method signature for the method to be 1.425 + * generated. 1.426 + * @param aCMethodName The C++ method name for the method to be generated. 1.427 + * @param aMethod The Java method to be wrapped by the C++ method being generated. 1.428 + * @param aClass The Java class to which the method belongs. 1.429 + */ 1.430 + private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod, 1.431 + Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded, 1.432 + boolean aNoThrow) { 1.433 + Class<?>[] argumentTypes = aMethod.getParameterTypes(); 1.434 + Class<?> returnType = aMethod.getReturnType(); 1.435 + 1.436 + writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded); 1.437 + 1.438 + boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType); 1.439 + 1.440 + writeFramePushBoilerplate(aMethod, isObjectReturningMethod, aNoThrow); 1.441 + 1.442 + // Marshall arguments, if we have any. 1.443 + boolean hasArguments = argumentTypes.length != 0; 1.444 + 1.445 + // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the 1.446 + // argument list while building this line. In the coming code block, we simultaneously 1.447 + // construct any argument marshalling code (Creation of jstrings, placement of arguments 1.448 + // into an argument array, etc. and the actual argument list passed to the function (in 1.449 + // argumentContent). 1.450 + StringBuilder argumentContent = new StringBuilder(); 1.451 + if (hasArguments) { 1.452 + argumentContent = getArgumentMarshalling(argumentTypes); 1.453 + } 1.454 + 1.455 + // Allocate a temporary variable to hold the return type from Java. 1.456 + wrapperMethodBodies.append(" "); 1.457 + if (!returnType.getCanonicalName().equals("void")) { 1.458 + if (isObjectReturningMethod) { 1.459 + wrapperMethodBodies.append("jobject"); 1.460 + } else { 1.461 + wrapperMethodBodies.append(Utils.getCReturnType(returnType)); 1.462 + } 1.463 + wrapperMethodBodies.append(" temp = "); 1.464 + } 1.465 + 1.466 + boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod); 1.467 + 1.468 + // The call into Java 1.469 + wrapperMethodBodies.append("env->") 1.470 + .append(Utils.getCallPrefix(returnType, isStaticJavaMethod)); 1.471 + if (argumentTypes.length > 2) { 1.472 + wrapperMethodBodies.append('A'); 1.473 + } 1.474 + 1.475 + wrapperMethodBodies.append('('); 1.476 + // If the underlying Java method is nonstatic, we provide the target object to the JNI. 1.477 + if (!isStaticJavaMethod) { 1.478 + wrapperMethodBodies.append("wrapped_obj, "); 1.479 + } else { 1.480 + // If this is a static underlying Java method, we need to use the class reference in our 1.481 + // call. 1.482 + wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", "); 1.483 + } 1.484 + 1.485 + wrapperMethodBodies.append(mMembersToIds.get(aMethod)); 1.486 + 1.487 + // Tack on the arguments, if any.. 1.488 + wrapperMethodBodies.append(argumentContent) 1.489 + .append(");\n"); 1.490 + 1.491 + // Check for exception and crash if any... 1.492 + if (!aNoThrow) { 1.493 + wrapperMethodBodies.append(" AndroidBridge::HandleUncaughtException(env);\n"); 1.494 + } 1.495 + 1.496 + // If we're returning an object, pop the callee's stack frame extracting our ref as the return 1.497 + // value. 1.498 + if (isObjectReturningMethod) { 1.499 + wrapperMethodBodies.append(" ") 1.500 + .append(Utils.getCReturnType(returnType)) 1.501 + .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" + 1.502 + " return ret;\n"); 1.503 + } else if (!returnType.getCanonicalName().equals("void")) { 1.504 + // If we're a primitive-returning function, just return the directly-obtained primative 1.505 + // from the call to Java. 1.506 + wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n" + 1.507 + " return temp;\n"); 1.508 + } else { 1.509 + // If we don't return anything, just pop the stack frame and move on with life. 1.510 + wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n"); 1.511 + } 1.512 + wrapperMethodBodies.append("}\n"); 1.513 + } 1.514 + 1.515 + /** 1.516 + * Generates the code to get the id of the given member on startup. 1.517 + * 1.518 + * @param aMember The Java member being wrapped. 1.519 + */ 1.520 + private void writeStartupCode(Member aMember) { 1.521 + wrapperStartupCode.append(" ") 1.522 + .append(mMembersToIds.get(aMember)) 1.523 + .append(" = get"); 1.524 + if (Utils.isMemberStatic(aMember)) { 1.525 + wrapperStartupCode.append("Static"); 1.526 + } 1.527 + 1.528 + boolean isField = aMember instanceof Field; 1.529 + if (isField) { 1.530 + wrapperStartupCode.append("Field(\""); 1.531 + } else { 1.532 + wrapperStartupCode.append("Method(\""); 1.533 + } 1.534 + if (aMember instanceof Constructor) { 1.535 + wrapperStartupCode.append("<init>"); 1.536 + } else { 1.537 + wrapperStartupCode.append(aMember.getName()); 1.538 + } 1.539 + 1.540 + wrapperStartupCode.append("\", \"") 1.541 + .append(Utils.getTypeSignatureStringForMember(aMember)) 1.542 + .append("\");\n"); 1.543 + } 1.544 + 1.545 + private void writeZeroingFor(Member aMember, final String aMemberName) { 1.546 + if (aMember instanceof Field) { 1.547 + zeroingCode.append("jfieldID "); 1.548 + } else { 1.549 + zeroingCode.append("jmethodID "); 1.550 + } 1.551 + zeroingCode.append(mCClassName) 1.552 + .append("::") 1.553 + .append(aMemberName) 1.554 + .append(" = 0;\n"); 1.555 + } 1.556 + 1.557 + /** 1.558 + * Write the field declaration for the C++ id field of the given member. 1.559 + * 1.560 + * @param aMember Member for which an id field needs to be generated. 1.561 + */ 1.562 + private void writeMemberIdField(Member aMember, final String aCMethodName) { 1.563 + String memberName = 'j'+ aCMethodName; 1.564 + 1.565 + if (aMember instanceof Field) { 1.566 + headerProtected.append(" static jfieldID "); 1.567 + } else { 1.568 + headerProtected.append(" static jmethodID "); 1.569 + } 1.570 + 1.571 + while(mTakenMemberNames.contains(memberName)) { 1.572 + memberName = 'j' + aCMethodName + mNameMunger; 1.573 + mNameMunger++; 1.574 + } 1.575 + 1.576 + writeZeroingFor(aMember, memberName); 1.577 + mMembersToIds.put(aMember, memberName); 1.578 + mTakenMemberNames.add(memberName); 1.579 + 1.580 + headerProtected.append(memberName) 1.581 + .append(";\n"); 1.582 + } 1.583 + 1.584 + /** 1.585 + * Helper function to add a provided method signature to the public section of the generated header. 1.586 + * 1.587 + * @param aSignature The header to add. 1.588 + */ 1.589 + private void writeSignatureToHeader(String aSignature) { 1.590 + headerPublic.append(" ") 1.591 + .append(aSignature) 1.592 + .append(";\n"); 1.593 + } 1.594 + 1.595 + /** 1.596 + * Get the finalised bytes to go into the generated wrappers file. 1.597 + * 1.598 + * @return The bytes to be written to the wrappers file. 1.599 + */ 1.600 + public String getWrapperFileContents() { 1.601 + wrapperStartupCode.append("}\n"); 1.602 + zeroingCode.append(wrapperStartupCode) 1.603 + .append(wrapperMethodBodies); 1.604 + wrapperMethodBodies.setLength(0); 1.605 + wrapperStartupCode.setLength(0); 1.606 + return zeroingCode.toString(); 1.607 + } 1.608 + 1.609 + /** 1.610 + * Get the finalised bytes to go into the generated header file. 1.611 + * 1.612 + * @return The bytes to be written to the header file. 1.613 + */ 1.614 + public String getHeaderFileContents() { 1.615 + if (!mHasEncounteredDefaultConstructor) { 1.616 + headerPublic.append(" ").append(mCClassName).append("() : AutoGlobalWrappedJavaObject() {};\n"); 1.617 + } 1.618 + headerProtected.append("};\n\n"); 1.619 + headerPublic.append(headerProtected); 1.620 + headerProtected.setLength(0); 1.621 + return headerPublic.toString(); 1.622 + } 1.623 +}