Invoke <clinit> where necessary.

This lets us run Long.toString and friends for integral types, though
we'll need a libcore hack to avoid ThreadLocals for the time being.

Change-Id: I04bba5914f1b7d00e4917922e65b9c27302a59ff
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 211b87d..b8cd891 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1177,7 +1177,7 @@
 
 bool ClassLinker::InitializeClass(Class* klass) {
   CHECK(klass->GetStatus() == Class::kStatusResolved ||
-        klass->GetStatus() == Class::kStatusError);
+      klass->GetStatus() == Class::kStatusError) << klass->GetStatus();
 
   Thread* self = Thread::Current();
 
@@ -1270,9 +1270,7 @@
 
   Method* clinit = klass->FindDeclaredDirectMethod("<clinit>", "()V");
   if (clinit != NULL) {
-    // JValue unused;
-    // TODO: dvmCallMethod(self, method, NULL, &unused);
-    // UNIMPLEMENTED(FATAL);
+    clinit->Invoke(self, NULL, NULL, NULL);
   }
 
   {
diff --git a/src/compiler/RuntimeUtilities.cc b/src/compiler/RuntimeUtilities.cc
index 298cef1..8c94d62 100644
--- a/src/compiler/RuntimeUtilities.cc
+++ b/src/compiler/RuntimeUtilities.cc
@@ -73,7 +73,9 @@
 {
     uint32_t size = (uint32_t)table[2] | (((uint32_t)table[3]) << 16);
     uint32_t size_in_bytes = size * table[1];
-    UNIMPLEMENTED(WARNING) << "Need to check if array.length() <= size";
+    if (static_cast<int32_t>(size) > array->GetLength()) {
+      UNIMPLEMENTED(FATAL) << "need to throw AIOOBE and unwind";
+    }
     memcpy((char*)array + art::Array::DataOffset().Int32Value(),
            (char*)&table[4], size_in_bytes);
 }
diff --git a/src/compiler_test.cc b/src/compiler_test.cc
index a1aadc8..b11dff6 100644
--- a/src/compiler_test.cc
+++ b/src/compiler_test.cc
@@ -296,35 +296,63 @@
   CompileDirectMethod(NULL, "java.lang.String", "<clinit>", "()V");
   CompileDirectMethod(NULL, "java.lang.String", "<init>", "(II[C)V");
   CompileDirectMethod(NULL, "java.lang.String", "<init>", "([CII)V");
+  CompileVirtualMethod(NULL, "java.lang.String", "_getChars", "(II[CI)V");
   CompileVirtualMethod(NULL, "java.lang.String", "charAt", "(I)C");
   CompileVirtualMethod(NULL, "java.lang.String", "length", "()I");
-  CompileVirtualMethod(NULL, "java.lang.String", "_getChars", "(II[CI)V");
 
+  CompileDirectMethod(NULL, "java.lang.AbstractStringBuilder", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.AbstractStringBuilder", "<init>", "(I)V");
-  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "append0", "(Ljava/lang/String;)V");
-  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "append0", "(C)V");
-  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "appendNull", "()V");
   CompileDirectMethod(NULL, "java.lang.AbstractStringBuilder", "enlargeBuffer", "(I)V");
+  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "append0", "(C)V");
+  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "append0", "(Ljava/lang/String;)V");
+  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "append0", "([CII)V");
+  CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "appendNull", "()V");
   CompileVirtualMethod(NULL, "java.lang.AbstractStringBuilder", "toString", "()Ljava/lang/String;");
 
+  CompileDirectMethod(NULL, "java.lang.StringBuilder", "<init>", "()V");
   CompileDirectMethod(NULL, "java.lang.StringBuilder", "<init>", "(I)V");
-  CompileVirtualMethod(NULL, "java.lang.StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
   CompileVirtualMethod(NULL, "java.lang.StringBuilder", "append", "(C)Ljava/lang/StringBuilder;");
+  CompileVirtualMethod(NULL, "java.lang.StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
+  CompileVirtualMethod(NULL, "java.lang.StringBuilder", "append", "(J)Ljava/lang/StringBuilder;");
+  CompileVirtualMethod(NULL, "java.lang.StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
   CompileVirtualMethod(NULL, "java.lang.StringBuilder", "toString", "()Ljava/lang/String;");
 
+  CompileDirectMethod(NULL, "java.lang.ThreadLocal", "<init>", "()V");
+  CompileVirtualMethod(NULL, "java.lang.ThreadLocal", "get", "()Ljava/lang/Object;");
+
+  CompileDirectMethod(NULL, "java.lang.Long", "toHexString", "(J)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.Long", "toString", "(J)Ljava/lang/String;");
   CompileDirectMethod(NULL, "java.lang.Long", "toString", "(JI)Ljava/lang/String;");
+
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "<clinit>", "()V");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "appendInt", "(Ljava/lang/AbstractStringBuilder;I)V");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "appendLong", "(Ljava/lang/AbstractStringBuilder;J)V");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "convertInt", "(Ljava/lang/AbstractStringBuilder;I)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "convertLong", "(Ljava/lang/AbstractStringBuilder;J)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "intIntoCharArray", "([CII)I");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "intToHexString", "(IZI)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "longToHexString", "(J)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "longToString", "(J)Ljava/lang/String;");
   CompileDirectMethod(NULL, "java.lang.IntegralToString", "longToString", "(JI)Ljava/lang/String;");
