build/annotationProcessors/CodeGenerator.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.annotationProcessors;
michael@0 6
michael@0 7 import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
michael@0 8 import org.mozilla.gecko.annotationProcessors.utils.Utils;
michael@0 9
michael@0 10 import java.lang.annotation.Annotation;
michael@0 11 import java.lang.reflect.Constructor;
michael@0 12 import java.lang.reflect.Field;
michael@0 13 import java.lang.reflect.Member;
michael@0 14 import java.lang.reflect.Method;
michael@0 15 import java.lang.reflect.Modifier;
michael@0 16 import java.util.HashMap;
michael@0 17 import java.util.HashSet;
michael@0 18
michael@0 19 public class CodeGenerator {
michael@0 20 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
michael@0 21 private static final Annotation[][] GETTER_ARGUMENT_ANNOTATIONS = new Annotation[0][0];
michael@0 22 private static final Annotation[][] SETTER_ARGUMENT_ANNOTATIONS = new Annotation[1][0];
michael@0 23
michael@0 24 // Buffers holding the strings to ultimately be written to the output files.
michael@0 25 private final StringBuilder zeroingCode = new StringBuilder();
michael@0 26 private final StringBuilder wrapperStartupCode = new StringBuilder();
michael@0 27 private final StringBuilder wrapperMethodBodies = new StringBuilder();
michael@0 28 private final StringBuilder headerPublic = new StringBuilder();
michael@0 29 private final StringBuilder headerProtected = new StringBuilder();
michael@0 30
michael@0 31 private final HashSet<String> seenClasses = new HashSet<String>();
michael@0 32
michael@0 33 private final String mCClassName;
michael@0 34
michael@0 35 private final Class<?> mClassToWrap;
michael@0 36
michael@0 37 private boolean mHasEncounteredDefaultConstructor;
michael@0 38
michael@0 39 // Used for creating unique names for method ID fields in the face of
michael@0 40 private final HashMap<Member, String> mMembersToIds = new HashMap<Member, String>();
michael@0 41 private final HashSet<String> mTakenMemberNames = new HashSet<String>();
michael@0 42 private int mNameMunger;
michael@0 43
michael@0 44 public CodeGenerator(Class<?> aClass, String aGeneratedName) {
michael@0 45 mClassToWrap = aClass;
michael@0 46 mCClassName = aGeneratedName;
michael@0 47
michael@0 48 // Write the file header things. Includes and so forth.
michael@0 49 // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with
michael@0 50 // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerPublic
michael@0 51 // with headerProtected.
michael@0 52 wrapperStartupCode.append("void ").append(mCClassName).append("::InitStubs(JNIEnv *jEnv) {\n" +
michael@0 53 " initInit();\n");
michael@0 54
michael@0 55 // Now we write the various GetStaticMethodID calls here...
michael@0 56 headerPublic.append("class ").append(mCClassName).append(" : public AutoGlobalWrappedJavaObject {\n" +
michael@0 57 "public:\n" +
michael@0 58 " static void InitStubs(JNIEnv *jEnv);\n");
michael@0 59 headerProtected.append("protected:");
michael@0 60
michael@0 61 generateWrapperMethod();
michael@0 62 }
michael@0 63
michael@0 64 /**
michael@0 65 * Emit a static method which takes an instance of the class being wrapped and returns an instance
michael@0 66 * of the C++ wrapper class backed by that object.
michael@0 67 */
michael@0 68 private void generateWrapperMethod() {
michael@0 69 headerPublic.append(" static ").append(mCClassName).append("* Wrap(jobject obj);\n" +
michael@0 70 " ").append(mCClassName).append("(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};\n");
michael@0 71
michael@0 72 wrapperMethodBodies.append("\n").append(mCClassName).append("* ").append(mCClassName).append("::Wrap(jobject obj) {\n" +
michael@0 73 " JNIEnv *env = GetJNIForThread();\n" +
michael@0 74 " ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" +
michael@0 75 " env->DeleteLocalRef(obj);\n" +
michael@0 76 " return ret;\n" +
michael@0 77 "}\n");
michael@0 78 }
michael@0 79
michael@0 80 private void generateMemberCommon(Member theMethod, String aCMethodName, Class<?> aClass) {
michael@0 81 ensureClassHeaderAndStartup(aClass);
michael@0 82 writeMemberIdField(theMethod, aCMethodName);
michael@0 83 writeStartupCode(theMethod);
michael@0 84 }
michael@0 85
michael@0 86 /**
michael@0 87 * Append the appropriate generated code to the buffers for the method provided.
michael@0 88 *
michael@0 89 * @param aMethodTuple The Java method, plus annotation data.
michael@0 90 */
michael@0 91 public void generateMethod(AnnotatableEntity aMethodTuple) {
michael@0 92 // Unpack the tuple and extract some useful fields from the Method..
michael@0 93 Method theMethod = aMethodTuple.getMethod();
michael@0 94
michael@0 95 String CMethodName = aMethodTuple.mAnnotationInfo.wrapperName;
michael@0 96
michael@0 97 generateMemberCommon(theMethod, CMethodName, mClassToWrap);
michael@0 98
michael@0 99 boolean isFieldStatic = Utils.isMemberStatic(theMethod);
michael@0 100 boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic;
michael@0 101
michael@0 102 Class<?>[] parameterTypes = theMethod.getParameterTypes();
michael@0 103 Class<?> returnType = theMethod.getReturnType();
michael@0 104
michael@0 105 // Get the C++ method signature for this method.
michael@0 106 String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName);
michael@0 107 String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic);
michael@0 108
michael@0 109 // Add the header signature to the header file.
michael@0 110 writeSignatureToHeader(headerSignature);
michael@0 111
michael@0 112 // Use the implementation signature to generate the method body...
michael@0 113 writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap,
michael@0 114 aMethodTuple.mAnnotationInfo.isStatic,
michael@0 115 aMethodTuple.mAnnotationInfo.isMultithreaded,
michael@0 116 aMethodTuple.mAnnotationInfo.noThrow);
michael@0 117 }
michael@0 118
michael@0 119 private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) {
michael@0 120 StringBuilder argumentContent = null;
michael@0 121
michael@0 122 if (isSetter) {
michael@0 123 Class<?>[] setterArguments = new Class<?>[]{aFieldType};
michael@0 124 // Marshall the argument..
michael@0 125 argumentContent = getArgumentMarshalling(setterArguments);
michael@0 126 }
michael@0 127
michael@0 128 boolean isObjectReturningMethod = Utils.isObjectType(aFieldType);
michael@0 129 wrapperMethodBodies.append(" ");
michael@0 130 if (isSetter) {
michael@0 131 wrapperMethodBodies.append("env->Set");
michael@0 132 } else {
michael@0 133 wrapperMethodBodies.append("return ");
michael@0 134
michael@0 135 if (isObjectReturningMethod) {
michael@0 136 wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">(");
michael@0 137 }
michael@0 138
michael@0 139 wrapperMethodBodies.append("env->Get");
michael@0 140 }
michael@0 141
michael@0 142 if (aIsFieldStatic) {
michael@0 143 wrapperMethodBodies.append("Static");
michael@0 144 }
michael@0 145 wrapperMethodBodies.append(Utils.getFieldType(aFieldType))
michael@0 146 .append("Field(");
michael@0 147
michael@0 148 // Static will require the class and the field id. Nonstatic, the object and the field id.
michael@0 149 if (aIsFieldStatic) {
michael@0 150 wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap));
michael@0 151 } else {
michael@0 152 wrapperMethodBodies.append("wrapped_obj");
michael@0 153 }
michael@0 154 wrapperMethodBodies.append(", j")
michael@0 155 .append(aFieldName);
michael@0 156 if (isSetter) {
michael@0 157 wrapperMethodBodies.append(argumentContent);
michael@0 158 }
michael@0 159
michael@0 160 if (!isSetter && isObjectReturningMethod) {
michael@0 161 wrapperMethodBodies.append(')');
michael@0 162 }
michael@0 163 wrapperMethodBodies.append(");\n" +
michael@0 164 "}\n");
michael@0 165 }
michael@0 166
michael@0 167 public void generateField(AnnotatableEntity aFieldTuple) {
michael@0 168 Field theField = aFieldTuple.getField();
michael@0 169
michael@0 170 // Handles a peculiar case when dealing with enum types. We don't care about this field.
michael@0 171 // It just gets in the way and stops our code from compiling.
michael@0 172 if (theField.getName().equals("$VALUES")) {
michael@0 173 return;
michael@0 174 }
michael@0 175
michael@0 176 String CFieldName = aFieldTuple.mAnnotationInfo.wrapperName;
michael@0 177
michael@0 178 Class<?> fieldType = theField.getType();
michael@0 179
michael@0 180 generateMemberCommon(theField, CFieldName, mClassToWrap);
michael@0 181
michael@0 182 boolean isFieldStatic = Utils.isMemberStatic(theField);
michael@0 183 boolean isFieldFinal = Utils.isMemberFinal(theField);
michael@0 184 boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic;
michael@0 185
michael@0 186 String getterName = "get" + CFieldName;
michael@0 187 String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName);
michael@0 188 String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic);
michael@0 189
michael@0 190 writeSignatureToHeader(getterHeaderSignature);
michael@0 191
michael@0 192 writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true);
michael@0 193
michael@0 194 generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false);
michael@0 195
michael@0 196 // If field not final, also generate a setter function.
michael@0 197 if (!isFieldFinal) {
michael@0 198 String setterName = "set" + CFieldName;
michael@0 199
michael@0 200 Class<?>[] setterArguments = new Class<?>[]{fieldType};
michael@0 201
michael@0 202 String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName);
michael@0 203 String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic);
michael@0 204
michael@0 205 writeSignatureToHeader(setterHeaderSignature);
michael@0 206
michael@0 207 writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true);
michael@0 208
michael@0 209 generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true);
michael@0 210 }
michael@0 211 }
michael@0 212
michael@0 213 public void generateConstructor(AnnotatableEntity aCtorTuple) {
michael@0 214 // Unpack the tuple and extract some useful fields from the Method..
michael@0 215 Constructor theCtor = aCtorTuple.getConstructor();
michael@0 216 String CMethodName = mCClassName;
michael@0 217
michael@0 218 generateMemberCommon(theCtor, mCClassName, mClassToWrap);
michael@0 219
michael@0 220 String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName);
michael@0 221 String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false);
michael@0 222
michael@0 223 // Slice off the "void " from the start of the constructor declaration.
michael@0 224 headerSignature = headerSignature.substring(5);
michael@0 225 implementationSignature = implementationSignature.substring(5);
michael@0 226
michael@0 227 // Add the header signatures to the header file.
michael@0 228 writeSignatureToHeader(headerSignature);
michael@0 229
michael@0 230 // Use the implementation signature to generate the method body...
michael@0 231 writeCtorBody(implementationSignature, theCtor,
michael@0 232 aCtorTuple.mAnnotationInfo.isMultithreaded,
michael@0 233 aCtorTuple.mAnnotationInfo.noThrow);
michael@0 234
michael@0 235 if (theCtor.getParameterTypes().length == 0) {
michael@0 236 mHasEncounteredDefaultConstructor = true;
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 /**
michael@0 241 * Writes the appropriate header and startup code to ensure the existence of a reference to the
michael@0 242 * class specified. If this is already done, does nothing.
michael@0 243 *
michael@0 244 * @param aClass The target class.
michael@0 245 */
michael@0 246 private void ensureClassHeaderAndStartup(Class<?> aClass) {
michael@0 247 String className = aClass.getCanonicalName();
michael@0 248 if (seenClasses.contains(className)) {
michael@0 249 return;
michael@0 250 }
michael@0 251
michael@0 252 zeroingCode.append("jclass ")
michael@0 253 .append(mCClassName)
michael@0 254 .append("::")
michael@0 255 .append(Utils.getClassReferenceName(aClass))
michael@0 256 .append(" = 0;\n");
michael@0 257
michael@0 258 // Add a field to hold the reference...
michael@0 259 headerProtected.append("\n static jclass ")
michael@0 260 .append(Utils.getClassReferenceName(aClass))
michael@0 261 .append(";\n");
michael@0 262
michael@0 263 // Add startup code to populate it..
michael@0 264 wrapperStartupCode.append('\n')
michael@0 265 .append(Utils.getStartupLineForClass(aClass));
michael@0 266
michael@0 267 seenClasses.add(className);
michael@0 268 }
michael@0 269
michael@0 270 /**
michael@0 271 * Write out the function startup boilerplate for the method described. Check for environment
michael@0 272 * existence,
michael@0 273 * @param methodSignature
michael@0 274 * @param returnType
michael@0 275 * @param aIsStatic
michael@0 276 * @param aIsThreaded
michael@0 277 */
michael@0 278 private void writeFunctionStartupBoilerPlate(String methodSignature, Class<?> returnType, boolean aIsStatic, boolean aIsThreaded) {
michael@0 279 // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
michael@0 280 wrapperMethodBodies.append('\n')
michael@0 281 .append(methodSignature)
michael@0 282 .append(" {\n");
michael@0 283
michael@0 284 wrapperMethodBodies.append(" JNIEnv *env = ");
michael@0 285 if (!aIsThreaded) {
michael@0 286 wrapperMethodBodies.append("AndroidBridge::GetJNIEnv();\n");
michael@0 287 } else {
michael@0 288 wrapperMethodBodies.append("GetJNIForThread();\n");
michael@0 289 }
michael@0 290 }
michael@0 291
michael@0 292 /**
michael@0 293 * Write out the appropriate JNI frame pushing boilerplate for a call to the member provided (
michael@0 294 * which must be a constructor or method).
michael@0 295 *
michael@0 296 * @param aMethod A constructor/method being wrapped.
michael@0 297 * @param aIsObjectReturningMethod Does the method being wrapped return an object?
michael@0 298 */
michael@0 299 private void writeFramePushBoilerplate(Member aMethod,
michael@0 300 boolean aIsObjectReturningMethod, boolean aNoThrow) {
michael@0 301 if (aMethod instanceof Field) {
michael@0 302 throw new IllegalArgumentException("Tried to push frame for a FIELD?!");
michael@0 303 }
michael@0 304
michael@0 305 Method m;
michael@0 306 Constructor c;
michael@0 307
michael@0 308 Class<?> returnType;
michael@0 309
michael@0 310 int localReferencesNeeded;
michael@0 311 if (aMethod instanceof Method) {
michael@0 312 m = (Method) aMethod;
michael@0 313 returnType = m.getReturnType();
michael@0 314 localReferencesNeeded = Utils.enumerateReferenceArguments(m.getParameterTypes());
michael@0 315 } else {
michael@0 316 c = (Constructor) aMethod;
michael@0 317 returnType = Void.class;
michael@0 318 localReferencesNeeded = Utils.enumerateReferenceArguments(c.getParameterTypes());
michael@0 319 }
michael@0 320
michael@0 321 // Determine the number of local refs required for our local frame..
michael@0 322 // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
michael@0 323 if (aIsObjectReturningMethod) {
michael@0 324 localReferencesNeeded++;
michael@0 325 }
michael@0 326 wrapperMethodBodies.append(
michael@0 327 " if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n");
michael@0 328 if (!aNoThrow) {
michael@0 329 wrapperMethodBodies.append(
michael@0 330 " AndroidBridge::HandleUncaughtException(env);\n" +
michael@0 331 " MOZ_ASSUME_UNREACHABLE(\"Exception should have caused crash.\");\n");
michael@0 332 } else {
michael@0 333 wrapperMethodBodies.append(
michael@0 334 " return").append(Utils.getFailureReturnForType(returnType)).append(";\n");
michael@0 335 }
michael@0 336 wrapperMethodBodies.append(
michael@0 337 " }\n\n");
michael@0 338 }
michael@0 339
michael@0 340 private StringBuilder getArgumentMarshalling(Class<?>[] argumentTypes) {
michael@0 341 StringBuilder argumentContent = new StringBuilder();
michael@0 342
michael@0 343 // If we have >2 arguments, use the jvalue[] calling approach.
michael@0 344 argumentContent.append(", ");
michael@0 345 if (argumentTypes.length > 2) {
michael@0 346 wrapperMethodBodies.append(" jvalue args[").append(argumentTypes.length).append("];\n");
michael@0 347 for (int aT = 0; aT < argumentTypes.length; aT++) {
michael@0 348 wrapperMethodBodies.append(" args[").append(aT).append("].")
michael@0 349 .append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
michael@0 350 }
michael@0 351
michael@0 352 // The only argument is the array of arguments.
michael@0 353 argumentContent.append("args");
michael@0 354 wrapperMethodBodies.append('\n');
michael@0 355 } else {
michael@0 356 // Otherwise, use the vanilla calling approach.
michael@0 357 boolean needsNewline = false;
michael@0 358 for (int aT = 0; aT < argumentTypes.length; aT++) {
michael@0 359 // If the argument is a string-esque type, create a jstring from it, otherwise
michael@0 360 // it can be passed directly.
michael@0 361 if (Utils.isCharSequence(argumentTypes[aT])) {
michael@0 362 wrapperMethodBodies.append(" jstring j").append(aT).append(" = AndroidBridge::NewJavaString(env, a").append(aT).append(");\n");
michael@0 363 needsNewline = true;
michael@0 364 // Ensure we refer to the newly constructed Java string - not to the original
michael@0 365 // parameter to the wrapper function.
michael@0 366 argumentContent.append('j').append(aT);
michael@0 367 } else {
michael@0 368 argumentContent.append('a').append(aT);
michael@0 369 }
michael@0 370 if (aT != argumentTypes.length - 1) {
michael@0 371 argumentContent.append(", ");
michael@0 372 }
michael@0 373 }
michael@0 374 if (needsNewline) {
michael@0 375 wrapperMethodBodies.append('\n');
michael@0 376 }
michael@0 377 }
michael@0 378
michael@0 379 return argumentContent;
michael@0 380 }
michael@0 381
michael@0 382 private void writeCtorBody(String implementationSignature, Constructor theCtor,
michael@0 383 boolean aIsThreaded, boolean aNoThrow) {
michael@0 384 Class<?>[] argumentTypes = theCtor.getParameterTypes();
michael@0 385
michael@0 386 writeFunctionStartupBoilerPlate(implementationSignature, Void.class, false, aIsThreaded);
michael@0 387
michael@0 388 writeFramePushBoilerplate(theCtor, false, aNoThrow);
michael@0 389
michael@0 390 // Marshall arguments for this constructor, if any...
michael@0 391 boolean hasArguments = argumentTypes.length != 0;
michael@0 392
michael@0 393 StringBuilder argumentContent = new StringBuilder();
michael@0 394 if (hasArguments) {
michael@0 395 argumentContent = getArgumentMarshalling(argumentTypes);
michael@0 396 }
michael@0 397
michael@0 398 // The call into Java
michael@0 399 wrapperMethodBodies.append(" Init(env->NewObject");
michael@0 400 if (argumentTypes.length > 2) {
michael@0 401 wrapperMethodBodies.append('A');
michael@0 402 }
michael@0 403
michael@0 404 wrapperMethodBodies.append('(');
michael@0 405
michael@0 406
michael@0 407 // Call takes class id, method id of constructor method, then arguments.
michael@0 408 wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)).append(", ");
michael@0 409
michael@0 410 wrapperMethodBodies.append(mMembersToIds.get(theCtor))
michael@0 411 // Tack on the arguments, if any..
michael@0 412 .append(argumentContent)
michael@0 413 .append("), env);\n" +
michael@0 414 " env->PopLocalFrame(nullptr);\n" +
michael@0 415 "}\n");
michael@0 416 }
michael@0 417
michael@0 418 /**
michael@0 419 * Generates the method body of the C++ wrapper function for the Java method indicated.
michael@0 420 *
michael@0 421 * @param methodSignature The previously-generated C++ method signature for the method to be
michael@0 422 * generated.
michael@0 423 * @param aCMethodName The C++ method name for the method to be generated.
michael@0 424 * @param aMethod The Java method to be wrapped by the C++ method being generated.
michael@0 425 * @param aClass The Java class to which the method belongs.
michael@0 426 */
michael@0 427 private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod,
michael@0 428 Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded,
michael@0 429 boolean aNoThrow) {
michael@0 430 Class<?>[] argumentTypes = aMethod.getParameterTypes();
michael@0 431 Class<?> returnType = aMethod.getReturnType();
michael@0 432
michael@0 433 writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded);
michael@0 434
michael@0 435 boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType);
michael@0 436
michael@0 437 writeFramePushBoilerplate(aMethod, isObjectReturningMethod, aNoThrow);
michael@0 438
michael@0 439 // Marshall arguments, if we have any.
michael@0 440 boolean hasArguments = argumentTypes.length != 0;
michael@0 441
michael@0 442 // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the
michael@0 443 // argument list while building this line. In the coming code block, we simultaneously
michael@0 444 // construct any argument marshalling code (Creation of jstrings, placement of arguments
michael@0 445 // into an argument array, etc. and the actual argument list passed to the function (in
michael@0 446 // argumentContent).
michael@0 447 StringBuilder argumentContent = new StringBuilder();
michael@0 448 if (hasArguments) {
michael@0 449 argumentContent = getArgumentMarshalling(argumentTypes);
michael@0 450 }
michael@0 451
michael@0 452 // Allocate a temporary variable to hold the return type from Java.
michael@0 453 wrapperMethodBodies.append(" ");
michael@0 454 if (!returnType.getCanonicalName().equals("void")) {
michael@0 455 if (isObjectReturningMethod) {
michael@0 456 wrapperMethodBodies.append("jobject");
michael@0 457 } else {
michael@0 458 wrapperMethodBodies.append(Utils.getCReturnType(returnType));
michael@0 459 }
michael@0 460 wrapperMethodBodies.append(" temp = ");
michael@0 461 }
michael@0 462
michael@0 463 boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod);
michael@0 464
michael@0 465 // The call into Java
michael@0 466 wrapperMethodBodies.append("env->")
michael@0 467 .append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
michael@0 468 if (argumentTypes.length > 2) {
michael@0 469 wrapperMethodBodies.append('A');
michael@0 470 }
michael@0 471
michael@0 472 wrapperMethodBodies.append('(');
michael@0 473 // If the underlying Java method is nonstatic, we provide the target object to the JNI.
michael@0 474 if (!isStaticJavaMethod) {
michael@0 475 wrapperMethodBodies.append("wrapped_obj, ");
michael@0 476 } else {
michael@0 477 // If this is a static underlying Java method, we need to use the class reference in our
michael@0 478 // call.
michael@0 479 wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", ");
michael@0 480 }
michael@0 481
michael@0 482 wrapperMethodBodies.append(mMembersToIds.get(aMethod));
michael@0 483
michael@0 484 // Tack on the arguments, if any..
michael@0 485 wrapperMethodBodies.append(argumentContent)
michael@0 486 .append(");\n");
michael@0 487
michael@0 488 // Check for exception and crash if any...
michael@0 489 if (!aNoThrow) {
michael@0 490 wrapperMethodBodies.append(" AndroidBridge::HandleUncaughtException(env);\n");
michael@0 491 }
michael@0 492
michael@0 493 // If we're returning an object, pop the callee's stack frame extracting our ref as the return
michael@0 494 // value.
michael@0 495 if (isObjectReturningMethod) {
michael@0 496 wrapperMethodBodies.append(" ")
michael@0 497 .append(Utils.getCReturnType(returnType))
michael@0 498 .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
michael@0 499 " return ret;\n");
michael@0 500 } else if (!returnType.getCanonicalName().equals("void")) {
michael@0 501 // If we're a primitive-returning function, just return the directly-obtained primative
michael@0 502 // from the call to Java.
michael@0 503 wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n" +
michael@0 504 " return temp;\n");
michael@0 505 } else {
michael@0 506 // If we don't return anything, just pop the stack frame and move on with life.
michael@0 507 wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n");
michael@0 508 }
michael@0 509 wrapperMethodBodies.append("}\n");
michael@0 510 }
michael@0 511
michael@0 512 /**
michael@0 513 * Generates the code to get the id of the given member on startup.
michael@0 514 *
michael@0 515 * @param aMember The Java member being wrapped.
michael@0 516 */
michael@0 517 private void writeStartupCode(Member aMember) {
michael@0 518 wrapperStartupCode.append(" ")
michael@0 519 .append(mMembersToIds.get(aMember))
michael@0 520 .append(" = get");
michael@0 521 if (Utils.isMemberStatic(aMember)) {
michael@0 522 wrapperStartupCode.append("Static");
michael@0 523 }
michael@0 524
michael@0 525 boolean isField = aMember instanceof Field;
michael@0 526 if (isField) {
michael@0 527 wrapperStartupCode.append("Field(\"");
michael@0 528 } else {
michael@0 529 wrapperStartupCode.append("Method(\"");
michael@0 530 }
michael@0 531 if (aMember instanceof Constructor) {
michael@0 532 wrapperStartupCode.append("<init>");
michael@0 533 } else {
michael@0 534 wrapperStartupCode.append(aMember.getName());
michael@0 535 }
michael@0 536
michael@0 537 wrapperStartupCode.append("\", \"")
michael@0 538 .append(Utils.getTypeSignatureStringForMember(aMember))
michael@0 539 .append("\");\n");
michael@0 540 }
michael@0 541
michael@0 542 private void writeZeroingFor(Member aMember, final String aMemberName) {
michael@0 543 if (aMember instanceof Field) {
michael@0 544 zeroingCode.append("jfieldID ");
michael@0 545 } else {
michael@0 546 zeroingCode.append("jmethodID ");
michael@0 547 }
michael@0 548 zeroingCode.append(mCClassName)
michael@0 549 .append("::")
michael@0 550 .append(aMemberName)
michael@0 551 .append(" = 0;\n");
michael@0 552 }
michael@0 553
michael@0 554 /**
michael@0 555 * Write the field declaration for the C++ id field of the given member.
michael@0 556 *
michael@0 557 * @param aMember Member for which an id field needs to be generated.
michael@0 558 */
michael@0 559 private void writeMemberIdField(Member aMember, final String aCMethodName) {
michael@0 560 String memberName = 'j'+ aCMethodName;
michael@0 561
michael@0 562 if (aMember instanceof Field) {
michael@0 563 headerProtected.append(" static jfieldID ");
michael@0 564 } else {
michael@0 565 headerProtected.append(" static jmethodID ");
michael@0 566 }
michael@0 567
michael@0 568 while(mTakenMemberNames.contains(memberName)) {
michael@0 569 memberName = 'j' + aCMethodName + mNameMunger;
michael@0 570 mNameMunger++;
michael@0 571 }
michael@0 572
michael@0 573 writeZeroingFor(aMember, memberName);
michael@0 574 mMembersToIds.put(aMember, memberName);
michael@0 575 mTakenMemberNames.add(memberName);
michael@0 576
michael@0 577 headerProtected.append(memberName)
michael@0 578 .append(";\n");
michael@0 579 }
michael@0 580
michael@0 581 /**
michael@0 582 * Helper function to add a provided method signature to the public section of the generated header.
michael@0 583 *
michael@0 584 * @param aSignature The header to add.
michael@0 585 */
michael@0 586 private void writeSignatureToHeader(String aSignature) {
michael@0 587 headerPublic.append(" ")
michael@0 588 .append(aSignature)
michael@0 589 .append(";\n");
michael@0 590 }
michael@0 591
michael@0 592 /**
michael@0 593 * Get the finalised bytes to go into the generated wrappers file.
michael@0 594 *
michael@0 595 * @return The bytes to be written to the wrappers file.
michael@0 596 */
michael@0 597 public String getWrapperFileContents() {
michael@0 598 wrapperStartupCode.append("}\n");
michael@0 599 zeroingCode.append(wrapperStartupCode)
michael@0 600 .append(wrapperMethodBodies);
michael@0 601 wrapperMethodBodies.setLength(0);
michael@0 602 wrapperStartupCode.setLength(0);
michael@0 603 return zeroingCode.toString();
michael@0 604 }
michael@0 605
michael@0 606 /**
michael@0 607 * Get the finalised bytes to go into the generated header file.
michael@0 608 *
michael@0 609 * @return The bytes to be written to the header file.
michael@0 610 */
michael@0 611 public String getHeaderFileContents() {
michael@0 612 if (!mHasEncounteredDefaultConstructor) {
michael@0 613 headerPublic.append(" ").append(mCClassName).append("() : AutoGlobalWrappedJavaObject() {};\n");
michael@0 614 }
michael@0 615 headerProtected.append("};\n\n");
michael@0 616 headerPublic.append(headerProtected);
michael@0 617 headerProtected.setLength(0);
michael@0 618 return headerPublic.toString();
michael@0 619 }
michael@0 620 }

mercurial