Bug 913985: Part 3 - Update the annotation processor to generate wrapper classes. r=kats
☠☠ backed out by b4bfcc931474 ☠ ☠
authorChris Kitching <ckitching@mozilla.com>
Mon, 30 Sep 2013 09:07:22 +0200
changeset 149231 b44508a5c02fec51d896e8f10d05c8e45a51f5ee
parent 149230 08844da6d9edcad252e780864f345a2f1855cd26
child 149232 d4aa6cf97712d3ca516d185ce8a0b8e994ef42dd
push id25380
push userryanvm@gmail.com
push dateMon, 30 Sep 2013 20:16:36 +0000
treeherdermozilla-central@1332fc1c15e1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs913985
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 913985: Part 3 - Update the annotation processor to generate wrapper classes. r=kats
build/annotationProcessors/AnnotationInfo.java
build/annotationProcessors/AnnotationProcessor.java
build/annotationProcessors/CodeGenerator.java
build/annotationProcessors/MethodWithAnnotationInfo.java
build/annotationProcessors/classloader/AnnotatableEntity.java
build/annotationProcessors/classloader/ClassWithOptions.java
build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java
build/annotationProcessors/classloader/JarClassIterator.java
build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java
build/annotationProcessors/utils/AlphabeticMethodComparator.java
build/annotationProcessors/utils/GeneratableElementIterator.java
build/annotationProcessors/utils/GeneratableEntryPointIterator.java
build/annotationProcessors/utils/Utils.java
mobile/android/base/Makefile.in
rename from build/annotationProcessors/MethodWithAnnotationInfo.java
rename to build/annotationProcessors/AnnotationInfo.java
--- a/build/annotationProcessors/MethodWithAnnotationInfo.java
+++ b/build/annotationProcessors/AnnotationInfo.java
@@ -1,24 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors;
 
-import java.lang.reflect.Method;
-
 /**
- * Object holding method and annotation. Used by GeneratableEntryPointIterator.
+ * Object holding annotation data. Used by GeneratableElementIterator.
  */
