Add support for linking classes.
Change-Id: I0026be6e4c919f7391fd83c654f58c3bc67f44e1
diff --git a/Android.common.mk b/Android.common.mk
index 3dce0b5..4798d63 100644
--- a/Android.common.mk
+++ b/Android.common.mk
@@ -41,8 +41,10 @@
LIBART_LOCAL_SRC_FILES := \
src/assembler.cc \
src/base64.cc \
+ src/class_linker.cc \
src/dex_file.cc \
src/dex_instruction.cc \
+ src/dex_verifier.cc \
src/memory_region.cc \
src/object.cc \
src/raw_dex_file.cc \
@@ -60,6 +62,7 @@
endif
TEST_LOCAL_SRC_FILES := \
+ src/class_linker_test.cc \
src/dex_file_test.cc \
src/dex_instruction_visitor_test.cc \
src/object_test.cc \
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_