Avoid a case of eager initialization.

Don't mark as initialized classes that contain static field
initialization.

Change-Id: Iedcabbdf355e8861eb7731650eee1467f68ae0cd
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 80dd7eb..c94347d 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -546,7 +546,7 @@
   for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) {
     Class* c = GetClassRoot(ClassRoot(i));
     if (!c->IsArrayClass() && !c->IsPrimitive()) {
-      EnsureInitialized(GetClassRoot(ClassRoot(i)), true);
+      EnsureInitialized(GetClassRoot(ClassRoot(i)), true, true);
       CHECK(!self->IsExceptionPending()) << PrettyTypeOf(self->GetException());
     }
   }
@@ -2338,7 +2338,7 @@
   CHECK_EQ(mh.GetReturnType(), mh2.GetReturnType());
 }
 
-bool ClassLinker::InitializeClass(Class* klass, bool can_run_clinit) {
+bool ClassLinker::InitializeClass(Class* klass, bool can_run_clinit, bool can_init_statics) {
   CHECK(klass->IsResolved() || klass->IsErroneous())
       << PrettyClass(klass) << " is " << klass->GetStatus();
 
@@ -2404,7 +2404,7 @@
 
   uint64_t t0 = NanoTime();
 
-  if (!InitializeSuperClass(klass, can_run_clinit)) {
+  if (!InitializeSuperClass(klass, can_run_clinit, can_init_statics)) {
     // Super class initialization failed, this can be because we can't run
     // super-class class initializers in which case we'll be verified.
     // Otherwise this class is erroneous.
@@ -2416,7 +2416,7 @@
     return false;
   }
 
-  InitializeStaticFields(klass);
+  bool has_static_field_initializers = InitializeStaticFields(klass);
 
   if (clinit != NULL) {
     clinit->Invoke(self, NULL, NULL, NULL);
@@ -2441,7 +2441,14 @@
       ++thread_stats->class_init_count;
       global_stats->class_init_time_ns += (t1 - t0);
       thread_stats->class_init_time_ns += (t1 - t0);
-      klass->SetStatus(Class::kStatusInitialized);
+      // Set the class as initialized except if we can't initialize static fields and static field
+      // initialization is necessary.
+      if (!can_init_statics && has_static_field_initializers) {
+        klass->SetStatus(Class::kStatusVerified);  // Don't leave class in initializing state.
+        success = false;
+      } else {
+        klass->SetStatus(Class::kStatusInitialized);
+      }
       if (VLOG_IS_ON(class_linker)) {
         ClassHelper kh(klass);
         LOG(INFO) << "Initialized class " << kh.GetDescriptor() << " from " << kh.GetLocation();
@@ -2577,7 +2584,7 @@
   return found1 == found2;
 }
 
-bool ClassLinker::InitializeSuperClass(Class* klass, bool can_run_clinit) {
+bool ClassLinker::InitializeSuperClass(Class* klass, bool can_run_clinit, bool can_init_fields) {
   CHECK(klass != NULL);
   if (!klass->IsInterface() && klass->HasSuperClass()) {
     Class* super_class = klass->GetSuperClass();
@@ -2585,7 +2592,7 @@
       CHECK(!super_class->IsInterface());
       Thread* self = Thread::Current();
       klass->MonitorEnter(self);
-      bool super_initialized = InitializeClass(super_class, can_run_clinit);
+      bool super_initialized = InitializeClass(super_class, can_run_clinit, can_init_fields);
       klass->MonitorExit(self);
       // TODO: check for a pending exception
       if (!super_initialized) {
@@ -2604,7 +2611,7 @@
   return true;
 }
 
-bool ClassLinker::EnsureInitialized(Class* c, bool can_run_clinit) {
+bool ClassLinker::EnsureInitialized(Class* c, bool can_run_clinit, bool can_init_fields) {
   CHECK(c != NULL);
   if (c->IsInitialized()) {
     return true;
@@ -2612,7 +2619,7 @@
 
   Thread* self = Thread::Current();
   ScopedThreadStateChange tsc(self, Thread::kRunnable);
-  bool success = InitializeClass(c, can_run_clinit);
+  bool success = InitializeClass(c, can_run_clinit, can_init_fields);
   if (!success) {
     CHECK(self->IsExceptionPending() || !can_run_clinit) << PrettyClass(c);
   }
@@ -2629,15 +2636,15 @@
   }
 }
 
-void ClassLinker::InitializeStaticFields(Class* klass) {
+bool ClassLinker::InitializeStaticFields(Class* klass) {
   size_t num_static_fields = klass->NumStaticFields();
   if (num_static_fields == 0) {
-    return;
+    return false;
   }
   DexCache* dex_cache = klass->GetDexCache();
   // TODO: this seems like the wrong check. do we really want !IsPrimitive && !IsArray?
   if (dex_cache == NULL) {
-    return;
+    return false;
   }
   ClassHelper kh(klass);
   const DexFile::ClassDef* dex_class_def = kh.GetClassDef();
@@ -2652,7 +2659,9 @@
     for (size_t i = 0; it.HasNext(); i++, it.Next()) {
       it.ReadValueToField(field_map[i]);
     }
+    return true;
   }
+  return false;
 }
 
 bool ClassLinker::LinkClass(SirtRef<Class>& klass, ObjectArray<Class>* interfaces) {
diff --git a/src/class_linker.h b/src/class_linker.h
index eb76738..3226a6f 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -222,7 +222,7 @@
   // Returns true on success, false if there's an exception pending.
   // can_run_clinit=false allows the compiler to attempt to init a class,
   // given the restriction that no <clinit> execution is possible.
-  bool EnsureInitialized(Class* c, bool can_run_clinit);
+  bool EnsureInitialized(Class* c, bool can_run_clinit, bool can_init_fields);
 
   // Initializes classes that have instances in the image but that have
   // <clinit> methods so they could not be initialized by the compiler.
@@ -365,11 +365,12 @@
   bool IsDexFileRegisteredLocked(const DexFile& dex_file) const;
   void RegisterOatFileLocked(const OatFile& oat_file);
 
-  bool InitializeClass(Class* klass, bool can_run_clinit);
+  bool InitializeClass(Class* klass, bool can_run_clinit, bool can_init_statics);
   bool WaitForInitializeClass(Class* klass, Thread* self, ObjectLock& lock);
   bool ValidateSuperClassDescriptors(const Class* klass);
-  bool InitializeSuperClass(Class* klass, bool can_run_clinit);
-  void InitializeStaticFields(Class* klass);
+  bool InitializeSuperClass(Class* klass, bool can_run_clinit, bool can_init_fields);
+  // Initialize static fields, returns true if fields were initialized.
+  bool InitializeStaticFields(Class* klass);
 
   bool IsSameDescriptorInDifferentClassContexts(const char* descriptor,
                                                 const Class* klass1,
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index f505d58..6fac6f8 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -835,7 +835,7 @@
 TEST_F(ClassLinkerTest, StaticFields) {
   SirtRef<ClassLoader> class_loader(LoadDex("Statics"));
   Class* statics = class_linker_->FindClass("LStatics;", class_loader.get());
-  class_linker_->EnsureInitialized(statics, true);
+  class_linker_->EnsureInitialized(statics, true, true);
 
   // Static final primitives that are initialized by a compile-time constant
   // expression resolve to a copy of a constant value from the constant pool.
diff --git a/src/compiler.cc b/src/compiler.cc
index 5507c36..bac39ef 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -1173,7 +1173,10 @@
     if (klass != NULL) {
       if (klass->IsVerified()) {
         // Only try to initialize classes that were successfully verified.
-        class_linker->EnsureInitialized(klass, false);
+        bool compiling_boot = Runtime::Current()->GetHeap()->GetSpaces().size() == 1;
+        bool can_init_static_fields = compiling_boot &&
+            IsImageClass(descriptor);
+        class_linker->EnsureInitialized(klass, false, can_init_static_fields);
       }
       // record the final class status if necessary
       Class::Status status = klass->GetStatus();
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 20af47a..fcd1689 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -68,7 +68,7 @@
     return NULL;
   }
   if (initialize) {
-    class_linker->EnsureInitialized(c, true);
+    class_linker->EnsureInitialized(c, true, true);
   }
   return AddLocalReference<jclass>(env, c);
 }
@@ -403,7 +403,7 @@
     return NULL;
   }
 
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
     return NULL;
   }
 
diff --git a/src/java_lang_reflect_Constructor.cc b/src/java_lang_reflect_Constructor.cc
index c908bb7..9a498f4 100644
--- a/src/java_lang_reflect_Constructor.cc
+++ b/src/java_lang_reflect_Constructor.cc
@@ -41,7 +41,7 @@
     return NULL;
   }
 
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
     DCHECK(Thread::Current()->IsExceptionPending());
     return NULL;
   }
diff --git a/src/java_lang_reflect_Field.cc b/src/java_lang_reflect_Field.cc
index 70636d6..a7785e7 100644
--- a/src/java_lang_reflect_Field.cc
+++ b/src/java_lang_reflect_Field.cc
@@ -26,7 +26,7 @@
 
 static bool GetFieldValue(Object* o, Field* f, JValue& value, bool allow_references) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true, true)) {
     return false;
   }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
@@ -157,7 +157,7 @@
 }
 
 static void SetFieldValue(Object* o, Field* f, const JValue& new_value, bool allow_references) {
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(f->GetDeclaringClass(), true, true)) {
     return;
   }
   switch (FieldHelper(f).GetTypeAsPrimitiveType()) {
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 949217b..c5ffec1 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -331,7 +331,7 @@
 
 static jmethodID FindMethodID(ScopedJniThreadState& ts, jclass jni_class, const char* name, const char* sig, bool is_static) {
   Class* c = Decode<Class*>(ts, jni_class);
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
     return NULL;
   }
 
@@ -366,7 +366,7 @@
 
 static jfieldID FindFieldID(ScopedJniThreadState& ts, jclass jni_class, const char* name, const char* sig, bool is_static) {
   Class* c = Decode<Class*>(ts, jni_class);
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
     return NULL;
   }
 
@@ -966,7 +966,7 @@
   static jobject AllocObject(JNIEnv* env, jclass java_class) {
     ScopedJniThreadState ts(env);
     Class* c = Decode<Class*>(ts, java_class);
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
       return NULL;
     }
     return AddLocalReference<jobject>(env, c->AllocObject());
@@ -984,7 +984,7 @@
   static jobject NewObjectV(JNIEnv* env, jclass java_class, jmethodID mid, va_list args) {
     ScopedJniThreadState ts(env);
     Class* c = Decode<Class*>(ts, java_class);
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
       return NULL;
     }
     Object* result = c->AllocObject();
@@ -1003,7 +1003,7 @@
   static jobject NewObjectA(JNIEnv* env, jclass java_class, jmethodID mid, jvalue* args) {
     ScopedJniThreadState ts(env);
     Class* c = Decode<Class*>(ts, java_class);
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
       return NULL;
     }
     Object* result = c->AllocObject();
@@ -2898,7 +2898,7 @@
   // If this is a static method, it could be called before the class
   // has been initialized.
   if (m->IsStatic()) {
-    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true)) {
+    if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(c, true, true)) {
       return NULL;
     }
   } else {
diff --git a/src/oat/runtime/support_stubs.cc b/src/oat/runtime/support_stubs.cc
index 8727a5d..5f7d635 100644
--- a/src/oat/runtime/support_stubs.cc
+++ b/src/oat/runtime/support_stubs.cc
@@ -141,7 +141,7 @@
     if (LIKELY(called->IsDirect() == !is_virtual)) {
       // Ensure that the called method's class is initialized.
       Class* called_class = called->GetDeclaringClass();
-      linker->EnsureInitialized(called_class, true);
+      linker->EnsureInitialized(called_class, true, true);
       if (LIKELY(called_class->IsInitialized())) {
         code = called->GetCode();
       } else if (called_class->IsInitializing()) {
diff --git a/src/reflection.cc b/src/reflection.cc
index ffc1e1f..008c1cd 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -54,7 +54,7 @@
   Method* m = reinterpret_cast<Method*>(mid);
 
   Class* declaring_class = m->GetDeclaringClass();
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaring_class, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(declaring_class, true, true)) {
     return NULL;
   }
 
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 0ee9990..7b907ae 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -229,7 +229,7 @@
         // we'd still be waiting for the lock.
         if (fields_class->IsInitializing()) {
           return resolved_field;
-        } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true)) {
+        } else if (Runtime::Current()->GetClassLinker()->EnsureInitialized(fields_class, true, true)) {
           return resolved_field;
         } else {
           DCHECK(self->IsExceptionPending());  // Throw exception and unwind
@@ -367,7 +367,7 @@
   if (klass == referring_class && MethodHelper(referrer).IsClassInitializer()) {
     return klass;
   }
-  if (!class_linker->EnsureInitialized(klass, true)) {
+  if (!class_linker->EnsureInitialized(klass, true, true)) {
     CHECK(self->IsExceptionPending());
     return NULL;  // Failure - Indicate to caller to deliver exception
   }
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 2f5ba5b..0229bc1 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -87,7 +87,7 @@
       return NULL;  // Failure
     }
   }
-  if (!runtime->GetClassLinker()->EnsureInitialized(klass, true)) {
+  if (!runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) {
     DCHECK(self->IsExceptionPending());
     return NULL;  // Failure
   }
diff --git a/src/thread.cc b/src/thread.cc
index c4934b0..5233dd1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -244,14 +244,14 @@
 }
 
 Object* Thread::GetMainThreadGroup() {
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(gThreadGroup, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(gThreadGroup, true, true)) {
     return NULL;
   }
   return gThreadGroup_mMain->GetObject(NULL);
 }
 
 Object* Thread::GetSystemThreadGroup() {
-  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(gThreadGroup, true)) {
+  if (!Runtime::Current()->GetClassLinker()->EnsureInitialized(gThreadGroup, true, true)) {
     return NULL;
   }
   return gThreadGroup_mSystem->GetObject(NULL);