-public class MethodWithAnnotationInfo {
-    public final Method method;
+public class AnnotationInfo {
     public final String wrapperName;
     public final boolean isStatic;
     public final boolean isMultithreaded;
 
-    public MethodWithAnnotationInfo(Method aMethod, String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
-        method = aMethod;
+    public AnnotationInfo(String aWrapperName, boolean aIsStatic, boolean aIsMultithreaded) {
         wrapperName = aWrapperName;
         isStatic = aIsStatic;
         isMultithreaded = aIsMultithreaded;
     }
 }
--- a/build/annotationProcessors/AnnotationProcessor.java
+++ b/build/annotationProcessors/AnnotationProcessor.java
@@ -1,26 +1,34 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors;
 
+import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
+import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
 import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
-import org.mozilla.gecko.annotationProcessors.utils.GeneratableEntryPointIterator;
+import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
 
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
 import java.util.Iterator;
 
 public class AnnotationProcessor {
     public static final String OUTFILE = "GeneratedJNIWrappers.cpp";
     public static final String HEADERFILE = "GeneratedJNIWrappers.h";
 
+    public static final String GENERATED_COMMENT =
+            "// GENERATED CODE\n" +
+            "// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
+            "// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
+            "// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
+
     public static void main(String[] args) {
         // We expect a list of jars on the commandline. If missing, whinge about it.
         if (args.length <= 1) {
             System.err.println("Usage: java AnnotationProcessor jarfiles ...");
             System.exit(1);
         }
 
         System.out.println("Processing annotations...");
@@ -28,49 +36,126 @@ public class AnnotationProcessor {
         // We want to produce the same output as last time as often as possible. Ordering of
         // generated statements, therefore, needs to be consistent.
         Arrays.sort(args);
 
         // Start the clock!
         long s = System.currentTimeMillis();
 
         // Get an iterator over the classes in the jar files given...
-        Iterator<Class<?>> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+        Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
+
+        StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
+        headerFile.append("#ifndef GeneratedJNIWrappers_h__\n" +
+                          "#define GeneratedJNIWrappers_h__\n\n" +
+                          "#include \"nsXPCOMStrings.h\"\n" +
+                          "#include \"AndroidJavaWrappers.h\"\n" +
+                          "\n" +
+                          "namespace mozilla {\n" +
+                          "namespace widget {\n" +
+                          "namespace android {\n" +
+                          "void InitStubs(JNIEnv *jEnv);\n\n");
 
-        CodeGenerator generatorInstance = new CodeGenerator();
+        StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
+        implementationFile.append("#include \"GeneratedJNIWrappers.h\"\n" +
+                                  "#include \"AndroidBridgeUtilities.h\"\n" +
+                                  "#include \"nsXPCOMStrings.h\"\n" +
+                                  "#include \"AndroidBridge.h\"\n" +
+                                  "\n" +
+                                  "namespace mozilla {\n" +
+                                  "namespace widget {\n" +
+                                  "namespace android {\n");
+
+        // Used to track the calls to the various class-specific initialisation functions.
+        StringBuilder stubInitialiser = new StringBuilder();
+        stubInitialiser.append("void InitStubs(JNIEnv *jEnv) {\n");
 
         while (jarClassIterator.hasNext()) {
-            Class<?> aClass = jarClassIterator.next();
+            ClassWithOptions aClassTuple = jarClassIterator.next();
+
+            CodeGenerator generatorInstance;
 
             // Get an iterator over the appropriately generated methods of this class
-            Iterator<MethodWithAnnotationInfo> methodIterator = new GeneratableEntryPointIterator(aClass.getDeclaredMethods());
+            Iterator<AnnotatableEntity> methodIterator = new GeneratableElementIterator(aClassTuple.wrappedClass);
+
+            if (!methodIterator.hasNext()) {
+                continue;
+            }
+            generatorInstance = new CodeGenerator(aClassTuple.wrappedClass, aClassTuple.generatedName);
+
+            stubInitialiser.append("    ").append(aClassTuple.generatedName).append("::InitStubs(jEnv);\n");
 
-            // Iterate all annotated methods in this class..
+            // Iterate all annotated members in this class..
             while (methodIterator.hasNext()) {
-                MethodWithAnnotationInfo aMethodTuple = methodIterator.next();
-                generatorInstance.generateMethod(aMethodTuple, aClass);
+                AnnotatableEntity aElementTuple = methodIterator.next();
+                switch (aElementTuple.mEntityType) {
+                    case METHOD:
+                        generatorInstance.generateMethod(aElementTuple);
+                        break;
+                    case FIELD:
+                        generatorInstance.generateField(aElementTuple);
+                        break;
+                    case CONSTRUCTOR:
+                        generatorInstance.generateConstructor(aElementTuple);
+                        break;
+                }
             }
 
+            headerFile.append(generatorInstance.getHeaderFileContents());
+            implementationFile.append(generatorInstance.getWrapperFileContents());
         }
 
-        writeOutputFiles(generatorInstance);
+        implementationFile.append('\n');
+        stubInitialiser.append("}");
+        implementationFile.append(stubInitialiser);
+
+        implementationFile.append("\n} /* android */\n" +
+                                    "} /* widget */\n" +
+                                    "} /* mozilla */\n");
+
+        headerFile.append("\n} /* android */\n" +
+                            "} /* widget */\n" +
+                            "} /* mozilla */\n" +
+                            "#endif\n");
+
+        writeOutputFiles(headerFile, implementationFile);
         long e = System.currentTimeMillis();
         System.out.println("Annotation processing complete in " + (e - s) + "ms");
     }
 
-    private static void writeOutputFiles(CodeGenerator aGenerator) {
+    private static void writeOutputFiles(StringBuilder aHeaderFile, StringBuilder aImplementationFile) {
+        FileOutputStream headerStream = null;
         try {
-            FileOutputStream outStream = new FileOutputStream(OUTFILE);
-            outStream.write(aGenerator.getWrapperFileContents());
+            headerStream = new FileOutputStream(OUTFILE);
+            headerStream.write(aImplementationFile.toString().getBytes());
         } catch (IOException e) {
             System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
             e.printStackTrace(System.err);
+        } finally {
+            if (headerStream != null) {
+                try {
+                    headerStream.close();
+                } catch (IOException e) {
+                    System.err.println("Unable to close headerStream due to "+e);
+                    e.printStackTrace(System.err);
+                }
+            }
         }
 
+        FileOutputStream outStream = null;
         try {
-            FileOutputStream headerStream = new FileOutputStream(HEADERFILE);
-            headerStream.write(aGenerator.getHeaderFileContents());
+            outStream = new FileOutputStream(HEADERFILE);
+            outStream.write(aHeaderFile.toString().getBytes());
         } catch (IOException e) {
-            System.err.println("Unable to write " + OUTFILE + ". Perhaps a permissions issue?");
+            System.err.println("Unable to write " + HEADERFILE + ". Perhaps a permissions issue?");
             e.printStackTrace(System.err);
+        } finally {
+            if (outStream != null) {
+                try {
+                    outStream.close();
+                } catch (IOException e) {
+                    System.err.println("Unable to close outStream due to "+e);
+                    e.printStackTrace(System.err);
+                }
+            }
         }
     }
 }
\ No newline at end of file
--- a/build/annotationProcessors/CodeGenerator.java
+++ b/build/annotationProcessors/CodeGenerator.java
@@ -1,336 +1,618 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors;
 
+import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
 import org.mozilla.gecko.annotationProcessors.utils.Utils;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.HashMap;
 import java.util.HashSet;
 
 public class CodeGenerator {
+    private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
+    private static final Annotation[][] GETTER_ARGUMENT_ANNOTATIONS = new Annotation[0][0];
+    private static final Annotation[][] SETTER_ARGUMENT_ANNOTATIONS = new Annotation[1][0];
+
     // Buffers holding the strings to ultimately be written to the output files.
+    private final StringBuilder zeroingCode = new StringBuilder();
     private final StringBuilder wrapperStartupCode = new StringBuilder();
     private final StringBuilder wrapperMethodBodies = new StringBuilder();
-    private final StringBuilder headerFields = new StringBuilder();
-    private final StringBuilder headerMethods = new StringBuilder();
+    private final StringBuilder headerPublic = new StringBuilder();
+    private final StringBuilder headerProtected = new StringBuilder();
 
     private final HashSet<String> seenClasses = new HashSet<String>();
 
-    private final String GENERATED_COMMENT = "// GENERATED CODE\n" +
-            "// Generated by the Java program at /build/annotationProcessors at compile time from\n" +
-            "// annotations on Java methods. To update, change the annotations on the corresponding Java\n" +
-            "// methods and rerun the build. Manually updating this file will cause your build to fail.\n\n";
+    private final String mCClassName;
+
+    private final Class<?> mClassToWrap;
+
+    private boolean mHasEncounteredDefaultConstructor;
 
-    public CodeGenerator() {
+    // Used for creating unique names for method ID fields in the face of
+    private final HashMap<Member, String> mMembersToIds = new HashMap<Member, String>();
+    private final HashSet<String> mTakenMemberNames = new HashSet<String>();
+    private int mNameMunger;
+
+    public CodeGenerator(Class<?> aClass, String aGeneratedName) {
+        mClassToWrap = aClass;
+        mCClassName = aGeneratedName;
+
         // Write the file header things. Includes and so forth.
         // GeneratedJNIWrappers.cpp is generated as the concatenation of wrapperStartupCode with
-        // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerFields
-        // with headerMethods.
-        wrapperStartupCode.append(GENERATED_COMMENT);
-        wrapperStartupCode.append(
-                "#include \"nsXPCOMStrings.h\"\n" +
-                "#include \"AndroidBridge.h\"\n" +
-                "#include \"AndroidBridgeUtilities.h\"\n" +
-                "\n" +
-                "#ifdef DEBUG\n" +
-                "#define ALOG_BRIDGE(args...) ALOG(args)\n" +
-                "#else\n" +
-                "#define ALOG_BRIDGE(args...) ((void)0)\n" +
-                "#endif\n" +
-                "\n" +
-                "using namespace mozilla;\n" +
-                "void AndroidBridge::InitStubs(JNIEnv *jEnv) {\n" +
-                "    initInit();\n");
+        // wrapperMethodBodies. Similarly, GeneratedJNIWrappers.h is the concatenation of headerPublic
+        // with headerProtected.
+        wrapperStartupCode.append("void ").append(mCClassName).append("::InitStubs(JNIEnv *jEnv) {\n" +
+                                  "    initInit();\n");
+
         // Now we write the various GetStaticMethodID calls here...
+        headerPublic.append("class ").append(mCClassName).append(" : public AutoGlobalWrappedJavaObject {\n" +
+                            "public:\n" +
+                            "    static void InitStubs(JNIEnv *jEnv);\n");
+        headerProtected.append("protected:");
 
-        headerFields.append("protected:\n\n");
-        headerMethods.append(GENERATED_COMMENT);
-        headerMethods.append("public:\n\n");
+        generateWrapperMethod();
+    }
+
+    /**
+     * Emit a static method which takes an instance of the class being wrapped and returns an instance
+     * of the C++ wrapper class backed by that object.
+     */
+    private void generateWrapperMethod() {
+        headerPublic.append("    static ").append(mCClassName).append("* Wrap(jobject obj);\n" +
+                "    ").append(mCClassName).append("(jobject obj, JNIEnv* env) : AutoGlobalWrappedJavaObject(obj, env) {};\n");
+
+        wrapperMethodBodies.append("\n").append(mCClassName).append("* ").append(mCClassName).append("::Wrap(jobject obj) {\n" +
+                "    JNIEnv *env = GetJNIForThread();\n\n" +
+                "    if (!env) {\n" +
+                "        ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
+                "        return NULL;\n" +
+                "    }\n\n" +
+                "    ").append(mCClassName).append("* ret = new ").append(mCClassName).append("(obj, env);\n" +
+                "    env->DeleteLocalRef(obj);\n" +
+                "    return ret;\n" +
+                "}\n");
+    }
+
+    private void generateMemberCommon(Member theMethod, String aCMethodName, Class<?> aClass) {
+        ensureClassHeaderAndStartup(aClass);
+        writeMemberIdField(theMethod, aCMethodName);
+        writeStartupCode(theMethod);
     }
 
     /**
      * Append the appropriate generated code to the buffers for the method provided.
      *
-     * @param aMethodTuple The Java method, plus the name for the generated method.
-     * @param aClass       The class to which the Java method belongs.
+     * @param aMethodTuple The Java method, plus annotation data.
      */
-    public void generateMethod(MethodWithAnnotationInfo aMethodTuple, Class<?> aClass) {
+    public void generateMethod(AnnotatableEntity aMethodTuple) {
         // Unpack the tuple and extract some useful fields from the Method..
-        Method aMethod = aMethodTuple.method;
-        String CMethodName = aMethodTuple.wrapperName;
+        Method theMethod = aMethodTuple.getMethod();
 
-        String javaMethodName = aMethod.getName();
+        String CMethodName = aMethodTuple.mAnnotationInfo.wrapperName;
+
+        generateMemberCommon(theMethod, CMethodName, mClassToWrap);
 
-        ensureClassHeaderAndStartup(aClass);
+        boolean isFieldStatic = Utils.isMemberStatic(theMethod);
+        boolean shallGenerateStatic = isFieldStatic || aMethodTuple.mAnnotationInfo.isStatic;
 
-        writeHeaderField(CMethodName);
-        writeStartupCode(CMethodName, javaMethodName, aMethod, aClass);
+        Class<?>[] parameterTypes = theMethod.getParameterTypes();
+        Class<?> returnType = theMethod.getReturnType();
 
         // Get the C++ method signature for this method.
-        String implementationSignature = Utils.getCImplementationMethodSignature(aMethod, CMethodName);
-        String headerSignature = Utils.getCHeaderMethodSignature(aMethod, CMethodName, aMethodTuple.isStatic);
+        String implementationSignature = Utils.getCImplementationMethodSignature(parameterTypes, returnType, CMethodName, mCClassName);
+        String headerSignature = Utils.getCHeaderMethodSignature(parameterTypes, theMethod.getParameterAnnotations(), returnType, CMethodName, mCClassName, shallGenerateStatic);
 
         // Add the header signature to the header file.
-        headerMethods.append(headerSignature);
-        headerMethods.append(";\n");
+        writeSignatureToHeader(headerSignature);
 
         // Use the implementation signature to generate the method body...
-        writeMethodBody(implementationSignature, CMethodName, aMethod, aClass, aMethodTuple.isStatic, aMethodTuple.isMultithreaded);
+        writeMethodBody(implementationSignature, CMethodName, theMethod, mClassToWrap, aMethodTuple.mAnnotationInfo.isStatic, aMethodTuple.mAnnotationInfo.isMultithreaded);
+    }
+
+    private void generateGetterOrSetterBody(Class<?> aFieldType, String aFieldName, boolean aIsFieldStatic, boolean isSetter) {
+        StringBuilder argumentContent = null;
+
+        if (isSetter) {
+            Class<?>[] setterArguments = new Class<?>[]{aFieldType};
+            // Marshall the argument..
+            argumentContent = getArgumentMarshalling(setterArguments);
+        }
+
+        boolean isObjectReturningMethod = Utils.isObjectType(aFieldType);
+        wrapperMethodBodies.append("    ");
+        if (isSetter) {
+            wrapperMethodBodies.append("env->Set");
+        } else {
+            wrapperMethodBodies.append("return ");
+
+            if (isObjectReturningMethod) {
+                wrapperMethodBodies.append("static_cast<").append(Utils.getCReturnType(aFieldType)).append(">(");
+            }
+
+            wrapperMethodBodies.append("env->Get");
+        }
+
+        if (aIsFieldStatic) {
+            wrapperMethodBodies.append("Static");
+        }
+        wrapperMethodBodies.append(Utils.getFieldType(aFieldType))
+                           .append("Field(");
+
+        // Static will require the class and the field id. Nonstatic, the object and the field id.
+        if (aIsFieldStatic) {
+            wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap));
+        } else {
+            wrapperMethodBodies.append("wrapped_obj");
+        }
+        wrapperMethodBodies.append(", j")
+                           .append(aFieldName);
+        if (isSetter) {
+            wrapperMethodBodies.append(argumentContent);
+        }
+
+        if (!isSetter && isObjectReturningMethod) {
+            wrapperMethodBodies.append(')');
+        }
+        wrapperMethodBodies.append(");\n" +
+                               "}\n");
+    }
+
+    public void generateField(AnnotatableEntity aFieldTuple) {
+        Field theField = aFieldTuple.getField();
+
+        // Handles a peculiar case when dealing with enum types. We don't care about this field.
+        // It just gets in the way and stops our code from compiling.
+        if (theField.getName().equals("$VALUES")) {
+            return;
+        }
+
+        String CFieldName = aFieldTuple.mAnnotationInfo.wrapperName;
+
+        Class<?> fieldType = theField.getType();
+
+        generateMemberCommon(theField, CFieldName, mClassToWrap);
+
+        boolean isFieldStatic = Utils.isMemberStatic(theField);
+        boolean isFieldFinal = Utils.isMemberFinal(theField);
+        boolean shallGenerateStatic = isFieldStatic || aFieldTuple.mAnnotationInfo.isStatic;
+
+        String getterName = "get" + CFieldName;
+        String getterSignature = Utils.getCImplementationMethodSignature(EMPTY_CLASS_ARRAY, fieldType, getterName, mCClassName);
+        String getterHeaderSignature = Utils.getCHeaderMethodSignature(EMPTY_CLASS_ARRAY, GETTER_ARGUMENT_ANNOTATIONS, fieldType, getterName, mCClassName, shallGenerateStatic);
+
+        writeSignatureToHeader(getterHeaderSignature);
+
+        writeFunctionStartupBoilerPlate(getterSignature, fieldType, isFieldStatic, true);
+
+        generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, false);
+
+        // If field not final, also generate a setter function.
+        if (!isFieldFinal) {
+            String setterName = "set" + CFieldName;
+
+            Class<?>[] setterArguments = new Class<?>[]{fieldType};
+
+            String setterSignature = Utils.getCImplementationMethodSignature(setterArguments, Void.class, setterName, mCClassName);
+            String setterHeaderSignature = Utils.getCHeaderMethodSignature(setterArguments, SETTER_ARGUMENT_ANNOTATIONS, Void.class, setterName, mCClassName, shallGenerateStatic);
+
+            writeSignatureToHeader(setterHeaderSignature);
+
+            writeFunctionStartupBoilerPlate(setterSignature, Void.class, isFieldStatic, true);
+
+            generateGetterOrSetterBody(fieldType, CFieldName, isFieldStatic, true);
+        }
+    }
+
+    public void generateConstructor(AnnotatableEntity aCtorTuple) {
+        // Unpack the tuple and extract some useful fields from the Method..
+        Constructor theCtor = aCtorTuple.getConstructor();
+        String CMethodName = mCClassName;
+
+        generateMemberCommon(theCtor, mCClassName, mClassToWrap);
+
+        String implementationSignature = Utils.getCImplementationMethodSignature(theCtor.getParameterTypes(), Void.class, CMethodName, mCClassName);
+        String headerSignature = Utils.getCHeaderMethodSignature(theCtor.getParameterTypes(), theCtor.getParameterAnnotations(), Void.class, CMethodName, mCClassName, false);
+
+        // Slice off the "void " from the start of the constructor declaration.
+        headerSignature = headerSignature.substring(5);
+        implementationSignature = implementationSignature.substring(5);
+
+        // Add the header signatures to the header file.
+        writeSignatureToHeader(headerSignature);
+
+        // Use the implementation signature to generate the method body...
+        writeCtorBody(implementationSignature, theCtor, aCtorTuple.mAnnotationInfo.isMultithreaded);
+
+        if (theCtor.getParameterTypes().length == 0) {
+            mHasEncounteredDefaultConstructor = true;
+        }
     }
 
     /**
      * Writes the appropriate header and startup code to ensure the existence of a reference to the
      * class specified. If this is already done, does nothing.
      *
      * @param aClass The target class.
      */
     private void ensureClassHeaderAndStartup(Class<?> aClass) {
         String className = aClass.getCanonicalName();
         if (seenClasses.contains(className)) {
             return;
         }
 
+        zeroingCode.append("jclass ")
+                   .append(mCClassName)
+                   .append("::")
+                   .append(Utils.getClassReferenceName(aClass))
+                   .append(" = 0;\n");
+
         // Add a field to hold the reference...
-        headerFields.append("\njclass ");
-        headerFields.append(Utils.getClassReferenceName(aClass));
-        headerFields.append(";\n");
+        headerProtected.append("\n    static jclass ")
+                       .append(Utils.getClassReferenceName(aClass))
+                       .append(";\n");
 
         // Add startup code to populate it..
-        wrapperStartupCode.append('\n');
-        wrapperStartupCode.append(Utils.getStartupLineForClass(aClass));
+        wrapperStartupCode.append('\n')
+                          .append(Utils.getStartupLineForClass(aClass));
 
         seenClasses.add(className);
     }
 
     /**
+     * Write out the function startup boilerplate for the method described. Check for environment
+     * existence,
+     * @param methodSignature
+     * @param returnType
+     * @param aIsStatic
+     * @param aIsThreaded
+     */
+    private void writeFunctionStartupBoilerPlate(String methodSignature, Class<?> returnType, boolean aIsStatic, boolean aIsThreaded) {
+        // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
+        wrapperMethodBodies.append('\n')
+                           .append(methodSignature)
+                           .append(" {\n");
+
+        wrapperMethodBodies.append("    JNIEnv *env = ");
+        if (!aIsThreaded) {
+            wrapperMethodBodies.append("AndroidBridge::GetJNIEnv();\n");
+        } else {
+            wrapperMethodBodies.append("GetJNIForThread();\n");
+        }
+        wrapperMethodBodies.append("    if (!env) {\n" +
+                                   "        ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
+                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                "    }\n\n");
+    }
+
+    /**
+     * Write out the appropriate JNI frame pushing boilerplate for a call to the member provided (
+     * which must be a constructor or method).
+     *
+     * @param aMethod A constructor/method being wrapped.
+     * @param aIsObjectReturningMethod Does the method being wrapped return an object?
+     */
+    private void writeFramePushBoilerplate(Member aMethod, boolean aIsObjectReturningMethod) {
+        if (aMethod instanceof Field) {
+            throw new IllegalArgumentException("Tried to push frame for a FIELD?!");
+        }
+
+        Method m;
+        Constructor c;
+
+        Class<?> returnType;
+
+        int localReferencesNeeded;
+        if (aMethod instanceof Method) {
+            m = (Method) aMethod;
+            returnType = m.getReturnType();
+            localReferencesNeeded = Utils.enumerateReferenceArguments(m.getParameterTypes());
+        } else {
+            c = (Constructor) aMethod;
+            returnType = Void.class;
+            localReferencesNeeded = Utils.enumerateReferenceArguments(c.getParameterTypes());
+        }
+
+        // Determine the number of local refs required for our local frame..
+        // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
+        if (aIsObjectReturningMethod) {
+            localReferencesNeeded++;
+        }
+        wrapperMethodBodies.append("    if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
+                "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
+                "        env->ExceptionDescribe();\n"+
+                "        env->ExceptionClear();\n" +
+                "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
+                "    }\n\n");
+
+    }
+
+    private StringBuilder getArgumentMarshalling(Class<?>[] argumentTypes) {
+        StringBuilder argumentContent = new StringBuilder();
+
+        // If we have >2 arguments, use the jvalue[] calling approach.
+        argumentContent.append(", ");
+        if (argumentTypes.length > 2) {
+            wrapperMethodBodies.append("    jvalue args[").append(argumentTypes.length).append("];\n");
+            for (int aT = 0; aT < argumentTypes.length; aT++) {
+                wrapperMethodBodies.append("    args[").append(aT).append("].")
+                                   .append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
+            }
+
+            // The only argument is the array of arguments.
+            argumentContent.append("args");
+            wrapperMethodBodies.append('\n');
+        } else {
+            // Otherwise, use the vanilla calling approach.
+            boolean needsNewline = false;
+            for (int aT = 0; aT < argumentTypes.length; aT++) {
+                // If the argument is a string-esque type, create a jstring from it, otherwise
+                // it can be passed directly.
+                if (Utils.isCharSequence(argumentTypes[aT])) {
+                    wrapperMethodBodies.append("    jstring j").append(aT).append(" = AndroidBridge::NewJavaString(env, a").append(aT).append(");\n");
+                    needsNewline = true;
+                    // Ensure we refer to the newly constructed Java string - not to the original
+                    // parameter to the wrapper function.
+                    argumentContent.append('j').append(aT);
+                } else {
+                    argumentContent.append('a').append(aT);
+                }
+                if (aT != argumentTypes.length - 1) {
+                    argumentContent.append(", ");
+                }
+            }
+            if (needsNewline) {
+                wrapperMethodBodies.append('\n');
+            }
+        }
+
+        return argumentContent;
+    }
+
+    private void writeCtorBody(String implementationSignature, Constructor theCtor, boolean aIsThreaded) {
+        Class<?>[] argumentTypes = theCtor.getParameterTypes();
+
+        writeFunctionStartupBoilerPlate(implementationSignature, Void.class, false, aIsThreaded);
+
+        writeFramePushBoilerplate(theCtor, false);
+
+        // Marshall arguments for this constructor, if any...
+        boolean hasArguments = argumentTypes.length != 0;
+
+        StringBuilder argumentContent = new StringBuilder();
+        if (hasArguments) {
+            argumentContent = getArgumentMarshalling(argumentTypes);
+        }
+
+        // The call into Java
+        wrapperMethodBodies.append("    Init(env->NewObject");
+        if (argumentTypes.length > 2) {
+            wrapperMethodBodies.append('A');
+        }
+
+        wrapperMethodBodies.append('(');
+
+
+        // Call takes class id, method id of constructor method, then arguments.
+        wrapperMethodBodies.append(Utils.getClassReferenceName(mClassToWrap)).append(", ");
+
+        wrapperMethodBodies.append(mMembersToIds.get(theCtor))
+        // Tack on the arguments, if any..
+                           .append(argumentContent)
+                           .append("), env);\n" +
+                                   "    env->PopLocalFrame(NULL);\n" +
+                                   "}\n");
+    }
+
+    /**
      * Generates the method body of the C++ wrapper function for the Java method indicated.
      *
      * @param methodSignature The previously-generated C++ method signature for the method to be
      *                        generated.
      * @param aCMethodName    The C++ method name for the method to be generated.
      * @param aMethod         The Java method to be wrapped by the C++ method being generated.
      * @param aClass          The Java class to which the method belongs.
      */
     private void writeMethodBody(String methodSignature, String aCMethodName, Method aMethod, Class<?> aClass, boolean aIsStaticBridgeMethod, boolean aIsMultithreaded) {
         Class<?>[] argumentTypes = aMethod.getParameterTypes();
         Class<?> returnType = aMethod.getReturnType();
 
-        // The start-of-function boilerplate. Does the bridge exist? Does the env exist? etc.
-        wrapperMethodBodies.append('\n');
-        wrapperMethodBodies.append(methodSignature);
-
-        wrapperMethodBodies.append(" {\n");
+        writeFunctionStartupBoilerPlate(methodSignature, returnType, aIsStaticBridgeMethod, aIsMultithreaded);
 
-        // Static stubs check the bridge instance has been created before trying to run.
-        if (aIsStaticBridgeMethod) {
-            wrapperMethodBodies.append("    if (!sBridge) {\n" +
-                                       "        ALOG_BRIDGE(\"Aborted: No sBridge - %s\", __PRETTY_FUNCTION__);\n" +
-                                       "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
-                                       "    }\n\n");
-        }
-        wrapperMethodBodies.append("    JNIEnv *env = ");
-        if (!aIsMultithreaded) {
-            wrapperMethodBodies.append("GetJNIEnv();\n");
-        } else {
-            wrapperMethodBodies.append("GetJNIForThread();\n");
-        }
-        wrapperMethodBodies.append("    if (!env) {\n" +
-                                   "        ALOG_BRIDGE(\"Aborted: No env - %s\", __PRETTY_FUNCTION__);\n" +
-                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
-                                   "    }\n\n");
+        boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.isObjectType(returnType);
 
-        boolean isObjectReturningMethod = !returnType.getCanonicalName().equals("void") && Utils.doesReturnObjectType(aMethod);
-
-        // Determine the number of local refs required for our local frame..
-        // AutoLocalJNIFrame is not applicable here due to it's inability to handle return values.
-        int localReferencesNeeded = Utils.enumerateReferenceArguments(aMethod);
-        if (isObjectReturningMethod) {
-            localReferencesNeeded++;
-        }
-        wrapperMethodBodies.append("    if (env->PushLocalFrame(").append(localReferencesNeeded).append(") != 0) {\n" +
-                                   "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
-                                   "        env->ExceptionDescribe();\n"+
-                                   "        env->ExceptionClear();\n" +
-                                   "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
-                                   "    }\n\n");
+        writeFramePushBoilerplate(aMethod, isObjectReturningMethod);
 
         // Marshall arguments, if we have any.
         boolean hasArguments = argumentTypes.length != 0;
 
         // We buffer the arguments to the call separately to avoid needing to repeatedly iterate the
         // argument list while building this line. In the coming code block, we simultaneously
         // construct any argument marshalling code (Creation of jstrings, placement of arguments
         // into an argument array, etc. and the actual argument list passed to the function (in
         // argumentContent).
         StringBuilder argumentContent = new StringBuilder();
         if (hasArguments) {
-            argumentContent.append(", ");
-            // If we have >2 arguments, use the jvalue[] calling approach.
-            if (argumentTypes.length > 2) {
-                wrapperMethodBodies.append("    jvalue args[").append(argumentTypes.length).append("];\n");
-                for (int aT = 0; aT < argumentTypes.length; aT++) {
-                    wrapperMethodBodies.append("    args[").append(aT).append("].");
-                    wrapperMethodBodies.append(Utils.getArrayArgumentMashallingLine(argumentTypes[aT], "a" + aT));
-                }
-
-                // The only argument is the array of arguments.
-                argumentContent.append("args");
-                wrapperMethodBodies.append('\n');
-            } else {
-                // Otherwise, use the vanilla calling approach.
-                boolean needsNewline = false;
-                for (int aT = 0; aT < argumentTypes.length; aT++) {
-                    // If the argument is a string-esque type, create a jstring from it, otherwise
-                    // it can be passed directly.
-                    if (Utils.isCharSequence(argumentTypes[aT])) {
-                        wrapperMethodBodies.append("    jstring j").append(aT).append(" = NewJavaString(env, a").append(aT).append(");\n");
-                        needsNewline = true;
-                        // Ensure we refer to the newly constructed Java string - not to the original
-                        // parameter to the wrapper function.
-                        argumentContent.append('j').append(aT);
-                    } else {
-                        argumentContent.append('a').append(aT);
-                    }
-                    if (aT != argumentTypes.length - 1) {
-                        argumentContent.append(", ");
-                    }
-                }
-                if (needsNewline) {
-                    wrapperMethodBodies.append('\n');
-                }
-            }
+            argumentContent = getArgumentMarshalling(argumentTypes);
         }
 
+        // Allocate a temporary variable to hold the return type from Java.
         wrapperMethodBodies.append("    ");
         if (!returnType.getCanonicalName().equals("void")) {
             if (isObjectReturningMethod) {
                 wrapperMethodBodies.append("jobject");
             } else {
                 wrapperMethodBodies.append(Utils.getCReturnType(returnType));
             }
             wrapperMethodBodies.append(" temp = ");
         }
 
-        boolean isStaticJavaMethod = Utils.isMethodStatic(aMethod);
+        boolean isStaticJavaMethod = Utils.isMemberStatic(aMethod);
 
         // The call into Java
-        wrapperMethodBodies.append("env->");
-        wrapperMethodBodies.append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
+        wrapperMethodBodies.append("env->")
+                           .append(Utils.getCallPrefix(returnType, isStaticJavaMethod));
         if (argumentTypes.length > 2) {
             wrapperMethodBodies.append('A');
         }
 
         wrapperMethodBodies.append('(');
         // If the underlying Java method is nonstatic, we provide the target object to the JNI.
         if (!isStaticJavaMethod) {
-            wrapperMethodBodies.append("aTarget, ");
+            wrapperMethodBodies.append("wrapped_obj, ");
         } else {
-            // If the stub to be generated is static, we need to use the singleton to access the class
-            // reference.
-            if (aIsStaticBridgeMethod) {
-                wrapperMethodBodies.append("sBridge->");
-            }
-            // If this is a static underlyin Java method, we need to use the class reference in our
+            // If this is a static underlying Java method, we need to use the class reference in our
             // call.
             wrapperMethodBodies.append(Utils.getClassReferenceName(aClass)).append(", ");
         }
 
-        // Write the method id out..
-        if (aIsStaticBridgeMethod) {
-            wrapperMethodBodies.append("sBridge->");
-        }
-        wrapperMethodBodies.append('j');
-        wrapperMethodBodies.append(aCMethodName);
+        wrapperMethodBodies.append(mMembersToIds.get(aMethod));
 
         // Tack on the arguments, if any..
-        wrapperMethodBodies.append(argumentContent);
-        wrapperMethodBodies.append(");\n\n");
+        wrapperMethodBodies.append(argumentContent)
+                           .append(");\n\n");
 
         // Check for exception and return the failure value..
         wrapperMethodBodies.append("    if (env->ExceptionCheck()) {\n" +
                                    "        ALOG_BRIDGE(\"Exceptional exit of: %s\", __PRETTY_FUNCTION__);\n" +
                                    "        env->ExceptionDescribe();\n" +
                                    "        env->ExceptionClear();\n" +
                                    "        env->PopLocalFrame(NULL);\n" +
                                    "        return").append(Utils.getFailureReturnForType(returnType)).append(";\n" +
-                                   "    }\n");
+                                   "    }\n\n");
 
         // If we're returning an object, pop the callee's stack frame extracting our ref as the return
         // value.
         if (isObjectReturningMethod) {
-            wrapperMethodBodies.append("    ");
-            wrapperMethodBodies.append(Utils.getCReturnType(returnType));
-            wrapperMethodBodies.append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
+            wrapperMethodBodies.append("    ")
+                               .append(Utils.getCReturnType(returnType))
+                               .append(" ret = static_cast<").append(Utils.getCReturnType(returnType)).append(">(env->PopLocalFrame(temp));\n" +
                                        "    return ret;\n");
         } else if (!returnType.getCanonicalName().equals("void")) {
             // If we're a primitive-returning function, just return the directly-obtained primative
             // from the call to Java.
             wrapperMethodBodies.append("    env->PopLocalFrame(NULL);\n" +
                                        "    return temp;\n");
         } else {
             // If we don't return anything, just pop the stack frame and move on with life.
             wrapperMethodBodies.append("    env->PopLocalFrame(NULL);\n");
         }
         wrapperMethodBodies.append("}\n");
     }
 
     /**
-     * Generates the code to get the method id of the given method on startup.
+     * Generates the code to get the id of the given member on startup.
      *
-     * @param aCMethodName    The C method name of the method being generated.
-     * @param aJavaMethodName The name of the Java method being wrapped.
-     * @param aMethod         The Java method being wrapped.
+     * @param aMember         The Java member being wrapped.
      */
-    private void writeStartupCode(String aCMethodName, String aJavaMethodName, Method aMethod, Class<?> aClass) {
-        wrapperStartupCode.append("    j");
-        wrapperStartupCode.append(aCMethodName);
-        wrapperStartupCode.append(" = get");
-        if (Utils.isMethodStatic(aMethod)) {
+    private void writeStartupCode(Member aMember) {
+        wrapperStartupCode.append("    ")
+                          .append(mMembersToIds.get(aMember))
+                          .append(" = get");
+        if (Utils.isMemberStatic(aMember)) {
             wrapperStartupCode.append("Static");
         }
-        wrapperStartupCode.append("Method(\"");
-        wrapperStartupCode.append(aJavaMethodName);
-        wrapperStartupCode.append("\", \"");
-        wrapperStartupCode.append(Utils.getTypeSignatureString(aMethod));
-        wrapperStartupCode.append("\");\n");
+
+        boolean isField = aMember instanceof Field;
+        if (isField) {
+            wrapperStartupCode.append("Field(\"");
+        } else {
+            wrapperStartupCode.append("Method(\"");
+        }
+        if (aMember instanceof Constructor) {
+            wrapperStartupCode.append("<init>");
+        } else {
+            wrapperStartupCode.append(aMember.getName());
+        }
+
+        wrapperStartupCode.append("\", \"")
+                          .append(Utils.getTypeSignatureStringForMember(aMember))
+                          .append("\");\n");
+    }
+
+    private void writeZeroingFor(Member aMember, final String aMemberName) {
+        if (aMember instanceof Field) {
+            zeroingCode.append("jfieldID ");
+        } else {
+            zeroingCode.append("jmethodID ");
+        }
+        zeroingCode.append(mCClassName)
+                   .append("::")
+                   .append(aMemberName)
+                   .append(" = 0;\n");
     }
 
     /**
-     * Create a method id field in the header file for the C method name provided.
+     * Write the field declaration for the C++ id field of the given member.
      *
-     * @param aMethodName C method name to generate a method id field for.
+     * @param aMember Member for which an id field needs to be generated.
      */
-    private void writeHeaderField(String aMethodName) {
-        headerFields.append("jmethodID j");
-        headerFields.append(aMethodName);
-        headerFields.append(";\n");
+    private void writeMemberIdField(Member aMember, final String aCMethodName) {
+        String memberName = 'j'+ aCMethodName;
+
+        if (aMember instanceof Field) {
+            headerProtected.append("    static jfieldID ");
+        } else {
+            headerProtected.append("    static jmethodID ");
+        }
+
+        while(mTakenMemberNames.contains(memberName)) {
+            memberName = 'j' + aCMethodName + mNameMunger;
+            mNameMunger++;
+        }
+
+        writeZeroingFor(aMember, memberName);
+        mMembersToIds.put(aMember, memberName);
+        mTakenMemberNames.add(memberName);
+
+        headerProtected.append(memberName)
+                       .append(";\n");
+    }
+
+    /**
+     * Helper function to add a provided method signature to the public section of the generated header.
+     *
+     * @param aSignature The header to add.
+     */
+    private void writeSignatureToHeader(String aSignature) {
+        headerPublic.append("    ")
+                    .append(aSignature)
+                    .append(";\n");
     }
 
     /**
      * Get the finalised bytes to go into the generated wrappers file.
      *
      * @return The bytes to be written to the wrappers file.
      */
-    public byte[] getWrapperFileContents() {
+    public String getWrapperFileContents() {
         wrapperStartupCode.append("}\n");
-        wrapperStartupCode.append(wrapperMethodBodies);
+        zeroingCode.append(wrapperStartupCode)
+                   .append(wrapperMethodBodies);
         wrapperMethodBodies.setLength(0);
-        return wrapperStartupCode.toString().getBytes();
+        wrapperStartupCode.setLength(0);
+        return zeroingCode.toString();
     }
 
     /**
      * Get the finalised bytes to go into the generated header file.
      *
      * @return The bytes to be written to the header file.
      */
-    public byte[] getHeaderFileContents() {
-        headerFields.append('\n');
-        headerFields.append(headerMethods);
-        headerMethods.setLength(0);
-        return headerFields.toString().getBytes();
+    public String getHeaderFileContents() {
+        if (!mHasEncounteredDefaultConstructor) {
+            headerPublic.append("    ").append(mCClassName).append("() : AutoGlobalWrappedJavaObject() {};\n");
+        }
+        headerProtected.append("};\n\n");
+        headerPublic.append(headerProtected);
+        headerProtected.setLength(0);
+        return headerPublic.toString();
     }
 }
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/AnnotatableEntity.java
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+/**
+ * Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite
+ * the fact that such things can be of either flavour.
+ */
+public class AnnotatableEntity {
+    public enum ENTITY_TYPE {METHOD, FIELD, CONSTRUCTOR}
+
+    private final Member mMember;
+    public final ENTITY_TYPE mEntityType;
+
+    public final AnnotationInfo mAnnotationInfo;
+
+    public AnnotatableEntity(Member aObject, AnnotationInfo aAnnotationInfo) {
+        mMember = aObject;
+        mAnnotationInfo = aAnnotationInfo;
+
+        if (aObject instanceof Method) {
+            mEntityType = ENTITY_TYPE.METHOD;
+        } else if (aObject instanceof Field) {
+            mEntityType = ENTITY_TYPE.FIELD;
+        } else {
+            mEntityType = ENTITY_TYPE.CONSTRUCTOR;
+        }
+    }
+
+    public Method getMethod() {
+        if (mEntityType != ENTITY_TYPE.METHOD) {
+            throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
+        }
+        return (Method) mMember;
+    }
+    public Field getField() {
+        if (mEntityType != ENTITY_TYPE.FIELD) {
+            throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
+        }
+        return (Field) mMember;
+    }
+    public Constructor getConstructor() {
+        if (mEntityType != ENTITY_TYPE.CONSTRUCTOR) {
+            throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
+        }
+        return (Constructor) mMember;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/annotationProcessors/classloader/ClassWithOptions.java
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.annotationProcessors.classloader;
+
+public class ClassWithOptions {
+    public final Class<?> wrappedClass;
+    public final String generatedName;
+
+    public ClassWithOptions(Class<?> someClass, String name) {
+        wrappedClass = someClass;
+        generatedName = name;
+    }
+}
--- a/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java
+++ b/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java
@@ -16,50 +16,45 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
 /**
  * A classloader which can be initialised with a list of jar files and which can provide an iterator
  * over the top level classes in the jar files it was initialised with.
  * classNames is kept sorted to ensure iteration order is consistent across program invocations.
  * Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its
  * contents.
- * This classloader does not support inner classes. (You probably shouldn't be putting JNI entry
- * points in inner classes anyway)
  */
 public class IterableJarLoadingURLClassLoader extends URLClassLoader {
     LinkedList<String> classNames = new LinkedList<String>();
 
     /**
      * Create an instance and return its iterator. Provides an iterator over the classes in the jar
      * files provided as arguments.
      * Inner classes are not supported.
      *
      * @param args A list of jar file names an iterator over the classes of which is desired.
      * @return An iterator over the top level classes in the jar files provided, in arbitrary order.
      */
-    public static Iterator<Class<?>> getIteratorOverJars(String[] args) {
+    public static Iterator<ClassWithOptions> getIteratorOverJars(String[] args) {
         URL[] urlArray = new URL[args.length];
         LinkedList<String> aClassNames = new LinkedList<String>();
 
         for (int i = 0; i < args.length; i++) {
             try {
                 urlArray[i] = (new File(args[i])).toURI().toURL();
 
                 Enumeration<JarEntry> entries = new JarFile(args[i]).entries();
                 while (entries.hasMoreElements()) {
                     JarEntry e = entries.nextElement();
                     String fName = e.getName();
                     if (!fName.endsWith(".class")) {
                         continue;
                     }
                     final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
-                    // Inner classes are not supported.
-                    if (className.contains("$")) {
-                        continue;
-                    }
+
                     aClassNames.add(className);
                 }
             } catch (IOException e) {
                 System.err.println("Error loading jar file \"" + args[i] + '"');
                 e.printStackTrace(System.err);
             }
         }
         Collections.sort(aClassNames);
--- a/build/annotationProcessors/classloader/JarClassIterator.java
+++ b/build/annotationProcessors/classloader/JarClassIterator.java
@@ -1,38 +1,77 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors.classloader;
 
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.util.Iterator;
 
 /**
  * Class for iterating over an IterableJarLoadingURLClassLoader's classes.
  */
-public class JarClassIterator implements Iterator<Class<?>> {
+public class JarClassIterator implements Iterator<ClassWithOptions> {
     private IterableJarLoadingURLClassLoader mTarget;
     private Iterator<String> mTargetClassListIterator;
 
     public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) {
         mTarget = aTarget;
         mTargetClassListIterator = aTarget.classNames.iterator();
     }
 
     @Override
     public boolean hasNext() {
         return mTargetClassListIterator.hasNext();
     }
 
     @Override
-    public Class<?> next() {
+    public ClassWithOptions next() {
         String className = mTargetClassListIterator.next();
         try {
-            return mTarget.loadClass(className);
+            Class<?> ret = mTarget.loadClass(className);
+            if (ret.getCanonicalName() == null || "null".equals(ret.getCanonicalName())) {
+                // Anonymous inner class - unsupported.
+                return next();
+            } else {
+                String generateName = null;
+                for (Annotation annotation : ret.getAnnotations()) {
+                    Class<?> annotationType = annotation.annotationType();
+                    if (annotationType.getCanonicalName().equals("org.mozilla.gecko.mozglue.generatorannotations.GeneratorOptions")) {
+                        try {
+                            // Determine the explicitly-given name of the stub to generate, if any.
+                            final Method generateNameMethod = annotationType.getDeclaredMethod("generatedClassName");
+                            generateNameMethod.setAccessible(true);
+                            generateName = (String) generateNameMethod.invoke(annotation);
+                            break;
+                        } catch (NoSuchMethodException e) {
+                            System.err.println("Unable to find expected field on GeneratorOptions annotation. Did the signature change?");
+                            e.printStackTrace(System.err);
+                            System.exit(3);
+                        } catch (IllegalAccessException e) {
+                            System.err.println("IllegalAccessException reading fields on GeneratorOptions annotation. Seems the semantics of Reflection have changed...");
+                            e.printStackTrace(System.err);
+                            System.exit(4);
+                        } catch (InvocationTargetException e) {
+                            System.err.println("InvocationTargetException reading fields on GeneratorOptions annotation. This really shouldn't happen.");
+                            e.printStackTrace(System.err);
+                            System.exit(5);
+                        }
+                    }
+                }
+
+                if (generateName == null) {
+                    generateName = ret.getSimpleName();
+                }
+
+                return new ClassWithOptions(ret, generateName);
+            }
         } catch (ClassNotFoundException e) {
             System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
             e.printStackTrace();
             System.exit(2);
         }
         return null;
     }
 
rename from build/annotationProcessors/utils/AlphabeticMethodComparator.java
rename to build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java
--- a/build/annotationProcessors/utils/AlphabeticMethodComparator.java
+++ b/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java
@@ -1,28 +1,81 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors.utils;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.Comparator;
 
-public class AlphabeticMethodComparator implements Comparator<Method> {
+public class AlphabeticAnnotatableEntityComparator<T extends Member> implements Comparator<T> {
     @Override
-    public int compare(Method lhs, Method rhs) {
+    public int compare(T aLhs, T aRhs) {
+        // Constructors, Methods, Fields.
+        boolean lIsConstructor = aLhs instanceof Constructor;
+        boolean rIsConstructor = aRhs instanceof Constructor;
+        boolean lIsMethod = aLhs instanceof Method;
+        boolean rIsField = aRhs instanceof Field;
+
+        if (lIsConstructor) {
+            if (!rIsConstructor) {
+                return -1;
+            }
+        } else if (lIsMethod) {
+            if (rIsConstructor) {
+                return 1;
+            } else if (rIsField) {
+                return -1;
+            }
+        } else {
+            if (!rIsField) {
+                return 1;
+            }
+        }
+
+        // Verify these objects are the same type and cast them.
+        if (aLhs instanceof Method) {
+            return compare((Method) aLhs, (Method) aRhs);
+        } else if (aLhs instanceof Field) {
+            return compare((Field) aLhs, (Field) aRhs);
+        } else {
+            return compare((Constructor) aLhs, (Constructor) aRhs);
+        }
+    }
+
+    // Alas, the type system fails us.
+    private static int compare(Method aLhs, Method aRhs) {
         // Initially, attempt to differentiate the methods be name alone..
-        String lName = lhs.getName();
-        String rName = rhs.getName();
+        String lName = aLhs.getName();
+        String rName = aRhs.getName();
 
         int ret = lName.compareTo(rName);
         if (ret != 0) {
             return ret;
         }
 
         // The names were the same, so we need to compare signatures to find their uniqueness..
-        lName = Utils.getTypeSignatureString(lhs);
-        rName = Utils.getTypeSignatureString(rhs);
+        lName = Utils.getTypeSignatureStringForMethod(aLhs);
+        rName = Utils.getTypeSignatureStringForMethod(aRhs);
+
+        return lName.compareTo(rName);
+    }
+
+    private static int compare(Constructor aLhs, Constructor aRhs) {
+        // The names will be the same, so we need to compare signatures to find their uniqueness..
+        String lName = Utils.getTypeSignatureString(aLhs);
+        String rName = Utils.getTypeSignatureString(aRhs);
+
+        return lName.compareTo(rName);
+    }
+
+    private static int compare(Field aLhs, Field aRhs) {
+        // Compare field names..
+        String lName = aLhs.getName();
+        String rName = aRhs.getName();
 
         return lName.compareTo(rName);
     }
 }
rename from build/annotationProcessors/utils/GeneratableEntryPointIterator.java
rename to build/annotationProcessors/utils/GeneratableElementIterator.java
--- a/build/annotationProcessors/utils/GeneratableEntryPointIterator.java
+++ b/build/annotationProcessors/utils/GeneratableElementIterator.java
@@ -1,57 +1,84 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors.utils;
 
-import org.mozilla.gecko.annotationProcessors.MethodWithAnnotationInfo;
+import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
+import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.Iterator;
-import java.util.List;
 
 /**
- * Iterator over the methods in a given method list which have the GeneratableAndroidBridgeTarget
+ * Iterator over the methods in a given method list which have the WrappedJNIMethod
  * annotation. Returns an object containing both the annotation (Which may contain interesting
  * parameters) and the argument.
  */
-public class GeneratableEntryPointIterator implements Iterator<MethodWithAnnotationInfo> {
-    private final Method[] mMethods;
-    private MethodWithAnnotationInfo mNextReturnValue;
-    private int mMethodIndex;
+public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
+    private final Member[] mObjects;
+    private AnnotatableEntity mNextReturnValue;
+    private int mElementIndex;
+
+    private boolean mIterateEveryEntry;
+
+    public GeneratableElementIterator(Class<?> aClass) {
+        // Get all the elements of this class as AccessibleObjects.
+        Member[] aMethods = aClass.getDeclaredMethods();
+        Member[] aFields = aClass.getDeclaredFields();
+        Member[] aCtors = aClass.getConstructors();
 
-    public GeneratableEntryPointIterator(Method[] aMethods) {
-        // Sort the methods into alphabetical order by name, to ensure we always iterate methods
-        // in the same order..
-        Arrays.sort(aMethods, new AlphabeticMethodComparator());
-        mMethods = aMethods;
+        // Shove them all into one buffer.
+        Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length];
+        int offset = 0;
+        System.arraycopy(aMethods, 0, objs, 0, aMethods.length);
+        offset += aMethods.length;
+        System.arraycopy(aFields, 0, objs, offset, aFields.length);
+        offset += aFields.length;
+        System.arraycopy(aCtors, 0, objs, offset, aCtors.length);
+
+        // Sort the elements to ensure determinism.
+        Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator());
+
+        mObjects = objs;
+
+        // Check for "Wrap ALL the things" flag.
+        for (Annotation annotation : aClass.getDeclaredAnnotations()) {
+            final String annotationTypeName = annotation.annotationType().getName();
+
+            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapEntireClassForJNI")) {
+                mIterateEveryEntry = true;
+                break;
+            }
+        }
 
         findNextValue();
     }
 
     /**
      * Find and cache the next appropriately annotated method, plus the annotation parameter, if
      * one exists. Otherwise cache null, so hasNext returns false.
      */
     private void findNextValue() {
-        while (mMethodIndex < mMethods.length) {
-            Method candidateMethod = mMethods[mMethodIndex];
-            mMethodIndex++;
-            for (Annotation annotation : candidateMethod.getDeclaredAnnotations()) {
-                // GeneratableAndroidBridgeTarget has a parameter. Use Reflection to obtain it.
+        while (mElementIndex < mObjects.length) {
+            Member candidateElement = mObjects[mElementIndex];
+            mElementIndex++;
+            for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) {
+                // WrappedJNIMethod has a parameter. Use Reflection to obtain it.
                 Class<? extends Annotation> annotationType = annotation.annotationType();
                 final String annotationTypeName = annotationType.getName();
 
-                if (annotationTypeName.equals("org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget")) {
+                if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI")) {
                     String stubName = null;
                     boolean isStaticStub = false;
                     boolean isMultithreadedStub = false;
                     try {
                         // Determine the explicitly-given name of the stub to generate, if any.
                         final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
                         stubNameMethod.setAccessible(true);
                         stubName = (String) stubNameMethod.invoke(annotation);
@@ -61,51 +88,61 @@ public class GeneratableEntryPointIterat
                         staticStubMethod.setAccessible(true);
                         isStaticStub = (Boolean) staticStubMethod.invoke(annotation);
 
                         // Determine if the generated stub is to allow calls from multiple threads.
                         final Method multithreadedStubMethod = annotationType.getDeclaredMethod("allowMultithread");
                         multithreadedStubMethod.setAccessible(true);
                         isMultithreadedStub = (Boolean) multithreadedStubMethod.invoke(annotation);
                     } catch (NoSuchMethodException e) {
-                        System.err.println("Unable to find expected field on GeneratableAndroidBridgeTarget annotation. Did the signature change?");
+                        System.err.println("Unable to find expected field on WrappedJNIMethod annotation. Did the signature change?");
                         e.printStackTrace(System.err);
                         System.exit(3);
                     } catch (IllegalAccessException e) {
-                        System.err.println("IllegalAccessException reading fields on GeneratableAndroidBridgeTarget annotation. Seems the semantics of Reflection have changed...");
+                        System.err.println("IllegalAccessException reading fields on WrappedJNIMethod annotation. Seems the semantics of Reflection have changed...");
                         e.printStackTrace(System.err);
                         System.exit(4);
                     } catch (InvocationTargetException e) {
-                        System.err.println("InvocationTargetException reading fields on GeneratableAndroidBridgeTarget annotation. This really shouldn't happen.");
+                        System.err.println("InvocationTargetException reading fields on WrappedJNIMethod annotation. This really shouldn't happen.");
                         e.printStackTrace(System.err);
                         System.exit(5);
                     }
-                    // If the method name was not explicitly given in the annotation generate one...
+
+                    // If the element name was not explicitly given in the annotation generate one...
                     if (stubName.isEmpty()) {
-                        String aMethodName = candidateMethod.getName();
-                        stubName = aMethodName.substring(0, 1).toUpperCase() + aMethodName.substring(1);
+                        String elementName = candidateElement.getName();
+                        stubName = elementName.substring(0, 1).toUpperCase() + elementName.substring(1);
                     }
 
-                    mNextReturnValue = new MethodWithAnnotationInfo(candidateMethod, stubName, isStaticStub, isMultithreadedStub);
+                    AnnotationInfo annotationInfo = new AnnotationInfo(stubName, isStaticStub, isMultithreadedStub);
+                    mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
                     return;
                 }
             }
+
+            // If no annotation found, we might be expected to generate anyway using default arguments,
+            // thanks to the "Generate everything" annotation.
+            if (mIterateEveryEntry) {
+                AnnotationInfo annotationInfo = new AnnotationInfo(candidateElement.getName(), false, false);
+                mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
+                return;
+            }
         }
         mNextReturnValue = null;
     }
 
     @Override
     public boolean hasNext() {
         return mNextReturnValue != null;
     }
 
     @Override
-    public MethodWithAnnotationInfo next() {
-        MethodWithAnnotationInfo ret = mNextReturnValue;
+    public AnnotatableEntity next() {
+        AnnotatableEntity ret = mNextReturnValue;
         findNextValue();
         return ret;
     }
 
     @Override
     public void remove() {
-        throw new UnsupportedOperationException("Removal of methods from GeneratableEntryPointIterator not supported.");
+        throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported.");
     }
 }
--- a/build/annotationProcessors/utils/Utils.java
+++ b/build/annotationProcessors/utils/Utils.java
@@ -1,15 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.annotationProcessors.utils;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.HashMap;
 
 /**
  * A collection of utility methods used by CodeGenerator. Largely used for translating types.
  */
 public class Utils {
@@ -65,33 +68,48 @@ public class Utils {
         sInstanceCallTypes.put("long", "CallLongMethod");
         sInstanceCallTypes.put("double", "CallDoubleMethod");
         sInstanceCallTypes.put("float", "CallFloatMethod");
         sInstanceCallTypes.put("char", "CallCharMethod");
         sInstanceCallTypes.put("byte", "CallByteMethod");
         sInstanceCallTypes.put("short", "CallShortMethod");
     }
 
+    private static final HashMap<String, String> sFieldTypes = new HashMap<String, String>();
+
+    static {
+        sFieldTypes.put("int", "Int");
+        sFieldTypes.put("boolean", "Boolean");
+        sFieldTypes.put("long", "Long");
+        sFieldTypes.put("double", "Double");
+        sFieldTypes.put("float", "Float");
+        sFieldTypes.put("char", "Char");
+        sFieldTypes.put("byte", "Byte");
+        sFieldTypes.put("short", "Short");
+    }
+
     private static final HashMap<String, String> sFailureReturns = new HashMap<String, String>();
 
     static {
+        sFailureReturns.put("java.lang.Void", "");
         sFailureReturns.put("void", "");
         sFailureReturns.put("int", " 0");
         sFailureReturns.put("boolean", " false");
         sFailureReturns.put("long", " 0");
         sFailureReturns.put("double", " 0.0");
         sFailureReturns.put("float", " 0.0");
         sFailureReturns.put("char", " 0");
         sFailureReturns.put("byte", " 0");
         sFailureReturns.put("short", " 0");
     }
 
     private static final HashMap<String, String> sCanonicalSignatureParts = new HashMap<String, String>();
 
     static {
+        sCanonicalSignatureParts.put("java/lang/Void", "V");
         sCanonicalSignatureParts.put("void", "V");
         sCanonicalSignatureParts.put("int", "I");
         sCanonicalSignatureParts.put("boolean", "Z");
         sCanonicalSignatureParts.put("long", "J");
         sCanonicalSignatureParts.put("double", "D");
         sCanonicalSignatureParts.put("float", "F");
         sCanonicalSignatureParts.put("char", "C");
         sCanonicalSignatureParts.put("byte", "B");
@@ -159,26 +177,43 @@ public class Utils {
 
     /**
      * For a given Java type, get the corresponding C++ type if we're returning it from a function.
      *
      * @param type The Java return type.
      * @return A string representation of the C++ return type.
      */
     public static String getCReturnType(Class<?> type) {
-        // Since there's only one thing we want to do differently...
+        if (type.getCanonicalName().equals("java.lang.Void")) {
+            return "void";
+        }
         String cParameterType = getCParameterType(type);
         if (cParameterType.equals("const nsAString&")) {
             return "jstring";
         } else {
             return cParameterType;
         }
     }
 
     /**
+     * Gets the type-specific part of the  JNI function to use to get or set a field of a given type.
+     *
+     * @param aFieldType The Java type of the field.
+     * @return A string representation of the JNI call function substring to use.
+     */
+    public static String getFieldType(Class<?> aFieldType) {
+        String name = aFieldType.getCanonicalName();
+
+        if (sFieldTypes.containsKey(name)) {
+            return sFieldTypes.get(name);
+        }
+        return "Object";
+    }
+
+    /**
      * Gets the appropriate JNI call function to use to invoke a Java method with the given return
      * type. This, plus a call postfix (Such as "A") forms a complete JNI call function name.
      *
      * @param aReturnType The Java return type of the method being generated.
      * @param isStatic Boolean indicating if the underlying Java method is declared static.
      * @return A string representation of the JNI call function prefix to use.
      */
     public static String getCallPrefix(Class<?> aReturnType, boolean isStatic) {
@@ -207,158 +242,221 @@ public class Utils {
         String name = type.getCanonicalName();
         if (sFailureReturns.containsKey(name)) {
             return sFailureReturns.get(name);
         }
         return " nullptr";
     }
 
     /**
-     * Get the canonical JNI type signature for a method.
+     * Helper method to get the type signature for methods, given argument and return type.
+     * Allows for the near-identical logic needed for constructors and methods to be shared.
+     * (Alas, constructor does not extend method)
      *
-     * @param aMethod The method to generate a signature for.
-     * @return The canonical JNI type signature for this method.
+     * @param arguments Argument types of the underlying method.
+     * @param returnType Return type of the underlying method.
+     * @return The canonical Java type string for the method. eg. (IIIIFZ)Lorg/mozilla/gecko/gfx/ViewTransform;
      */
-    public static String getTypeSignatureString(Method aMethod) {
-        Class<?>[] arguments = aMethod.getParameterTypes();
-        Class<?> returnType = aMethod.getReturnType();
-
+    private static String getTypeSignatureInternal(Class<?>[] arguments, Class<?> returnType) {
         StringBuilder sb = new StringBuilder();
         sb.append('(');
         // For each argument, write its signature component to the buffer..
         for (int i = 0; i < arguments.length; i++) {
             writeTypeSignature(sb, arguments[i]);
         }
         sb.append(')');
         // Write the return value's signature..
         writeTypeSignature(sb, returnType);
         return sb.toString();
     }
 
     /**
-     * Helper method used by getTypeSignatureString to build the signature. Write the subsignature
+     * Get the canonical JNI type signature for a Field.
+     *
+     * @param aField The field to generate a signature for.
+     * @return The canonical JNI type signature for this method.
+     */
+    protected static String getTypeSignatureStringForField(Field aField) {
+        StringBuilder sb = new StringBuilder();
+        writeTypeSignature(sb, aField.getType());
+        return sb.toString();
+    }
+
+    /**
+     * Get the canonical JNI type signature for a method.
+     *
+     * @param aMethod The method to generate a signature for.
+     * @return The canonical JNI type signature for this method.
+     */
+    protected static String getTypeSignatureStringForMethod(Method aMethod) {
+        Class<?>[] arguments = aMethod.getParameterTypes();
+        Class<?> returnType = aMethod.getReturnType();
+
+        return getTypeSignatureInternal(arguments, returnType);
+    }
+
+    /**
+     * Get the canonical JNI type signature for a Constructor.
+     *
+     * @param aConstructor The Constructor to generate a signature for.
+     * @return The canonical JNI type signature for this method.
+     */
+    protected static String getTypeSignatureStringForConstructor(Constructor aConstructor) {
+        Class<?>[] arguments = aConstructor.getParameterTypes();
+
+        return getTypeSignatureInternal(arguments, Void.class);
+    }
+
+    public static String getTypeSignatureStringForMember(Member aMember) {
+        if (aMember instanceof Method) {
+            return getTypeSignatureStringForMethod((Method) aMember);
+        } else if (aMember instanceof Field) {
+            return getTypeSignatureStringForField((Field) aMember);
+        } else {
+            return getTypeSignatureStringForConstructor((Constructor) aMember);
+        }
+    }
+
+    public static String getTypeSignatureString(Constructor aConstructor) {
+        Class<?>[] arguments = aConstructor.getParameterTypes();
+
+        StringBuilder sb = new StringBuilder();
+        sb.append('(');
+        // For each argument, write its signature component to the buffer..
+        for (int i = 0; i < arguments.length; i++) {
+            writeTypeSignature(sb, arguments[i]);
+        }
+
+        // Constructors always return Void.
+        sb.append(")V");
+        return sb.toString();
+    }
+
+    /**
+     * Helper method used by getTypeSignatureStringForMethod to build the signature. Write the subsignature
      * of a given type into the buffer.
      *
      * @param sb The buffer to write into.
      * @param c  The type of the element to write the subsignature of.
      */
     private static void writeTypeSignature(StringBuilder sb, Class<?> c) {
         String name = c.getCanonicalName().replaceAll("\\.", "/");
+
         // Determine if this is an array type and, if so, peel away the array operators..
         int len = name.length();
         while (name.endsWith("[]")) {
             sb.append('[');
             name = name.substring(0, len - 2);
         }
 
+        if (c.isArray()) {
+            c = c.getComponentType();
+        }
+
+        Class<?> containerClass = c.getDeclaringClass();
+        if (containerClass != null) {
+            // Is an inner class. Add the $ symbol.
+            final int lastSlash = name.lastIndexOf('/');
+            name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
+        }
+
         // Look in the hashmap for the remainder...
         if (sCanonicalSignatureParts.containsKey(name)) {
             // It was a primitive type, so lookup was a success.
             sb.append(sCanonicalSignatureParts.get(name));
         } else {
             // It was a reference type - generate.
             sb.append('L');
             sb.append(name);
             sb.append(';');
         }
     }
 
     /**
      * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
      * generating header files and method bodies.
      *
-     * @param aMethod      The Java method to generate the corresponding wrapper signature for.
-     * @param aCMethodName The name of the generated method this is to be the signatgure for.
-     * @return The generated method signature.
+     * @param aArgumentTypes Argument types of the Java method being wrapped.
+     * @param aReturnType Return type of the Java method being wrapped.
+     * @param aCMethodName Name of the method to generate in the C++ class.
+     * @param aCClassName Name of the C++ class into which the method is declared.
+     * @return The C++ method implementation signature for the method described.
      */
-    public static String getCImplementationMethodSignature(Method aMethod, String aCMethodName) {
-        Class<?>[] argumentTypes = aMethod.getParameterTypes();
-        Class<?> returnType = aMethod.getReturnType();
+    public static String getCImplementationMethodSignature(Class<?>[] aArgumentTypes, Class<?> aReturnType, String aCMethodName, String aCClassName) {
+        StringBuilder retBuffer = new StringBuilder();
 
-        StringBuilder retBuffer = new StringBuilder();
-        // Write return type..
-        retBuffer.append(getCReturnType(returnType));
-        retBuffer.append(" AndroidBridge::");
+        retBuffer.append(getCReturnType(aReturnType));
+        retBuffer.append(' ');
+        retBuffer.append(aCClassName);
+        retBuffer.append("::");
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
-        // For an instance method, the first argument is the target object.
-        if (!isMethodStatic(aMethod)) {
-            retBuffer.append("jobject aTarget");
-            if (argumentTypes.length > 0) {
-                retBuffer.append(", ");
-            }
-        }
-
         // Write argument types...
-        for (int aT = 0; aT < argumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(argumentTypes[aT]));
+        for (int aT = 0; aT < aArgumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
-            if (aT != argumentTypes.length - 1) {
+            if (aT != aArgumentTypes.length - 1) {
                 retBuffer.append(", ");
             }
         }
         retBuffer.append(')');
         return retBuffer.toString();
     }
 
     /**
      * Produces a C method signature, sans semicolon, for the given Java Method. Useful for both
      * generating header files and method bodies.
      *
      * @param aMethod      The Java method to generate the corresponding wrapper signature for.
      * @param aCMethodName The name of the generated method this is to be the signatgure for.
      * @return The generated method signature.
      */
-    public static String getCHeaderMethodSignature(Method aMethod, String aCMethodName, boolean aIsStaticStub) {
-        Class<?>[] argumentTypes = aMethod.getParameterTypes();
-
-        // The annotations on the parameters of this method, in declaration order.
-        // Importantly - the same order as those in argumentTypes.
-        Annotation[][] argumentAnnotations = aMethod.getParameterAnnotations();
-        Class<?> returnType = aMethod.getReturnType();
-
+    /**
+     *
+     * @param aArgumentTypes Argument types of the Java method being wrapped.
+     * @param aArgumentAnnotations The annotations on the Java method arguments. Used to specify
+     *                             default values etc.
+     * @param aReturnType Return type of the Java method being wrapped.
+     * @param aCMethodName Name of the method to generate in the C++ class.
+     * @param aCClassName Name of the C++ class into which the method is declared.e
+     * @param aIsStaticStub true if the generated C++ method should be static, false otherwise.
+     * @return The generated C++ header method signature for the method described.
+     */
+    public static String getCHeaderMethodSignature(Class<?>[] aArgumentTypes, Annotation[][] aArgumentAnnotations, Class<?> aReturnType, String aCMethodName, String aCClassName, boolean aIsStaticStub) {
         StringBuilder retBuffer = new StringBuilder();
 
         // Add the static keyword, if applicable.
         if (aIsStaticStub) {
             retBuffer.append("static ");
         }
 
         // Write return type..
-        retBuffer.append(getCReturnType(returnType));
+        retBuffer.append(getCReturnType(aReturnType));
         retBuffer.append(' ');
         retBuffer.append(aCMethodName);
         retBuffer.append('(');
 
-        // For an instance method, the first argument is the target object.
-        if (!isMethodStatic(aMethod)) {
-            retBuffer.append("jobject aTarget");
-            if (argumentTypes.length > 0) {
-                retBuffer.append(", ");
-            }
-        }
-
         // Write argument types...
-        for (int aT = 0; aT < argumentTypes.length; aT++) {
-            retBuffer.append(getCParameterType(argumentTypes[aT]));
+        for (int aT = 0; aT < aArgumentTypes.length; aT++) {
+            retBuffer.append(getCParameterType(aArgumentTypes[aT]));
             retBuffer.append(" a");
             // We, imaginatively, call our arguments a1, a2, a3...
             // The only way to preserve the names from Java would be to parse the
             // Java source, which would be computationally hard.
             retBuffer.append(aT);
 
             // Append the default value, if there is one..
-            retBuffer.append(getDefaultValueString(argumentTypes[aT], argumentAnnotations[aT]));
+            retBuffer.append(getDefaultValueString(aArgumentTypes[aT], aArgumentAnnotations[aT]));
 
-            if (aT != argumentTypes.length - 1) {
+            if (aT != aArgumentTypes.length - 1) {
                 retBuffer.append(", ");
             }
         }
         retBuffer.append(')');
         return retBuffer.toString();
     }
 
     /**
@@ -370,17 +468,17 @@ public class Utils {
      * @param aArgumentAnnotations The annotations on the argument to consider.
      * @return An appropriate string to append to the signature of this argument assigning it to a
      *         default value (Or not, as applicable).
      */
     public static String getDefaultValueString(Class<?> aArgumentType, Annotation[] aArgumentAnnotations) {
         for (int i = 0; i < aArgumentAnnotations.length; i++) {
             Class<? extends Annotation> annotationType = aArgumentAnnotations[i].annotationType();
             final String annotationTypeName = annotationType.getName();
-            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.OptionalGeneratedParameter")) {
+            if (annotationTypeName.equals("org.mozilla.gecko.mozglue.generatorannotations.OptionalGeneratedParameter")) {
                 return " = " + getDefaultParameterValueForType(aArgumentType);
             }
         }
         return "";
     }
 
     /**
      * Helper method to return an appropriate default parameter value for an argument of a given type.
@@ -400,24 +498,23 @@ public class Utils {
         } else {
             return "nullptr";
         }
     }
 
     /**
      * Helper method that returns the number of reference types in the arguments of m.
      *
-     * @param m The method to consider.
+     * @param aArgs The method arguments to consider.
      * @return How many of the arguments of m are nonprimitive.
      */
-    public static int enumerateReferenceArguments(Method m) {
+    public static int enumerateReferenceArguments(Class<?>[] aArgs) {
         int ret = 0;
-        Class<?>[] args = m.getParameterTypes();
-        for (int i = 0; i < args.length; i++) {
-            String name = args[i].getCanonicalName();
+        for (int i = 0; i < aArgs.length; i++) {
+            String name = aArgs[i].getCanonicalName();
             if (!sBasicCTypes.containsKey(name)) {
                 ret++;
             }
         }
         return ret;
     }
 
     /**
@@ -447,35 +544,33 @@ public class Utils {
         StringBuilder sb = new StringBuilder();
 
         String name = type.getCanonicalName();
         if (sCanonicalSignatureParts.containsKey(name)) {
             sb.append(sCanonicalSignatureParts.get(name).toLowerCase());
             sb.append(" = ").append(argName).append(";\n");
         } else {
             if (isCharSequence(type)) {
-                sb.append("l = NewJavaString(env, ").append(argName).append(");\n");
+                sb.append("l = AndroidBridge::NewJavaString(env, ").append(argName).append(");\n");
             } else {
                 sb.append("l = ").append(argName).append(";\n");
             }
         }
 
         return sb.toString();
     }
 
     /**
-     * Returns true if the method provided returns an object type. Returns false if it returns a
-     * primitive type.
+     * Returns true if the type provided is an object type. Returns false otherwise
      *
-     * @param aMethod The method to consider.
-     * @return true if the method provided returns an object type, false otherwise.
+     * @param aType The type to consider.
+     * @return true if the method provided is an object type, false otherwise.
      */
-    public static boolean doesReturnObjectType(Method aMethod) {
-        Class<?> returnType = aMethod.getReturnType();
-        return !sBasicCTypes.containsKey(returnType.getCanonicalName());
+    public static boolean isObjectType(Class<?> aType) {
+        return !sBasicCTypes.containsKey(aType.getCanonicalName());
     }
 
     /**
      * For a given Java class, get the name of the value in C++ which holds a reference to it.
      *
      * @param aClass Target Java class.
      * @return The name of the C++ jclass entity referencing the given class.
      */
@@ -490,17 +585,26 @@ public class Utils {
      * @param aClass The target Java class.
      * @return The generated code to populate the reference to the class.
      */
     public static String getStartupLineForClass(Class<?> aClass) {
         StringBuilder sb = new StringBuilder();
         sb.append("    ");
         sb.append(getClassReferenceName(aClass));
         sb.append(" = getClassGlobalRef(\"");
-        sb.append(aClass.getCanonicalName().replaceAll("\\.", "/"));
+
+        String name = aClass.getCanonicalName().replaceAll("\\.", "/");
+        Class<?> containerClass = aClass.getDeclaringClass();
+        if (containerClass != null) {
+            // Is an inner class. Add the $ symbol.
+            final int lastSlash = name.lastIndexOf('/');
+            name = name.substring(0, lastSlash) + '$' + name.substring(lastSlash+1);
+        }
+
+        sb.append(name);
         sb.append("\");\n");
         return sb.toString();
     }
 
     /**
      * Helper method to determine if this object implements CharSequence
      * @param aClass Class to check for CharSequence-esqueness
      * @return True if the given class implements CharSequence, false otherwise.
@@ -515,16 +619,26 @@ public class Utils {
                 return true;
             }
         }
         return false;
     }
 
     /**
      * Helper method to read the modifier bits of the given method to determine if it is static.
-     * @param aMethod The Method to check.
+     * @param aMember The Member to check.
      * @return true of the method is declared static, false otherwise.
      */
-    public static boolean isMethodStatic(Method aMethod) {
-        int aMethodModifiers = aMethod.getModifiers();
+    public static boolean isMemberStatic(Member aMember) {
+        int aMethodModifiers = aMember.getModifiers();
         return Modifier.isStatic(aMethodModifiers);
     }
+
+    /**
+     * Helper method to read the modifier bits of the given method to determine if it is static.
+     * @param aMember The Member to check.
+     * @return true of the method is declared static, false otherwise.
+     */
+    public static boolean isMemberFinal(Member aMember) {
+        int aMethodModifiers = aMember.getModifiers();
+        return Modifier.isFinal(aMethodModifiers);
+    }
 }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -1306,21 +1306,23 @@ endif
 
 jni-stubs.inc: jars/gecko-browser.jar jars/gecko-mozglue.jar jars/gecko-util.jar jars/sync-thirdparty.jar
 	$(JAVAH) -o javah.out -bootclasspath $(JAVA_BOOTCLASSPATH) -classpath $(subst $(NULL) $(NULL),:,$^) $(CLASSES_WITH_JNI)
 	$(PYTHON) $(topsrcdir)/mobile/android/base/jni-generator.py javah.out $@
 
 ANNOTATION_PROCESSOR_JAVA_FILES = \
   build/annotationProcessors/AnnotationProcessor.java \
   build/annotationProcessors/CodeGenerator.java \
-  build/annotationProcessors/MethodWithAnnotationInfo.java \
+  build/annotationProcessors/AnnotationInfo.java \
+  build/annotationProcessors/classloader/AnnotatableEntity.java \
+  build/annotationProcessors/classloader/ClassWithOptions.java \
   build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java \
   build/annotationProcessors/classloader/JarClassIterator.java \
-  build/annotationProcessors/utils/AlphabeticMethodComparator.java \
-  build/annotationProcessors/utils/GeneratableEntryPointIterator.java \
+  build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java \
+  build/annotationProcessors/utils/GeneratableElementIterator.java \
   build/annotationProcessors/utils/Utils.java \
   $(NULL)
 
 GeneratedJNIWrappers.cpp: jars/gecko-browser.jar jars/gecko-mozglue.jar jars/gecko-util.jar jars/sync-thirdparty.jar
 	$(JAVAC) $(addprefix $(topsrcdir)/,$(ANNOTATION_PROCESSOR_JAVA_FILES)) -d $(CURDIR)
 	$(JAVA) -classpath $(JAVA_BOOTCLASSPATH):$(CURDIR) org.mozilla.gecko.annotationProcessors.AnnotationProcessor $^
 
 PP_RES_XML= \