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