New ClassLinker::CreateArrayClass

- Added ClassLinker::CreateArrayClass for use by ClassLinker::FindClass
- Replaced hand crafted ClassLinker::char_array_class_ initialization with call to FindClass
- Removed ClassLinker::LoadClass public interfaces, kept only one internally
- Removed JType
- Cleanedup gtest naming convention
- Added RuntimeTest for common test initialization such as Thread::Init
- Switched from assert to DCHECK which found some bit rotted code
- Expanded class_linker_test to array classes and also also array rank
  and interface count in existing cases

Change-Id: Ie3d71c8b434c8521f4ea8d2f07b1c4c905ee1d90
diff --git a/src/assembler_x86_test.cc b/src/assembler_x86_test.cc
index 3c2a02d..e49e91f 100644
--- a/src/assembler_x86_test.cc
+++ b/src/assembler_x86_test.cc
@@ -5,6 +5,6 @@
 #include <stdio.h>
 #include "gtest/gtest.h"
 
-TEST(AssemblerX86, Init) {
+TEST(AssemblerX86Test, Init) {
   ASSERT_TRUE(true);
 }
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 332900b..5c4fa5c 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -30,39 +30,50 @@
   // Allocate and partially initialize the Class, Object, Field, Method classes.
   // Initialization will be completed when the definitions are loaded.
   java_lang_Class_ = reinterpret_cast<Class*>(Heap::AllocRaw(sizeof(Class), NULL));
+  CHECK(java_lang_Class_ != NULL);
   java_lang_Class_->descriptor_ = "Ljava/lang/Class;";
   java_lang_Class_->object_size_ = sizeof(Class);
   java_lang_Class_->klass_ = java_lang_Class_;
 
   java_lang_Object_ = AllocClass(NULL);
+  CHECK(java_lang_Object_ != NULL);
   java_lang_Object_->descriptor_ = "Ljava/lang/Object;";
 
   java_lang_Class_->super_class_ = java_lang_Object_;
 
   java_lang_ref_Field_ = AllocClass(NULL);
+  CHECK(java_lang_ref_Field_ != NULL);
   java_lang_ref_Field_->descriptor_ = "Ljava/lang/ref/Field;";
 
   java_lang_ref_Method_ = AllocClass(NULL);
+  CHECK(java_lang_ref_Method_ != NULL);
   java_lang_ref_Method_->descriptor_ = "Ljava/lang/Method;";
 
-  // Allocate and initialize the primitive type classes.
-  primitive_byte_ = CreatePrimitiveClass(kTypeByte, "B");
-  primitive_char_ = CreatePrimitiveClass(kTypeChar, "C");
-  primitive_double_ = CreatePrimitiveClass(kTypeDouble, "D");
-  primitive_float_ = CreatePrimitiveClass(kTypeFloat, "F");
-  primitive_int_ = CreatePrimitiveClass(kTypeInt, "I");
-  primitive_long_ = CreatePrimitiveClass(kTypeLong, "J");
-  primitive_short_ = CreatePrimitiveClass(kTypeShort, "S");
-  primitive_boolean_ = CreatePrimitiveClass(kTypeBoolean, "Z");
-  primitive_void_ = CreatePrimitiveClass(kTypeVoid, "V");
+  java_lang_Cloneable_ = AllocClass(NULL);
+  CHECK(java_lang_Cloneable_ != NULL);
+  java_lang_Cloneable_->descriptor_ = "Ljava/lang/Cloneable;";
 
-  char_array_class_ = AllocClass(NULL);
-  char_array_class_->descriptor_ = "[C";
-  char_array_class_->status_ = Class::kStatusInitialized;
-  char_array_class_->component_type_ = primitive_char_;
-  char_array_class_->array_rank_ = 1;
-  char_array_class_->primitive_type_ = Class::kPrimNot;
-  char_array_class_->super_class_ = java_lang_Object_;
+  java_io_Serializable_ = AllocClass(NULL);
+  CHECK(java_io_Serializable_ != NULL);
+  java_io_Serializable_->descriptor_ = "Ljava/io/Serializable;";
+
+  java_lang_String_ = AllocClass(NULL);
+  CHECK(java_lang_String_ != NULL);
+  java_lang_String_->descriptor_ = "Ljava/lang/String;";
+
+  // Allocate and initialize the primitive type classes.
+  primitive_byte_ = CreatePrimitiveClass("B");
+  primitive_char_ = CreatePrimitiveClass("C");
+  primitive_double_ = CreatePrimitiveClass("D");
+  primitive_float_ = CreatePrimitiveClass("F");
+  primitive_int_ = CreatePrimitiveClass("I");
+  primitive_long_ = CreatePrimitiveClass("J");
+  primitive_short_ = CreatePrimitiveClass("S");
+  primitive_boolean_ = CreatePrimitiveClass("Z");
+  primitive_void_ = CreatePrimitiveClass("V");
+
+  char_array_class_ = FindSystemClass("[C");
+  CHECK(char_array_class_ != NULL);
 }
 
 Class* ClassLinker::AllocClass(DexFile* dex_file) {
@@ -86,20 +97,22 @@
 Class* ClassLinker::FindClass(const StringPiece& descriptor,
                               Object* class_loader) {
   Thread* self = Thread::Current();
+  DCHECK(self != NULL);
   CHECK(!self->IsExceptionPending());
   // Find the class in the loaded classes table.
   Class* klass = LookupClass(descriptor, class_loader);
   if (klass == NULL) {
     // Class is not yet loaded.
-    const RawDexFile::ClassDef* class_def;
-    // No .dex file specified, search the class path.
+    if (descriptor[0] == '[') {
+      return CreateArrayClass(descriptor, class_loader);
+    }
     ClassPathEntry pair = FindInClassPath(descriptor);
     if (pair.first == NULL) {
-      LG << "Class " << descriptor << " really not found";
+      LG << "Class " << descriptor << " not found"; // TODO: NoClassDefFoundError
       return NULL;
     }
     DexFile* dex_file = pair.first;
-    class_def = pair.second;
+    const RawDexFile::ClassDef* class_def = pair.second;
     // Load the class from the dex file.
     if (descriptor == "Ljava/lang/Object;") {
       klass = java_lang_Object_;
@@ -118,6 +131,12 @@
       klass = java_lang_ref_Method_;
       klass->dex_file_ = dex_file;
       klass->object_size_ = sizeof(Method);
+    } else if (descriptor == "Ljava/lang/Cloneable;") {
+      klass = java_lang_Cloneable_;
+      klass->dex_file_ = dex_file;
+    } else if (descriptor == "Ljava/io/Serializable;") {
+      klass = java_io_Serializable_;
+      klass->dex_file_ = dex_file;
     } else if (descriptor == "Ljava/lang/String;") {
       klass = java_lang_String_;
       klass->dex_file_ = dex_file;
@@ -125,12 +144,7 @@
     } else {
       klass = AllocClass(dex_file);
     }
-    bool is_loaded = LoadClass(*class_def, klass);
-    if (!is_loaded) {
-      // TODO: this occurs only when a dex file is provided.
-      LG << "Class not found";  // TODO: NoClassDefFoundError
-      return NULL;
-    }
+    LoadClass(*class_def, klass);
     // Check for a pending exception during load
     if (self->IsExceptionPending()) {
       // TODO: free native allocations in klass
@@ -180,17 +194,7 @@
   return klass;
 }
 
-bool ClassLinker::LoadClass(const StringPiece& descriptor, Class* klass) {
-  const RawDexFile* raw = klass->GetDexFile()->GetRaw();
-  const RawDexFile::ClassDef* class_def = raw->FindClassDef(descriptor);
-  if (class_def == NULL) {
-    return false;
-  } else {
-    return LoadClass(*class_def, klass);
-  }
-}
-
-bool ClassLinker::LoadClass(const RawDexFile::ClassDef& class_def, Class* klass) {
+void ClassLinker::LoadClass(const RawDexFile::ClassDef& class_def, Class* klass) {
   CHECK(klass != NULL);
   CHECK(klass->dex_file_ != NULL);
   const RawDexFile* raw = klass->GetDexFile()->GetRaw();
@@ -278,7 +282,6 @@
       // TODO: register maps
     }
   }
-  return true;
 }
 
 void ClassLinker::LoadInterfaces(const RawDexFile::ClassDef& class_def,
@@ -349,47 +352,225 @@
   class_path_.push_back(dex_file);
 }
 
-Class* ClassLinker::CreatePrimitiveClass(JType type, const char* descriptor) {
+Class* ClassLinker::CreatePrimitiveClass(const StringPiece& descriptor) {
   Class* klass = AllocClass(NULL);
   CHECK(klass != NULL);
   klass->super_class_ = NULL;
   klass->access_flags_ = kAccPublic | kAccFinal | kAccAbstract;
   klass->descriptor_ = descriptor;
+  klass->descriptor_alloc_ = NULL;
   klass->status_ = Class::kStatusInitialized;
+  bool success = InsertClass(klass);
+  CHECK(success);
   return klass;
 }
 
-Class* ClassLinker::FindPrimitiveClass(JType type) {
+/*
+ * Create an array class (i.e. the class object for the array, not the
+ * array itself).  "descriptor" looks like "[C" or "[[[[B" or
+ * "[Ljava/lang/String;".
+ *
+ * If "descriptor" refers to an array of primitives, look up the
+ * primitive type's internally-generated class object.
+ *
+ * "loader" is the class loader of the class that's referring to us.  It's
+ * used to ensure that we're looking for the element type in the right
+ * context.  It does NOT become the class loader for the array class; that
+ * always comes from the base element class.
+ *
+ * Returns NULL with an exception raised on failure.
+ */
+Class* ClassLinker::CreateArrayClass(const StringPiece& descriptor, Object* class_loader)
+{
+    CHECK(descriptor[0] == '[');
+    DCHECK(java_lang_Class_ != NULL);
+    DCHECK(java_lang_Object_ != NULL);
+
+    /*
+     * Identify the underlying element class and the array dimension depth.
+     */
+    Class* component_type_ = NULL;
+    int array_rank;
+    if (descriptor[1] == '[') {
+        /* array of arrays; keep descriptor and grab stuff from parent */
+        Class* outer = FindClass(descriptor.substr(1), class_loader);
+        if (outer != NULL) {
+            /* want the base class, not "outer", in our component_type_ */
+            component_type_ = outer->component_type_;
+            array_rank = outer->array_rank_ + 1;
+        } else {
+            DCHECK(component_type_ == NULL);     /* make sure we fail */
+        }
+    } else {
+        array_rank = 1;
+        if (descriptor[1] == 'L') {
+            /* array of objects; strip off "[" and look up descriptor. */
+            const StringPiece subDescriptor = descriptor.substr(1);
+            LG << "searching for element class '" << subDescriptor << "'";
+            component_type_ = FindClass(subDescriptor, class_loader); // TODO FindClassNoInit
+        } else {
+            /* array of a primitive type */
+            component_type_ = FindPrimitiveClass(descriptor[1]);
+        }
+    }
+
+    if (component_type_ == NULL) {
+        /* failed */
+        DCHECK(Thread::Current()->IsExceptionPending());
+        return NULL;
+    }
+
+    /*
+     * See if the component type is already loaded.  Array classes are
+     * always associated with the class loader of their underlying
+     * element type -- an array of Strings goes with the loader for
+     * java/lang/String -- so we need to look for it there.  (The
+     * caller should have checked for the existence of the class
+     * before calling here, but they did so with *their* class loader,
+     * not the component type's loader.)
+     *
+     * If we find it, the caller adds "loader" to the class' initiating
+     * loader list, which should prevent us from going through this again.
+     *
+     * This call is unnecessary if "loader" and "component_type_->class_loader_"
+     * are the same, because our caller (FindClass) just did the
+     * lookup.  (Even if we get this wrong we still have correct behavior,
+     * because we effectively do this lookup again when we add the new
+     * class to the hash table --- necessary because of possible races with
+     * other threads.)
+     */
+    if (class_loader != component_type_->class_loader_) {
+        Class* new_class = LookupClass(descriptor, component_type_->class_loader_);
+        if (new_class != NULL) {
+            return new_class;
+        }
+    }
+
+    /*
+     * Fill out the fields in the Class.
+     *
+     * It is possible to execute some methods against arrays, because
+     * all arrays are subclasses of java_lang_Object_, so we need to set
+     * up a vtable.  We can just point at the one in java_lang_Object_.
+     *
+     * Array classes are simple enough that we don't need to do a full
+     * link step.
+     */
+    Class* new_class = AllocClass(NULL);
+    if (new_class == NULL) {
+      return NULL;
+    }
+    new_class->descriptor_alloc_ = new std::string(descriptor.data(),
+                                                   descriptor.size());
+    new_class->descriptor_.set(new_class->descriptor_alloc_->data(),
+                               new_class->descriptor_alloc_->size());
+    new_class->super_class_ = java_lang_Object_;
+    new_class->vtable_count_ = java_lang_Object_->vtable_count_;
+    new_class->vtable_ = java_lang_Object_->vtable_;
+    new_class->primitive_type_ = Class::kPrimNot;
+    new_class->component_type_ = component_type_;
+    new_class->class_loader_ = component_type_->class_loader_;
+    new_class->array_rank_ = array_rank;
+    new_class->status_ = Class::kStatusInitialized;
+
+    /* don't need to set new_class->object_size_ */
+
+    /*
+     * All arrays have java/lang/Cloneable and java/io/Serializable as
+     * interfaces.  We need to set that up here, so that stuff like
+     * "instanceof" works right.
+     *
+     * Note: The GC could run during the call to FindSystemClassNoInit(),
+     * so we need to make sure the class object is GC-valid while we're in
+     * there.  Do this by clearing the interface list so the GC will just
+     * think that the entries are null.
+     *
+     * TODO?
+     * We may want to create a single, global copy of "interfaces" and
+     * "iftable" somewhere near the start and just point to those (and
+     * remember not to free them for arrays).
+     */
+    new_class->interface_count_ = 2;
+    new_class->interfaces_ = new Class*[2];
+    memset(new_class->interfaces_, 0, sizeof(Class*) * 2);
+    new_class->interfaces_[0] = java_lang_Cloneable_;
+    new_class->interfaces_[1] = java_io_Serializable_;
+    /*
+     * We assume that Cloneable/Serializable don't have superinterfaces --
+     * normally we'd have to crawl up and explicitly list all of the
+     * supers as well.  These interfaces don't have any methods, so we
+     * don't have to worry about the ifviPool either.
+     */
+    new_class->iftable_count_ = 2;
+    new_class->iftable_ = new InterfaceEntry[2];
+    memset(new_class->iftable_, 0, sizeof(InterfaceEntry) * 2);
+    new_class->iftable_[0].SetClass(new_class->interfaces_[0]);
+    new_class->iftable_[1].SetClass(new_class->interfaces_[1]);
+
+    /*
+     * Inherit access flags from the component type.  Arrays can't be
+     * used as a superclass or interface, so we want to add "final"
+     * and remove "interface".
+     *
+     * Don't inherit any non-standard flags (e.g., kAccFinal)
+     * from component_type_.  We assume that the array class does not
+     * override finalize().
+     */
+    new_class->access_flags_ = ((new_class->component_type_->access_flags_ &
+                                 ~kAccInterface) | kAccFinal) & kAccJavaFlagsMask;
+
+    if (InsertClass(new_class)) {
+      return new_class;
+    }
+    /*
+     * Another thread must have loaded the class after we
+     * started but before we finished.  Abandon what we've
+     * done.
+     *
+     * (Yes, this happens.)
+     */
+
+    /* Grab the winning class.
+     */
+    Class* other_class = LookupClass(descriptor, component_type_->class_loader_);
+    DCHECK(other_class != NULL);
+    return other_class;
+}
+
+Class* ClassLinker::FindPrimitiveClass(char type) {
   switch (type) {
-    case kTypeByte:
+    case 'B':
       CHECK(primitive_byte_ != NULL);
       return primitive_byte_;
-    case kTypeChar:
+    case 'C':
       CHECK(primitive_char_ != NULL);
       return primitive_char_;
-    case kTypeDouble:
+    case 'D':
       CHECK(primitive_double_ != NULL);
       return primitive_double_;
-    case kTypeFloat:
+    case 'F':
       CHECK(primitive_float_ != NULL);
       return primitive_float_;
-    case kTypeInt:
+    case 'I':
       CHECK(primitive_int_ != NULL);
       return primitive_int_;
-    case kTypeLong:
+    case 'J':
       CHECK(primitive_long_ != NULL);
       return primitive_long_;
-    case kTypeShort:
+    case 'S':
       CHECK(primitive_short_ != NULL);
       return primitive_short_;
-    case kTypeBoolean:
+    case 'Z':
       CHECK(primitive_boolean_ != NULL);
       return primitive_boolean_;
-    case kTypeVoid:
+    case 'V':
       CHECK(primitive_void_ != NULL);
       return primitive_void_;
+    case 'L':
+    case '[':
+      LOG(ERROR) << "Not a primitive type " << static_cast<int>(type);
     default:
-      LOG(FATAL) << "Unknown primitive type " << static_cast<int>(type);
+      LOG(ERROR) << "Unknown primitive type " << static_cast<int>(type);
   };
   return NULL;  // Not reachable.
 }
@@ -470,7 +651,7 @@
       if (klass->GetStatus() == Class::kStatusInitializing) {
         continue;
       }
-      assert(klass->GetStatus() == Class::kStatusInitialized ||
+      DCHECK(klass->GetStatus() == Class::kStatusInitialized ||
              klass->GetStatus() == Class::kStatusError);
       if (klass->IsErroneous()) {
         /*
@@ -497,7 +678,7 @@
       return false;
     }
 
-    assert(klass->status < CLASS_INITIALIZING);
+    DCHECK(klass->status_ < Class::kStatusInitializing);
 
     klass->clinit_thread_id_ = self->GetId();
     klass->status_ = Class::kStatusInitializing;
@@ -903,8 +1084,8 @@
     ifCount += klass->interfaces_[i]->iftable_count_;
   }
   if (ifCount == 0) {
-    assert(klass->iftable_count_ == 0);
-    assert(klass->iftable == NULL);
+    DCHECK(klass->iftable_count_ == 0);
+    DCHECK(klass->iftable_ == NULL);
     return true;
   }
   klass->iftable_ = new InterfaceEntry[ifCount * sizeof(InterfaceEntry)];
@@ -917,7 +1098,7 @@
   size_t idx = super_ifcount;
   for (size_t i = 0; i < klass->interface_count_; i++) {
     Class* interf = klass->interfaces_[i];
-    assert(interf != NULL);
+    DCHECK(interf != NULL);
     if (!interf->IsInterface()) {
       LG << "Class implements non-interface class";  // TODO: IncompatibleClassChangeError
       return false;
@@ -1065,7 +1246,7 @@
 
     if (c != 'J' && c != 'D') {
       // The field that comes next is 32-bit, so just advance past it.
-      assert(c != '[' && c != 'L');
+      DCHECK(c != '[' && c != 'L');
       pField->SetOffset(field_offset);
       field_offset += sizeof(uint32_t);
       i++;
@@ -1095,7 +1276,7 @@
 
   // Alignment is good, shuffle any double-wide fields forward, and
   // finish assigning field offsets to all fields.
-  assert(i == klass->NumInstanceFields() || (field_offset & 0x04) == 0);
+  DCHECK(i == klass->NumInstanceFields() || (field_offset & 0x04) == 0);
   for ( ; i < klass->NumInstanceFields(); i++) {
     InstanceField* pField = klass->GetInstanceField(i);
     char c = pField->GetType();
@@ -1131,20 +1312,20 @@
     char c = pField->GetType();
 
     if (c == 'D' || c == 'J') {
-      assert((pField->offset_ & 0x07) == 0);
+      DCHECK((pField->offset_ & 0x07) == 0);
     }
 
     if (c != '[' && c != 'L') {
       if (!j) {
-        assert(i == klass->num_reference_ifields_);
+        DCHECK(i == klass->num_reference_ifields_);
         j = 1;
       }
     } else if (j) {
-      assert(false);
+      DCHECK(false);
     }
   }
   if (!j) {
-    assert(klass->num_reference_ifields_ == klass->NumInstanceFields());
+    DCHECK(klass->num_reference_ifields_ == klass->NumInstanceFields());
   }
 #endif
 
@@ -1192,8 +1373,7 @@
   }
   const char* descriptor = dex_file->GetRaw()->dexStringByTypeIdx(class_idx);
   if (descriptor[0] != '\0' && descriptor[1] == '\0') {
-    JType type = static_cast<JType>(descriptor[0]);
-    resolved = FindPrimitiveClass(type);
+    resolved = FindPrimitiveClass(descriptor[0]);
   } else {
     resolved = FindClass(descriptor, referrer->GetClassLoader());
   }
@@ -1207,7 +1387,7 @@
     }
     dex_file->SetResolvedClass(resolved, class_idx);
   } else {
-    CHECK(Thread::Current()->IsExceptionPending());
+    DCHECK(Thread::Current()->IsExceptionPending());
   }
   return resolved;
 }
diff --git a/src/class_linker.h b/src/class_linker.h
index 7f85da5..47f8c24 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -33,11 +33,7 @@
     return FindClass(descriptor, NULL);
   }
 
-  bool LoadClass(const StringPiece& descriptor, Class* klass);
-
-  bool LoadClass(const RawDexFile::ClassDef& class_def, Class* klass);
-
-  Class* FindPrimitiveClass(JType type);
+  Class* FindPrimitiveClass(char type);
 
   bool InitializeClass(Class* klass);
 
@@ -58,7 +54,11 @@
 
   void Init();
 
-  Class* CreatePrimitiveClass(JType type, const char* descriptor);
+  Class* CreatePrimitiveClass(const StringPiece& descriptor);
+
+  Class* CreateArrayClass(const StringPiece& descriptor, Object* class_loader);
+
+  void LoadClass(const RawDexFile::ClassDef& class_def, Class* klass);
 
   void LoadInterfaces(const RawDexFile::ClassDef& class_def, Class *klass);
 
@@ -113,6 +113,14 @@
 
   // TODO: classpath
 
+  Class* java_lang_Class_;
+  Class* java_lang_Object_;
+  Class* java_lang_ref_Field_;
+  Class* java_lang_ref_Method_;
+  Class* java_lang_Cloneable_;
+  Class* java_io_Serializable_;
+  Class* java_lang_String_;
+
   Class* primitive_boolean_;
   Class* primitive_char_;
   Class* primitive_float_;
@@ -123,11 +131,6 @@
   Class* primitive_long_;
   Class* primitive_void_;
 
-  Class* java_lang_Class_;
-  Class* java_lang_Object_;
-  Class* java_lang_ref_Field_;
-  Class* java_lang_ref_Method_;
-  Class* java_lang_String_;
   Class* char_array_class_;
 
   DISALLOW_COPY_AND_ASSIGN(ClassLinker);
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 251b6cb..00fb75c 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -8,53 +8,129 @@
 
 namespace art {
 
-TEST(ClassLinker, LoadNonexistent) {
+class ClassLinkerTest : public RuntimeTest {};
+
+TEST_F(ClassLinkerTest, FindClassNonexistent) {
   scoped_ptr<DexFile> dex(DexFile::OpenBase64(kMyClassDex));
   ASSERT_TRUE(dex != NULL);
 
   scoped_ptr<ClassLinker> linker(ClassLinker::Create());
   linker->AppendToClassPath(dex.get());
 
-  scoped_ptr<Class> klass(linker.get()->AllocClass(dex.get()));
-  bool result1 = linker->LoadClass("NoSuchClass", klass.get());
-  EXPECT_FALSE(result1);
-  bool result2 = linker->LoadClass("LNoSuchClass;", klass.get());
-  EXPECT_FALSE(result2);
+  Class* result1 = linker.get()->FindClass("NoSuchClass;", NULL);
+  EXPECT_TRUE(result1 == NULL);
+  Class* result2 = linker.get()->FindClass("LNoSuchClass;", NULL);
+  EXPECT_TRUE(result2 == NULL);
 }
 
-TEST(ClassLinker, Load) {
-  scoped_ptr<DexFile> dex(DexFile::OpenBase64(kNestedDex));
-  ASSERT_TRUE(dex != NULL);
+TEST_F(ClassLinkerTest, FindClassNested) {
+  scoped_ptr<DexFile> objectDex(DexFile::OpenBase64(kJavaLangDex));
+  ASSERT_TRUE(objectDex != NULL);
+  scoped_ptr<DexFile> nestedDex(DexFile::OpenBase64(kNestedDex));
+  ASSERT_TRUE(nestedDex != NULL);
 
   scoped_ptr<ClassLinker> linker(ClassLinker::Create());
-  linker->AppendToClassPath(dex.get());
+  linker->AppendToClassPath(objectDex.get());
+  linker->AppendToClassPath(nestedDex.get());
 
-  scoped_ptr<Class> klass(linker.get()->AllocClass(dex.get()));
-  bool result = linker->LoadClass("LNested;", klass.get());
-  ASSERT_TRUE(result);
+  Class* outer = linker.get()->FindClass("LNested;", NULL);
+  ASSERT_TRUE(outer != NULL);
+  EXPECT_EQ(0U, outer->NumVirtualMethods());
+  EXPECT_EQ(1U, outer->NumDirectMethods());
 
-  uint32_t vmeth = klass->NumVirtualMethods();
-  EXPECT_EQ(vmeth, 0U);
-
-  uint32_t dmeth = klass->NumDirectMethods();
-  EXPECT_EQ(dmeth, 1U);
+  Class* inner = linker.get()->FindClass("LNested$Inner;", NULL);
+  ASSERT_TRUE(inner != NULL);
+  EXPECT_EQ(0U, inner->NumVirtualMethods());
+  EXPECT_EQ(1U, inner->NumDirectMethods());
 }
 
-TEST(ClassLinker, FindClass) {
-  ASSERT_TRUE(Thread::Init());
-  ASSERT_TRUE(Thread::Attach() != NULL);
+static void AssertNonExistantClass(ClassLinker* linker, const StringPiece& descriptor) {
+  EXPECT_TRUE(linker->FindClass(descriptor, NULL) == NULL);
+}
+
+static void AssertPrimitiveClass(ClassLinker* linker, const StringPiece& descriptor) {
+  Class* primitive = linker->FindClass(descriptor, NULL);
+  ASSERT_TRUE(primitive != NULL);
+  ASSERT_TRUE(primitive->GetClass() != NULL);
+  ASSERT_EQ(primitive->GetClass(), primitive->GetClass()->GetClass());
+  EXPECT_TRUE(primitive->GetClass()->GetSuperClass() != NULL);
+  ASSERT_EQ(descriptor, primitive->GetDescriptor());
+  EXPECT_TRUE(primitive->GetSuperClass() == NULL);
+  EXPECT_FALSE(primitive->HasSuperClass());
+  EXPECT_TRUE(primitive->GetComponentType() == NULL);
+  EXPECT_TRUE(primitive->GetStatus() == Class::kStatusInitialized);
+  EXPECT_FALSE(primitive->IsErroneous());
+  EXPECT_TRUE(primitive->IsVerified());
+  EXPECT_TRUE(primitive->IsLinked());
+  EXPECT_FALSE(primitive->IsArray());
+  EXPECT_EQ(0, primitive->array_rank_);
+  EXPECT_FALSE(primitive->IsInterface());
+  EXPECT_TRUE(primitive->IsPublic());
+  EXPECT_TRUE(primitive->IsFinal());
+  EXPECT_TRUE(primitive->IsPrimitive());
+  EXPECT_EQ(0U, primitive->NumDirectMethods());
+  EXPECT_EQ(0U, primitive->NumVirtualMethods());
+  EXPECT_EQ(0U, primitive->NumInstanceFields());
+  EXPECT_EQ(0U, primitive->NumStaticFields());
+  EXPECT_EQ(0U, primitive->interface_count_);
+}
+
+static void AssertArrayClass(ClassLinker* linker,
+                             const StringPiece& array_descriptor,
+                             int32_t array_rank,
+                             const StringPiece& component_type) {
+  Class* array = linker->FindClass(array_descriptor, NULL);
+  ASSERT_TRUE(array != NULL);
+  ASSERT_TRUE(array->GetClass() != NULL);
+  ASSERT_EQ(array->GetClass(), array->GetClass()->GetClass());
+  EXPECT_TRUE(array->GetClass()->GetSuperClass() != NULL);
+  ASSERT_EQ(array_descriptor, array->GetDescriptor());
+  EXPECT_TRUE(array->GetSuperClass() != NULL);
+  EXPECT_EQ(linker->FindSystemClass("Ljava/lang/Object;"), array->GetSuperClass());
+  EXPECT_TRUE(array->HasSuperClass());
+  ASSERT_TRUE(array->GetComponentType() != NULL);
+  ASSERT_TRUE(array->GetComponentType()->GetDescriptor() != NULL);
+  EXPECT_EQ(component_type, array->GetComponentType()->GetDescriptor());
+  EXPECT_TRUE(array->GetStatus() == Class::kStatusInitialized);
+  EXPECT_FALSE(array->IsErroneous());
+  EXPECT_TRUE(array->IsVerified());
+  EXPECT_TRUE(array->IsLinked());
+  EXPECT_TRUE(array->IsArray());
+  EXPECT_EQ(array_rank, array->array_rank_);
+  EXPECT_FALSE(array->IsInterface());
+  EXPECT_EQ(array->GetComponentType()->IsPublic(), array->IsPublic());
+  EXPECT_TRUE(array->IsFinal());
+  EXPECT_FALSE(array->IsPrimitive());
+  EXPECT_EQ(0U, array->NumDirectMethods());
+  EXPECT_EQ(0U, array->NumVirtualMethods());
+  EXPECT_EQ(0U, array->NumInstanceFields());
+  EXPECT_EQ(0U, array->NumStaticFields());
+  EXPECT_EQ(2U, array->interface_count_);
+}
+
+TEST_F(ClassLinkerTest, FindClass) {
+  scoped_ptr<ClassLinker> linker(ClassLinker::Create());
+
+  StringPiece expected = "BCDFIJSZV";
+  for (int ch = 0; ch < 255; ch++) {
+    char* s = reinterpret_cast<char*>(&ch);
+    StringPiece descriptor(s, 1);
+    if (expected.find(ch) == StringPiece::npos) {
+      AssertNonExistantClass(linker.get(), descriptor);
+    } else {
+      AssertPrimitiveClass(linker.get(), descriptor);
+    }
+  }
 
   scoped_ptr<DexFile> dex(DexFile::OpenBase64(kMyClassDex));
   ASSERT_TRUE(dex != NULL);
-
-  scoped_ptr<ClassLinker> linker(ClassLinker::Create());
   linker->AppendToClassPath(dex.get());
 
   Class* JavaLangObject = linker->FindClass("Ljava/lang/Object;", NULL);
   ASSERT_TRUE(JavaLangObject != NULL);
   ASSERT_TRUE(JavaLangObject->GetClass() != NULL);
   ASSERT_EQ(JavaLangObject->GetClass(), JavaLangObject->GetClass()->GetClass());
-  EXPECT_EQ(JavaLangObject->GetClass()->GetSuperClass(), JavaLangObject);
+  EXPECT_EQ(JavaLangObject, JavaLangObject->GetClass()->GetSuperClass());
   ASSERT_TRUE(JavaLangObject->GetDescriptor() == "Ljava/lang/Object;");
   EXPECT_TRUE(JavaLangObject->GetSuperClass() == NULL);
   EXPECT_FALSE(JavaLangObject->HasSuperClass());
@@ -63,6 +139,7 @@
   EXPECT_FALSE(JavaLangObject->IsVerified());
   EXPECT_TRUE(JavaLangObject->IsLinked());
   EXPECT_FALSE(JavaLangObject->IsArray());
+  EXPECT_EQ(0, JavaLangObject->array_rank_);
   EXPECT_FALSE(JavaLangObject->IsInterface());
   EXPECT_TRUE(JavaLangObject->IsPublic());
   EXPECT_FALSE(JavaLangObject->IsFinal());
@@ -71,12 +148,14 @@
   EXPECT_EQ(0U, JavaLangObject->NumVirtualMethods());
   EXPECT_EQ(0U, JavaLangObject->NumInstanceFields());
   EXPECT_EQ(0U, JavaLangObject->NumStaticFields());
+  EXPECT_EQ(0U, JavaLangObject->interface_count_);
+
 
   Class* MyClass = linker->FindClass("LMyClass;", NULL);
   ASSERT_TRUE(MyClass != NULL);
   ASSERT_TRUE(MyClass->GetClass() != NULL);
   ASSERT_EQ(MyClass->GetClass(), MyClass->GetClass()->GetClass());
-  EXPECT_EQ(MyClass->GetClass()->GetSuperClass(), JavaLangObject);
+  EXPECT_EQ(JavaLangObject, MyClass->GetClass()->GetSuperClass());
   ASSERT_TRUE(MyClass->GetDescriptor() == "LMyClass;");
   EXPECT_TRUE(MyClass->GetSuperClass() == JavaLangObject);
   EXPECT_TRUE(MyClass->HasSuperClass());
@@ -86,6 +165,7 @@
   EXPECT_FALSE(MyClass->IsVerified());
   EXPECT_TRUE(MyClass->IsLinked());
   EXPECT_FALSE(MyClass->IsArray());
+  EXPECT_EQ(0, JavaLangObject->array_rank_);
   EXPECT_FALSE(MyClass->IsInterface());
   EXPECT_FALSE(MyClass->IsPublic());
   EXPECT_FALSE(MyClass->IsFinal());
@@ -94,9 +174,17 @@
   EXPECT_EQ(0U, MyClass->NumVirtualMethods());
   EXPECT_EQ(0U, MyClass->NumInstanceFields());
   EXPECT_EQ(0U, MyClass->NumStaticFields());
+  EXPECT_EQ(0U, MyClass->interface_count_);
 
   EXPECT_EQ(JavaLangObject->GetClass()->GetClass(), MyClass->GetClass()->GetClass());
 
+  // created by class_linker
+  AssertArrayClass(linker.get(), "[C", 1, "C");
+  // synthesized on the fly
+  AssertArrayClass(linker.get(), "[[C", 2, "C");
+  AssertArrayClass(linker.get(), "[[[LMyClass;", 3, "LMyClass;");
+  // or not available at all
+  AssertNonExistantClass(linker.get(), "[[[[LNonExistantClass;");
 }
 
 }  // namespace art
diff --git a/src/common_test.h b/src/common_test.h
index 9747261..b9392c8 100644
--- a/src/common_test.h
+++ b/src/common_test.h
@@ -1,7 +1,40 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
+#include "src/thread.h"
+
+#include "gtest/gtest.h"
+
 namespace art {
 
+class RuntimeTest : public testing::Test {
+ protected:
+  virtual void SetUp() {
+    ASSERT_TRUE(Thread::Init());
+    ASSERT_TRUE(Thread::Attach() != NULL);
+  }
+};
+
+// package java.lang;
+// public class Object {}
+//
+// package java.lang;
+// public interface Cloneable {}
+//
+// package java.io;
+// public interface Serializable {}
+static const char kJavaLangDex[] =
+  "ZGV4CjAzNQAdS5NB20pfz7Z3u9Jh2IVuB3Hxe6BzR9FAAgAAcAAAAHhWNBIAAAAAAAAAALgBAAAI"
+  "AAAAcAAAAAQAAACQAAAAAQAAAKAAAAAAAAAAAAAAAAEAAACsAAAAAwAAALQAAAAsAQAAFAEAACgB"
+  "AAAwAQAAQAEAAFgBAABvAQAAgwEAAJABAACjAQAAAgAAAAMAAAAEAAAABwAAAAcAAAADAAAAAAAA"
+  "AAIAAAAAAAAAAgAAAAEAAAD/////AAAAAAUAAAAAAAAAqwEAAAAAAAAAAAAAAQYAAAIAAAAAAAAA"
+  "BgAAAAAAAAAAAAAAAAAAAAEAAAABBgAAAgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQABAAAAAACm"
+  "AQAAAQAAAA4AAAAGPGluaXQ+AA5DbG9uZWFibGUuamF2YQAWTGphdmEvaW8vU2VyaWFsaXphYmxl"
+  "OwAVTGphdmEvbGFuZy9DbG9uZWFibGU7ABJMamF2YS9sYW5nL09iamVjdDsAC09iamVjdC5qYXZh"
+  "ABFTZXJpYWxpemFibGUuamF2YQABVgADAAcOAAAAAQAAgYAElAIAAAALAAAAAAAAAAEAAAAAAAAA"
+  "AQAAAAgAAABwAAAAAgAAAAQAAACQAAAAAwAAAAEAAACgAAAABQAAAAEAAACsAAAABgAAAAMAAAC0"
+  "AAAAASAAAAEAAAAUAQAAAiAAAAgAAAAoAQAAAyAAAAEAAACmAQAAACAAAAEAAACrAQAAABAAAAEA"
+  "AAC4AQAA";
+
 // package java.lang;
 // public class Object {}
 //
diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc
index e12f8e2..0424cde 100644
--- a/src/dex_file_test.cc
+++ b/src/dex_file_test.cc
@@ -10,7 +10,7 @@
 
 namespace art {
 
-TEST(DexFile, Open) {
+TEST(DexFileTest, Open) {
   scoped_ptr<DexFile> dex(DexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(dex != NULL);
 }
diff --git a/src/dex_instruction_visitor_test.cc b/src/dex_instruction_visitor_test.cc
index 768fc50..3889d73 100644
--- a/src/dex_instruction_visitor_test.cc
+++ b/src/dex_instruction_visitor_test.cc
@@ -10,7 +10,7 @@
 
 class TestVisitor : public DexInstructionVisitor<TestVisitor> {};
 
-TEST(Instruction, Init) {
+TEST(InstructionTest, Init) {
   scoped_ptr<TestVisitor> visitor(new TestVisitor);
 }
 
@@ -25,7 +25,7 @@
   }
 };
 
-TEST(Instruction, Count) {
+TEST(InstructionTest, Count) {
   CountVisitor v0;
   uint16_t c0[] = {};
   v0.Visit(c0, sizeof(c0));
diff --git a/src/object.h b/src/object.h
index aec8c34..4f0a50c 100644
--- a/src/object.h
+++ b/src/object.h
@@ -33,20 +33,6 @@
   Object* l;
 };
 
-enum JType {
-  kTypeByte = 'B',
-  kTypeChar = 'C',
-  kTypeDouble = 'D',
-  kTypeFloat = 'F',
-  kTypeInt = 'I',
-  kTypeLong = 'J',
-  kTypeShort = 'S',
-  kTypeBoolean = 'Z',
-  kTypeClass = 'L',
-  kTypeArray= '[',
-  kTypeVoid = 'V',
-};
-
 static const uint32_t kAccPublic = 0x0001; // class, field, method, ic
 static const uint32_t kAccPrivate = 0x0002; // field, method, ic
 static const uint32_t kAccProtected = 0x0004; // field, method, ic
@@ -68,12 +54,13 @@
 
 static const uint32_t kAccMiranda = 0x8000;  // method
 
+static const uint32_t kAccJavaFlagsMask = 0xffff;  // bits set from Java sources (low 16)
+
 static const uint32_t kAccConstructor = 0x00010000; // method (Dalvik only)
 static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (Dalvik only)
 
-
 /*
- * Definitions for packing refOffsets in ClassObject.
+ * Definitions for packing refOffsets in Class.
  */
 /*
  * A magic value for refOffsets. Ignore the bits and walk the super
@@ -622,7 +609,7 @@
 
   // Proxy classes have their descriptor allocated on the native heap.
   // When this field is non-NULL it must be explicitly freed.
-  char* descriptor_alloc_;
+  std::string* descriptor_alloc_;
 
   // access flags; low 16 bits are defined by VM spec
   uint32_t access_flags_;  // TODO: make an instance field?
@@ -714,7 +701,7 @@
   // These describe the layout of the contents of a
   // DataObject-compatible Object.  Note that only the fields directly
   // declared by this class are listed in ifields; fields declared by
-  // a superclass are listed in the superclass's ClassObject.ifields.
+  // a superclass are listed in the superclass's Class.ifields.
   //
   // All instance fields that refer to objects are guaranteed to be at
   // the beginning of the field list.  ifieldRefCount specifies the
diff --git a/src/object_test.cc b/src/object_test.cc
index c138854..21aa111 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -13,7 +13,9 @@
 
 namespace art {
 
-TEST(Object, IsInSamePackage) {
+class ObjectTest : public RuntimeTest {};
+
+TEST_F(ObjectTest, IsInSamePackage) {
   // Matches
   EXPECT_TRUE(Class::IsInSamePackage("Ljava/lang/Object;",
                                      "Ljava/lang/Class"));
@@ -27,18 +29,22 @@
                                       "Ljava/lang/reflect/Method;"));
 }
 
+class MethodTest : public RuntimeTest {};
+
 // TODO: test 0 argument methods
 // TODO: make this test simpler and shorter
-TEST(Method, ProtoCompare) {
-  scoped_ptr<DexFile> dex_file(DexFile::OpenBase64(kProtoCompareDex));
-  ASSERT_TRUE(dex_file != NULL);
+TEST_F(MethodTest, ProtoCompare) {
+  scoped_ptr<DexFile> object_dex_file(DexFile::OpenBase64(kJavaLangDex));
+  ASSERT_TRUE(object_dex_file != NULL);
+  scoped_ptr<DexFile> proto_dex_file(DexFile::OpenBase64(kProtoCompareDex));
+  ASSERT_TRUE(proto_dex_file != NULL);
 
   scoped_ptr<ClassLinker> linker(ClassLinker::Create());
-  linker->AppendToClassPath(dex_file.get());
+  linker->AppendToClassPath(object_dex_file.get());
+  linker->AppendToClassPath(proto_dex_file.get());
 
-  scoped_ptr<Class> klass(linker.get()->AllocClass(dex_file.get()));
-  bool result = linker->LoadClass("LProtoCompare;", klass.get());
-  ASSERT_TRUE(result);
+  Class* klass = linker->FindClass("LProtoCompare;", NULL);
+  ASSERT_TRUE(klass != NULL);
 
   ASSERT_EQ(4U, klass->NumVirtualMethods());
 
@@ -85,22 +91,24 @@
   EXPECT_FALSE(m1->HasSameNameAndPrototype(m2));
 }
 
-TEST(Method, ProtoCompare2) {
-  scoped_ptr<DexFile> dex_file1(DexFile::OpenBase64(kProtoCompareDex));
-  ASSERT_TRUE(dex_file1 != NULL);
-  scoped_ptr<DexFile> dex_file2(DexFile::OpenBase64(kProtoCompare2Dex));
-  ASSERT_TRUE(dex_file2 != NULL);
+TEST_F(MethodTest, ProtoCompare2) {
+  scoped_ptr<DexFile> object_dex_file(DexFile::OpenBase64(kJavaLangDex));
+  ASSERT_TRUE(object_dex_file != NULL);
+  scoped_ptr<DexFile> proto1_dex_file(DexFile::OpenBase64(kProtoCompareDex));
+  ASSERT_TRUE(proto1_dex_file != NULL);
+  scoped_ptr<DexFile> proto2_dex_file(DexFile::OpenBase64(kProtoCompare2Dex));
+  ASSERT_TRUE(proto2_dex_file != NULL);
   scoped_ptr<ClassLinker> linker1(ClassLinker::Create());
-  linker1->AppendToClassPath(dex_file1.get());
+  linker1->AppendToClassPath(object_dex_file.get());
+  linker1->AppendToClassPath(proto1_dex_file.get());
   scoped_ptr<ClassLinker> linker2(ClassLinker::Create());
-  linker2->AppendToClassPath(dex_file2.get());
+  linker2->AppendToClassPath(object_dex_file.get());
+  linker2->AppendToClassPath(proto2_dex_file.get());
 
-  scoped_ptr<Class> klass1(linker1.get()->AllocClass(dex_file1.get()));
-  bool result1 = linker1->LoadClass("LProtoCompare;", klass1.get());
-  ASSERT_TRUE(result1);
-  scoped_ptr<Class> klass2(linker2.get()->AllocClass(dex_file2.get()));
-  bool result2 = linker2->LoadClass("LProtoCompare2;", klass2.get());
-  ASSERT_TRUE(result2);
+  Class* klass1 = linker1->FindClass("LProtoCompare;", NULL);
+  ASSERT_TRUE(klass1 != NULL);
+  Class* klass2 = linker2->FindClass("LProtoCompare2;", NULL);
+  ASSERT_TRUE(klass2 != NULL);
 
   Method* m1_1 = klass1->GetVirtualMethod(0);
   ASSERT_EQ("m1", m1_1->GetName());
diff --git a/src/raw_dex_file_test.cc b/src/raw_dex_file_test.cc
index 8b0c130..a67ad77 100644
--- a/src/raw_dex_file_test.cc
+++ b/src/raw_dex_file_test.cc
@@ -10,12 +10,12 @@
 
 namespace art {
 
-TEST(RawDexFile, Open) {
+TEST(RawDexFileTest, Open) {
   scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(raw != NULL);
 }
 
-TEST(RawDexFile, Header) {
+TEST(RawDexFileTest, Header) {
   scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(raw != NULL);
 
@@ -43,7 +43,7 @@
   EXPECT_EQ(320U, header.data_off_);
 }
 
-TEST(RawDexFile, ClassDefs) {
+TEST(RawDexFileTest, ClassDefs) {
   scoped_ptr<RawDexFile> raw(RawDexFile::OpenBase64(kNestedDex));
   ASSERT_TRUE(raw != NULL);
   EXPECT_EQ(2U, raw->NumClassDefs());