Merge branch 'master' of https://code.google.com/p/dexmaker

Conflicts:
	src/test/java/com/google/dexmaker/DexMakerTest.java
diff --git a/src/main/java/com/google/dexmaker/DexMaker.java b/src/main/java/com/google/dexmaker/DexMaker.java
index bfa83af..7bb088d 100644
--- a/src/main/java/com/google/dexmaker/DexMaker.java
+++ b/src/main/java/com/google/dexmaker/DexMaker.java
@@ -212,6 +212,11 @@
     public void declare(TypeId<?> type, String sourceFile, int flags,
             TypeId<?> supertype, TypeId<?>... interfaces) {
         TypeDeclaration declaration = getTypeDeclaration(type);
+        int supportedFlags = Modifier.PUBLIC | Modifier.FINAL | Modifier.ABSTRACT;
+        if ((flags & ~supportedFlags) != 0) {
+            throw new IllegalArgumentException("Unexpected flag: "
+                    + Integer.toHexString(flags));
+        }
         if (declaration.declared) {
             throw new IllegalStateException("already declared: " + type);
         }
@@ -223,29 +228,11 @@
     }
 
     /**
-     * Declares a constructor. The name of {@code method} must be "<init>",
-     * as it is on all instances returned by {@link TypeId#getConstructor}.
+     * Declares a method or constructor.
      *
      * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link
      *     Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC},
-     *     {@link Modifier#FINAL}, {@link Modifier#SYNCHRONIZED} and {@link
-     *     Modifier#VARARGS}.
-     *     <p><strong>Warning:</strong> the {@link Modifier#SYNCHRONIZED} flag
-     *     is insufficient to generate a synchronized method. You must also use
-     *     {@link Code#monitorEnter} and {@link Code#monitorExit} to acquire
-     *     a monitor.
-     */
-    public Code declareConstructor(MethodId<?, ?> method, int flags) {
-        return declare(method, flags | ACC_CONSTRUCTOR);
-    }
-
-    /**
-     * Declares a method. The name of {@code method} must not be "<init>".
-     *
-     * @param flags a bitwise combination of {@link Modifier#PUBLIC}, {@link
-     *     Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC},
-     *     {@link Modifier#FINAL}, {@link Modifier#SYNCHRONIZED} and {@link
-     *     Modifier#VARARGS}.
+     *     {@link Modifier#FINAL} and {@link Modifier#SYNCHRONIZED}.
      *     <p><strong>Warning:</strong> the {@link Modifier#SYNCHRONIZED} flag
      *     is insufficient to generate a synchronized method. You must also use
      *     {@link Code#monitorEnter} and {@link Code#monitorExit} to acquire
@@ -256,10 +243,23 @@
         if (typeDeclaration.methods.containsKey(method)) {
             throw new IllegalStateException("already declared: " + method);
         }
+
+        int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
+                | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED;
+        if ((flags & ~supportedFlags) != 0) {
+            throw new IllegalArgumentException("Unexpected flag: "
+                    + Integer.toHexString(flags));
+        }
+
         // replace the SYNCHRONIZED flag with the DECLARED_SYNCHRONIZED flag
         if ((flags & Modifier.SYNCHRONIZED) != 0) {
             flags = (flags & ~Modifier.SYNCHRONIZED) | AccessFlags.ACC_DECLARED_SYNCHRONIZED;
         }
+
+        if (method.isConstructor()) {
+            flags |= ACC_CONSTRUCTOR;
+        }
+
         MethodDeclaration methodDeclaration = new MethodDeclaration(method, flags);
         typeDeclaration.methods.put(method, methodDeclaration);
         return methodDeclaration.code;
@@ -272,12 +272,27 @@
      *     Modifier#PRIVATE}, {@link Modifier#PROTECTED}, {@link Modifier#STATIC},
      *     {@link Modifier#FINAL}, {@link Modifier#VOLATILE}, and {@link
      *     Modifier#TRANSIENT}.
+     * @param staticValue a constant representing the initial value for the
+     *     static field, possibly null. This must be null if this field is
+     *     non-static.
      */
     public void declare(FieldId<?, ?> fieldId, int flags, Object staticValue) {
         TypeDeclaration typeDeclaration = getTypeDeclaration(fieldId.declaringType);
         if (typeDeclaration.fields.containsKey(fieldId)) {
             throw new IllegalStateException("already declared: " + fieldId);
         }
+
+        int supportedFlags = Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED
+                | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE | Modifier.TRANSIENT;
+        if ((flags & ~supportedFlags) != 0) {
+            throw new IllegalArgumentException("Unexpected flag: "
+                    + Integer.toHexString(flags));
+        }
+
+        if ((flags & Modifier.STATIC) == 0 && staticValue != null) {
+            throw new IllegalArgumentException("staticValue is non-null, but field is not static");
+        }
+
         FieldDeclaration fieldDeclaration = new FieldDeclaration(fieldId, flags, staticValue);
         typeDeclaration.fields.put(fieldId, fieldDeclaration);
     }
