Expand test 046 and move reflective class initialization later.

We have no reason to do this later; we're just being conservative. We
might want to revisit this in a future release so you only pay once,
when you cache your Field or Method instance.

Change-Id: Ib18fa75c0648561d3c5e1554cb3955ed57ab9314
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 43573b5..5c834f7 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -26,12 +26,14 @@
 
 namespace art {
 
-static Class* DecodeInitializedClass(JNIEnv* env, jclass java_class) {
+static Class* DecodeClass(JNIEnv* env, jobject java_class) {
   Class* c = Decode<Class*>(env, java_class);
   DCHECK(c != NULL);
   DCHECK(c->IsClass());
-  bool initialized = Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true);
-  return initialized ? c : NULL;
+  // TODO: we could EnsureInitialized here, rather than on every reflective get/set or invoke .
+  // For now, we conservatively preserve the old dalvik behavior. A quick "IsInitialized" check
+  // every time probably doesn't make much difference to reflection performance anyway.
+  return c;
 }
 
 // "name" is in "binary name" format, e.g. "dalvik.system.Debug$1".
@@ -72,7 +74,7 @@
 }
 
 static jint Class_getAnnotationDirectoryOffset(JNIEnv* env, jclass javaClass) {
-  Class* c = Decode<Class*>(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   if (c->IsPrimitive() || c->IsArrayClass() || c->IsProxyClass()) {
     return 0;  // primitive, array and proxy classes don't have class definitions
   }
@@ -106,7 +108,7 @@
 }
 
 static jobjectArray Class_getDeclaredConstructors(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  Class* c = DecodeInitializedClass(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -130,7 +132,7 @@
 }
 
 static jobjectArray Class_getDeclaredFields(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  Class* c = DecodeInitializedClass(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -172,7 +174,7 @@
 }
 
 static jobjectArray Class_getDeclaredMethods(JNIEnv* env, jclass javaClass, jboolean publicOnly) {
-  Class* c = DecodeInitializedClass(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -201,7 +203,7 @@
 }
 
 static jobject Class_getDex(JNIEnv* env, jobject javaClass) {
-  Class* c = Decode<Class*>(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
 
   DexCache* dex_cache = c->GetDexCache();
   if (dex_cache == NULL) {
@@ -260,7 +262,7 @@
 
 static jobject Class_getDeclaredConstructorOrMethod(JNIEnv* env, jclass javaClass, jstring javaName,
                                                     jobjectArray javaArgs) {
-  Class* c = DecodeInitializedClass(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   if (c == NULL) {
     return NULL;
   }
@@ -281,7 +283,7 @@
 }
 
 static jobject Class_getDeclaredFieldNative(JNIEnv* env, jclass java_class, jobject jname) {
-  Class* c = DecodeInitializedClass(env, java_class);
+  Class* c = DecodeClass(env, java_class);
   if (c == NULL) {
     return NULL;
   }
@@ -309,20 +311,20 @@
 
 static jstring Class_getNameNative(JNIEnv* env, jobject javaThis) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  Class* c = Decode<Class*>(env, javaThis);
+  Class* c = DecodeClass(env, javaThis);
   return AddLocalReference<jstring>(env, c->ComputeName());
 }
 
 static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(Decode<Class*>(env, javaThis));
+  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(DecodeClass(env, javaThis));
   return AddLocalReference<jobjectArray>(env, c->GetInterfaces()->Clone());
 }
 
 static jboolean Class_isAssignableFrom(JNIEnv* env, jobject javaLhs, jclass javaRhs) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  Class* lhs = Decode<Class*>(env, javaLhs);
-  Class* rhs = Decode<Class*>(env, javaRhs);
+  Class* lhs = DecodeClass(env, javaLhs);
+  Class* rhs = Decode<Class*>(env, javaRhs); // Can be null.
   if (rhs == NULL) {
     Thread::Current()->ThrowNewException("Ljava/lang/NullPointerException;", "class == null");
     return JNI_FALSE;
@@ -331,7 +333,7 @@
 }
 
 static jboolean Class_isInstance(JNIEnv* env, jobject javaClass, jobject javaObject) {
-  Class* c = Decode<Class*>(env, javaClass);
+  Class* c = DecodeClass(env, javaClass);
   Object* o = Decode<Object*>(env, javaObject);
   if (o == NULL) {
     return JNI_FALSE;
@@ -370,7 +372,7 @@
 
 static jobject Class_newInstanceImpl(JNIEnv* env, jobject javaThis) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  Class* c = Decode<Class*>(env, javaThis);
+  Class* c = DecodeClass(env, javaThis);
   if (c->IsPrimitive() || c->IsInterface() || c->IsArrayClass() || c->IsAbstract()) {
     Thread::Current()->ThrowNewExceptionF("Ljava/lang/InstantiationException;",
         "Class %s can not be instantiated", PrettyDescriptor(ClassHelper(c).GetDescriptor()).c_str());
diff --git a/src/java_lang_reflect_Field.cc b/src/java_lang_reflect_Field.cc
index 4eea533..70636d6 100644
--- a/src/java_lang_reflect_Field.cc
+++ b/src/java_lang_reflect_Field.cc
@@ -26,7 +26,9 @@
 
 static bool GetFieldValue(Object* o, Field* f, JValue& value, bool allow_references) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  DCHECK(f->GetDeclaringClass()->IsInitialized());
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true)) {
+    return false;
+  }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
   case Primitive::kPrimBoolean:
     value.z = f->GetBoolean(o);
@@ -155,7 +157,9 @@
 }
 
 static void SetFieldValue(Object* o, Field* f, const JValue& new_value, bool allow_references) {
-  DCHECK(f->GetDeclaringClass()->IsInitialized());
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true)) {
+    return;
+  }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
   case Primitive::kPrimBoolean:
     f->SetBoolean(o, new_value.z);
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index 82fe87c..8de373c 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -94,11 +94,21 @@
 public method
 checkType invoking null
 checkType got expected exception
-calling const-class NoisyInitUser.class
-called const-class NoisyInitUser.class
+calling const-class FieldNoisyInitUser.class
+called const-class FieldNoisyInitUser.class
+got fields
+got field
+FieldNoisyInitUser is initializing
+read field value
+FieldNoisyInit is initializing
+
+calling const-class MethodNoisyInitUser.class
+called const-class MethodNoisyInitUser.class
 got methods
-NoisyInitUser is initializing
-NoisyInit is initializing
+got method
+MethodNoisyInitUser is initializing
+invoked method
+MethodNoisyInit is initializing
 
 generic field: java.util.List<java.lang.String>
 generic method fancyMethod params='[1] java.util.ArrayList<java.lang.String>' ret='java.util.Map<java.lang.Integer, java.lang.String>'
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index 104ead3..cf0a7e6 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -402,19 +402,48 @@
         }
     }
 
-    public static void checkInit() {
-      System.out.println("calling const-class NoisyInitUser.class");
-      Class niuClass = NoisyInitUser.class;
-      System.out.println("called const-class NoisyInitUser.class");
-      Method[] methods;
+    public static void checkClinitForFields() throws Exception {
+      // Loading a class constant shouldn't run <clinit>.
+      System.out.println("calling const-class FieldNoisyInitUser.class");
+      Class niuClass = FieldNoisyInitUser.class;
+      System.out.println("called const-class FieldNoisyInitUser.class");
 
-        methods = niuClass.getDeclaredMethods();
-        System.out.println("got methods");
-        /* neither NoisyInit nor NoisyInitUser should be initialized yet */
-        NoisyInitUser niu = new NoisyInitUser();
-        NoisyInit ni = new NoisyInit();
+      // Getting the declared fields doesn't run <clinit>.
+      Field[] fields = niuClass.getDeclaredFields();
+      System.out.println("got fields");
 
-        System.out.println("");
+      Field field = niuClass.getField("staticField");
+      System.out.println("got field");
+      field.get(null);
+      System.out.println("read field value");
+
+      // FieldNoisyInitUser should now be initialized, but FieldNoisyInit shouldn't be initialized yet.
+      FieldNoisyInitUser niu = new FieldNoisyInitUser();
+      FieldNoisyInit ni = new FieldNoisyInit();
+
+      System.out.println("");
+    }
+
+    public static void checkClinitForMethods() throws Exception {
+      // Loading a class constant shouldn't run <clinit>.
+      System.out.println("calling const-class MethodNoisyInitUser.class");
+      Class niuClass = MethodNoisyInitUser.class;
+      System.out.println("called const-class MethodNoisyInitUser.class");
+
+      // Getting the declared methods doesn't run <clinit>.
+      Method[] methods = niuClass.getDeclaredMethods();
+      System.out.println("got methods");
+
+      Method method = niuClass.getMethod("staticMethod", (Class[]) null);
+      System.out.println("got method");
+      method.invoke(null);
+      System.out.println("invoked method");
+
+      // MethodNoisyInitUser should now be initialized, but MethodNoisyInit shouldn't be initialized yet.
+      MethodNoisyInitUser niu = new MethodNoisyInitUser();
+      MethodNoisyInit ni = new MethodNoisyInit();
+
+      System.out.println("");
     }
 
 
@@ -479,13 +508,14 @@
     }
 
 
-    public static void main(String[] args) {
+    public static void main(String[] args) throws Exception {
         Main test = new Main();
         test.run();
 
         checkAccess();
         checkType();
-        checkInit();
+        checkClinitForFields();
+        checkClinitForMethods();
         checkGeneric();
     }
 }
@@ -557,17 +587,34 @@
     public static double staticDouble = 3.3;
 }
 
-class NoisyInit {
-    static {
-        System.out.println("NoisyInit is initializing");
-        //Throwable th = new Throwable();
-        //th.printStackTrace();
-    }
+class FieldNoisyInit {
+  static {
+    System.out.println("FieldNoisyInit is initializing");
+    //Throwable th = new Throwable();
+    //th.printStackTrace();
+  }
 }
 
-class NoisyInitUser {
-    static {
-        System.out.println("NoisyInitUser is initializing");
-    }
-    public void createNoisyInit(NoisyInit ni) {}
+class FieldNoisyInitUser {
+  static {
+    System.out.println("FieldNoisyInitUser is initializing");
+  }
+  public static int staticField;
+  public static FieldNoisyInit noisy;
+}
+
+class MethodNoisyInit {
+  static {
+    System.out.println("MethodNoisyInit is initializing");
+    //Throwable th = new Throwable();
+    //th.printStackTrace();
+  }
+}
+
+class MethodNoisyInitUser {
+  static {
+    System.out.println("MethodNoisyInitUser is initializing");
+  }
+  public static void staticMethod() {}
+  public void createMethodNoisyInit(MethodNoisyInit ni) {}
 }