michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.annotationProcessors; michael@0: michael@0: import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; michael@0: import org.mozilla.gecko.annotationProcessors.utils.Utils; michael@0: michael@0: import java.lang.annotation.Annotation; michael@0: import java.lang.reflect.Constructor; michael@0: import java.lang.reflect.Field; michael@0: import java.lang.reflect.Member; michael@0: import java.lang.reflect.Method; michael@0: import java.lang.reflect.Modifier; michael@0: import java.util.HashMap; michael@0: import java.util.HashSet; michael@0: michael@0: public class CodeGenerator { michael@0: private static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; michael@0: private static final Annotation[][] GETTER_ARGUMENT_ANNOTATIONS = new Annotation[0][0]; michael@0: private static final Annotation[][] SETTER_ARGUMENT_ANNOTATIONS = new Annotation[1][0]; michael@0: michael@0: // Buffers holding the strings to ultimately be written to the output files. michael@0: private final StringBuilder zeroingCode = new StringBuilder(); michael@0: private final StringBuilder wrapperStartupCode = new StringBuilder(); michael@0: private final StringBuilder wrapperMethodBodies = new StringBuilder(); michael@0: private final StringBuilder headerPublic = new StringBuilder(); michael@0: private final StringBuilder headerProtected = new StringBuilder(); michael@0: michael@0: private final HashSet seenClasses = new HashSet(); michael@0: michael@0: private final String mCClassName; michael@0: michael@0: private final Class mClassToWrap; michael@0: michael@0: private boolean mHasEncounteredDefaultConstructor; michael@0: michael@0: // Used for creating unique names for method ID fields in the face of michael@0: private final HashMap mMembersToIds = new HashMap(); michael@0: private final HashSet mTakenMemberNames = new HashSet(); michael@0: private int mNameMunger; michael@0: michael@0: public CodeGenerator(Class aClass, String aGeneratedName) { michael@0: mClassToWrap = aClass; michael@0: mCClassName = aGeneratedName; michael@0: michael@0: // Write the file header things. Includes and so forth. michael@0: // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with michael@0: // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerPublic michael@0: // with headerProtected. michael@0: wrapperStartupCode.append("void ").append(mCClassName).append("::InitStubs(JNIEnv *jEnv) {\n" + michael@0: " initInit();\n"); michael@0: michael@0: // Now we write the various GetStaticMethodID calls here... michael@0: headerPublic.append("class ").append(mCClassName).append(" : public AutoGlobalWrappedJavaObject {\n" + michael@0: "public:\n" + michael@0: " static void InitStubs(JNIEnv *jEnv);\n"); michael@0: headerProtected.append("protected:"); michael@0: michael@0: generateWrapperMethod(); michael@0: } michael@0: michael@0: /** michael@0: * Emit a static method which takes an instance of the class being wrapped and returns an instance michael@0: * of the C++ wrapper class backed by that object. michael@0: */ michael@0: private void generateWrapperMethod() { michael@0: headerPublic.append(" static ").append(mCClassName).append("* Wrap(jobject obj);\n" + michael@0: " ").append(mCClassName).append("(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};\n"); michael@0: michael@0: wrapperMethodBodies.append("\n").append(mCClassName).append("* ").append(mCClassName).append("::Wrap(jobject obj) {\n" + michael@0: " JNIEnv *env = GetJNIForThread();\n" + michael@0: " ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" + michael@0: " env->DeleteLocalRef(obj);\n" + michael@0: " return ret;\n" + michael@0: "}\n"); michael@0: } michael@0: michael@0: private void generateMemberCommon(Member theMethod, String aCMethodName, Class aClass) { michael@0: ensureClassHeaderAndStartup(aClass); michael@0: writeMemberIdField(theMethod, aCMethodName); michael@0: writeStartupCode(theMethod); michael@0: } michael@0: michael@0: /** michael@0: * Append the appropriate generated code to the buffers for the method provided. michael@0: * michael@0: * @param aMethodTuple The Java method, plus annotation data. michael@0: */ michael@0: public void generateMethod(AnnotatableEntity aMethodTuple) { michael@0: // Unpack the tuple and extract some useful fields from the Method.. michael@0: Method theMethod = aMethodTuple.getMethod(); michael@0: michael@0: String CMethodName = aMethodTuple.mAnnotationInfo.wrapperName; michael@0: michael@0: generateMemberCommon(theMethod, CMethodName, mClassToWrap); michael@0: michael@0: boolean isFieldStatic = Utils.isMemberStatic(theMethod); michael@0: boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic; michael@0: michael@0: Class[] parameterTypes = theMethod.getParameterTypes(); michael@0: Class returnType = theMethod.getReturnType(); michael@0: michael@0: // Get the C++ method signature for this method. michael@0: String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName); michael@0: String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic); michael@0: michael@0: // Add the header signature to the header file. michael@0: writeSignatureToHeader(headerSignature); michael@0: michael@0: // Use the implementation signature to generate the method body... michael@0: writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap, michael@0: aMethodTuple.mAnnotationInfo.isStatic, michael@0: aMethodTuple.mAnnotationInfo.isMultithreaded, michael@0: aMethodTuple.mAnnotationInfo.noThrow); michael@0: } michael@0: michael@0: private void generateGetterOrSetterBody(Class aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) { michael@0: StringBuilder argumentContent = null; michael@0: michael@0: if (isSetter) { michael@0: Class[] setterArguments = new Class[]{aFieldType}; michael@0: // Marshall the argument.. michael@0: argumentContent = getArgumentMarshalling(setterArguments); michael@0: } michael@0: michael@0: boolean isObjectReturningMethod = Utils.isObjectType(aFieldType); michael@0: wrapperMethodBodies.append(" "); michael@0: if (isSetter) { michael@0: wrapperMethodBodies.append("env->Set"); michael@0: } else { michael@0: wrapperMethodBodies.append("return "); michael@0: michael@0: if (isObjectReturningMethod) { michael@0: wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">("); michael@0: } michael@0: michael@0: wrapperMethodBodies.append("env->Get"); michael@0: } michael@0: michael@0: if (aIsFieldStatic) { michael@0: wrapperMethodBodies.append("Static"); michael@0: } michael@0: wrapperMethodBodies.append(Utils.getFieldType(aFieldType)) michael@0: .append("Field("); michael@0: michael@0: // Static will require the class and the field id. Nonstatic, the object and the field id. michael@0: if (aIsFieldStatic) { michael@0: wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)); michael@0: } else { michael@0: wrapperMethodBodies.append("wrapped_obj"); michael@0: } michael@0: wrapperMethodBodies.append(", j") michael@0: .append(aFieldName); michael@0: if (isSetter) { michael@0: wrapperMethodBodies.append(argumentContent); michael@0: } michael@0: michael@0: if (!isSetter && isObjectReturningMethod) { michael@0: wrapperMethodBodies.append(')'); michael@0: } michael@0: wrapperMethodBodies.append(");\n" + michael@0: "}\n"); michael@0: } michael@0: michael@0: public void generateField(AnnotatableEntity aFieldTuple) { michael@0: Field theField = aFieldTuple.getField(); michael@0: michael@0: // Handles a peculiar case when dealing with enum types. We don't care about this field. michael@0: // It just gets in the way and stops our code from compiling. michael@0: if (theField.getName().equals("$VALUES")) { michael@0: return; michael@0: } michael@0: michael@0: String CFieldName = aFieldTuple.mAnnotationInfo.wrapperName; michael@0: michael@0: Class fieldType = theField.getType(); michael@0: michael@0: generateMemberCommon(theField, CFieldName, mClassToWrap); michael@0: michael@0: boolean isFieldStatic = Utils.isMemberStatic(theField); michael@0: boolean isFieldFinal = Utils.isMemberFinal(theField); michael@0: boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic; michael@0: michael@0: String getterName = "get" + CFieldName; michael@0: String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName); michael@0: String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic); michael@0: michael@0: writeSignatureToHeader(getterHeaderSignature); michael@0: michael@0: writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true); michael@0: michael@0: generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false); michael@0: michael@0: // If field not final, also generate a setter function. michael@0: if (!isFieldFinal) { michael@0: String setterName = "set" + CFieldName; michael@0: michael@0: Class[] setterArguments = new Class[]{fieldType}; michael@0: michael@0: String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName); michael@0: String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic); michael@0: michael@0: writeSignatureToHeader(setterHeaderSignature); michael@0: michael@0: writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true); michael@0: michael@0: generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true); michael@0: } michael@0: } michael@0: michael@0: public void generateConstructor(AnnotatableEntity aCtorTuple) { michael@0: // Unpack the tuple and extract some useful fields from the Method.. michael@0: Constructor theCtor = aCtorTuple.getConstructor(); michael@0: String CMethodName = mCClassName; michael@0: michael@0: generateMemberCommon(theCtor, mCClassName, mClassToWrap); michael@0: michael@0: String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName); michael@0: String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false); michael@0: michael@0: // Slice off the "void " from the start of the constructor declaration. michael@0: headerSignature = headerSignature.substring(5); michael@0: implementationSignature = implementationSignature.substring(5); michael@0: michael@0: // Add the header signatures to the header file. michael@0: writeSignatureToHeader(headerSignature); michael@0: michael@0: // Use the implementation signature to generate the method body... michael@0: writeCtorBody(implementationSignature, theCtor, michael@0: aCtorTuple.mAnnotationInfo.isMultithreaded, michael@0: aCtorTuple.mAnnotationInfo.noThrow); michael@0: michael@0: if (theCtor.getParameterTypes().length == 0) { michael@0: mHasEncounteredDefaultConstructor = true; michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Writes the appropriate header and startup code to ensure the existence of a reference to the michael@0: * class specified. If this is already done, does nothing. michael@0: * michael@0: * @param aClass The target class. michael@0: */ michael@0: private void ensureClassHeaderAndStartup(Class aClass) { michael@0: String className = aClass.getCanonicalName(); michael@0: if (seenClasses.contains(className)) { michael@0: return; michael@0: } michael@0: michael@0: zeroingCode.append("jclass ") michael@0: .append(mCClassName) michael@0: .append("::") michael@0: .append(Utils.getClassReferenceName(aClass)) michael@0: .append(" = 0;\n"); michael@0: michael@0: // Add a field to hold the reference... michael@0: headerProtected.append("\n static jclass ") michael@0: .append(Utils.getClassReferenceName(aClass)) michael@0: .append(";\n"); michael@0: michael@0: // Add startup code to populate it.. michael@0: wrapperStartupCode.append('\n') michael@0: .append(Utils.getStartupLineForClass(aClass)); michael@0: michael@0: seenClasses.add(className); michael@0: } michael@0: michael@0: /** michael@0: * Write out the function startup boilerplate for the method described. Check for environment michael@0: * existence, michael@0: * @param methodSignature michael@0: * @param returnType michael@0: * @param aIsStatic michael@0: * @param aIsThreaded michael@0: */ michael@0: private void writeFunctionStartupBoilerPlate(String methodSignature, Class returnType, boolean aIsStatic, boolean aIsThreaded) { michael@0: // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc. michael@0: wrapperMethodBodies.append('\n') michael@0: .append(methodSignature) michael@0: .append(" {\n"); michael@0: michael@0: wrapperMethodBodies.append(" JNIEnv *env = "); michael@0: if (!aIsThreaded) { michael@0: wrapperMethodBodies.append("AndroidBridge::GetJNIEnv();\n"); michael@0: } else { michael@0: wrapperMethodBodies.append("GetJNIForThread();\n"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Write out the appropriate JNI frame pushing boilerplate for a call to the member provided ( michael@0: * which must be a constructor or method). michael@0: * michael@0: * @param aMethod A constructor/method being wrapped. michael@0: * @param aIsObjectReturningMethod Does the method being wrapped return an object? michael@0: */ michael@0: private void writeFramePushBoilerplate(Member aMethod, michael@0: boolean aIsObjectReturningMethod, boolean aNoThrow) { michael@0: if (aMethod instanceof Field) { michael@0: throw new IllegalArgumentException("Tried to push frame for a FIELD?!"); michael@0: } michael@0: michael@0: Method m; michael@0: Constructor c; michael@0: michael@0: Class returnType; michael@0: michael@0: int localReferencesNeeded; michael@0: if (aMethod instanceof Method) { michael@0: m = (Method) aMethod; michael@0: returnType = m.getReturnType(); michael@0: localReferencesNeeded = Utils.enumerateReferenceArguments(m.getParameterTypes()); michael@0: } else { michael@0: c = (Constructor) aMethod; michael@0: returnType = Void.class; michael@0: localReferencesNeeded = Utils.enumerateReferenceArguments(c.getParameterTypes()); michael@0: } michael@0: michael@0: // Determine the number of local refs required for our local frame.. michael@0: // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values. michael@0: if (aIsObjectReturningMethod) { michael@0: localReferencesNeeded++; michael@0: } michael@0: wrapperMethodBodies.append( michael@0: " if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n"); michael@0: if (!aNoThrow) { michael@0: wrapperMethodBodies.append( michael@0: " AndroidBridge::HandleUncaughtException(env);\n" + michael@0: " MOZ_ASSUME_UNREACHABLE(\"Exception should have caused crash.\");\n"); michael@0: } else { michael@0: wrapperMethodBodies.append( michael@0: " return").append(Utils.getFailureReturnForType(returnType)).append(";\n"); michael@0: } michael@0: wrapperMethodBodies.append( michael@0: " }\n\n"); michael@0: } michael@0: michael@0: private StringBuilder getArgumentMarshalling(Class[] argumentTypes) { michael@0: StringBuilder argumentContent = new StringBuilder(); michael@0: michael@0: // If we have >2 arguments, use the jvalue[] calling approach. michael@0: argumentContent.append(", "); michael@0: if (argumentTypes.length > 2) { michael@0: wrapperMethodBodies.append(" jvalue args[").append(argumentTypes.length).append("];\n"); michael@0: for (int aT = 0; aT < argumentTypes.length; aT++) { michael@0: wrapperMethodBodies.append(" args[").append(aT).append("].") michael@0: .append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT)); michael@0: } michael@0: michael@0: // The only argument is the array of arguments. michael@0: argumentContent.append("args"); michael@0: wrapperMethodBodies.append('\n'); michael@0: } else { michael@0: // Otherwise, use the vanilla calling approach. michael@0: boolean needsNewline = false; michael@0: for (int aT = 0; aT < argumentTypes.length; aT++) { michael@0: // If the argument is a string-esque type, create a jstring from it, otherwise michael@0: // it can be passed directly. michael@0: if (Utils.isCharSequence(argumentTypes[aT])) { michael@0: wrapperMethodBodies.append(" jstring j").append(aT).append(" = AndroidBridge::NewJavaString(env, a").append(aT).append(");\n"); michael@0: needsNewline = true; michael@0: // Ensure we refer to the newly constructed Java string - not to the original michael@0: // parameter to the wrapper function. michael@0: argumentContent.append('j').append(aT); michael@0: } else { michael@0: argumentContent.append('a').append(aT); michael@0: } michael@0: if (aT != argumentTypes.length - 1) { michael@0: argumentContent.append(", "); michael@0: } michael@0: } michael@0: if (needsNewline) { michael@0: wrapperMethodBodies.append('\n'); michael@0: } michael@0: } michael@0: michael@0: return argumentContent; michael@0: } michael@0: michael@0: private void writeCtorBody(String implementationSignature, Constructor theCtor, michael@0: boolean aIsThreaded, boolean aNoThrow) { michael@0: Class[] argumentTypes = theCtor.getParameterTypes(); michael@0: michael@0: writeFunctionStartupBoilerPlate(implementationSignature, Void.class, false, aIsThreaded); michael@0: michael@0: writeFramePushBoilerplate(theCtor, false, aNoThrow); michael@0: michael@0: // Marshall arguments for this constructor, if any... michael@0: boolean hasArguments = argumentTypes.length != 0; michael@0: michael@0: StringBuilder argumentContent = new StringBuilder(); michael@0: if (hasArguments) { michael@0: argumentContent = getArgumentMarshalling(argumentTypes); michael@0: } michael@0: michael@0: // The call into Java michael@0: wrapperMethodBodies.append(" Init(env->NewObject"); michael@0: if (argumentTypes.length > 2) { michael@0: wrapperMethodBodies.append('A'); michael@0: } michael@0: michael@0: wrapperMethodBodies.append('('); michael@0: michael@0: michael@0: // Call takes class id, method id of constructor method, then arguments. michael@0: wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)).append(", "); michael@0: michael@0: wrapperMethodBodies.append(mMembersToIds.get(theCtor)) michael@0: // Tack on the arguments, if any.. michael@0: .append(argumentContent) michael@0: .append("), env);\n" + michael@0: " env->PopLocalFrame(nullptr);\n" + michael@0: "}\n"); michael@0: } michael@0: michael@0: /** michael@0: * Generates the method body of the C++ wrapper function for the Java method indicated. michael@0: * michael@0: * @param methodSignature The previously-generated C++ method signature for the method to be michael@0: * generated. michael@0: * @param aCMethodName The C++ method name for the method to be generated. michael@0: * @param aMethod The Java method to be wrapped by the C++ method being generated. michael@0: * @param aClass The Java class to which the method belongs. michael@0: */ michael@0: private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod, michael@0: Class aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded, michael@0: boolean aNoThrow) { michael@0: Class[] argumentTypes = aMethod.getParameterTypes(); michael@0: Class returnType = aMethod.getReturnType(); michael@0: michael@0: writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded); michael@0: michael@0: boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType); michael@0: michael@0: writeFramePushBoilerplate(aMethod, isObjectReturningMethod, aNoThrow); michael@0: michael@0: // Marshall arguments, if we have any. michael@0: boolean hasArguments = argumentTypes.length != 0; michael@0: michael@0: // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the michael@0: // argument list while building this line. In the coming code block, we simultaneously michael@0: // construct any argument marshalling code (Creation of jstrings, placement of arguments michael@0: // into an argument array, etc. and the actual argument list passed to the function (in michael@0: // argumentContent). michael@0: StringBuilder argumentContent = new StringBuilder(); michael@0: if (hasArguments) { michael@0: argumentContent = getArgumentMarshalling(argumentTypes); michael@0: } michael@0: michael@0: // Allocate a temporary variable to hold the return type from Java. michael@0: wrapperMethodBodies.append(" "); michael@0: if (!returnType.getCanonicalName().equals("void")) { michael@0: if (isObjectReturningMethod) { michael@0: wrapperMethodBodies.append("jobject"); michael@0: } else { michael@0: wrapperMethodBodies.append(Utils.getCReturnType(returnType)); michael@0: } michael@0: wrapperMethodBodies.append(" temp = "); michael@0: } michael@0: michael@0: boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod); michael@0: michael@0: // The call into Java michael@0: wrapperMethodBodies.append("env->") michael@0: .append(Utils.getCallPrefix(returnType, isStaticJavaMethod)); michael@0: if (argumentTypes.length > 2) { michael@0: wrapperMethodBodies.append('A'); michael@0: } michael@0: michael@0: wrapperMethodBodies.append('('); michael@0: // If the underlying Java method is nonstatic, we provide the target object to the JNI. michael@0: if (!isStaticJavaMethod) { michael@0: wrapperMethodBodies.append("wrapped_obj, "); michael@0: } else { michael@0: // If this is a static underlying Java method, we need to use the class reference in our michael@0: // call. michael@0: wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", "); michael@0: } michael@0: michael@0: wrapperMethodBodies.append(mMembersToIds.get(aMethod)); michael@0: michael@0: // Tack on the arguments, if any.. michael@0: wrapperMethodBodies.append(argumentContent) michael@0: .append(");\n"); michael@0: michael@0: // Check for exception and crash if any... michael@0: if (!aNoThrow) { michael@0: wrapperMethodBodies.append(" AndroidBridge::HandleUncaughtException(env);\n"); michael@0: } michael@0: michael@0: // If we're returning an object, pop the callee's stack frame extracting our ref as the return michael@0: // value. michael@0: if (isObjectReturningMethod) { michael@0: wrapperMethodBodies.append(" ") michael@0: .append(Utils.getCReturnType(returnType)) michael@0: .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" + michael@0: " return ret;\n"); michael@0: } else if (!returnType.getCanonicalName().equals("void")) { michael@0: // If we're a primitive-returning function, just return the directly-obtained primative michael@0: // from the call to Java. michael@0: wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n" + michael@0: " return temp;\n"); michael@0: } else { michael@0: // If we don't return anything, just pop the stack frame and move on with life. michael@0: wrapperMethodBodies.append(" env->PopLocalFrame(nullptr);\n"); michael@0: } michael@0: wrapperMethodBodies.append("}\n"); michael@0: } michael@0: michael@0: /** michael@0: * Generates the code to get the id of the given member on startup. michael@0: * michael@0: * @param aMember The Java member being wrapped. michael@0: */ michael@0: private void writeStartupCode(Member aMember) { michael@0: wrapperStartupCode.append(" ") michael@0: .append(mMembersToIds.get(aMember)) michael@0: .append(" = get"); michael@0: if (Utils.isMemberStatic(aMember)) { michael@0: wrapperStartupCode.append("Static"); michael@0: } michael@0: michael@0: boolean isField = aMember instanceof Field; michael@0: if (isField) { michael@0: wrapperStartupCode.append("Field(\""); michael@0: } else { michael@0: wrapperStartupCode.append("Method(\""); michael@0: } michael@0: if (aMember instanceof Constructor) { michael@0: wrapperStartupCode.append(""); michael@0: } else { michael@0: wrapperStartupCode.append(aMember.getName()); michael@0: } michael@0: michael@0: wrapperStartupCode.append("\", \"") michael@0: .append(Utils.getTypeSignatureStringForMember(aMember)) michael@0: .append("\");\n"); michael@0: } michael@0: michael@0: private void writeZeroingFor(Member aMember, final String aMemberName) { michael@0: if (aMember instanceof Field) { michael@0: zeroingCode.append("jfieldID "); michael@0: } else { michael@0: zeroingCode.append("jmethodID "); michael@0: } michael@0: zeroingCode.append(mCClassName) michael@0: .append("::") michael@0: .append(aMemberName) michael@0: .append(" = 0;\n"); michael@0: } michael@0: michael@0: /** michael@0: * Write the field declaration for the C++ id field of the given member. michael@0: * michael@0: * @param aMember Member for which an id field needs to be generated. michael@0: */ michael@0: private void writeMemberIdField(Member aMember, final String aCMethodName) { michael@0: String memberName = 'j'+ aCMethodName; michael@0: michael@0: if (aMember instanceof Field) { michael@0: headerProtected.append(" static jfieldID "); michael@0: } else { michael@0: headerProtected.append(" static jmethodID "); michael@0: } michael@0: michael@0: while(mTakenMemberNames.contains(memberName)) { michael@0: memberName = 'j' + aCMethodName + mNameMunger; michael@0: mNameMunger++; michael@0: } michael@0: michael@0: writeZeroingFor(aMember, memberName); michael@0: mMembersToIds.put(aMember, memberName); michael@0: mTakenMemberNames.add(memberName); michael@0: michael@0: headerProtected.append(memberName) michael@0: .append(";\n"); michael@0: } michael@0: michael@0: /** michael@0: * Helper function to add a provided method signature to the public section of the generated header. michael@0: * michael@0: * @param aSignature The header to add. michael@0: */ michael@0: private void writeSignatureToHeader(String aSignature) { michael@0: headerPublic.append(" ") michael@0: .append(aSignature) michael@0: .append(";\n"); michael@0: } michael@0: michael@0: /** michael@0: * Get the finalised bytes to go into the generated wrappers file. michael@0: * michael@0: * @return The bytes to be written to the wrappers file. michael@0: */ michael@0: public String getWrapperFileContents() { michael@0: wrapperStartupCode.append("}\n"); michael@0: zeroingCode.append(wrapperStartupCode) michael@0: .append(wrapperMethodBodies); michael@0: wrapperMethodBodies.setLength(0); michael@0: wrapperStartupCode.setLength(0); michael@0: return zeroingCode.toString(); michael@0: } michael@0: michael@0: /** michael@0: * Get the finalised bytes to go into the generated header file. michael@0: * michael@0: * @return The bytes to be written to the header file. michael@0: */ michael@0: public String getHeaderFileContents() { michael@0: if (!mHasEncounteredDefaultConstructor) { michael@0: headerPublic.append(" ").append(mCClassName).append("() : AutoGlobalWrappedJavaObject() {};\n"); michael@0: } michael@0: headerProtected.append("};\n\n"); michael@0: headerPublic.append(headerProtected); michael@0: headerProtected.setLength(0); michael@0: return headerPublic.toString(); michael@0: } michael@0: }