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);
   }
diff --git a/test/SystemMethods/SystemMethods.java b/test/SystemMethods/SystemMethods.java
index f14df18..3d76548 100644
--- a/test/SystemMethods/SystemMethods.java
+++ b/test/SystemMethods/SystemMethods.java
@@ -24,7 +24,6 @@
     String[] digits = new String[] {
       "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
     };
-    System.logI("hello world");
     long t = System.currentTimeMillis();
     for (int i = 7; i >= 0; --i) {
       int b = ((int) (t >> (i * 8))) & 0xff;
@@ -34,31 +33,63 @@
     return 123;
   }
 
+  private static String[] STRING_DIGITS = new String[] {
+    "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
+  };
+
   public static int test2() {
-    System.logI("creating char[]...");
     char[] cs = new char[20];
-    System.logI("...created char[]");
-    String[] digits = new String[] {
-      "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f",
-    };
     long t = System.currentTimeMillis();
-    System.logI("creating StringBuilder...");
     StringBuilder sb = new StringBuilder(20);
-    System.logI("...created StringBuilder");
     for (int i = 7; i >= 0; --i) {
       int b = ((int) (t >> (i * 8))) & 0xff;
-      // TODO: StringBuilder.append(C) works, but StringBuilder.append(Ljava/lang/String;) doesn't.
-      System.logI("calling append...");
-      sb.append(digits[(b >> 4) & 0xf].charAt(0));
-      System.logI("...called append");
-      System.logI("calling append...");
-      sb.append(digits[b & 0xf].charAt(0));
-      System.logI("...called append");
+      sb.append(STRING_DIGITS[(b >> 4) & 0xf]);
+      sb.append(STRING_DIGITS[b & 0xf]);
     }
-    System.logI("calling toString...");
     String result = sb.toString();
-    System.logI("...called toString");
     System.logI(result);
     return 123;
   }
+
+  private static char[] DIGITS = new char[] {
+    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
+    'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+    'u', 'v', 'w', 'x', 'y', 'z'
+  };
+
+  public static int test3() {
+    long t = System.currentTimeMillis();
+
+    long v = t;
+    //        int i = (int) v;
+    //        if (v >= 0 && i == v) {
+    //            return intToHexString(i, false, 0);
+    //        }
+
+    int bufLen = 16;  // Max number of hex digits in a long
+    char[] buf = new char[bufLen];
+    int cursor = bufLen;
+
+    do {
+      buf[--cursor] = DIGITS[((int) v) & 0xF];
+    } while ((v >>>= 4) != 0);
+
+    String s = new String(buf, cursor, bufLen - cursor);
+    System.logI(s);
+
+    System.logI(IntegralToString.longToHexString(t));
+    System.logI(Long.toHexString(t));
+    System.logI(Long.toString(t));
+    return 123;
+  }
+
+  private static int i = 4;
+  private static long j = 0x0123456789abcdefL;
+
+  public static int test4() {
+    String s = "int=" + i + " long=" + j;
+    System.logI(s);
+    return 123;
+  }
 }