Add support for linking classes.

Change-Id: I0026be6e4c919f7391fd83c654f58c3bc67f44e1
diff --git a/src/class_linker.cc b/src/class_linker.cc
new file mode 100644
index 0000000..4924810
--- /dev/null
+++ b/src/class_linker.cc
@@ -0,0 +1,891 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: cshapiro@google.com (Carl Shapiro)
+
+#include "src/class_linker.h"
+
+#include <vector>
+#include <utility>
+
+#include "src/dex_verifier.h"
+#include "src/logging.h"
+#include "src/monitor.h"
+#include "src/object.h"
+#include "src/raw_dex_file.h"
+#include "src/scoped_ptr.h"
+#include "src/thread.h"
+#include "src/utils.h"
+
+namespace art {
+
+Class* ClassLinker::FindClass(const char* descriptor,
+                              Object* class_loader,
+                              DexFile* dex_file) {
+  Thread* self = Thread::Self();
+  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.
+    if (dex_file == NULL) {
+      // No .dex file specified, search the class path.
+      dex_file = FindInClassPath(descriptor);
+      if (dex_file == NULL) {
+        LG << "Class really not found";
+        return NULL;
+      }
+    }
+    // Load the class from the dex file.
+    klass = dex_file->LoadClass(descriptor);
+    if (klass == NULL) {
+      // TODO: this occurs only when a dex file is provided.
+      LG << "Class not found";  // TODO: NoClassDefFoundError
+      return NULL;
+    }
+    // Check for a pending exception during load
+    if (self->IsExceptionPending()) {
+      // TODO: free native allocations in klass
+      return NULL;
+    }
+    {
+      ObjectLock lock(klass);
+      klass->clinit_thread_id_ = self->GetThreadId();
+      // Add the newly loaded class to the loaded classes table.
+      bool success = InsertClass(klass);
+      if (!success) {
+        // We may fail to insert if we raced with another thread.
+        klass->clinit_thread_id_ = 0;
+        // TODO: free native allocations in klass
+        klass = LookupClass(descriptor, class_loader);
+        CHECK(klass != NULL);
+      } else {
+        // Link the class.
+        if (!LinkClass(klass)) {
+          // Linking failed.
+          // TODO: CHECK(self->IsExceptionPending());
+          lock.NotifyAll();
+          return NULL;
+        }
+      }
+    }
+  }
+  // Link the class if it has not already been linked.
+  if (!klass->IsLinked() && !klass->IsErroneous()) {
+    ObjectLock lock(klass);
+    // Check for circular dependencies between classes.
+    if (!klass->IsLinked() && klass->clinit_thread_id_ == self->GetThreadId()) {
+      LG << "Recursive link";  // TODO: ClassCircularityError
+      return NULL;
+    }
+    // Wait for the pending initialization to complete.
+    while (!klass->IsLinked() && !klass->IsErroneous()) {
+      lock.Wait();
+    }
+  }
+  if (klass->IsErroneous()) {
+    LG << "EarlierClassFailure";  // TODO: EarlierClassFailure
+    return NULL;
+  }
+  // Return the loaded class.  No exceptions should be pending.
+  CHECK(!self->IsExceptionPending());
+  return klass;
+}
+
+DexFile* ClassLinker::FindInClassPath(const char* descriptor) {
+  for (size_t i = 0; i != class_path_.size(); ++i) {
+    DexFile* dex_file = class_path_[i];
+    if (dex_file->HasClass(descriptor)) {
+      return dex_file;
+    }
+  }
+  return NULL;
+}
+
+void ClassLinker::AppendToClassPath(DexFile* dex_file) {
+  class_path_.push_back(dex_file);
+}
+
+
+Class* ClassLinker::FindPrimitiveClass(const char* descriptor) {
+  return NULL;  // TODO
+}
+
+bool ClassLinker::InsertClass(Class* klass) {
+  // TODO: acquire classes_lock_
+  const char* key = klass->GetDescriptor().data();
+  bool success = classes_.insert(std::make_pair(key, klass)).second;
+  // TODO: release classes_lock_
+  return success;
+}
+
+Class* ClassLinker::LookupClass(const char* descriptor, Object* class_loader) {
+  // TODO: acquire classes_lock_
+  Table::iterator it = classes_.find(descriptor);
+  // TODO: release classes_lock_
+  if (it == classes_.end()) {
+    return NULL;
+  } else {
+    return (*it).second;
+  }
+}
+
+bool ClassLinker::InitializeClass(Class* klass) {
+  CHECK(klass->GetStatus() == Class::kStatusResolved ||
+        klass->GetStatus() == Class::kStatusError);
+
+  Thread* self = Thread::Self();
+
+  {
+    ObjectLock lock(klass);
+
+    if (klass->GetStatus() < Class::kStatusVerified) {
+      if (klass->IsErroneous()) {
+        LG << "re-initializing failed class";  // TODO: throw
+        return false;
+      }
+
+      CHECK(klass->GetStatus() == Class::kStatusResolved);
+
+      klass->status_ = Class::kStatusVerifying;
+      if (!DexVerify::VerifyClass(klass)) {
+        LG << "Verification failed";  // TODO: ThrowVerifyError
+        Object* exception = self->GetException();
+        klass->SetObjectAt(OFFSETOF_MEMBER(Class, verify_error_class_),
+                           exception->GetClass());
+        klass->SetStatus(Class::kStatusError);
+        return false;
+      }
+
+      klass->SetStatus(Class::kStatusVerified);
+    }
+
+    if (klass->status_ == Class::kStatusInitialized) {
+      return true;
+    }
+
+    while (klass->status_ == Class::kStatusInitializing) {
+      // we caught somebody else in the act; was it us?
+      if (klass->clinit_thread_id_ == self->GetThreadId()) {
+        LG << "recursive <clinit>";
+        return true;
+      }
+
+      CHECK(!self->IsExceptionPending());
+
+      lock.Wait();  // TODO: check for interruption
+
+      // When we wake up, repeat the test for init-in-progress.  If
+      // there's an exception pending (only possible if
+      // "interruptShouldThrow" was set), bail out.
+      if (self->IsExceptionPending()) {
+        CHECK(false);
+        LG << "Exception in initialization.";  // TODO: ExceptionInInitializerError
+        klass->SetStatus(Class::kStatusError);
+        return false;
+      }
+      if (klass->GetStatus() == Class::kStatusInitializing) {
+        continue;
+      }
+      assert(klass->GetStatus() == Class::kStatusInitialized ||
+             klass->GetStatus() == Class::kStatusError);
+      if (klass->IsErroneous()) {
+        /*
+         * The caller wants an exception, but it was thrown in a
+         * different thread.  Synthesize one here.
+         */
+        LG << "<clinit> failed";  // TODO: throw UnsatisfiedLinkError
+        return false;
+      }
+      return true;  // otherwise, initialized
+    }
+
+    // see if we failed previously
+    if (klass->IsErroneous()) {
+      // might be wise to unlock before throwing; depends on which class
+      // it is that we have locked
+
+      // TODO: throwEarlierClassFailure(klass);
+      return false;
+    }
+
+    if (!ValidateSuperClassDescriptors(klass)) {
+      klass->SetStatus(Class::kStatusError);
+      return false;
+    }
+
+    assert(klass->status < CLASS_INITIALIZING);
+
+    klass->clinit_thread_id_ = self->GetThreadId();
+    klass->status_ = Class::kStatusInitializing;
+  }
+
+  if (!InitializeSuperClass(klass)) {
+    return false;
+  }
+
+  InitializeStaticFields(klass);
+
+  Method* clinit = klass->FindDirectMethodLocally("<clinit>", "()V");
+  if (clinit != NULL) {
+  } else {
+    // JValue unused;
+    // TODO: dvmCallMethod(self, method, NULL, &unused);
+    CHECK(!"unimplemented");
+  }
+
+  {
+    ObjectLock lock(klass);
+
+    if (self->IsExceptionPending()) {
+      klass->SetStatus(Class::kStatusError);
+    } else {
+      klass->SetStatus(Class::kStatusInitialized);
+    }
+    lock.NotifyAll();
+  }
+
+  return true;
+}
+
+bool ClassLinker::ValidateSuperClassDescriptors(const Class* klass) {
+  if (klass->IsInterface()) {
+    return true;
+  }
+  // begin with the methods local to the superclass
+  if (klass->HasSuperClass() &&
+      klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) {
+    const Class* super = klass->GetSuperClass();
+    for (int i = super->NumVirtualMethods() - 1; i >= 0; --i) {
+      const Method* method = klass->GetVirtualMethod(i);
+      if (method != super->GetVirtualMethod(i) &&
+          !HasSameMethodDescriptorClasses(method, super, klass)) {
+        LG << "Classes resolve differently in superclass";
+        return false;
+      }
+    }
+  }
+  for (size_t i = 0; i < klass->iftable_count_; ++i) {
+    const InterfaceEntry* iftable = &klass->iftable_[i];
+    Class* interface = iftable->GetClass();
+    if (klass->GetClassLoader() != interface->GetClassLoader()) {
+      for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
+        uint32_t vtable_index = iftable->method_index_array_[j];
+        const Method* method = klass->GetVirtualMethod(vtable_index);
+        if (!HasSameMethodDescriptorClasses(method, interface,
+                                            method->GetClass())) {
+          LG << "Classes resolve differently in interface";  // TODO: LinkageError
+          return false;
+        }
+      }
+    }
+  }
+  return true;
+}
+
+bool ClassLinker::HasSameMethodDescriptorClasses(const Method* method,
+                                                const Class* klass1,
+                                                const Class* klass2) {
+  const RawDexFile* raw = method->GetClass()->GetDexFile()->GetRaw();
+  const RawDexFile::ProtoId& proto_id = raw->GetProtoId(method->proto_idx_);
+  RawDexFile::ParameterIterator *it;
+  for (it = raw->GetParameterIterator(proto_id); it->HasNext(); it->Next()) {
+    const char* descriptor = it->GetDescriptor();
+    if (descriptor == NULL) {
+      break;
+    }
+    if (descriptor[0] == 'L' || descriptor[0] == '[') {
+      // Found a non-primitive type.
+      if (!HasSameDescriptorClasses(descriptor, klass1, klass2)) {
+        return false;
+      }
+    }
+  }
+  // Check the return type
+  const char* descriptor = raw->GetReturnTypeDescriptor(proto_id);
+  if (descriptor[0] == 'L' || descriptor[0] == '[') {
+    if (HasSameDescriptorClasses(descriptor, klass1, klass2)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+// Returns true if classes referenced by the descriptor are the
+// same classes in klass1 as they are in klass2.
+bool ClassLinker::HasSameDescriptorClasses(const char* descriptor,
+                                          const Class* klass1,
+                                          const Class* klass2) {
+  CHECK(descriptor != NULL);
+  CHECK(klass1 != NULL);
+  CHECK(klass2 != NULL);
+#if 0
+  Class* found1 = FindClassNoInit(descriptor, klass1->GetClassLoader());
+  // TODO: found1 == NULL
+  Class* found2 = FindClassNoInit(descriptor, klass2->GetClassLoader());
+  // TODO: found2 == NULL
+  // TODO: lookup found1 in initiating loader list
+  if (found1 == NULL || found2 == NULL) {
+    Thread::Self()->ClearException();
+    if (found1 == found2) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+#endif
+  return true;
+}
+
+bool ClassLinker::InitializeSuperClass(Class* klass) {
+  CHECK(klass != NULL);
+  // TODO: assert klass lock is acquired
+  if (!klass->IsInterface() && klass->HasSuperClass()) {
+    Class* super_class = klass->GetSuperClass();
+    if (super_class->GetStatus() != Class::kStatusInitialized) {
+      CHECK(!super_class->IsInterface());
+      klass->MonitorExit();
+      bool super_initialized = InitializeClass(super_class);
+      klass->MonitorEnter();
+      // TODO: check for a pending exception
+      if (!super_initialized) {
+        klass->SetStatus(Class::kStatusError);
+        klass->NotifyAll();
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+void ClassLinker::InitializeStaticFields(Class* klass) {
+  if (klass->NumStaticFields() == 0) {
+    return;
+  } else {
+    LOG(FATAL) << "Unimplemented";
+  }
+}
+
+bool ClassLinker::LinkClass(Class* klass) {
+  CHECK(klass->status_ == Class::kStatusIdx ||
+        klass->status_ == Class::kStatusLoaded);
+  if (klass->status_ == Class::kStatusIdx) {
+    if (!LinkInterfaces(klass)) {
+      return false;
+    }
+  }
+  if (!LinkSuperClass(klass)) {
+    return false;
+  }
+  if (!LinkMethods(klass)) {
+    return false;
+  }
+  if (!LinkInstanceFields(klass)) {
+    return false;
+  }
+  CreateReferenceOffsets(klass);
+  CHECK_EQ(klass->status_, Class::kStatusLoaded);
+  klass->status_ = Class::kStatusResolved;
+  return true;
+}
+
+bool ClassLinker::LinkInterfaces(Class* klass) {
+  scoped_array<uint32_t> interfaces_idx;
+  // TODO: store interfaces_idx in the Class object
+  // TODO: move this outside of link interfaces
+  if (klass->interface_count_ > 0) {
+    interfaces_idx.reset(new uint32_t[klass->interface_count_]);
+    memcpy(interfaces_idx.get(), klass->interfaces_, klass->interface_count_);
+    memset(klass->interfaces_, 0, klass->interface_count_);
+  }
+  // Mark the class as loaded.
+  klass->status_ = Class::kStatusLoaded;
+  if (klass->super_class_idx_ != RawDexFile::kDexNoIndex) {
+    Class* super_class = ResolveClass(klass, klass->super_class_idx_);
+    if (super_class == NULL) {
+      LG << "Failed to resolve superclass";
+      return false;
+    }
+    klass->super_class_ = super_class;  // TODO: write barrier
+  }
+  if (klass->interface_count_ > 0) {
+    for (size_t i = 0; i < klass->interface_count_; ++i) {
+      uint32_t idx = interfaces_idx[i];
+      klass->interfaces_[i] = ResolveClass(klass, idx);
+      if (klass->interfaces_[i] == NULL) {
+        LG << "Failed to resolve interface";
+        return false;
+      }
+      // Verify
+      if (!klass->CanAccess(klass->interfaces_[i])) {
+        LG << "Inaccessible interface";
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool ClassLinker::LinkSuperClass(Class* klass) {
+  CHECK(!klass->IsPrimitive());
+  const Class* super = klass->GetSuperClass();
+  if (klass->GetDescriptor() == "Ljava/lang/Object;") {
+    if (super != NULL) {
+      LG << "Superclass must not be defined";  // TODO: ClassFormatError
+      return false;
+    }
+    // TODO: clear finalize attribute
+    return true;
+  }
+  if (super == NULL) {
+    LG << "No superclass defined";  // TODO: LinkageError
+    return false;
+  }
+  // Verify
+  if (super->IsFinal()) {
+    LG << "Superclass is declared final";  // TODO: IncompatibleClassChangeError
+    return false;
+  }
+  if (super->IsInterface()) {
+    LG << "Superclass is an interface";  // TODO: IncompatibleClassChangeError
+    return false;
+  }
+  if (!klass->CanAccess(super)) {
+    LG << "Superclass is inaccessible";  // TODO: IllegalAccessError
+    return false;
+  }
+  return true;
+}
+
+// Populate the class vtable and itable.
+bool ClassLinker::LinkMethods(Class* klass) {
+  if (klass->IsInterface()) {
+    // No vtable.
+    size_t count = klass->NumVirtualMethods();
+    if (!IsUint(16, count)) {
+      LG << "Too many methods on interface";  // TODO: VirtualMachineError
+      return false;
+    }
+    for (size_t i = 0; i < count; ++count) {
+      klass->GetVirtualMethod(i)->method_index_ = i;
+    }
+  } else {
+    // Link virtual method tables
+    LinkVirtualMethods(klass);
+
+    // Link interface method tables
+    LinkInterfaceMethods(klass);
+
+    // Insert stubs.
+    LinkAbstractMethods(klass);
+  }
+  return true;
+}
+
+bool ClassLinker::LinkVirtualMethods(Class* klass) {
+  uint32_t max_count = klass->NumVirtualMethods();
+  if (klass->GetSuperClass() != NULL) {
+    max_count += klass->GetSuperClass()->NumVirtualMethods();
+  } else {
+    CHECK(klass->GetDescriptor() == "Ljava/lang/Object;");
+  }
+  // TODO: do not assign to the vtable field until it is fully constructed.
+  // TODO: make this a vector<Method*> instead?
+  klass->vtable_ = new Method*[max_count];
+  if (klass->HasSuperClass()) {
+    memcpy(klass->vtable_,
+           klass->GetSuperClass()->vtable_,
+           klass->GetSuperClass()->vtable_count_ * sizeof(Method*));
+    size_t actual_count = klass->GetSuperClass()->vtable_count_;
+    // See if any of our virtual methods override the superclass.
+    for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+      Method* local_method = klass->GetVirtualMethod(i);
+      size_t j = 0;
+      for (; j < klass->GetSuperClass()->vtable_count_; ++j) {
+        const Method* super_method = klass->vtable_[j];
+        if (local_method->HasSameNameAndPrototype(super_method)) {
+          // Verify
+          if (super_method->IsFinal()) {
+            LG << "Method overrides final method"; // TODO: VirtualMachineError
+            return false;
+          }
+          klass->vtable_[j] = local_method;
+          local_method->method_index_ = j;
+          break;
+        }
+      }
+      if (j == klass->GetSuperClass()->vtable_count_) {
+        // Not overriding, append.
+        klass->vtable_[actual_count] = local_method;
+        local_method->method_index_ = actual_count;
+        actual_count += 1;
+      }
+    }
+    if (!IsUint(16, actual_count)) {
+      LG << "Too many methods defined on class";  // TODO: VirtualMachineError
+      return false;
+    }
+    CHECK_LE(actual_count, max_count);
+    if (actual_count < max_count) {
+      Method** new_vtable = new Method*[actual_count];
+      memcpy(new_vtable, klass->vtable_, actual_count * sizeof(Method*));
+      delete klass->vtable_;
+      klass->vtable_ = new_vtable;
+      LG << "shrunk vtable: "
+         << "was " << max_count << ", "
+         << "now " << actual_count;
+    }
+    klass->vtable_count_ = actual_count;
+  } else {
+    CHECK(klass->GetDescriptor() == "Ljava/lang/Object;");
+    if (!IsUint(16, klass->NumVirtualMethods())) {
+      LG << "Too many methods";  // TODO: VirtualMachineError
+      return false;
+    }
+    for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+      klass->vtable_[i] = klass->GetVirtualMethod(i);
+      klass->GetVirtualMethod(i)->method_index_ = i & 0xFFFF;
+    }
+    klass->vtable_count_ = klass->NumVirtualMethods();
+  }
+  return true;
+}
+
+bool ClassLinker::LinkInterfaceMethods(Class* klass) {
+  int pool_offset = 0;
+  int pool_size = 0;
+  int miranda_count = 0;
+  int miranda_alloc = 0;
+  size_t super_ifcount;
+  if (klass->HasSuperClass()) {
+    super_ifcount = klass->GetSuperClass()->iftable_count_;
+  } else {
+    super_ifcount = 0;
+  }
+  size_t ifCount = super_ifcount;
+  ifCount += klass->interface_count_;
+  for (size_t i = 0; i < klass->interface_count_; i++) {
+    ifCount += klass->interfaces_[i]->iftable_count_;
+  }
+  if (ifCount == 0) {
+    assert(klass->iftable_count_ == 0);
+    assert(klass->iftable == NULL);
+    return true;
+  }
+  klass->iftable_ = new InterfaceEntry[ifCount * sizeof(InterfaceEntry)];
+  memset(klass->iftable_, 0x00, sizeof(InterfaceEntry) * ifCount);
+  if (super_ifcount != 0) {
+    memcpy(klass->iftable_, klass->GetSuperClass()->iftable_,
+           sizeof(InterfaceEntry) * super_ifcount);
+  }
+  // Flatten the interface inheritance hierarchy.
+  size_t idx = super_ifcount;
+  for (size_t i = 0; i < klass->interface_count_; i++) {
+    Class* interf = klass->interfaces_[i];
+    assert(interf != NULL);
+    if (!interf->IsInterface()) {
+      LG << "Class implements non-interface class";  // TODO: IncompatibleClassChangeError
+      return false;
+    }
+    klass->iftable_[idx++].SetClass(interf);
+    for (size_t j = 0; j < interf->iftable_count_; j++) {
+      klass->iftable_[idx++].SetClass(interf->iftable_[j].GetClass());
+    }
+  }
+  CHECK_EQ(idx, ifCount);
+  klass->iftable_count_ = ifCount;
+  if (klass->IsInterface() || super_ifcount == ifCount) {
+    return true;
+  }
+  for (size_t i = super_ifcount; i < ifCount; i++) {
+    pool_size += klass->iftable_[i].GetClass()->NumVirtualMethods();
+  }
+  if (pool_size == 0) {
+    return true;
+  }
+  klass->ifvi_pool_count_ = pool_size;
+  klass->ifvi_pool_ = new uint32_t[pool_size];
+  std::vector<Method*> miranda_list;
+  for (size_t i = super_ifcount; i < ifCount; ++i) {
+    klass->iftable_[i].method_index_array_ = klass->ifvi_pool_ + pool_offset;
+    Class* interface = klass->iftable_[i].GetClass();
+    pool_offset += interface->NumVirtualMethods();    // end here
+    for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) {
+      Method* interface_method = interface->GetVirtualMethod(j);
+      int k;  // must be signed
+      for (k = klass->vtable_count_ - 1; k >= 0; --k) {
+        if (interface_method->HasSameNameAndPrototype(klass->vtable_[k])) {
+          if (klass->vtable_[k]->IsPublic()) {
+            LG << "Implementation not public";
+            return false;
+          }
+          klass->iftable_[i].method_index_array_[j] = k;
+          break;
+        }
+      }
+      if (k < 0) {
+        if (miranda_count == miranda_alloc) {
+          miranda_alloc += 8;
+          if (miranda_list.empty()) {
+            miranda_list.resize(miranda_alloc);
+          } else {
+            miranda_list.resize(miranda_alloc);
+          }
+        }
+        int mir;
+        for (mir = 0; mir < miranda_count; mir++) {
+          if (miranda_list[mir]->HasSameNameAndPrototype(interface_method)) {
+            break;
+          }
+        }
+        // point the interface table at a phantom slot index
+        klass->iftable_[i].method_index_array_[j] = klass->vtable_count_ + mir;
+        if (mir == miranda_count) {
+          miranda_list[miranda_count++] = interface_method;
+        }
+      }
+    }
+  }
+  if (miranda_count != 0) {
+    Method* newVirtualMethods;
+    Method* meth;
+    int oldMethodCount, oldVtableCount;
+    if (klass->virtual_methods_ == NULL) {
+      newVirtualMethods = new Method[klass->NumVirtualMethods() + miranda_count];
+
+    } else {
+      newVirtualMethods = new Method[klass->NumVirtualMethods() + miranda_count];
+      memcpy(newVirtualMethods,
+             klass->virtual_methods_,
+             klass->NumVirtualMethods() * sizeof(Method));
+
+    }
+    if (newVirtualMethods != klass->virtual_methods_) {
+      Method* meth = newVirtualMethods;
+      for (size_t i = 0; i < klass->NumVirtualMethods(); i++, meth++) {
+        klass->vtable_[meth->method_index_] = meth;
+      }
+    }
+    oldMethodCount = klass->NumVirtualMethods();
+    klass->virtual_methods_ = newVirtualMethods;
+    klass->num_virtual_methods_ += miranda_count;
+
+    CHECK(klass->vtable_ != NULL);
+    oldVtableCount = klass->vtable_count_;
+    klass->vtable_count_ += miranda_count;
+
+    meth = klass->virtual_methods_ + oldMethodCount;
+    for (int i = 0; i < miranda_count; i++, meth++) {
+      memcpy(meth, miranda_list[i], sizeof(Method));
+      meth->klass_ = klass;
+      meth->access_flags_ |= kAccMiranda;
+      meth->method_index_ = 0xFFFF & (oldVtableCount + i);
+      klass->vtable_[oldVtableCount + i] = meth;
+    }
+  }
+  return true;
+}
+
+void ClassLinker::LinkAbstractMethods(Class* klass) {
+  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+    Method* method = klass->GetVirtualMethod(i);
+    if (method->IsAbstract()) {
+      method->insns_ = reinterpret_cast<uint16_t*>(0xFFFFFFFF);  // TODO: AbstractMethodError
+    }
+  }
+}
+
+bool ClassLinker::LinkInstanceFields(Class* klass) {
+  int fieldOffset;
+  if (klass->GetSuperClass() != NULL) {
+    fieldOffset = klass->GetSuperClass()->object_size_;
+  } else {
+    fieldOffset = OFFSETOF_MEMBER(DataObject, fields_);
+  }
+  // Move references to the front.
+  klass->num_reference_ifields_ = 0;
+  size_t i = 0;
+  size_t j = klass->NumInstanceFields() - 1;
+  for (size_t i = 0; i < klass->NumInstanceFields(); i++) {
+    InstanceField* pField = klass->GetInstanceField(i);
+    char c = pField->GetType();
+
+    if (c != '[' && c != 'L') {
+      while (j > i) {
+        InstanceField* refField = klass->GetInstanceField(j--);
+        char rc = refField->GetType();
+        if (rc == '[' || rc == 'L') {
+          pField->Swap(refField);
+          c = rc;
+          klass->num_reference_ifields_++;
+          break;
+        }
+      }
+    } else {
+      klass->num_reference_ifields_++;
+    }
+    if (c != '[' && c != 'L') {
+      break;
+    }
+    pField->SetOffset(fieldOffset);
+    fieldOffset += sizeof(uint32_t);
+  }
+
+  // Now we want to pack all of the double-wide fields together.  If
+  // we're not aligned, though, we want to shuffle one 32-bit field
+  // into place.  If we can't find one, we'll have to pad it.
+  if (i != klass->NumInstanceFields() && (fieldOffset & 0x04) != 0) {
+    InstanceField* pField = klass->GetInstanceField(i);
+    char c = pField->GetType();
+
+    if (c != 'J' && c != 'D') {
+      // The field that comes next is 32-bit, so just advance past it.
+      assert(c != '[' && c != 'L');
+      pField->SetOffset(fieldOffset);
+      fieldOffset += sizeof(uint32_t);
+      i++;
+    } else {
+      // Next field is 64-bit, so search for a 32-bit field we can
+      // swap into it.
+      bool found = false;
+      j = klass->NumInstanceFields() - 1;
+      while (j > i) {
+        InstanceField* singleField = klass->GetInstanceField(j--);
+        char rc = singleField->GetType();
+        if (rc != 'J' && rc != 'D') {
+          pField->Swap(singleField);
+          pField->SetOffset(fieldOffset);
+          fieldOffset += sizeof(uint32_t);
+          found = true;
+          i++;
+          break;
+        }
+      }
+      if (!found) {
+        fieldOffset += sizeof(uint32_t);
+      }
+    }
+  }
+
+  // Alignment is good, shuffle any double-wide fields forward, and
+  // finish assigning field offsets to all fields.
+  assert(i == klass->NumInstanceFields() || (fieldOffset & 0x04) == 0);
+  j = klass->NumInstanceFields() - 1;
+  for ( ; i < klass->NumInstanceFields(); i++) {
+    InstanceField* pField = klass->GetInstanceField(i);
+    char c = pField->GetType();
+    if (c != 'D' && c != 'J') {
+      while (j > i) {
+        InstanceField* doubleField = klass->GetInstanceField(j--);
+        char rc = doubleField->GetType();
+        if (rc == 'D' || rc == 'J') {
+          pField->Swap(doubleField);
+          c = rc;
+          break;
+        }
+      }
+    } else {
+      // This is a double-wide field, leave it be.
+    }
+
+    pField->SetOffset(fieldOffset);
+    fieldOffset += sizeof(uint32_t);
+    if (c == 'J' || c == 'D')
+      fieldOffset += sizeof(uint32_t);
+  }
+
+#ifndef NDEBUG
+  /* Make sure that all reference fields appear before
+   * non-reference fields, and all double-wide fields are aligned.
+   */
+  j = 0;  // seen non-ref
+  for (i = 0; i < klass->NumInstanceFields(); i++) {
+    InstanceField *pField = &klass->ifields[i];
+    char c = pField->GetType();
+
+    if (c == 'D' || c == 'J') {
+      assert((pField->offset_ & 0x07) == 0);
+    }
+
+    if (c != '[' && c != 'L') {
+      if (!j) {
+        assert(i == klass->num_reference_ifields_);
+        j = 1;
+      }
+    } else if (j) {
+      assert(false);
+    }
+  }
+  if (!j) {
+    assert(klass->num_reference_ifields_ == klass->NumInstanceFields());
+  }
+#endif
+
+  klass->object_size_ = fieldOffset;
+  return true;
+}
+
+//  Set the bitmap of reference offsets, refOffsets, from the ifields
+//  list.
+void ClassLinker::CreateReferenceOffsets(Class* klass) {
+  uint32_t reference_offsets = 0;
+  if (klass->HasSuperClass()) {
+    reference_offsets = klass->GetSuperClass()->GetReferenceOffsets();
+  }
+  // If our superclass overflowed, we don't stand a chance.
+  if (reference_offsets != CLASS_WALK_SUPER) {
+    // All of the fields that contain object references are guaranteed
+    // to be at the beginning of the ifields list.
+    for (size_t i = 0; i < klass->NumReferenceInstanceFields(); ++i) {
+      // Note that, per the comment on struct InstField, f->byteOffset
+      // is the offset from the beginning of obj, not the offset into
+      // obj->instanceData.
+      const InstanceField* field = klass->GetInstanceField(i);
+      size_t byte_offset = field->GetOffset();
+      CHECK_GE(byte_offset, CLASS_SMALLEST_OFFSET);
+      CHECK_EQ(byte_offset & (CLASS_OFFSET_ALIGNMENT - 1), 0);
+      if (CLASS_CAN_ENCODE_OFFSET(byte_offset)) {
+        uint32_t new_bit = CLASS_BIT_FROM_OFFSET(byte_offset);
+        CHECK_NE(new_bit, 0);
+        reference_offsets |= new_bit;
+      } else {
+        reference_offsets = CLASS_WALK_SUPER;
+        break;
+      }
+    }
+    klass->SetReferenceOffsets(reference_offsets);
+  }
+}
+
+Class* ClassLinker::ResolveClass(Class* referrer, uint32_t class_idx) {
+  DexFile* dex_file = referrer->GetDexFile();
+  Class* resolved = dex_file->GetResolvedClass(class_idx);
+  if (resolved != NULL) {
+    return resolved;
+  }
+  const char* descriptor = dex_file->GetRaw()->dexStringByTypeIdx(class_idx);
+  if (descriptor[0] != '\0' && descriptor[1] == '\0') {
+    resolved = FindPrimitiveClass(descriptor);
+  } else {
+    resolved = FindClass(descriptor, referrer->GetClassLoader(), NULL);
+  }
+  if (resolved != NULL) {
+    Class* check = resolved->IsArray() ? resolved->component_type_ : resolved;
+    if (referrer->GetDexFile() != check->GetDexFile()) {
+      if (check->GetClassLoader() != NULL) {
+        LG << "Class resolved by unexpected DEX";  // TODO: IllegalAccessError
+        return NULL;
+      }
+    }
+    dex_file->SetResolvedClass(class_idx, resolved);
+  } else {
+    CHECK(Thread::Self()->IsExceptionPending());
+  }
+  return resolved;
+}
+
+Method* ResolveMethod(const Class* referrer, uint32_t method_idx,
+                      /*MethodType*/ int method_type) {
+  CHECK(false);
+  return NULL;
+}
+
+}  // namespace art
diff --git a/src/class_linker.h b/src/class_linker.h
new file mode 100644
index 0000000..2021705
--- /dev/null
+++ b/src/class_linker.h
@@ -0,0 +1,91 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_CLASS_LINKER_H_
+#define ART_SRC_CLASS_LINKER_H_
+
+#include <map>
+#include <utility>
+#include <vector>
+
+#include "src/macros.h"
+#include "src/thread.h"
+#include "src/object.h"
+
+namespace art {
+
+class ClassLinker {
+ public:
+  ClassLinker() {}
+  ~ClassLinker() {}
+
+  // Finds a class by its descriptor name.
+  Class* FindClass(const char* descriptor,
+                   Object* class_loader,
+                   DexFile* dex_file);
+
+  Class* FindPrimitiveClass(const char* descriptor);
+
+  bool InitializeClass(Class* klass);
+
+  Class* LookupClass(const char* descriptor, Object* class_loader);
+
+  Class* ResolveClass(Class* klass, uint32_t idx);
+
+  DexFile* FindInClassPath(const char* descriptor);
+
+  void AppendToClassPath(DexFile* dex_file);
+
+ private:
+  // Inserts a class into the class table.  Returns true if the class
+  // was inserted.
+  bool InsertClass(Class* klass);
+
+  bool InitializeSuperClass(Class* klass);
+
+  void InitializeStaticFields(Class* klass);
+
+  bool ValidateSuperClassDescriptors(const Class* klass);
+
+  bool HasSameDescriptorClasses(const char* descriptor,
+                                const Class* klass1,
+                                const Class* klass2);
+
+  bool HasSameMethodDescriptorClasses(const Method* descriptor,
+                                      const Class* klass1,
+                                      const Class* klass2);
+
+  bool LinkClass(Class* klass);
+
+  bool LinkSuperClass(Class* klass);
+
+  bool LinkInterfaces(Class* klass);
+
+  bool LinkMethods(Class* klass);
+
+  bool LinkVirtualMethods(Class* klass);
+
+  bool LinkInterfaceMethods(Class* klass);
+
+  void LinkAbstractMethods(Class* klass);
+
+  bool LinkInstanceFields(Class* klass);
+
+  void CreateReferenceOffsets(Class* klass);
+
+  std::vector<DexFile*> class_path_;
+
+  // TODO: multimap
+  typedef std::map<const char*, Class*, CStringLt> Table;
+
+  Table classes_;
+
+  Mutex* classes_lock_;
+
+  // TODO: classpath
+
+  DISALLOW_COPY_AND_ASSIGN(ClassLinker);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_CLASS_LINKER_H_
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
new file mode 100644
index 0000000..7bb9ecb
--- /dev/null
+++ b/src/class_linker_test.cc
@@ -0,0 +1,31 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "src/class_linker.h"
+#include "src/dex_file.h"
+#include "gtest/gtest.h"
+
+namespace art {
+
+static const char kMyClassDex[] =
+"ZGV4CjAzNQDyNgSqujckc8oAvOKGLvz3+wrLfW9ED5AIAgAAcAAAAHhWNBIAAAAAAAAAAIABAAAG"
+"AAAAcAAAAAMAAACIAAAAAQAAAJQAAAAAAAAAAAAAAAIAAACgAAAAAgAAALAAAAAYAQAA8AAAABwB"
+"AAAkAQAALwEAAEMBAABRAQAAXgEAAAEAAAACAAAABQAAAAUAAAACAAAAAAAAAAAAAAAAAAAAAQAA"
+"AAAAAAABAAAAAQAAAP////8AAAAABAAAAAAAAABrAQAAAAAAAAAAAAAAAAAAAQAAAAAAAAADAAAA"
+"AAAAAHUBAAAAAAAAAQABAAAAAABhAQAAAQAAAA4AAAABAAEAAQAAAGYBAAAEAAAAcBABAAAADgAG"
+"PGluaXQ+AAlMTXlDbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwAMTXlDbGFzcy5qYXZhAAtPYmpl"
+"Y3QuamF2YQABVgADAAcOAAEABw4AAAABAAGBgATwAQAAAQAAgIAEhAIACwAAAAAAAAABAAAAAAAA"
+"AAEAAAAGAAAAcAAAAAIAAAADAAAAiAAAAAMAAAABAAAAlAAAAAUAAAACAAAAoAAAAAYAAAACAAAA"
+"sAAAAAEgAAACAAAA8AAAAAIgAAAGAAAAHAEAAAMgAAACAAAAYQEAAAAgAAACAAAAawEAAAAQAAAB"
+"AAAAgAEAAA==";
+
+TEST(ClassLinker, FindClass) {
+  scoped_ptr<DexFile> dex(DexFile::OpenBase64(kMyClassDex));
+  ASSERT_TRUE(dex != NULL);
+
+  ClassLinker linker;
+  linker.AppendToClassPath(dex.get());
+  Class* klass = linker.FindClass("LMyClass;", NULL, dex.get());
+  EXPECT_TRUE(klass != NULL);
+}
+
+}  // namespace art
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 8a78789..bb016b0 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -37,16 +37,16 @@
 
 void DexFile::Init() {
   num_strings_ = raw_->NumStringIds();
-  strings_ = new String*[num_strings_];
+  strings_ = new String*[num_strings_]();
 
   num_classes_ = raw_->NumTypeIds();
-  classes_ = new Class*[num_classes_];
+  classes_ = new Class*[num_classes_]();
 
   num_methods_ = raw_->NumMethodIds();
-  methods_ = new Method*[num_methods_];
+  methods_ = new Method*[num_methods_]();
 
   num_fields_ = raw_->NumFieldIds();
-  fields_ = new Field*[num_fields_];
+  fields_ = new Field*[num_fields_]();
 }
 
 Class* DexFile::LoadClass(const char* descriptor) {
@@ -71,20 +71,21 @@
   CHECK(klass != NULL);  // TODO: throw an OOME
 
   klass->klass_ = NULL;  // TODO
-  klass->descriptor_ = descriptor;
+  klass->descriptor_.set(descriptor);
+  klass->descriptor_alloc_ = NULL;
   klass->access_flags_ = class_def.access_flags_;
   klass->class_loader_ = NULL;  // TODO
   klass->dex_file_ = this;
   klass->primitive_type_ = Class::kPrimNot;
   klass->status_ = Class::kStatusIdx;
 
-  klass->super_ = reinterpret_cast<Class*>(class_def.superclass_idx_);
-  klass->super_idx_ = class_def.superclass_idx_;
+  klass->super_class_ = NULL;
+  klass->super_class_idx_ = class_def.superclass_idx_;
 
   klass->num_sfields_ = header.static_fields_size_;
   klass->num_ifields_ = header.instance_fields_size_;
-  klass->num_dmethods_ = header.direct_methods_size_;
-  klass->num_vmethods_ = header.virtual_methods_size_;
+  klass->num_direct_methods_ = header.direct_methods_size_;
+  klass->num_virtual_methods_ = header.virtual_methods_size_;
 
   klass->source_file_ = raw_->dexGetSourceFile(class_def);
 
@@ -102,11 +103,11 @@
   }
 
   // Load instance fields.
-  if (klass->num_ifields_ != 0) {
+  if (klass->NumInstanceFields() != 0) {
     // TODO: append instance fields to class object
-    klass->ifields_ = new IField[klass->num_ifields_];
+    klass->ifields_ = new InstanceField[klass->NumInstanceFields()];
     uint32_t last_idx = 0;
-    for (size_t i = 0; i < klass->num_ifields_; ++i) {
+    for (size_t i = 0; i < klass->NumInstanceFields(); ++i) {
       RawDexFile::Field raw_field;
       raw_->dexReadClassDataField(&class_data, &raw_field, &last_idx);
       LoadField(klass, raw_field, &klass->ifields_[i]);
@@ -114,28 +115,28 @@
   }
 
   // Load direct methods.
-  if (klass->num_dmethods_ != 0) {
+  if (klass->NumDirectMethods() != 0) {
     // TODO: append direct methods to class object
-    klass->dmethods_ = new Method[klass->num_dmethods_];
+    klass->direct_methods_ = new Method[klass->NumDirectMethods()];
     uint32_t last_idx = 0;
-    for (size_t i = 0; i < klass->num_dmethods_; ++i) {
+    for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
       RawDexFile::Method raw_method;
       raw_->dexReadClassDataMethod(&class_data, &raw_method, &last_idx);
-      LoadMethod(klass, raw_method, &klass->dmethods_[i]);
+      LoadMethod(klass, raw_method, klass->GetDirectMethod(i));
       // TODO: register maps
     }
   }
 
   // Load virtual methods.
-  if (klass->num_vmethods_ != 0) {
+  if (klass->NumVirtualMethods() != 0) {
     // TODO: append virtual methods to class object
-    klass->vmethods_ = new Method[klass->num_vmethods_];
-    memset(klass->vmethods_, 0xff, sizeof(Method));
+    klass->virtual_methods_ = new Method[klass->NumVirtualMethods()];
+    memset(klass->virtual_methods_, 0xff, sizeof(Method));
     uint32_t last_idx = 0;
-    for (size_t i = 0; i < klass->num_vmethods_; ++i) {
+    for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
       RawDexFile::Method raw_method;
       raw_->dexReadClassDataMethod(&class_data, &raw_method, &last_idx);
-      LoadMethod(klass, raw_method, &klass->vmethods_[i]);
+      LoadMethod(klass, raw_method, klass->GetVirtualMethod(i));
       // TODO: register maps
     }
   }
@@ -169,10 +170,10 @@
                          Method* dst) {
   const RawDexFile::MethodId& method_id = raw_->GetMethodId(src.method_idx_);
   dst->klass_ = klass;
-  dst->name_ = raw_->dexStringById(method_id.name_idx_);
+  dst->name_.set(raw_->dexStringById(method_id.name_idx_));
   dst->dex_file_ = this;
   dst->proto_idx_ = method_id.proto_idx_;
-  dst->shorty_ = raw_->GetShorty(method_id.proto_idx_);
+  dst->shorty_.set(raw_->GetShorty(method_id.proto_idx_));
   dst->access_flags_ = src.access_flags_;
 
   // TODO: check for finalize method
diff --git a/src/dex_file.h b/src/dex_file.h
index 3545c07..07650c2 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -5,11 +5,15 @@
 
 #include "src/globals.h"
 #include "src/macros.h"
-#include "src/object.h"
 #include "src/raw_dex_file.h"
 
 namespace art {
 
+class Class;
+class Field;
+class Method;
+class String;
+
 class DexFile {
  public:
   // Opens a .dex file from the file system.  Returns NULL on failure.
@@ -27,11 +31,11 @@
   // Close and deallocate.
   ~DexFile();
 
-  size_t NumTypes() {
+  size_t NumTypes() const {
     return num_classes_;
   }
 
-  size_t NumMethods() {
+  size_t NumMethods() const {
     return num_methods_;
   }
 
@@ -39,6 +43,22 @@
 
   Class* LoadClass(const RawDexFile::ClassDef& class_def);
 
+  bool HasClass(const char* descriptor) {
+    return raw_->FindClassDef(descriptor) != NULL;
+  }
+
+  RawDexFile* GetRaw() const {
+    return raw_.get();
+  }
+
+  Class* GetResolvedClass(uint32_t class_idx) const {
+    return classes_[class_idx];
+  }
+
+  void SetResolvedClass(uint32_t class_idx, Class* resolved) {
+    classes_[class_idx] = resolved;
+  }
+
  private:
   DexFile(RawDexFile* raw) : raw_(raw) {};
 
diff --git a/src/dex_file_test.cc b/src/dex_file_test.cc
index b46cbb5..ea42519 100644
--- a/src/dex_file_test.cc
+++ b/src/dex_file_test.cc
@@ -1,6 +1,7 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
 #include "src/dex_file.h"
+#include "src/object.h"
 #include "src/scoped_ptr.h"
 
 #include <stdio.h>
@@ -8,8 +9,6 @@
 
 namespace art {
 
-// Nested.java
-//
 // class Nested {
 //     class Inner {
 //     }
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
new file mode 100644
index 0000000..7840827
--- /dev/null
+++ b/src/dex_verifier.cc
@@ -0,0 +1,33 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "src/dex_verifier.h"
+
+
+namespace art {
+
+bool DexVerify::VerifyClass(Class* klass) {
+  if (klass->IsVerified()) {
+    return true;
+  }
+  for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
+    Method* method = klass->GetDirectMethod(i);
+    if (!VerifyMethod(method)) {
+      LG << "Verifier rejected class " << klass->GetDescriptor();
+      return false;
+    }
+  }
+  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+    Method* method = klass->GetVirtualMethod(i);
+    if (!VerifyMethod(method)) {
+      LG << "Verifier rejected class " << klass->GetDescriptor();
+      return false;
+    }
+  }
+  return true;
+}
+
+bool DexVerify::VerifyMethod(Method* method) {
+  return true;  // TODO
+}
+
+}  // namespace art
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
new file mode 100644
index 0000000..eb4c6e3
--- /dev/null
+++ b/src/dex_verifier.h
@@ -0,0 +1,23 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#ifndef ART_SRC_DEX_VERIFY_H_
+#define ART_SRC_DEX_VERIFY_H_
+
+#include "src/macros.h"
+#include "src/object.h"
+
+namespace art {
+
+class DexVerify {
+ public:
+  static bool VerifyClass(Class* klass);
+
+ private:
+  static bool VerifyMethod(Method* method);
+
+  DISALLOW_COPY_AND_ASSIGN(DexVerify);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_DEX_VERIFY_H_
diff --git a/src/globals.h b/src/globals.h
index 1904203..73afb7c 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -29,7 +29,7 @@
 const int kBitsPerByte = 8;
 const int kBitsPerByteLog2 = 3;
 const int kBitsPerPointer = kPointerSize * kBitsPerByte;
-const int kBitsPerWord = kWordSize * kBitsPerWord;
+const int kBitsPerWord = kWordSize * kBitsPerByte;
 const int kBitsPerInt = kIntSize * kBitsPerByte;
 
 }  // namespace art
diff --git a/src/heap.h b/src/heap.h
index 2673932..340e51c 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -13,6 +13,7 @@
  public:
   static Class* AllocClass(size_t size) {
     byte* raw = new byte[size];
+    memset(raw, 0, size);
     return reinterpret_cast<Class*>(raw);
   }
 };
diff --git a/src/monitor.h b/src/monitor.h
new file mode 100644
index 0000000..f624056
--- /dev/null
+++ b/src/monitor.h
@@ -0,0 +1,71 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: cshapiro@google.com (Carl Shapiro)
+
+#ifndef ART_SRC_MONITOR_H_
+#define ART_SRC_MONITOR_H_
+
+#include "src/logging.h"
+#include "src/macros.h"
+
+namespace art {
+
+class Monitor {
+ public:
+  void Enter() {
+  }
+
+  void Exit() {
+  }
+
+  void Notify() {
+  }
+
+  void NotifyAll() {
+  }
+
+  void Wait() {
+  }
+
+  void Wait(int64_t timeout) {
+    Wait(timeout, 0);
+  }
+
+  void Wait(int64_t timeout, int32_t nanos) {
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Monitor);
+
+};
+
+class MonitorLock {
+ public:
+  MonitorLock(Monitor* monitor) : monitor_(monitor) {
+    CHECK(monitor != NULL);
+    monitor_->Enter();
+  }
+
+  ~MonitorLock() {
+    monitor_->Exit();
+  }
+
+  void Wait(int64_t millis = 0) {
+    monitor_->Wait(millis);
+  }
+
+  void Notify() {
+    monitor_->Notify();
+  }
+
+  void NotifyAll() {
+    monitor_->NotifyAll();
+  }
+
+ private:
+  Monitor* const monitor_;
+  DISALLOW_COPY_AND_ASSIGN(MonitorLock);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_MONITOR_H_
diff --git a/src/object.cc b/src/object.cc
index bc7cc86..b691480 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -1,8 +1,10 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
 #include "src/globals.h"
-#include "src/object.h"
 #include "src/logging.h"
+#include "src/object.h"
+#include "src/dex_file.h"
+#include "src/raw_dex_file.h"
 
 #include <string.h>
 
@@ -21,6 +23,15 @@
   }
 }
 
+#if 0
+bool Class::IsInSamePackage(const char* descriptor1, const char* descriptor2) {
+  size_t size = std::min(descriptor1.size(), descriptor2.size());
+  std::pair<const char*, const char*> pos;
+  pos = std::mismatch(descriptor1.data(), size, descriptor2.data());
+  return !(*(pos.second).rfind('/') != npos && descriptor2.rfind('/') != npos);
+}
+#endif
+
 bool Class::IsInSamePackage(const Class* that) const {
   const Class* klass1 = this;
   const Class* klass2 = that;
@@ -28,18 +39,19 @@
     return true;
   }
   // Class loaders must match.
-  if (klass1->class_loader_ != klass2->class_loader_) {
+  if (klass1->GetClassLoader() != klass2->GetClassLoader()) {
     return false;
   }
   // Arrays are in the same package when their element classes are.
   if (klass1->IsArray()) {
-    klass1 = klass1->array_element_class_;
+    klass1 = klass1->GetComponentType();
   }
   if (klass2->IsArray()) {
-    klass2 = klass2->array_element_class_;
+    klass2 = klass2->GetComponentType();
   }
   // Compare the package part of the descriptor string.
-  return IsInSamePackage(klass1->descriptor_, klass2->descriptor_);
+  return IsInSamePackage(klass1->descriptor_.data(),
+                         klass2->descriptor_.data());
 }
 
 uint32_t Method::NumArgRegisters() {
@@ -56,4 +68,54 @@
   return num_registers;
 }
 
+bool Method::HasSameArgumentTypes(const Method* that) const {
+  const RawDexFile* raw1 = this->dex_file_->GetRaw();
+  const RawDexFile::ProtoId& proto1 = raw1->GetProtoId(this->proto_idx_);
+  const RawDexFile* raw2 = that->dex_file_->GetRaw();
+  const RawDexFile::ProtoId& proto2 = raw2->GetProtoId(that->proto_idx_);
+
+  // TODO: compare ProtoId objects for equality and exit early
+
+  const RawDexFile::TypeList* type_list1 = raw1->GetProtoParameters(proto1);
+  size_t arity1 = (type_list1 == NULL) ? 0 : type_list1->Size();
+  const RawDexFile::TypeList* type_list2 = raw2->GetProtoParameters(proto2);
+  size_t arity2 = (type_list2 == NULL) ? 0 : type_list2->Size();
+
+  if (arity1 != arity2) {
+    return false;
+  }
+
+  for (size_t i = 0; i < arity1; ++i) {
+    uint32_t type_idx1 = type_list1->GetTypeItem(i).type_idx_;
+    const char* type1 = raw1->dexStringByTypeIdx(type_idx1);
+
+    uint32_t type_idx2 = type_list2->GetTypeItem(i).type_idx_;
+    const char* type2 = raw2->dexStringByTypeIdx(type_idx2);
+
+    if (strcmp(type1, type2) != 0) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool Method::HasSameReturnType(const Method* that) const {
+  const RawDexFile* raw1 = this->dex_file_->GetRaw();
+  const RawDexFile::ProtoId& proto1 = raw1->GetProtoId(this->proto_idx_);
+  const char* type1 = raw1->dexStringByTypeIdx(proto1.return_type_idx_);
+
+  const RawDexFile* raw2 = that->dex_file_->GetRaw();
+  const RawDexFile::ProtoId& proto2 = raw2->GetProtoId(that->proto_idx_);
+  const char* type2 = raw2->dexStringByTypeIdx(proto2.return_type_idx_);
+
+  return (strcmp(type1, type2) == 0);
+}
+
+Method* Class::FindDirectMethodLocally(const StringPiece& name,
+                                       const StringPiece& descriptor) const {
+  return NULL;  // TODO
+}
+
+
 }  // namespace art
diff --git a/src/object.h b/src/object.h
index fb68419..98e464a 100644
--- a/src/object.h
+++ b/src/object.h
@@ -3,20 +3,23 @@
 #ifndef ART_SRC_OBJECT_H_
 #define ART_SRC_OBJECT_H_
 
+#include "src/dex_file.h"
 #include "src/globals.h"
 #include "src/macros.h"
+#include "src/stringpiece.h"
+#include "src/monitor.h"
 
 namespace art {
 
 class Array;
 class Class;
 class DexFile;
-class IField;
+class InstanceField;
 class InterfaceEntry;
 class Monitor;
 class Method;
 class Object;
-class SField;
+class StaticField;
 
 union JValue {
   uint8_t z;
@@ -49,18 +52,142 @@
 static const uint32_t kAccAnnotation = 0x2000; // class, ic (1.5)
 static const uint32_t kAccEnum = 0x4000; // class, field, ic (1.5)
 
+static const uint32_t kAccMiranda = 0x8000;  // method
+
 static const uint32_t kAccConstructor = 0x00010000; // method (Dalvik only)
 static const uint32_t kAccDeclaredSynchronized = 0x00020000; // method (Dalvik only)
 
 
+/*
+ * Definitions for packing refOffsets in ClassObject.
+ */
+/*
+ * A magic value for refOffsets. Ignore the bits and walk the super
+ * chain when this is the value.
+ * [This is an unlikely "natural" value, since it would be 30 non-ref instance
+ * fields followed by 2 ref instance fields.]
+ */
+#define CLASS_WALK_SUPER ((unsigned int)(3))
+#define CLASS_SMALLEST_OFFSET (sizeof(struct Object))
+#define CLASS_BITS_PER_WORD (sizeof(unsigned long int) * 8)
+#define CLASS_OFFSET_ALIGNMENT 4
+#define CLASS_HIGH_BIT ((unsigned int)1 << (CLASS_BITS_PER_WORD - 1))
+/*
+ * Given an offset, return the bit number which would encode that offset.
+ * Local use only.
+ */
+#define _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) \
+    (((unsigned int)(byteOffset) - CLASS_SMALLEST_OFFSET) / \
+     CLASS_OFFSET_ALIGNMENT)
+/*
+ * Is the given offset too large to be encoded?
+ */
+#define CLASS_CAN_ENCODE_OFFSET(byteOffset) \
+    (_CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset) < CLASS_BITS_PER_WORD)
+/*
+ * Return a single bit, encoding the offset.
+ * Undefined if the offset is too large, as defined above.
+ */
+#define CLASS_BIT_FROM_OFFSET(byteOffset) \
+    (CLASS_HIGH_BIT >> _CLASS_BIT_NUMBER_FROM_OFFSET(byteOffset))
+/*
+ * Return an offset, given a bit number as returned from CLZ.
+ */
+#define CLASS_OFFSET_FROM_CLZ(rshift) \
+    (((int)(rshift) * CLASS_OFFSET_ALIGNMENT) + CLASS_SMALLEST_OFFSET)
+
+
 class Object {
  public:
+  Class* GetClass() const {
+    return klass_;
+  }
+
+  void MonitorEnter() {
+    monitor_->Enter();
+  }
+
+  void MonitorExit() {
+    monitor_->Exit();
+  }
+
+  void Notify() {
+    monitor_->Notify();
+  }
+
+  void NotifyAll() {
+    monitor_->NotifyAll();
+  }
+
+  void Wait() {
+    monitor_->Wait();
+  }
+
+  void Wait(int64_t timeout) {
+    monitor_->Wait(timeout);
+  }
+
+  void Wait(int64_t timeout, int32_t nanos) {
+    monitor_->Wait(timeout, nanos);
+  }
+
+  void SetObjectAt(size_t offset, Object* new_value) {
+    byte* raw_addr = reinterpret_cast<byte*>(this) + offset;
+    *reinterpret_cast<Object**>(raw_addr) = new_value;
+    // TODO: write barrier
+  }
+
+ public:
   Class* klass_;
-  Monitor* lock_;
+  Monitor* monitor_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Object);
+};
+
+class ObjectLock {
+ public:
+  ObjectLock(Object* object) : obj_(object) {
+    CHECK(object != NULL);
+    obj_->MonitorEnter();
+  }
+
+  ~ObjectLock() {
+    obj_->MonitorExit();
+  }
+
+  void Wait(int64_t millis = 0) {
+    return obj_->Wait(millis);
+  }
+
+  void Notify() {
+    obj_->Notify();
+  }
+
+  void NotifyAll() {
+    obj_->NotifyAll();
+  }
+
+ private:
+  Object* obj_;
+  DISALLOW_COPY_AND_ASSIGN(ObjectLock);
 };
 
 class Field {
  public:
+  Class* GetClass() const {
+    return klass_;
+  }
+
+  const char* GetName() const {
+    return name_;
+  }
+
+  char GetType() const {  // TODO: return type
+    return signature_[0];
+  }
+
+ public:  // TODO: private
   // The class in which this field is declared.
   Class* klass_;
 
@@ -73,19 +200,152 @@
 };
 
 // Instance fields.
-class IField : public Field {
-  uint32_t offset_;
+class InstanceField : public Field {
+ public:
+  uint32_t GetOffset() const {
+    return offset_;
+  }
+
+  void SetOffset(size_t num_bytes) {
+    offset_ = num_bytes;
+  }
+
+  // TODO: stl::swap
+  void Swap(InstanceField* that) {
+    InstanceField tmp;
+    memcpy(&tmp, this, sizeof(InstanceField));
+    memcpy(this, that, sizeof(InstanceField));
+    memcpy(that, &tmp, sizeof(InstanceField));
+  }
+
+ private:
+  size_t offset_;
 };
 
 // Static fields.
-class SField : public Field {
+class StaticField : public Field {
+ private:
   JValue value_;
 };
 
+class Method {
+ public:
+  // Returns the method name.
+  // TODO: example
+  const StringPiece& GetName() const {
+    return name_;
+  }
+
+  Class* GetClass() const {
+    return klass_;
+  }
+
+  // const char* GetReturnTypeDescriptor() const {
+  //   return dex_file_->GetRaw()->dexStringByTypeIdx(proto_id_.return_type_id_);
+  // }
+
+  // Returns true if the method is declared public.
+  bool IsPublic() const {
+    return (access_flags_ & kAccPublic) != 0;
+  }
+
+  // Returns true if the method is declared private.
+  bool IsPrivate() const {
+    return (access_flags_ & kAccPrivate) != 0;
+  }
+
+  // Returns true if the method is declared static.
+  bool IsStatic() const {
+    return (access_flags_ & kAccStatic) != 0;
+  }
+
+  // Returns true if the method is declared synchronized.
+  bool IsSynchronized() const {
+    uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized;
+    return (access_flags_ & synchonized) != 0;
+  }
+
+  // Returns true if the method is declared final.
+  bool IsFinal() const {
+    return (access_flags_ & kAccFinal) != 0;
+  }
+
+  // Returns true if the method is declared native.
+  bool IsNative() const {
+    return (access_flags_ & kAccNative) != 0;
+  }
+
+  // Returns true if the method is declared abstract.
+  bool IsAbstract() const {
+    return (access_flags_ & kAccAbstract) != 0;
+  }
+
+  bool IsSynthetic() const {
+    return (access_flags_ & kAccSynthetic) != 0;
+  }
+
+  // Number of argument registers required by the prototype.
+  uint32_t NumArgRegisters();
+
+  bool HasSameNameAndPrototype(const Method* that) const {
+    return HasSameName(that) && HasSamePrototype(that);
+  }
+
+  bool HasSameName(const Method* that) const {
+    return this->GetName() == that->GetName();
+  }
+
+  bool HasSamePrototype(const Method* that) const {
+    return HasSameReturnType(that) && HasSameArgumentTypes(that);
+  }
+
+  bool HasSameReturnType(const Method* that) const;
+
+  bool HasSameArgumentTypes(const Method* that) const;
+
+ public:  // TODO: private
+  // the class we are a part of
+  Class* klass_;
+
+  // access flags; low 16 bits are defined by spec (could be uint16_t?)
+  uint32_t access_flags_;
+
+  // For concrete virtual methods, this is the offset of the method
+  // in "vtable".
+  //
+  // For abstract methods in an interface class, this is the offset
+  // of the method in "iftable[n]->methodIndexArray".
+  uint16_t method_index_;
+
+  // Method bounds; not needed for an abstract method.
+  //
+  // For a native method, we compute the size of the argument list, and
+  // set "insSize" and "registerSize" equal to it.
+  uint16_t num_registers_;  // ins + locals
+  uint16_t num_outs_;
+  uint16_t num_ins_;
+
+  // method name, e.g. "<init>" or "eatLunch"
+  StringPiece name_;
+
+  // A pointer to the DEX file this class was loaded from or NULL for
+  // proxy objects.
+  DexFile* dex_file_;
+
+  // Method prototype descriptor string (return and argument types).
+  uint32_t proto_idx_;
+
+  // The short-form method descriptor string.
+  StringPiece shorty_;
+
+  // A pointer to the memory-mapped DEX code.
+  const uint16_t* insns_;
+};
+
 // Class objects.
 class Class : public Object {
  public:
-  enum ClassStatus {
+  enum Status {
     kStatusError = -1,
     kStatusNotReady = 0,
     kStatusIdx = 1,  // loaded, DEX idx in super or ifaces
@@ -101,6 +361,55 @@
     kPrimNot = -1
   };
 
+  Class* GetSuperClass() const {
+    return super_class_;
+  }
+
+  uint32_t GetSuperClassIdx() const {
+    return super_class_idx_;
+  }
+
+  bool HasSuperClass() const {
+    return super_class_ != NULL;
+  }
+
+  Object* GetClassLoader() const {
+    return class_loader_;
+  }
+
+  DexFile* GetDexFile() const {
+    return dex_file_;
+  }
+
+  Class* GetComponentType() const {
+    return component_type_;
+  }
+
+  const StringPiece& GetDescriptor() const {
+    return descriptor_;
+  }
+
+  Status GetStatus() const {
+    return status_;
+  }
+
+  void SetStatus(Status new_status) {
+    // TODO: validate transition
+    status_ = new_status;
+  }
+
+  bool IsErroneous() const {
+    return GetStatus() == kStatusError;
+  }
+
+  bool IsVerified() const {
+    return GetStatus() >= kStatusVerified;
+  }
+
+  bool IsLinked() const {
+    return GetStatus() >= kStatusResolved;
+  }
+
   // Returns true if this class is in the same packages as that class.
   bool IsInSamePackage(const Class* that) const;
 
@@ -143,23 +452,64 @@
 
   // Returns true if this class can access that class.
   bool CanAccess(const Class* that) const {
-    return that->IsPublic() && this->IsInSamePackage(that);
+    return that->IsPublic() || this->IsInSamePackage(that);
   }
 
   // Returns the size in bytes of a class object instance with the
   // given number of static fields.
   static size_t Size(size_t num_sfields) {
-    return OFFSETOF_MEMBER(Class, sfields_) + sizeof(SField) * num_sfields;
+    return OFFSETOF_MEMBER(Class, sfields_) + sizeof(StaticField) * num_sfields;
   }
 
-  uint32_t NumDirectMethods() {
-    return num_dmethods_;
+  // Returns the number of static, private, and constructor methods.
+  size_t NumDirectMethods() const {
+    return num_direct_methods_;
   }
 
-  uint32_t NumVirtualMethods() {
-    return num_vmethods_;
+  Method* GetDirectMethod(uint32_t i) const {
+    return &direct_methods_[i];
   }
 
+  // Returns the number of non-inherited virtual methods.
+  size_t NumVirtualMethods() const {
+    return num_virtual_methods_;
+  }
+
+  Method* GetVirtualMethod(uint32_t i) const {
+    return &virtual_methods_[i];
+  }
+
+  size_t NumInstanceFields() const {
+    return num_ifields_;
+  }
+
+  size_t NumReferenceInstanceFields() const {
+    return num_reference_ifields_;
+  }
+
+  InstanceField* GetInstanceField(uint32_t i) {  // TODO: uint16_t
+    return &ifields_[i];
+  }
+
+  size_t NumStaticFields() const {
+    return num_sfields_;
+  }
+
+  StaticField* GetStaticField(uint32_t i) {  // TODO: uint16_t
+    return &sfields_[i];
+  }
+
+  uint32_t GetReferenceOffsets() const {
+    return reference_offsets_;
+  }
+
+  void SetReferenceOffsets(uint32_t new_reference_offsets) {
+    reference_offsets_ = new_reference_offsets;
+  }
+
+  Method* FindDirectMethodLocally(const StringPiece& name,
+                                  const StringPiece& descriptor) const;
+
  public: // TODO: private
   // leave space for instance data; we could access fields directly if
   // we freeze the definition of java/lang/Class
@@ -169,7 +519,7 @@
 
   // UTF-8 descriptor for the class from constant pool
   // ("Ljava/lang/Class;"), or on heap if generated ("[C")
-  const char* descriptor_;
+  StringPiece descriptor_;
 
   // Proxy classes have their descriptor allocated on the native heap.
   // When this field is non-NULL it must be explicitly freed.
@@ -178,15 +528,12 @@
   // access flags; low 16 bits are defined by VM spec
   uint32_t access_flags_;  // TODO: make an instance field?
 
-  // VM-unique class serial number, nonzero, set very early
-  //uint32_t serial_number_;
-
   // DexFile from which we came; needed to resolve constant pool entries
   // (will be NULL for VM-generated, e.g. arrays and primitive classes)
   DexFile* dex_file_;
 
   // state of class initialization
-  ClassStatus status_;
+  Status status_;
 
   // if class verify fails, we must return same error on subsequent tries
   Class* verify_error_class_;
@@ -196,12 +543,12 @@
 
   // Total object size; used when allocating storage on gc heap.  (For
   // interfaces and abstract classes this will be zero.)
-  uint32_t object_size_;
+  size_t object_size_;
 
   // For array classes, the class object for base element, for
   // instanceof/checkcast (for String[][][], this will be String).
   // Otherwise, NULL.
-  Class* array_element_class_;  // TODO: make an instance field
+  Class* component_type_;  // TODO: make an instance field
 
   // For array classes, the number of array dimensions, e.g. int[][]
   // is 2.  Otherwise 0.
@@ -212,8 +559,8 @@
 
   // The superclass, or NULL if this is java.lang.Object or a
   // primitive type.
-  Class* super_;  // TODO: make an instance field
-  uint32_t super_idx_;
+  Class* super_class_;  // TODO: make an instance field
+  uint32_t super_class_idx_;
 
   // defining class loader, or NULL for the "bootstrap" system loader
   Object* class_loader_;  // TODO: make an instance field
@@ -224,21 +571,21 @@
   //InitiatingLoaderList initiating_loader_list_;
 
   // array of interfaces this class implements directly
-  uint32_t interface_count_;
+  size_t interface_count_;
   Class** interfaces_;
 
   // static, private, and <init> methods
-  uint32_t num_dmethods_;
-  Method* dmethods_;
+  size_t num_direct_methods_;
+  Method* direct_methods_;
 
   // virtual methods defined in this class; invoked through vtable
-  uint32_t num_vmethods_;
-  Method* vmethods_;
+  size_t num_virtual_methods_;
+  Method* virtual_methods_;
 
   // Virtual method table (vtable), for use by "invoke-virtual".  The
   // vtable from the superclass is copied in, and virtual methods from
   // our class either replace those from the super or are appended.
-  int32_t vtable_count_;
+  size_t vtable_count_;
   Method** vtable_;
 
   // Interface table (iftable), one entry per interface supported by
@@ -254,14 +601,14 @@
   //
   // For every interface a concrete class implements, we create a list
   // of virtualMethod indices for the methods in the interface.
-  int iftable_count_;
+  size_t iftable_count_;
   InterfaceEntry* iftable_;
 
   // The interface vtable indices for iftable get stored here.  By
   // placing them all in a single pool for each class that implements
   // interfaces, we decrease the number of allocations.
-  int ifvipool_count;
-  int* ifvipool_;
+  size_t ifvi_pool_count_;
+  uint32_t* ifvi_pool_;
 
   // instance fields
   //
@@ -273,24 +620,29 @@
   // All instance fields that refer to objects are guaranteed to be at
   // the beginning of the field list.  ifieldRefCount specifies the
   // number of reference fields.
-  uint32_t num_ifields_;
+  size_t num_ifields_;
 
   // number of fields that are object refs
-  uint32_t num_reference_ifields_;
-  IField* ifields_;
+  size_t num_reference_ifields_;
+  InstanceField* ifields_;
 
-  // bitmap of offsets of ifields
-  uint32_t ref_offsets_;
+  // Bitmap of offsets of ifields.
+  uint32_t reference_offsets_;
 
   // source file name, if known.  Otherwise, NULL.
   const char* source_file_;
 
   // Static fields
-  uint32_t num_sfields_;
-  SField sfields_[];  // MUST be last item
+  size_t num_sfields_;
+  StaticField sfields_[];  // MUST be last item
 };
 
-class String : Object {
+class DataObject : public Object {
+ public:
+  uint32_t fields_[1];
+};
+
+class String : public Object {
  public:
   Array* array_;
 
@@ -301,94 +653,31 @@
   uint32_t count_;
 };
 
-class Array : Object {
+class Array : public Object {
  public:
   // The number of array elements.
   uint32_t length_;
 };
 
-class Method {
+class InterfaceEntry {
  public:
-  // Returns true if the method is declared public.
-  bool IsPublic() {
-    return (access_flags_ & kAccPublic) != 0;
-  }
+  Class* GetClass() const {
+    return klass_;
+  };
 
-  // Returns true if the method is declared private.
-  bool IsPrivate() {
-    return (access_flags_ & kAccPrivate) != 0;
-  }
+  void SetClass(Class* klass) {
+    klass_ = klass;
+  };
 
-  // Returns true if the method is declared static.
-  bool IsStatic() {
-    return (access_flags_ & kAccStatic) != 0;
-  }
-
-  // Returns true if the method is declared synchronized.
-  bool IsSynchronized() {
-    uint32_t synchonized = kAccSynchronized | kAccDeclaredSynchronized;
-    return (access_flags_ & synchonized) != 0;
-  }
-
-  // Returns true if the method is declared final.
-  bool IsFinal() {
-    return (access_flags_ & kAccFinal) != 0;
-  }
-
-  // Returns true if the method is declared native.
-  bool IsNative() {
-    return (access_flags_ & kAccNative) != 0;
-  }
-
-  // Returns true if the method is declared abstract.
-  bool IsAbstract() {
-    return (access_flags_ & kAccAbstract) != 0;
-  }
-
-  bool IsSynthetic() {
-    return (access_flags_ & kAccSynthetic) != 0;
-  }
-
-  // Number of argument registers required by the prototype.
-  uint32_t NumArgRegisters();
-
- public:  // TODO: private
-  // the class we are a part of
+ private:
+  // Points to the interface class.
   Class* klass_;
 
-  // access flags; low 16 bits are defined by spec (could be uint16_t?)
-  uint32_t access_flags_;
-
-  // For concrete virtual methods, this is the offset of the method
-  // in "vtable".
-  //
-  // For abstract methods in an interface class, this is the offset
-  // of the method in "iftable[n]->methodIndexArray".
-  uint16_t method_index_;
-
-  // Method bounds; not needed for an abstract method.
-  //
-  // For a native method, we compute the size of the argument list, and
-  // set "insSize" and "registerSize" equal to it.
-  uint16_t num_registers_;  // ins + locals
-  uint16_t num_outs_;
-  uint16_t num_ins_;
-
-  // method name, e.g. "<init>" or "eatLunch"
-  const char* name_;
-
-  // A pointer to the DEX file this class was loaded from or NULL for
-  // proxy objects.
-  DexFile* dex_file_;
-
-  // Method prototype descriptor string (return and argument types).
-  uint32_t proto_idx_;
-
-  // The short-form method descriptor string.
-  const char* shorty_;
-
-  // A pointer to the memory-mapped DEX code.
-  const uint16_t* insns_;
+ public:  // TODO: private
+  // Index into array of vtable offsets.  This points into the
+  // ifviPool, which holds the vtables for all interfaces declared by
+  // this class.
+  uint32_t* method_index_array_;
 };
 
 }  // namespace art
diff --git a/src/object_test.cc b/src/object_test.cc
index d6d22c3..7cbf086 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -1,7 +1,9 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 // Author: cshapiro@google.com (Carl Shapiro)
 
+#include "src/dex_file.h"
 #include "src/object.h"
+#include "src/scoped_ptr.h"
 
 #include <stdio.h>
 #include "gtest/gtest.h"
@@ -22,4 +24,146 @@
                                       "Ljava/lang/reflect/Method;"));
 }
 
+// class ProtoCompare {
+//     int m1(short x, int y, long z) { return x + y + (int)z; }
+//     int m2(short x, int y, long z) { return x + y + (int)z; }
+//     int m3(long x, int y, short z) { return (int)x + y + z; }
+//     long m4(long x, int y, short z) { return x + y + z; }
+// }
+static const char kProtoCompareDex[] =
+  "ZGV4CjAzNQBLUetu+TVZ8gsYsCOFoij7ecsHaGSEGA8gAwAAcAAAAHhWNBIAAAAAAAAAAIwCAAAP"
+  "AAAAcAAAAAYAAACsAAAABAAAAMQAAAAAAAAAAAAAAAYAAAD0AAAAAQAAACQBAADcAQAARAEAAN4B"
+  "AADmAQAA6QEAAO8BAAD1AQAA+AEAAP4BAAAOAgAAIgIAADUCAAA4AgAAOwIAAD8CAABDAgAARwIA"
+  "AAEAAAAEAAAABgAAAAcAAAAJAAAACgAAAAIAAAAAAAAAyAEAAAMAAAAAAAAA1AEAAAUAAAABAAAA"
+  "yAEAAAoAAAAFAAAAAAAAAAIAAwAAAAAAAgABAAsAAAACAAEADAAAAAIAAAANAAAAAgACAA4AAAAD"
+  "AAMAAAAAAAIAAAAAAAAAAwAAAAAAAAAIAAAAAAAAAHACAAAAAAAAAQABAAEAAABLAgAABAAAAHAQ"
+  "BQAAAA4ABwAFAAAAAABQAgAABQAAAJAAAwSEUbAQDwAAAAcABQAAAAAAWAIAAAUAAACQAAMEhFGw"
+  "EA8AAAAGAAUAAAAAAGACAAAEAAAAhCCwQLBQDwAJAAUAAAAAAGgCAAAFAAAAgXC7UIGCuyAQAAAA"
+  "AwAAAAEAAAAEAAAAAwAAAAQAAAABAAY8aW5pdD4AAUkABElKSVMABElTSUoAAUoABEpKSVMADkxQ"
+  "cm90b0NvbXBhcmU7ABJMamF2YS9sYW5nL09iamVjdDsAEVByb3RvQ29tcGFyZS5qYXZhAAFTAAFW"
+  "AAJtMQACbTIAAm0zAAJtNAABAAcOAAIDAAAABw4AAwMAAAAHDgAEAwAAAAcOAAUDAAAABw4AAAAB"
+  "BACAgATEAgEA3AIBAPgCAQCUAwEArAMAAAwAAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAA"
+  "BgAAAKwAAAADAAAABAAAAMQAAAAFAAAABgAAAPQAAAAGAAAAAQAAACQBAAABIAAABQAAAEQBAAAB"
+  "EAAAAgAAAMgBAAACIAAADwAAAN4BAAADIAAABQAAAEsCAAAAIAAAAQAAAHACAAAAEAAAAQAAAIwC"
+  "AAA=";
+
+// 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);
+
+  Class* klass = dex_file->LoadClass("LProtoCompare;");
+  ASSERT_TRUE(klass != NULL);
+
+  ASSERT_EQ(4U, klass->NumVirtualMethods());
+
+  Method* m1 = klass->GetVirtualMethod(0);
+  ASSERT_STREQ("m1", m1->GetName().data());
+
+  Method* m2 = klass->GetVirtualMethod(1);
+  ASSERT_STREQ("m2", m2->GetName().data());
+
+  Method* m3 = klass->GetVirtualMethod(2);
+  ASSERT_STREQ("m3", m3->GetName().data());
+
+  Method* m4 = klass->GetVirtualMethod(3);
+  ASSERT_STREQ("m4", m4->GetName().data());
+
+  EXPECT_TRUE(m1->HasSameReturnType(m2));
+  EXPECT_TRUE(m2->HasSameReturnType(m1));
+
+  EXPECT_TRUE(m1->HasSameReturnType(m2));
+  EXPECT_TRUE(m2->HasSameReturnType(m1));
+
+  EXPECT_FALSE(m1->HasSameReturnType(m4));
+  EXPECT_FALSE(m4->HasSameReturnType(m1));
+
+  EXPECT_TRUE(m1->HasSameArgumentTypes(m2));
+  EXPECT_TRUE(m2->HasSameArgumentTypes(m1));
+
+  EXPECT_FALSE(m1->HasSameArgumentTypes(m3));
+  EXPECT_FALSE(m3->HasSameArgumentTypes(m1));
+
+  EXPECT_FALSE(m1->HasSameArgumentTypes(m4));
+  EXPECT_FALSE(m4->HasSameArgumentTypes(m1));
+
+  EXPECT_TRUE(m1->HasSamePrototype(m2));
+  EXPECT_TRUE(m2->HasSamePrototype(m1));
+
+  EXPECT_FALSE(m1->HasSamePrototype(m3));
+  EXPECT_FALSE(m3->HasSamePrototype(m1));
+
+  EXPECT_FALSE(m3->HasSamePrototype(m4));
+  EXPECT_FALSE(m4->HasSamePrototype(m3));
+
+  EXPECT_FALSE(m1->HasSameName(m2));
+  EXPECT_FALSE(m1->HasSameNameAndPrototype(m2));
+}
+
+// class ProtoCompare2 {
+//     int m1(short x, int y, long z) { return x + y + (int)z; }
+//     int m2(short x, int y, long z) { return x + y + (int)z; }
+//     int m3(long x, int y, short z) { return (int)x + y + z; }
+//     long m4(long x, int y, short z) { return x + y + z; }
+// }
+static const char kProtoCompare2Dex[] =
+  "ZGV4CjAzNQDVUXj687EpyTTDJZEZPA8dEYnDlm0Ir6YgAwAAcAAAAHhWNBIAAAAAAAAAAIwCAAAP"
+  "AAAAcAAAAAYAAACsAAAABAAAAMQAAAAAAAAAAAAAAAYAAAD0AAAAAQAAACQBAADcAQAARAEAAN4B"
+  "AADmAQAA6QEAAO8BAAD1AQAA+AEAAP4BAAAPAgAAIwIAADcCAAA6AgAAPQIAAEECAABFAgAASQIA"
+  "AAEAAAAEAAAABgAAAAcAAAAJAAAACgAAAAIAAAAAAAAAyAEAAAMAAAAAAAAA1AEAAAUAAAABAAAA"
+  "yAEAAAoAAAAFAAAAAAAAAAIAAwAAAAAAAgABAAsAAAACAAEADAAAAAIAAAANAAAAAgACAA4AAAAD"
+  "AAMAAAAAAAIAAAAAAAAAAwAAAAAAAAAIAAAAAAAAAHICAAAAAAAAAQABAAEAAABNAgAABAAAAHAQ"
+  "BQAAAA4ABwAFAAAAAABSAgAABQAAAJAAAwSEUbAQDwAAAAcABQAAAAAAWgIAAAUAAACQAAMEhFGw"
+  "EA8AAAAGAAUAAAAAAGICAAAEAAAAhCCwQLBQDwAJAAUAAAAAAGoCAAAFAAAAgXC7UIGCuyAQAAAA"
+  "AwAAAAEAAAAEAAAAAwAAAAQAAAABAAY8aW5pdD4AAUkABElKSVMABElTSUoAAUoABEpKSVMAD0xQ"
+  "cm90b0NvbXBhcmUyOwASTGphdmEvbGFuZy9PYmplY3Q7ABJQcm90b0NvbXBhcmUyLmphdmEAAVMA"
+  "AVYAAm0xAAJtMgACbTMAAm00AAEABw4AAgMAAAAHDgADAwAAAAcOAAQDAAAABw4ABQMAAAAHDgAA"
+  "AAEEAICABMQCAQDcAgEA+AIBAJQDAQCsAwwAAAAAAAAAAQAAAAAAAAABAAAADwAAAHAAAAACAAAA"
+  "BgAAAKwAAAADAAAABAAAAMQAAAAFAAAABgAAAPQAAAAGAAAAAQAAACQBAAABIAAABQAAAEQBAAAB"
+  "EAAAAgAAAMgBAAACIAAADwAAAN4BAAADIAAABQAAAE0CAAAAIAAAAQAAAHICAAAAEAAAAQAAAIwC"
+  "AAA=";
+
+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);
+
+  Class* klass1 = dex_file1->LoadClass("LProtoCompare;");
+  ASSERT_TRUE(klass1 != NULL);
+  Class* klass2 = dex_file2->LoadClass("LProtoCompare2;");
+  ASSERT_TRUE(klass2 != NULL);
+
+  Method* m1_1 = klass1->GetVirtualMethod(0);
+  ASSERT_STREQ("m1", m1_1->GetName().data());
+  Method* m2_1 = klass1->GetVirtualMethod(1);
+  ASSERT_STREQ("m2", m2_1->GetName().data());
+  Method* m3_1 = klass1->GetVirtualMethod(2);
+  ASSERT_STREQ("m3", m3_1->GetName().data());
+  Method* m4_1 = klass1->GetVirtualMethod(3);
+  ASSERT_STREQ("m4", m4_1->GetName().data());
+
+  Method* m1_2 = klass2->GetVirtualMethod(0);
+  ASSERT_STREQ("m1", m1_2->GetName().data());
+  Method* m2_2 = klass2->GetVirtualMethod(1);
+  ASSERT_STREQ("m2", m2_2->GetName().data());
+  Method* m3_2 = klass2->GetVirtualMethod(2);
+  ASSERT_STREQ("m3", m3_2->GetName().data());
+  Method* m4_2 = klass2->GetVirtualMethod(3);
+  ASSERT_STREQ("m4", m4_2->GetName().data());
+
+  EXPECT_TRUE(m1_1->HasSameNameAndPrototype(m1_2));
+  EXPECT_TRUE(m1_2->HasSameNameAndPrototype(m1_1));
+
+  EXPECT_TRUE(m2_1->HasSameNameAndPrototype(m2_2));
+  EXPECT_TRUE(m2_2->HasSameNameAndPrototype(m2_1));
+
+  EXPECT_TRUE(m3_1->HasSameNameAndPrototype(m3_2));
+  EXPECT_TRUE(m3_2->HasSameNameAndPrototype(m3_1));
+
+  EXPECT_TRUE(m4_1->HasSameNameAndPrototype(m4_2));
+  EXPECT_TRUE(m4_2->HasSameNameAndPrototype(m4_1));
+}
+
 }  // namespace art
diff --git a/src/raw_dex_file.h b/src/raw_dex_file.h
index 8363f1f..56b6ff6 100644
--- a/src/raw_dex_file.h
+++ b/src/raw_dex_file.h
@@ -114,6 +114,37 @@
     TypeItem list_[1];  // elements of the list
   };
 
+  class ParameterIterator {
+   public:
+    ParameterIterator(const RawDexFile& raw, const ProtoId& proto_id)
+        : raw_(raw), size_(0), pos_(0) {
+      type_list_ = raw_.GetProtoParameters(proto_id);
+      if (type_list_ != NULL) {
+        size_ = type_list_->Size();
+      }
+    }
+    bool HasNext() const { return pos_ != size_; }
+    void Next() { ++pos_; }
+    const char* GetDescriptor() {
+      uint32_t type_idx = type_list_->GetTypeItem(pos_).type_idx_;
+      return raw_.dexStringByTypeIdx(type_idx);
+    }
+   private:
+    const RawDexFile& raw_;
+    const TypeList* type_list_;
+    uint32_t size_;
+    uint32_t pos_;
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ParameterIterator);
+  };
+
+  ParameterIterator* GetParameterIterator(const ProtoId& proto_id) const {
+    return new ParameterIterator(*this, proto_id);
+  }
+
+  const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const {
+    return dexStringByTypeIdx(proto_id.return_type_idx_);
+  }
+
   // Raw code_item.
   struct Code {
     uint16_t registers_size_;
@@ -210,6 +241,7 @@
   }
 
   // Returns a pointer to the memory mapped class data.
+  // TODO: return a stream
   const byte* GetClassData(const ClassDef& class_def) const {
     if (class_def.class_data_off_ == 0) {
       LG << "class_def.class_data_off_ == 0";
@@ -269,7 +301,7 @@
     return class_defs_[idx];
   }
 
-  const TypeList* GetInterfacesList(const ClassDef& class_def) {
+  const TypeList* GetInterfacesList(const ClassDef& class_def) const {
     if (class_def.interfaces_off_ == 0) {
         return NULL;
     } else {
@@ -293,6 +325,15 @@
     return dexStringById(proto_id.shorty_idx_);
   }
 
+  const TypeList* GetProtoParameters(const ProtoId& proto_id) const {
+    if (proto_id.parameters_off_ == 0) {
+      return NULL;
+    } else {
+      const byte* addr = base_ + proto_id.parameters_off_;
+      return reinterpret_cast<const TypeList*>(addr);
+    }
+  }
+
   // From libdex...
 
   // Returns a pointer to the UTF-8 string data referred to by the
@@ -316,6 +357,7 @@
     return dexStringById(type_id.descriptor_idx_);
   }
 
+  // TODO: encoded_field is actually a stream of bytes
   void dexReadClassDataField(const byte** encoded_field,
                              RawDexFile::Field* field,
                              uint32_t* last_idx) const {
@@ -325,6 +367,7 @@
     *last_idx = idx;
   }
 
+  // TODO: encoded_method is actually a stream of bytes
   void dexReadClassDataMethod(const byte** encoded_method,
                               RawDexFile::Method* method,
                               uint32_t* last_idx) const {
diff --git a/src/raw_dex_file_test.cc b/src/raw_dex_file_test.cc
index 6bfd82a..1dd1485 100644
--- a/src/raw_dex_file_test.cc
+++ b/src/raw_dex_file_test.cc
@@ -1,5 +1,6 @@
 // Copyright 2011 Google Inc. All Rights Reserved.
 
+#include "src/dex_file.h"
 #include "src/raw_dex_file.h"
 #include "src/scoped_ptr.h"
 
@@ -8,8 +9,6 @@
 
 namespace art {
 
-// Nested.java
-//
 // class Nested {
 //     class Inner {
 //     }
diff --git a/src/stringpiece.cc b/src/stringpiece.cc
index 098c6ef..27e24d1 100644
--- a/src/stringpiece.cc
+++ b/src/stringpiece.cc
@@ -11,33 +11,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <iostream>
-#include <utility>
 #include "src/stringpiece.h"
 
-using art::StringPiece;
+#include <iostream>
+#include <utility>
 
-std::ostream& operator<<(std::ostream& o, const StringPiece& piece) {
-  o.write(piece.data(), piece.size());
-  return o;
-}
-
-bool operator==(const StringPiece& x, const StringPiece& y) {
-  int len = x.size();
-  if (len != y.size()) {
-    return false;
-  }
-  const char* p = x.data();
-  const char* p2 = y.data();
-  // Test last byte in case strings share large common prefix
-  if ((len > 0) && (p[len-1] != p2[len-1])) return false;
-  const char* p_limit = p + len;
-  for (; p < p_limit; p++, p2++) {
-    if (*p != *p2)
-      return false;
-  }
-  return true;
-}
+namespace art {
 
 bool operator<(const art::StringPiece& x, const art::StringPiece& y) {
   const int r = memcmp(x.data(), y.data(),
@@ -110,3 +89,10 @@
 }
 
 const StringPiece::size_type StringPiece::npos = size_type(-1);
+
+}  // namespace art
+
+std::ostream& operator<<(std::ostream& o, const art::StringPiece& piece) {
+  o.write(piece.data(), piece.size());
+  return o;
+}
diff --git a/src/stringpiece.h b/src/stringpiece.h
index ca12212..3aefa57 100644
--- a/src/stringpiece.h
+++ b/src/stringpiece.h
@@ -151,9 +151,34 @@
   StringPiece substr(size_type pos, size_type n = npos) const;
 };
 
-}  // namespace art
+// This large function is defined inline so that in a fairly common case where
+// one of the arguments is a literal, the compiler can elide a lot of the
+// following comparisons.
+inline bool operator==(const art::StringPiece& x, const art::StringPiece& y) {
+  int len = x.size();
+  if (len != y.size()) {
+    return false;
+  }
 
-bool operator==(const art::StringPiece& x, const art::StringPiece& y);
+  const char* p1 = x.data();
+  const char* p2 = y.data();
+  if (p1 == p2) {
+    return true;
+  }
+  if (len <= 0) {
+    return true;
+  }
+
+  // Test last byte in case strings share large common prefix
+  if (p1[len-1] != p2[len-1]) return false;
+  if (len == 1) return true;
+
+  // At this point we can, but don't have to, ignore the last byte.  We use
+  // this observation to fold the odd-length case into the even-length case.
+  len &= ~1;
+
+  return memcmp(p1, p2, len) == 0;
+}
 
 inline bool operator!=(const art::StringPiece& x, const art::StringPiece& y) {
   return !(x == y);
@@ -173,6 +198,8 @@
   return !(x < y);
 }
 
+}  // namespace art
+
 // allow StringPiece to be logged
 extern std::ostream& operator<<(std::ostream& o, const art::StringPiece& piece);
 
diff --git a/src/thread.h b/src/thread.h
new file mode 100644
index 0000000..b1bfb33
--- /dev/null
+++ b/src/thread.h
@@ -0,0 +1,96 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+// Author: cshapiro@google.com (Carl Shapiro)
+
+#ifndef ART_SRC_THREAD_H_
+#define ART_SRC_THREAD_H_
+
+#include "src/globals.h"
+#include "src/logging.h"
+#include "src/macros.h"
+
+namespace art {
+
+class Object;
+class Thread;
+
+class Mutex {
+ public:
+  virtual ~Mutex() {}
+
+  void Lock() {}
+
+  bool TryLock() { return true; }
+
+  void Unlock() {}
+
+  const char* GetName() { return name_; }
+
+  Thread* GetOwner() { return owner_; }
+
+ public:  // TODO: protected
+  explicit Mutex(const char* name) : name_(name), owner_(NULL) {}
+
+  void SetOwner(Thread* thread) { owner_ = thread; }
+
+ private:
+  const char* name_;
+
+  Thread* owner_;
+
+  DISALLOW_COPY_AND_ASSIGN(Mutex);
+};
+
+class MutexLock {
+ public:
+  explicit MutexLock(Mutex *mu) : mu_(mu) {
+    mu_->Lock();
+  }
+  ~MutexLock() { mu_->Unlock(); }
+ private:
+  Mutex* const mu_;
+  DISALLOW_COPY_AND_ASSIGN(MutexLock);
+};
+
+class Thread {
+ public:
+  static Thread* Self() {
+    static Thread self;
+    return &self; // TODO
+  }
+
+  uint32_t GetThreadId() {
+    return thread_id_;
+  }
+
+  bool IsExceptionPending() const {
+    return false;  // TODO exception_ != NULL;
+  }
+
+  Object* GetException() const {
+    return exception_;
+  }
+
+  void SetException(Object* new_exception) {
+    CHECK(new_exception != NULL);
+    // TODO: CHECK(exception_ == NULL);
+    exception_ = new_exception;  // TODO
+  }
+
+  void ClearException() {
+    exception_ = NULL;
+  }
+
+ private:
+  Thread() : thread_id_(1234), exception_(NULL) {}
+  ~Thread() {}
+
+  uint32_t thread_id_;
+
+  Object* exception_;
+
+  DISALLOW_COPY_AND_ASSIGN(Thread);
+};
+
+}  // namespace art
+
+#endif  // ART_SRC_THREAD_H_