diff --git a/src/main/java/com/google/dexmaker/MethodId.java b/src/main/java/com/google/dexmaker/MethodId.java
index f437b90..b7875fe 100644
--- a/src/main/java/com/google/dexmaker/MethodId.java
+++ b/src/main/java/com/google/dexmaker/MethodId.java
@@ -56,6 +56,13 @@
     }
 
     /**
+     * Returns true if this method is a constructor for its declaring class.
+     */
+    public boolean isConstructor() {
+        return name.equals("<init>");
+    }
+
+    /**
      * Returns the method's name. This is "<init>" if this is a constructor.
      */
     public String getName() {
diff --git a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
index 5f2c025..2b40160 100644
--- a/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
+++ b/src/main/java/com/google/dexmaker/stock/ProxyBuilder.java
@@ -489,7 +489,7 @@
             }
             TypeId<?>[] types = classArrayToTypeArray(constructor.getParameterTypes());
             MethodId<?, ?> method = generatedType.getConstructor(types);
-            Code constructorCode = dexMaker.declareConstructor(method, PUBLIC);
+            Code constructorCode = dexMaker.declare(method, PUBLIC);
             Local<G> thisRef = constructorCode.getThis(generatedType);
             Local<?>[] params = new Local[types.length];
             for (int i = 0; i < params.length; ++i) {
diff --git a/src/test/java/com/google/dexmaker/DexMakerTest.java b/src/test/java/com/google/dexmaker/DexMakerTest.java
index cfd994c..2355916 100644
--- a/src/test/java/com/google/dexmaker/DexMakerTest.java
+++ b/src/test/java/com/google/dexmaker/DexMakerTest.java
@@ -22,7 +22,9 @@
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import static java.lang.reflect.Modifier.ABSTRACT;
 import static java.lang.reflect.Modifier.FINAL;
+import static java.lang.reflect.Modifier.NATIVE;
 import static java.lang.reflect.Modifier.PRIVATE;
 import static java.lang.reflect.Modifier.PROTECTED;
 import static java.lang.reflect.Modifier.PUBLIC;
@@ -419,7 +421,7 @@
         FieldId<G, Integer> fieldId = generated.getField(TypeId.INT, "a");
         dexMaker.declare(fieldId, PUBLIC | FINAL, null);
         MethodId<?, Void> constructor = GENERATED.getConstructor(TypeId.INT);
-        Code code = dexMaker.declareConstructor(constructor, PUBLIC);
+        Code code = dexMaker.declare(constructor, PUBLIC);
         Local<G> thisRef = code.getThis(generated);
         Local<Integer> parameter = code.getParameter(0, TypeId.INT);
         code.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef);
@@ -1534,7 +1536,7 @@
         assertEquals(5, intArrayLength.invoke(null, new Object[] { new int[5] }));
 
         Method longArrayLength = arrayLengthMethod(LONG_ARRAY);
-        assertEquals(0, longArrayLength.invoke(null, new Object[]{new long[0]}));
+        assertEquals(0, longArrayLength.invoke(null, new Object[] { new long[0] }));
         assertEquals(5, longArrayLength.invoke(null, new Object[] { new long[5] }));
 
         Method objectArrayLength = arrayLengthMethod(OBJECT_ARRAY);
@@ -1732,27 +1734,67 @@
         assertEquals(6, getMethod().invoke(null, 3));
     }
 
+    public void testPrivateClassesAreUnsupported() {
+        try {
+            dexMaker.declare(TypeId.get("LPrivateClass;"), "PrivateClass.generated", PRIVATE,
+                    TypeId.OBJECT);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testAbstractMethodsAreUnsupported() {
+        MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
+        try {
+            dexMaker.declare(methodId, ABSTRACT);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testNativeMethodsAreUnsupported() {
+        MethodId<?, Void> methodId = GENERATED.getMethod(TypeId.VOID, "call");
+        try {
+            dexMaker.declare(methodId, NATIVE);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testSynchronizedFieldsAreUnsupported() {
+        try {
+            FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "synchronizedField");
+            dexMaker.declare(fieldId, SYNCHRONIZED, null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testInitialValueWithNonStaticField() {
+        try {
+            FieldId<?, ?> fieldId = GENERATED.getField(TypeId.OBJECT, "nonStaticField");
+            dexMaker.declare(fieldId, 0, 1);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
     // TODO: cast primitive to non-primitive
     // TODO: cast non-primitive to primitive
     // TODO: cast byte to integer
     // TODO: cast byte to long
     // TODO: cast long to byte
-
     // TODO: fail if a label is unreachable (never navigated to)
-
     // TODO: more strict type parameters: Integer on methods
-
     // TODO: don't generate multiple times (?)
-
+    // TODO: test array types
     // TODO: test generating an interface
     // TODO: declare native method or abstract method
-
     // TODO: get a thrown exception 'e' into a local
-
     // TODO: move a primitive or reference
 
     private void addDefaultConstructor() {
-        Code code = dexMaker.declareConstructor(GENERATED.getConstructor(), PUBLIC);
+        Code code = dexMaker.declare(GENERATED.getConstructor(), PUBLIC);
         Local<?> thisRef = code.getThis(GENERATED);
         code.invokeDirect(TypeId.OBJECT.getConstructor(), null, thisRef);
         code.returnVoid();