build/annotationProcessors/CodeGenerator.java

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

mercurial