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