| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef ART_SRC_OBJECT_UTILS_H_ |
| #define ART_SRC_OBJECT_UTILS_H_ |
| |
| #include "class_linker.h" |
| #include "dex_cache.h" |
| #include "dex_file.h" |
| #include "intern_table.h" |
| #include "monitor.h" |
| #include "object.h" |
| #include "runtime.h" |
| #include "UniquePtr.h" |
| |
| #include <string> |
| |
| namespace art { |
| |
| class ObjectLock { |
| public: |
| explicit ObjectLock(Object* object) : self_(Thread::Current()), obj_(object) { |
| CHECK(object != NULL); |
| obj_->MonitorEnter(self_); |
| } |
| |
| ~ObjectLock() { |
| obj_->MonitorExit(self_); |
| } |
| |
| void Wait() { |
| return Monitor::Wait(self_, obj_, 0, 0, false); |
| } |
| |
| void Notify() { |
| obj_->Notify(); |
| } |
| |
| void NotifyAll() { |
| obj_->NotifyAll(); |
| } |
| |
| private: |
| Thread* self_; |
| Object* obj_; |
| DISALLOW_COPY_AND_ASSIGN(ObjectLock); |
| }; |
| |
| class ClassHelper { |
| public: |
| ClassHelper(const Class* c = NULL, ClassLinker* l = NULL) |
| : class_def_(NULL), |
| class_linker_(l), |
| dex_cache_(NULL), |
| dex_file_(NULL), |
| interface_type_list_(NULL), |
| klass_(c) { |
| } |
| |
| void ChangeClass(const Class* new_c) { |
| DCHECK(new_c != NULL); |
| if (dex_cache_ != NULL) { |
| DexCache* new_c_dex_cache = new_c->GetDexCache(); |
| if (new_c_dex_cache != dex_cache_) { |
| dex_cache_ = new_c_dex_cache; |
| dex_file_ = NULL; |
| } |
| } |
| klass_ = new_c; |
| interface_type_list_ = NULL; |
| class_def_ = NULL; |
| } |
| |
| // The returned const char* is only guaranteed to be valid for the lifetime of the ClassHelper. |
| // If you need it longer, copy it into a std::string. |
| const char* GetDescriptor() { |
| if (UNLIKELY(klass_->IsArrayClass())) { |
| return GetArrayDescriptor(); |
| } else if (UNLIKELY(klass_->IsPrimitive())) { |
| return Primitive::Descriptor(klass_->GetPrimitiveType()); |
| } else if (UNLIKELY(klass_->IsProxyClass())) { |
| descriptor_ = GetClassLinker()->GetDescriptorForProxy(klass_); |
| return descriptor_.c_str(); |
| } else { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::TypeId& type_id = dex_file.GetTypeId(klass_->GetDexTypeIndex()); |
| return dex_file.GetTypeDescriptor(type_id); |
| } |
| } |
| |
| const char* GetArrayDescriptor() { |
| std::string result("["); |
| const Class* saved_klass = klass_; |
| ChangeClass(klass_->GetComponentType()); |
| result += GetDescriptor(); |
| ChangeClass(saved_klass); |
| descriptor_ = result; |
| return descriptor_.c_str(); |
| } |
| |
| const DexFile::ClassDef* GetClassDef() { |
| const DexFile::ClassDef* result = class_def_; |
| if (result == NULL) { |
| result = GetDexFile().FindClassDef(GetDescriptor()); |
| class_def_ = result; |
| } |
| return result; |
| } |
| |
| uint32_t NumInterfaces() { |
| if (klass_->IsPrimitive()) { |
| return 0; |
| } else if (klass_->IsArrayClass()) { |
| return 2; |
| } else if (klass_->IsProxyClass()) { |
| return klass_->GetIfTable()->GetLength(); |
| } else { |
| const DexFile::TypeList* interfaces = GetInterfaceTypeList(); |
| if (interfaces == NULL) { |
| return 0; |
| } else { |
| return interfaces->Size(); |
| } |
| } |
| } |
| |
| uint16_t GetInterfaceTypeIdx(uint32_t idx) { |
| DCHECK(!klass_->IsPrimitive()); |
| DCHECK(!klass_->IsArrayClass()); |
| return GetInterfaceTypeList()->GetTypeItem(idx).type_idx_; |
| } |
| |
| Class* GetInterface(uint32_t idx) { |
| DCHECK(!klass_->IsPrimitive()); |
| if (klass_->IsArrayClass()) { |
| if (idx == 0) { |
| return GetClassLinker()->FindSystemClass("Ljava/lang/Cloneable;"); |
| } else { |
| DCHECK_EQ(1U, idx); |
| return GetClassLinker()->FindSystemClass("Ljava/io/Serializable;"); |
| } |
| } else if (klass_->IsProxyClass()) { |
| return klass_->GetIfTable()->Get(idx)->GetInterface(); |
| } else { |
| uint16_t type_idx = GetInterfaceTypeIdx(idx); |
| Class* interface = GetDexCache()->GetResolvedType(type_idx); |
| if (interface == NULL) { |
| interface = GetClassLinker()->ResolveType(GetDexFile(), type_idx, klass_); |
| CHECK(interface != NULL || Thread::Current()->IsExceptionPending()); |
| } |
| return interface; |
| } |
| } |
| |
| const char* GetSourceFile() { |
| std::string descriptor(GetDescriptor()); |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::ClassDef* dex_class_def = dex_file.FindClassDef(descriptor); |
| CHECK(dex_class_def != NULL); |
| return dex_file.GetSourceFile(*dex_class_def); |
| } |
| |
| std::string GetLocation() { |
| return GetDexCache()->GetLocation()->ToModifiedUtf8(); |
| } |
| |
| const DexFile& GetDexFile() { |
| const DexFile* result = dex_file_; |
| if (result == NULL) { |
| const DexCache* dex_cache = GetDexCache(); |
| result = &GetClassLinker()->FindDexFile(dex_cache); |
| dex_file_ = result; |
| } |
| return *result; |
| } |
| |
| private: |
| const DexFile::TypeList* GetInterfaceTypeList() { |
| const DexFile::TypeList* result = interface_type_list_; |
| if (result == NULL) { |
| const DexFile::ClassDef* class_def = GetClassDef(); |
| if (class_def != NULL) { |
| result = GetDexFile().GetInterfacesList(*class_def); |
| interface_type_list_ = result; |
| } |
| } |
| return result; |
| } |
| |
| DexCache* GetDexCache() { |
| DexCache* result = dex_cache_; |
| if (result == NULL) { |
| result = klass_->GetDexCache(); |
| dex_cache_ = result; |
| } |
| return result; |
| } |
| |
| ClassLinker* GetClassLinker() { |
| ClassLinker* result = class_linker_; |
| if (result == NULL) { |
| result = Runtime::Current()->GetClassLinker(); |
| class_linker_ = result; |
| } |
| return result; |
| } |
| |
| const DexFile::ClassDef* class_def_; |
| ClassLinker* class_linker_; |
| DexCache* dex_cache_; |
| const DexFile* dex_file_; |
| const DexFile::TypeList* interface_type_list_; |
| const Class* klass_; |
| std::string descriptor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ClassHelper); |
| }; |
| |
| class FieldHelper { |
| public: |
| FieldHelper() : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(NULL) {} |
| explicit FieldHelper(const Field* f) : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), field_(f) {} |
| FieldHelper(const Field* f, ClassLinker* l) : class_linker_(l), dex_cache_(NULL), dex_file_(NULL), |
| field_(f) {} |
| |
| void ChangeField(const Field* new_f) { |
| DCHECK(new_f != NULL); |
| if (dex_cache_ != NULL) { |
| DexCache* new_f_dex_cache = new_f->GetDeclaringClass()->GetDexCache(); |
| if (new_f_dex_cache != dex_cache_) { |
| dex_cache_ = new_f_dex_cache; |
| dex_file_ = NULL; |
| } |
| } |
| field_ = new_f; |
| } |
| const char* GetName() { |
| uint32_t field_index = field_->GetDexFieldIndex(); |
| if (field_index != DexFile::kDexNoIndex) { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetFieldName(dex_file.GetFieldId(field_index)); |
| } else { |
| // Proxy classes have a single static field called "throws" |
| CHECK(field_->GetDeclaringClass()->IsProxyClass()); |
| DCHECK(field_->IsStatic()); |
| return "throws"; |
| } |
| } |
| String* GetNameAsString() { |
| uint32_t field_index = field_->GetDexFieldIndex(); |
| if (field_index != DexFile::kDexNoIndex) { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); |
| return GetClassLinker()->ResolveString(dex_file, field_id.name_idx_, GetDexCache()); |
| } else { |
| // Proxy classes have a single static field called "throws" |
| CHECK(field_->GetDeclaringClass()->IsProxyClass()); |
| DCHECK(field_->IsStatic()); |
| return Runtime::Current()->GetInternTable()->InternStrong("throws"); |
| } |
| } |
| Class* GetType() { |
| uint32_t field_index = field_->GetDexFieldIndex(); |
| if (field_index != DexFile::kDexNoIndex) { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); |
| Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_); |
| if (type == NULL) { |
| type = GetClassLinker()->ResolveType(field_id.type_idx_, field_); |
| CHECK(type != NULL || Thread::Current()->IsExceptionPending()); |
| } |
| return type; |
| } else { |
| // Proxy classes have a single static field called "throws" whose type is Class[][] |
| CHECK(field_->GetDeclaringClass()->IsProxyClass()); |
| DCHECK(field_->IsStatic()); |
| return GetClassLinker()->FindSystemClass("[[Ljava/lang/Class;"); |
| } |
| } |
| const char* GetTypeDescriptor() { |
| uint32_t field_index = field_->GetDexFieldIndex(); |
| if (field_index != DexFile::kDexNoIndex) { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index); |
| return dex_file.GetFieldTypeDescriptor(field_id); |
| } else { |
| // Proxy classes have a single static field called "throws" whose type is Class[][] |
| CHECK(field_->GetDeclaringClass()->IsProxyClass()); |
| DCHECK(field_->IsStatic()); |
| return "[[Ljava/lang/Class;"; |
| } |
| } |
| Primitive::Type GetTypeAsPrimitiveType() { |
| return Primitive::GetType(GetTypeDescriptor()[0]); |
| } |
| bool IsPrimitiveType() { |
| Primitive::Type type = GetTypeAsPrimitiveType(); |
| return type != Primitive::kPrimNot; |
| } |
| size_t FieldSize() { |
| Primitive::Type type = GetTypeAsPrimitiveType(); |
| return Primitive::FieldSize(type); |
| } |
| |
| // The returned const char* is only guaranteed to be valid for the lifetime of the FieldHelper. |
| // If you need it longer, copy it into a std::string. |
| const char* GetDeclaringClassDescriptor() { |
| uint16_t type_idx = field_->GetDeclaringClass()->GetDexTypeIndex(); |
| if (type_idx != DexFile::kDexNoIndex16) { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); |
| } else { |
| // Most likely a proxy class |
| ClassHelper kh(field_->GetDeclaringClass()); |
| declaring_class_descriptor_ = kh.GetDescriptor(); |
| return declaring_class_descriptor_.c_str(); |
| } |
| } |
| |
| private: |
| DexCache* GetDexCache() { |
| DexCache* result = dex_cache_; |
| if (result == NULL) { |
| result = field_->GetDeclaringClass()->GetDexCache(); |
| dex_cache_ = result; |
| } |
| return result; |
| } |
| ClassLinker* GetClassLinker() { |
| ClassLinker* result = class_linker_; |
| if (result == NULL) { |
| result = Runtime::Current()->GetClassLinker(); |
| class_linker_ = result; |
| } |
| return result; |
| } |
| const DexFile& GetDexFile() { |
| const DexFile* result = dex_file_; |
| if (result == NULL) { |
| const DexCache* dex_cache = GetDexCache(); |
| result = &GetClassLinker()->FindDexFile(dex_cache); |
| dex_file_ = result; |
| } |
| return *result; |
| } |
| |
| ClassLinker* class_linker_; |
| DexCache* dex_cache_; |
| const DexFile* dex_file_; |
| const Field* field_; |
| std::string declaring_class_descriptor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(FieldHelper); |
| }; |
| |
| class MethodHelper { |
| public: |
| MethodHelper() : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), method_(NULL), |
| shorty_(NULL), shorty_len_(0) {} |
| explicit MethodHelper(const Method* m) : class_linker_(NULL), dex_cache_(NULL), dex_file_(NULL), |
| method_(NULL), shorty_(NULL), shorty_len_(0) { |
| SetMethod(m); |
| } |
| MethodHelper(const Method* m, ClassLinker* l) : class_linker_(l), dex_cache_(NULL), |
| dex_file_(NULL), method_(NULL), shorty_(NULL), shorty_len_(0) { |
| SetMethod(m); |
| } |
| |
| void ChangeMethod(Method* new_m) { |
| DCHECK(new_m != NULL); |
| if (dex_cache_ != NULL) { |
| Class* klass = new_m->GetDeclaringClass(); |
| if (klass->IsProxyClass()) { |
| dex_cache_ = NULL; |
| dex_file_ = NULL; |
| } else { |
| DexCache* new_m_dex_cache = klass->GetDexCache(); |
| if (new_m_dex_cache != dex_cache_) { |
| dex_cache_ = new_m_dex_cache; |
| dex_file_ = NULL; |
| } |
| } |
| } |
| SetMethod(new_m); |
| shorty_ = NULL; |
| } |
| const char* GetName() { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetMethodName(dex_file.GetMethodId(method_->GetDexMethodIndex())); |
| } |
| String* GetNameAsString() { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); |
| return GetClassLinker()->ResolveString(dex_file, method_id.name_idx_, GetDexCache()); |
| } |
| const char* GetShorty() { |
| const char* result = shorty_; |
| if (result == NULL) { |
| const DexFile& dex_file = GetDexFile(); |
| result = dex_file.GetMethodShorty(dex_file.GetMethodId(method_->GetDexMethodIndex()), |
| &shorty_len_); |
| shorty_ = result; |
| } |
| return result; |
| } |
| int32_t GetShortyLength() { |
| if (shorty_ == NULL) { |
| GetShorty(); |
| } |
| return shorty_len_; |
| } |
| const std::string GetSignature() { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetMethodSignature(dex_file.GetMethodId(method_->GetDexMethodIndex())); |
| } |
| const DexFile::ProtoId& GetPrototype() { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetMethodPrototype(dex_file.GetMethodId(method_->GetDexMethodIndex())); |
| } |
| const DexFile::TypeList* GetParameterTypeList() { |
| const DexFile::ProtoId& proto = GetPrototype(); |
| return GetDexFile().GetProtoParameters(proto); |
| } |
| ObjectArray<Class>* GetParameterTypes() { |
| const DexFile::TypeList* params = GetParameterTypeList(); |
| Class* array_class = GetClassLinker()->FindSystemClass("[Ljava/lang/Class;"); |
| uint32_t num_params = params == NULL ? 0 : params->Size(); |
| ObjectArray<Class>* result = ObjectArray<Class>::Alloc(array_class, num_params); |
| for (uint32_t i = 0; i < num_params; i++) { |
| Class* param_type = GetClassFromTypeIdx(params->GetTypeItem(i).type_idx_); |
| result->Set(i, param_type); |
| } |
| return result; |
| } |
| Class* GetReturnType() { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); |
| const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); |
| uint16_t return_type_idx = proto_id.return_type_idx_; |
| return GetClassFromTypeIdx(return_type_idx); |
| } |
| const char* GetReturnTypeDescriptor() { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::MethodId& method_id = dex_file.GetMethodId(method_->GetDexMethodIndex()); |
| const DexFile::ProtoId& proto_id = dex_file.GetMethodPrototype(method_id); |
| uint16_t return_type_idx = proto_id.return_type_idx_; |
| return dex_file.GetTypeDescriptor(dex_file.GetTypeId(return_type_idx)); |
| } |
| int32_t GetLineNumFromNativePC(uintptr_t raw_pc) { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetLineNumFromPC(method_, method_->ToDexPC(raw_pc)); |
| } |
| const char* GetDeclaringClassDescriptor() { |
| Class* klass = method_->GetDeclaringClass(); |
| DCHECK(!klass->IsProxyClass()); |
| uint16_t type_idx = klass->GetDexTypeIndex(); |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); |
| } |
| const char* GetDeclaringClassSourceFile() { |
| const char* descriptor = GetDeclaringClassDescriptor(); |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::ClassDef* dex_class_def = dex_file.FindClassDef(descriptor); |
| CHECK(dex_class_def != NULL); |
| return dex_file.GetSourceFile(*dex_class_def); |
| } |
| bool IsStatic() { |
| return method_->IsStatic(); |
| } |
| bool IsClassInitializer() { |
| return IsStatic() && StringPiece(GetName()) == "<clinit>"; |
| } |
| size_t NumArgs() { |
| // "1 +" because the first in Args is the receiver. |
| // "- 1" because we don't count the return type. |
| return (IsStatic() ? 0 : 1) + GetShortyLength() - 1; |
| } |
| // Is the specified parameter a long or double, where parameter 0 is 'this' for instance methods |
| bool IsParamALongOrDouble(size_t param) { |
| CHECK_LT(param, NumArgs()); |
| if (IsStatic()) { |
| param++; // 0th argument must skip return value at start of the shorty |
| } else if (param == 0) { |
| return false; // this argument |
| } |
| char ch = GetShorty()[param]; |
| return (ch == 'J' || ch == 'D'); |
| } |
| // Is the specified parameter a reference, where parameter 0 is 'this' for instance methods |
| bool IsParamAReference(size_t param) { |
| CHECK_LT(param, NumArgs()); |
| if (IsStatic()) { |
| param++; // 0th argument must skip return value at start of the shorty |
| } else if (param == 0) { |
| return true; // this argument |
| } |
| return GetShorty()[param] == 'L'; // An array also has a shorty character of 'L' (not '[') |
| } |
| bool HasSameNameAndSignature(MethodHelper* other) { |
| if (GetDexCache() == other->GetDexCache()) { |
| const DexFile& dex_file = GetDexFile(); |
| const DexFile::MethodId& mid = dex_file.GetMethodId(method_->GetDexMethodIndex()); |
| const DexFile::MethodId& other_mid = |
| dex_file.GetMethodId(other->method_->GetDexMethodIndex()); |
| return mid.name_idx_ == other_mid.name_idx_ && mid.proto_idx_ == other_mid.proto_idx_; |
| } |
| StringPiece name(GetName()); |
| StringPiece other_name(other->GetName()); |
| return name == other_name && GetSignature() == other->GetSignature(); |
| } |
| const DexFile::CodeItem* GetCodeItem() { |
| return GetDexFile().GetCodeItem(method_->GetCodeItemOffset()); |
| } |
| bool IsResolvedTypeIdx(uint16_t type_idx) const { |
| return method_->GetDexCacheResolvedTypes()->Get(type_idx) != NULL; |
| } |
| Class* GetClassFromTypeIdx(uint16_t type_idx) { |
| Class* type = method_->GetDexCacheResolvedTypes()->Get(type_idx); |
| if (type == NULL) { |
| type = GetClassLinker()->ResolveType(type_idx, method_); |
| CHECK(type != NULL || Thread::Current()->IsExceptionPending()); |
| } |
| return type; |
| } |
| const char* GetTypeDescriptorFromTypeIdx(uint16_t type_idx) { |
| const DexFile& dex_file = GetDexFile(); |
| return dex_file.GetTypeDescriptor(dex_file.GetTypeId(type_idx)); |
| } |
| Class* GetDexCacheResolvedType(uint16_t type_idx) { |
| return GetDexCache()->GetResolvedType(type_idx); |
| } |
| const DexFile& GetDexFile() { |
| const DexFile* result = dex_file_; |
| if (result == NULL) { |
| const DexCache* dex_cache = GetDexCache(); |
| result = &GetClassLinker()->FindDexFile(dex_cache); |
| dex_file_ = result; |
| } |
| return *result; |
| } |
| private: |
| // Set the method_ field, for proxy methods looking up the interface method via the resolved |
| // methods table. |
| void SetMethod(const Method* method) { |
| if (method != NULL) { |
| Class* klass = method->GetDeclaringClass(); |
| if (klass->IsProxyClass()) { |
| method = GetClassLinker()->FindMethodForProxy(klass, method); |
| CHECK(method != NULL); |
| } |
| } |
| method_ = method; |
| } |
| DexCache* GetDexCache() { |
| DexCache* result = dex_cache_; |
| if (result == NULL) { |
| Class* klass = method_->GetDeclaringClass(); |
| result = klass->GetDexCache(); |
| dex_cache_ = result; |
| } |
| return result; |
| } |
| ClassLinker* GetClassLinker() { |
| ClassLinker* result = class_linker_; |
| if (result == NULL) { |
| result = Runtime::Current()->GetClassLinker(); |
| class_linker_ = result; |
| } |
| return result; |
| } |
| |
| ClassLinker* class_linker_; |
| DexCache* dex_cache_; |
| const DexFile* dex_file_; |
| const Method* method_; |
| const char* shorty_; |
| int32_t shorty_len_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MethodHelper); |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_SRC_OBJECT_UTILS_H_ |