+  CompileDirectMethod(NULL, "java.lang.IntegralToString", "stringOf", "([C)Ljava/lang/String;");
 
   CompileDirectMethod(NULL, "java.lang.System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V");
-  CompileDirectMethod(NULL, "java.lang.System", "log", "(CLjava/lang/String;Ljava/lang/Throwable;)V");
-  CompileDirectMethod(NULL, "java.lang.System", "logI", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
-  CompileDirectMethod(NULL, "java.lang.System", "logI", "(Ljava/lang/String;)V");
   CompileDirectMethod(NULL, "java.lang.System", "currentTimeMillis", "()J");
+  CompileDirectMethod(NULL, "java.lang.System", "log", "(CLjava/lang/String;Ljava/lang/Throwable;)V");
+  CompileDirectMethod(NULL, "java.lang.System", "logI", "(Ljava/lang/String;)V");
+  CompileDirectMethod(NULL, "java.lang.System", "logI", "(Ljava/lang/String;Ljava/lang/Throwable;)V");
+
+  CompileDirectMethod(NULL, "java.util.Arrays", "checkOffsetAndCount", "(III)V");
 
   const ClassLoader* class_loader = LoadDex("SystemMethods");
+
+  CompileDirectMethod(class_loader, "SystemMethods", "<clinit>", "()V");
+
   AssertStaticIntMethod(class_loader, "SystemMethods", "test0", "()I", 123);
   AssertStaticIntMethod(class_loader, "SystemMethods", "test1", "()I", 123);
   AssertStaticIntMethod(class_loader, "SystemMethods", "test2", "()I", 123);
+  AssertStaticIntMethod(class_loader, "SystemMethods", "test3", "()I", 123);
+  AssertStaticIntMethod(class_loader, "SystemMethods", "test4", "()I", 123);
 }
 
 
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index bd1ccec..ef15e8e 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -196,28 +196,8 @@
 
 JValue InvokeWithArgArray(ScopedJniThreadState& ts, Object* receiver,
                           Method* method, byte* args) {
-  Thread* self = ts.Self();
-
-  // Push a transition back into managed code onto the linked list in thread
-  CHECK_EQ(Thread::kRunnable, self->GetState());
-  NativeToManagedRecord record;
-  self->PushNativeToManagedRecord(&record);
-
-  // Call the invoke stub associated with the method
-  // Pass everything as arguments
-  const Method::InvokeStub* stub = method->GetInvokeStub();
   JValue result;
-
-  if (method->HasCode() && stub != NULL) {
-    (*stub)(method, receiver, self, args, &result);
-  } else {
-    LOG(WARNING) << "Not invoking method with no associated code: "
-                 << PrettyMethod(method);
-    result.j = 0;
-  }
-
-  // Pop transition
-  self->PopNativeToManagedRecord(record);
+  method->Invoke(ts.Self(), receiver, args, &result);
   return result;
 }
 
diff --git a/src/mark_sweep.cc b/src/mark_sweep.cc
index ac9a7cd..a2f1a56 100644
--- a/src/mark_sweep.cc
+++ b/src/mark_sweep.cc
@@ -511,11 +511,11 @@
   if (*cleared != NULL) {
     Thread* self = Thread::Current();
     DCHECK(self != NULL);
-    // TODO: Method *meth = gDvm.methJavaLangRefReferenceQueueAdd;
-    // DCHECK(meth != NULL);
-    // JValue unused;
+    // TODO: Method* m = gDvm.methJavaLangRefReferenceQueueAdd;
+    // DCHECK(m != NULL);
     // Object* reference = *cleared;
-    // TODO: dvmCallMethod(self, meth, NULL, &unused, reference);
+    // args = {reference}
+    // TODO: m->Invoke(self, NULL, args, NULL);
     UNIMPLEMENTED(FATAL);
     *cleared = NULL;
   }
diff --git a/src/object.cc b/src/object.cc
index f92f746..8b0c976 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -56,57 +56,48 @@
   return Runtime::Current()->GetClassLinker()->ResolveType(GetTypeIdx(), this);
 }
 
-uint32_t Field::Get32StaticFromCode(uint32_t field_idx, const Method* referrer) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return 0;
+Field* FindFieldFromCode(uint32_t field_idx, const Method* referrer) {
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  Field* f = class_linker->ResolveField(field_idx, referrer);
+  if (f != NULL) {
+    Class* c = f->GetDeclaringClass();
+    // If the class is already initializing, we must be inside <clinit>, or
+    // we'd still be waiting for the lock.
+    if (c->GetStatus() == Class::kStatusInitializing || class_linker->EnsureInitialized(c)) {
+      return f;
+    }
   }
+  UNIMPLEMENTED(FATAL) << "throw an error and unwind";
+  return NULL;
+}
+
+uint32_t Field::Get32StaticFromCode(uint32_t field_idx, const Method* referrer) {
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(field->GetType()->PrimitiveSize() == sizeof(int32_t));
   return field->Get32(NULL);
 }
 void Field::Set32StaticFromCode(uint32_t field_idx, const Method* referrer, uint32_t new_value) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return;
-  }
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(field->GetType()->PrimitiveSize() == sizeof(int32_t));
   field->Set32(NULL, new_value);
 }
 uint64_t Field::Get64StaticFromCode(uint32_t field_idx, const Method* referrer) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return 0;
-  }
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(field->GetType()->PrimitiveSize() == sizeof(int64_t));
   return field->Get64(NULL);
 }
 void Field::Set64StaticFromCode(uint32_t field_idx, const Method* referrer, uint64_t new_value) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return;
-  }
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(field->GetType()->PrimitiveSize() == sizeof(int64_t));
   field->Set64(NULL, new_value);
 }
 Object* Field::GetObjStaticFromCode(uint32_t field_idx, const Method* referrer) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return 0;
-  }
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(!field->GetType()->IsPrimitive());
   return field->GetObj(NULL);
 }
 void Field::SetObjStaticFromCode(uint32_t field_idx, const Method* referrer, Object* new_value) {
-  Field* field = Runtime::Current()->GetClassLinker()->ResolveField(field_idx, referrer);
-  if (field == NULL) {
-    UNIMPLEMENTED(FATAL) << "throw an error";
-    return;
-  }
+  Field* field = FindFieldFromCode(field_idx, referrer);
   DCHECK(!field->GetType()->IsPrimitive());
   field->SetObj(NULL, new_value);
 }
@@ -483,6 +474,28 @@
       OFFSET_OF_OBJECT_MEMBER(Method, invoke_stub_), invoke_stub, false);
 }
 
+void Method::Invoke(Thread* self, Object* receiver, byte* args, JValue* result) const {
+  // Push a transition back into managed code onto the linked list in thread.
+  CHECK_EQ(Thread::kRunnable, self->GetState());
+  NativeToManagedRecord record;
+  self->PushNativeToManagedRecord(&record);
+
+  // Call the invoke stub associated with the method.
+  // Pass everything as arguments.
+  const Method::InvokeStub* stub = GetInvokeStub();
+  if (HasCode() && stub != NULL) {
+    (*stub)(this, receiver, self, args, result);
+  } else {
+    LOG(WARNING) << "Not invoking method with no associated code: " << PrettyMethod(this);
+    if (result != NULL) {
+      result->j = 0;
+    }
+  }
+
+  // Pop transition.
+  self->PopNativeToManagedRecord(record);
+}
+
 void Class::SetStatus(Status new_status) {
   CHECK(new_status > GetStatus() || new_status == kStatusError ||
         Runtime::Current() == NULL);  // no runtime implies we're not initialized
diff --git a/src/object.h b/src/object.h
index 9b3bf88..8504612 100644
--- a/src/object.h
+++ b/src/object.h
@@ -602,7 +602,7 @@
 class Method : public AccessibleObject {
  public:
   // An function that invokes a method with an array of its arguments.
-  typedef void InvokeStub(Method* method,
+  typedef void InvokeStub(const Method* method,
                           Object* obj,
                           Thread* thread,
                           byte* args,
@@ -833,6 +833,8 @@
   // Size in bytes of the return value
   size_t ReturnSize() const;
 
+  void Invoke(Thread* self, Object* receiver, byte* args, JValue* result) const;
+
   const ByteArray* GetCodeArray() const {
     return GetFieldPtr<const ByteArray*>(OFFSET_OF_OBJECT_MEMBER(Method, code_array_), false);
   }