diff options
author | 2011-10-14 03:29:56 -0700 | |
---|---|---|
committer | 2011-10-14 21:45:27 -0700 | |
commit | 466bb25416b88fabd5d4387b7c7e5cc1ece78b8c (patch) | |
tree | 8d0952cbd850f1d0ac2e43a8372ce571fb4982d1 | |
parent | a10cd94bb77ed66fa0a8b66141c4504045c92d30 (diff) |
Proxy implementation
This rounds out the proxy implementation by adding missing pieces to the
class linker, extending tests and fixing issues in the runtime support.
There are also some tweaks for performance and to clean up Method/Object
a little.
A unit test of the functionality is "art/test/run-test 044"
Change-Id: Id94102d10b81cd9b12b95ba8618f6187490204c4
-rw-r--r-- | src/class_linker.cc | 100 | ||||
-rw-r--r-- | src/class_linker.h | 5 | ||||
-rw-r--r-- | src/class_linker_test.cc | 6 | ||||
-rw-r--r-- | src/java_lang_reflect_Proxy.cc | 2 | ||||
-rw-r--r-- | src/jni_internal.cc | 1 | ||||
-rw-r--r-- | src/object.cc | 95 | ||||
-rw-r--r-- | src/object.h | 57 | ||||
-rw-r--r-- | src/object_test.cc | 16 | ||||
-rw-r--r-- | src/reflection.cc | 9 | ||||
-rw-r--r-- | src/runtime_support.cc | 90 | ||||
-rw-r--r-- | src/runtime_support.h | 2 | ||||
-rw-r--r-- | src/runtime_support_asm.S | 10 | ||||
-rw-r--r-- | src/thread.cc | 12 | ||||
-rw-r--r-- | test/044-proxy/expected.txt | 10 | ||||
-rw-r--r-- | test/044-proxy/src/BasicTest.java | 11 | ||||
-rw-r--r-- | test/044-proxy/src/Main.java | 1 | ||||
-rw-r--r-- | test/044-proxy/src/ReturnsAndArgPassing.java | 206 |
17 files changed, 509 insertions, 124 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc index 9fd8f0b249..53bcf7e93f 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -19,6 +19,7 @@ #include "oat_file.h" #include "object.h" #include "runtime.h" +#include "runtime_support.h" #include "ScopedLocalRef.h" #include "space.h" #include "stl_util.h" @@ -127,6 +128,7 @@ const char* ClassLinker::class_roots_descriptors_[] = { "Ljava/lang/reflect/Constructor;", "Ljava/lang/reflect/Field;", "Ljava/lang/reflect/Method;", + "Ljava/lang/reflect/Proxy;", "Ljava/lang/ClassLoader;", "Ldalvik/system/BaseDexClassLoader;", "Ldalvik/system/PathClassLoader;", @@ -420,6 +422,12 @@ void ClassLinker::Init(const std::string& boot_class_path) { Class* Method_class = FindSystemClass("Ljava/lang/reflect/Method;"); CHECK_EQ(java_lang_reflect_Method, Method_class); + // End of special init trickery, subsequent classes may be loaded via FindSystemClass + + // Create java.lang.reflect.Proxy root + Class* java_lang_reflect_Proxy = FindSystemClass("Ljava/lang/reflect/Proxy;"); + SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy); + // java.lang.ref classes need to be specially flagged, but otherwise are normal classes Class* java_lang_ref_Reference = FindSystemClass("Ljava/lang/ref/Reference;"); SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference); @@ -1291,6 +1299,7 @@ void ClassLinker::RegisterDexFile(const DexFile& dex_file, DexCache* dex_cache) } const DexFile& ClassLinker::FindDexFile(const DexCache* dex_cache) const { + CHECK(dex_cache != NULL); MutexLock mu(lock_); for (size_t i = 0; i != dex_caches_.size(); ++i) { if (dex_caches_[i] == dex_cache) { @@ -1533,72 +1542,85 @@ void ClassLinker::VerifyClass(Class* klass) { } Class* ClassLinker::CreateProxyClass(String* name, ObjectArray<Class>* interfaces, - ClassLoader* loader, ObjectArray<Method>* methods, ObjectArray<Object>* throws) { + ClassLoader* loader, ObjectArray<Method>* methods, ObjectArray<ObjectArray<Class> >* throws) { Class* klass = AllocClass(GetClassRoot(kJavaLangClass), sizeof(ProxyClass)); CHECK(klass != NULL); klass->SetObjectSize(sizeof(Proxy)); - klass->SetDescriptor(intern_table_->InternStrong(name)); + const char* descriptor = DotToDescriptor(name->ToModifiedUtf8().c_str()).c_str();; + klass->SetDescriptor(intern_table_->InternStrong(descriptor)); klass->SetAccessFlags(kAccPublic | kAccFinal); klass->SetClassLoader(loader); - klass->SetStatus(Class::kStatusInitialized); - klass->SetInterfaces(interfaces); + klass->SetStatus(Class::kStatusInitialized); // no loading or initializing necessary + Class* proxy_class = GetClassRoot(kJavaLangReflectProxy); + klass->SetSuperClass(proxy_class); // The super class is java.lang.reflect.Proxy + klass->SetInterfaces(interfaces); // The interfaces are the array of interfaces specified + // Proxies have 1 direct method, the constructor klass->SetDirectMethods(AllocObjectArray<Method>(1)); klass->SetDirectMethod(0, CreateProxyConstructor(klass)); + // Create virtual method using specified prototypes size_t num_virtual_methods = methods->GetLength(); klass->SetVirtualMethods(AllocObjectArray<Method>(num_virtual_methods)); for (size_t i = 0; i < num_virtual_methods; ++i) { Method* prototype = methods->Get(i); klass->SetVirtualMethod(i, CreateProxyMethod(klass, prototype, throws->Get(i))); } - + // Link the virtual methods, creating vtable and iftables if (!LinkMethods(klass)) { DCHECK(Thread::Current()->IsExceptionPending()); return NULL; } - return klass; } Method* ClassLinker::CreateProxyConstructor(Class* klass) { - Method* constructor = AllocMethod(); + // Create constructor for Proxy that must initialize h + Class* proxy_class = GetClassRoot(kJavaLangReflectProxy); + ObjectArray<Method>* proxy_direct_methods = proxy_class->GetDirectMethods(); + CHECK_EQ(proxy_direct_methods->GetLength(), 12); + Method* proxy_constructor = proxy_direct_methods->Get(2); + // Clone the existing constructor of Proxy (our constructor would just invoke it so steal its + // code_ too) + Method* constructor = down_cast<Method*>(proxy_constructor->Clone()); + // Make this constructor public and fix the class to be our Proxy version + constructor->SetAccessFlags((constructor->GetAccessFlags() & ~kAccProtected) | kAccPublic); constructor->SetDeclaringClass(klass); - constructor->SetName(intern_table_->InternStrong("<init>")); - constructor->SetSignature(intern_table_->InternStrong("(Ljava/lang/reflect/InvocationHandler;)V")); - constructor->SetShorty(intern_table_->InternStrong("LV")); - constructor->SetAccessFlags(kAccPublic | kAccNative); - - // TODO: return type - // TODO: code block - + // Sanity checks + CHECK(constructor->IsConstructor()); + CHECK(constructor->GetName()->Equals("<init>")); + CHECK(constructor->GetSignature()->Equals("(Ljava/lang/reflect/InvocationHandler;)V")); + DCHECK(constructor->IsPublic()); return constructor; } -Method* ClassLinker::CreateProxyMethod(Class* klass, Method* prototype, Object* throws) { - Method* method = AllocMethod(); +Method* ClassLinker::CreateProxyMethod(Class* klass, Method* prototype, + ObjectArray<Class>* throws) { + // We steal everything from the prototype (such as DexCache, invoke stub, etc.) then specialise + // as necessary + Method* method = down_cast<Method*>(prototype->Clone()); + + // Set class to be the concrete proxy class and clear the abstract flag, modify exceptions to + // the intersection of throw exceptions as defined in Proxy method->SetDeclaringClass(klass); - method->SetName(const_cast<String*>(prototype->GetName())); - method->SetSignature(const_cast<String*>(prototype->GetSignature())); - method->SetShorty(prototype->GetShorty()); - method->SetAccessFlags(prototype->GetAccessFlags()); + method->SetAccessFlags((method->GetAccessFlags() & ~kAccAbstract) | kAccFinal); method->SetExceptionTypes(throws); - // TODO: return type - // method->SetReturnTypeIdx(dex_file.GetProtoId(method_id.proto_idx_).return_type_idx_); - - // TODO: code block - // method->SetCodeItemOffset(src.code_off_); - // method->SetDexCacheStrings(klass->GetDexCache()->GetStrings()); - // method->SetDexCacheResolvedTypes(klass->GetDexCache()->GetResolvedTypes()); - // method->SetDexCacheResolvedMethods(klass->GetDexCache()->GetResolvedMethods()); - // method->SetDexCacheResolvedFields(klass->GetDexCache()->GetResolvedFields()); - // method->SetDexCacheCodeAndDirectMethods(klass->GetDexCache()->GetCodeAndDirectMethods()); - // method->SetDexCacheInitializedStaticStorage(klass->GetDexCache()->GetInitializedStaticStorage()); - // method->SetNumRegisters(code_item->registers_size_); - // method->SetNumIns(code_item->ins_size_); - // method->SetNumOuts(code_item->outs_size_); - // LinkCode(method, oat_class.get(), method_index); + // At runtime the method looks like a reference and argument saving method, clone the code + // related parameters from this method. + Method* refs_and_args = Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs); + method->SetCoreSpillMask(refs_and_args->GetCoreSpillMask()); + method->SetFpSpillMask(refs_and_args->GetFpSpillMask()); + method->SetFrameSizeInBytes(refs_and_args->GetFrameSizeInBytes()); + method->SetCode(reinterpret_cast<void*>(art_proxy_invoke_handler)); + + // Basic sanity + DCHECK(method->GetName()->Equals(prototype->GetName())); + DCHECK(method->GetSignature()->Equals(prototype->GetSignature())); + DCHECK(method->GetShorty()->Equals(prototype->GetShorty())); + + // More complex sanity - via dex cache + CHECK_EQ(method->GetReturnType(), prototype->GetReturnType()); return method; } @@ -2092,7 +2114,7 @@ bool ClassLinker::LinkVirtualMethods(Class* klass) { size_t j = 0; for (; j < actual_count; ++j) { Method* super_method = vtable->Get(j); - if (local_method->HasSameNameAndDescriptor(super_method)) { + if (local_method->HasSameNameAndSignature(super_method)) { // Verify if (super_method->IsFinal()) { ThrowLinkageError("Method %s.%s overrides final method in class %s", @@ -2212,7 +2234,7 @@ bool ClassLinker::LinkInterfaceMethods(Class* klass) { // matter which direction we go. We walk it backward anyway.) for (k = vtable->GetLength() - 1; k >= 0; --k) { Method* vtable_method = vtable->Get(k); - if (interface_method->HasSameNameAndDescriptor(vtable_method)) { + if (interface_method->HasSameNameAndSignature(vtable_method)) { if (!vtable_method->IsPublic()) { Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", "Implementation not public: %s", PrettyMethod(vtable_method).c_str()); @@ -2225,7 +2247,7 @@ bool ClassLinker::LinkInterfaceMethods(Class* klass) { if (k < 0) { Method* miranda_method = NULL; for (size_t mir = 0; mir < miranda_list.size(); mir++) { - if (miranda_list[mir]->HasSameNameAndDescriptor(interface_method)) { + if (miranda_list[mir]->HasSameNameAndSignature(interface_method)) { miranda_method = miranda_list[mir]; break; } diff --git a/src/class_linker.h b/src/class_linker.h index 37802ae67f..91d6259bc7 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -227,7 +227,7 @@ class ClassLinker { void VerifyClass(Class* klass); Class* CreateProxyClass(String* name, ObjectArray<Class>* interfaces, ClassLoader* loader, - ObjectArray<Method>* methods, ObjectArray<Object>* throws); + ObjectArray<Method>* methods, ObjectArray<ObjectArray<Class> >* throws); private: ClassLinker(InternTable*); @@ -347,7 +347,7 @@ class ClassLinker { } Method* CreateProxyConstructor(Class* klass); - Method* CreateProxyMethod(Class* klass, Method* prototype, Object* throws); + Method* CreateProxyMethod(Class* klass, Method* prototype, ObjectArray<Class>* throws); // lock to protect ClassLinker state mutable Mutex lock_; @@ -377,6 +377,7 @@ class ClassLinker { kJavaLangReflectConstructor, kJavaLangReflectField, kJavaLangReflectMethod, + kJavaLangReflectProxy, kJavaLangClassLoader, kDalvikSystemBaseDexClassLoader, kDalvikSystemPathClassLoader, diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc index 34e02f02c9..e604ca023e 100644 --- a/src/class_linker_test.cc +++ b/src/class_linker_test.cc @@ -864,8 +864,8 @@ TEST_F(ClassLinkerTest, StaticFields) { Field* s3 = statics->FindStaticField("s3", class_linker_->FindClass("S", class_loader)); EXPECT_TRUE(s3->GetType()->IsPrimitiveShort()); - EXPECT_EQ(65000, s3->GetShort(NULL)); - s3->SetShort(NULL, 65001); + EXPECT_EQ(-536, s3->GetShort(NULL)); + s3->SetShort(NULL, -535); Field* s4 = statics->FindStaticField("s4", class_linker_->FindClass("I", class_loader)); EXPECT_TRUE(s4->GetType()->IsPrimitiveInt()); @@ -895,7 +895,7 @@ TEST_F(ClassLinkerTest, StaticFields) { EXPECT_EQ(false, s0->GetBoolean(NULL)); EXPECT_EQ(6, s1->GetByte(NULL)); EXPECT_EQ('b', s2->GetChar(NULL)); - EXPECT_EQ(65001, s3->GetShort(NULL)); + EXPECT_EQ(-535, s3->GetShort(NULL)); EXPECT_EQ(2000000001, s4->GetInt(NULL)); EXPECT_EQ(0x34567890abcdef12LL, s5->GetLong(NULL)); EXPECT_EQ(0.75, s6->GetFloat(NULL)); diff --git a/src/java_lang_reflect_Proxy.cc b/src/java_lang_reflect_Proxy.cc index 821bdd80cf..55ecd08a68 100644 --- a/src/java_lang_reflect_Proxy.cc +++ b/src/java_lang_reflect_Proxy.cc @@ -29,7 +29,7 @@ static jclass Proxy_generateProxy(JNIEnv* env, jclass, jstring javaName, jobject ObjectArray<Class>* interfaces = Decode<ObjectArray<Class>*>(env, javaInterfaces); ClassLoader* loader = Decode<ClassLoader*>(env, javaLoader); ObjectArray<Method>* methods = Decode<ObjectArray<Method>*>(env, javaMethods); - ObjectArray<Object>* throws = Decode<ObjectArray<Object>*>(env, javaThrows); + ObjectArray<ObjectArray<Class> >* throws = Decode<ObjectArray<ObjectArray<Class> >*>(env, javaThrows); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Class* result = class_linker->CreateProxyClass(name, interfaces, loader, methods, throws); return AddLocalReference<jclass>(env, result); diff --git a/src/jni_internal.cc b/src/jni_internal.cc index 7e75dfffe9..7ce29d4913 100644 --- a/src/jni_internal.cc +++ b/src/jni_internal.cc @@ -93,6 +93,7 @@ template Class* Decode<Class*>(JNIEnv*, jobject); template ClassLoader* Decode<ClassLoader*>(JNIEnv*, jobject); template Object* Decode<Object*>(JNIEnv*, jobject); template ObjectArray<Class>* Decode<ObjectArray<Class>*>(JNIEnv*, jobject); +template ObjectArray<ObjectArray<Class> >* Decode<ObjectArray<ObjectArray<Class> >*>(JNIEnv*, jobject); template ObjectArray<Object>* Decode<ObjectArray<Object>*>(JNIEnv*, jobject); template ObjectArray<StackTraceElement>* Decode<ObjectArray<StackTraceElement>*>(JNIEnv*, jobject); template ObjectArray<Method>* Decode<ObjectArray<Method>*>(JNIEnv*, jobject); diff --git a/src/object.cc b/src/object.cc index aeaa19b24d..e8aeecdaff 100644 --- a/src/object.cc +++ b/src/object.cc @@ -201,12 +201,12 @@ void Field::SetChar(Object* object, uint16_t c) const { Set32(object, c); } -uint16_t Field::GetShort(const Object* object) const { +int16_t Field::GetShort(const Object* object) const { DCHECK(GetType()->IsPrimitiveShort()); return Get32(object); } -void Field::SetShort(Object* object, uint16_t s) const { +void Field::SetShort(Object* object, int16_t s) const { DCHECK(GetType()->IsPrimitiveShort()); Set32(object, s); } @@ -560,9 +560,28 @@ size_t Method::ReturnSize() const { return ShortyCharToSize(GetShorty()->CharAt(0)); } -bool Method::HasSameNameAndDescriptor(const Method* that) const { - return (this->GetName()->Equals(that->GetName()) && - this->GetSignature()->Equals(that->GetSignature())); +Method* Method::FindOverriddenMethod() const { + if (IsStatic()) { + return NULL; + } + Class* declaring_class = GetDeclaringClass(); + Class* super_class = declaring_class->GetSuperClass(); + uint16_t method_index = GetMethodIndex(); + ObjectArray<Method>* super_class_vtable = super_class->GetVTable(); + Method* result = NULL; + if (super_class_vtable != NULL && method_index < super_class_vtable->GetLength()) { + result = super_class_vtable->Get(method_index); + } else { + ObjectArray<Class>* interfaces = declaring_class->GetInterfaces(); + String* name = GetName(); + String* signature = GetSignature(); + for (int32_t i = 0; i < interfaces->GetLength() && result == NULL; i++) { + Class* interface = interfaces->Get(i); + result = interface->FindInterfaceMethod(name, signature); + } + } + DCHECK (result == NULL || HasSameNameAndSignature(result)); + return result; } uint32_t Method::ToDexPC(const uintptr_t pc) const { @@ -990,8 +1009,7 @@ Method* Class::FindVirtualMethodForInterface(Method* method) { return NULL; } -Method* Class::FindInterfaceMethod(const StringPiece& name, - const StringPiece& signature) { +Method* Class::FindInterfaceMethod(const StringPiece& name, const StringPiece& signature) const { // Check the current class before checking the interfaces. Method* method = FindVirtualMethod(name, signature); if (method != NULL) { @@ -1009,6 +1027,24 @@ Method* Class::FindInterfaceMethod(const StringPiece& name, return NULL; } +Method* Class::FindInterfaceMethod(String* name, String* signature) const { + // Check the current class before checking the interfaces. + Method* method = FindVirtualMethod(name, signature); + if (method != NULL) { + return method; + } + int32_t iftable_count = GetIfTableCount(); + ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + for (int32_t i = 0; i < iftable_count; i++) { + Class* interface = iftable->Get(i)->GetInterface(); + method = interface->FindVirtualMethod(name, signature); + if (method != NULL) { + return method; + } + } + return NULL; +} + Method* Class::FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature) { for (size_t i = 0; i < NumDirectMethods(); ++i) { @@ -1033,20 +1069,41 @@ Method* Class::FindDirectMethod(const StringPiece& name, } Method* Class::FindDeclaredVirtualMethod(const StringPiece& name, - const StringPiece& signature) { + const StringPiece& signature) const { for (size_t i = 0; i < NumVirtualMethods(); ++i) { Method* method = GetVirtualMethod(i); - if (method->GetName()->Equals(name) && - method->GetSignature()->Equals(signature)) { + if (method->GetName()->Equals(name) && method->GetSignature()->Equals(signature)) { return method; } } return NULL; } -Method* Class::FindVirtualMethod(const StringPiece& name, - const StringPiece& signature) { - for (Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { +Method* Class::FindDeclaredVirtualMethod(String* name, String* signature) const { + for (size_t i = 0; i < NumVirtualMethods(); ++i) { + Method* method = GetVirtualMethod(i); + if (method->GetName() == name && method->GetSignature() == signature) { + return method; + } else { + LOG(INFO) << "Find (" << name->ToModifiedUtf8() << ", " << signature->ToModifiedUtf8() + << ") != " << PrettyMethod(method); + } + } + return NULL; +} + +Method* Class::FindVirtualMethod(const StringPiece& name, const StringPiece& signature) const { + for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { + Method* method = klass->FindDeclaredVirtualMethod(name, signature); + if (method != NULL) { + return method; + } + } + return NULL; +} + +Method* Class::FindVirtualMethod(String* name, String* signature) const { + for (const Class* klass = this; klass != NULL; klass = klass->GetSuperClass()) { Method* method = klass->FindDeclaredVirtualMethod(name, signature); if (method != NULL) { return method; @@ -1175,6 +1232,9 @@ template class PrimitiveArray<int32_t>; // IntArray template class PrimitiveArray<int64_t>; // LongArray template class PrimitiveArray<int16_t>; // ShortArray +// Explicitly instantiate Class[][] +template class ObjectArray<ObjectArray<Class> >; + // TODO: get global references for these Class* String::java_lang_String_ = NULL; @@ -1350,6 +1410,15 @@ std::string String::ToModifiedUtf8() const { return result; } +bool Throwable::IsCheckedException() const { + Class* error = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/Error;"); + if (InstanceOf(error)) { + return false; + } + Class* jlre = Runtime::Current()->GetClassLinker()->FindSystemClass("Ljava/lang/RuntimeException;"); + return !InstanceOf(jlre); +} + Class* StackTraceElement::java_lang_StackTraceElement_ = NULL; void StackTraceElement::SetClass(Class* java_lang_StackTraceElement) { diff --git a/src/object.h b/src/object.h index a4e9c7a475..8f6f75d1d1 100644 --- a/src/object.h +++ b/src/object.h @@ -447,8 +447,8 @@ class MANAGED Field : public AccessibleObject { void SetByte(Object* object, int8_t b) const; uint16_t GetChar(const Object* object) const; void SetChar(Object* object, uint16_t c) const; - uint16_t GetShort(const Object* object) const; - void SetShort(Object* object, uint16_t s) const; + int16_t GetShort(const Object* object) const; + void SetShort(Object* object, int16_t s) const; int32_t GetInt(const Object* object) const; void SetInt(Object* object, int32_t i) const; int64_t GetLong(const Object* object) const; @@ -534,7 +534,7 @@ class MANAGED Method : public AccessibleObject { } // Returns the method name, e.g. "<init>" or "eatLunch" - const String* GetName() const; + String* GetName() const; void SetName(String* new_name); @@ -550,11 +550,13 @@ class MANAGED Method : public AccessibleObject { void SetShorty(String* new_shorty); - const String* GetSignature() const; + String* GetSignature() const; void SetSignature(String* new_signature); - bool HasSameNameAndDescriptor(const Method* that) const; + bool HasSameNameAndSignature(const Method* that) const { + return GetName() == that->GetName() && GetSignature() == that->GetSignature(); + } uint32_t GetAccessFlags() const; @@ -704,6 +706,9 @@ class MANAGED Method : public AccessibleObject { ObjectArray<StaticStorageBase>* GetDexCacheInitializedStaticStorage() const; void SetDexCacheInitializedStaticStorage(ObjectArray<StaticStorageBase>* new_value); + // Find the method that this method overrides + Method* FindOverriddenMethod() const; + void SetReturnTypeIdx(uint32_t new_return_type_idx); Class* GetReturnType() const; @@ -949,10 +954,13 @@ class MANAGED Method : public AccessibleObject { SetField32(OFFSET_OF_OBJECT_MEMBER(Method, fp_spill_mask_), fp_spill_mask, false); } - void SetExceptionTypes(Object* exception_types) { - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), exception_types, false); + ObjectArray<Class>* GetExceptionTypes() const { + return GetFieldObject<ObjectArray<Class>*>( + OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), false); } + void SetExceptionTypes(ObjectArray<Class>* exception_types); + ObjectArray<Class>* GetJavaParameterTypes() const { return GetFieldObject<ObjectArray<Class>*>( OFFSET_OF_OBJECT_MEMBER(Method, java_parameter_types_), false); @@ -1083,11 +1091,10 @@ class MANAGED Method : public AccessibleObject { const uint32_t* mapping_table_; - // For concrete virtual methods, this is the offset of the method - // in Class::vtable_. + // For concrete virtual methods, this is the offset of the method in Class::vtable_. // - // For abstract methods in an interface class, this is the offset - // of the method in "iftable_->Get(n)->GetMethodArray()". + // For abstract methods in an interface class, this is the offset of the method in + // "iftable_->Get(n)->GetMethodArray()". uint32_t method_index_; // The target native method registered with this method @@ -1722,8 +1729,8 @@ class MANAGED Class : public StaticStorageBase { // method for this class. Method* FindVirtualMethodForInterface(Method* method); - Method* FindInterfaceMethod(const StringPiece& name, - const StringPiece& descriptor); + Method* FindInterfaceMethod(const StringPiece& name, const StringPiece& descriptor) const; + Method* FindInterfaceMethod(String* name, String* descriptor) const; Method* FindVirtualMethodForVirtualOrInterface(Method* method) { if (method->IsDirect()) { @@ -1735,11 +1742,11 @@ class MANAGED Class : public StaticStorageBase { return FindVirtualMethodForVirtual(method); } - Method* FindDeclaredVirtualMethod(const StringPiece& name, - const StringPiece& signature); + Method* FindDeclaredVirtualMethod(const StringPiece& name, const StringPiece& signature) const; + Method* FindDeclaredVirtualMethod(String* name, String* signature) const; - Method* FindVirtualMethod(const StringPiece& name, - const StringPiece& descriptor); + Method* FindVirtualMethod(const StringPiece& name, const StringPiece& descriptor) const; + Method* FindVirtualMethod(String* name, String* descriptor) const; Method* FindDeclaredDirectMethod(const StringPiece& name, const StringPiece& signature); @@ -2529,11 +2536,9 @@ inline MemberOffset Field::GetOffsetDuringLinking() const { GetField32(OFFSET_OF_OBJECT_MEMBER(Field, offset_), false)); } -inline const String* Method::GetName() const { +inline String* Method::GetName() const { DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous()); - const String* result = - GetFieldObject<const String*>( - OFFSET_OF_OBJECT_MEMBER(Method, name_), false); + String* result = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Method, name_), false); DCHECK(result != NULL); return result; } @@ -2577,10 +2582,9 @@ inline void Method::SetShorty(String* new_shorty) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, shorty_), new_shorty, false); } -inline const String* Method::GetSignature() const { +inline String* Method::GetSignature() const { DCHECK(GetDeclaringClass()->IsLoaded() || GetDeclaringClass()->IsErroneous()); - const String* result = - GetFieldObject<const String*>(OFFSET_OF_OBJECT_MEMBER(Method, signature_), false); + String* result = GetFieldObject<String*>(OFFSET_OF_OBJECT_MEMBER(Method, signature_), false); DCHECK(result != NULL); return result; } @@ -2590,6 +2594,10 @@ inline void Method::SetSignature(String* new_signature) { new_signature, false); } +inline void Method::SetExceptionTypes(ObjectArray<Class>* exception_types) { + SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Method, java_exception_types_), exception_types, false); +} + inline uint32_t Class::GetAccessFlags() const { // Check class is loaded or this is java.lang.String that has a // circularity issue during loading the names of its members @@ -2656,6 +2664,7 @@ class MANAGED Throwable : public Object { new_detail_message, false); } + bool IsCheckedException() const; private: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". Throwable* cause_; diff --git a/src/object_test.cc b/src/object_test.cc index a180d5e2be..e5af663851 100644 --- a/src/object_test.cc +++ b/src/object_test.cc @@ -278,17 +278,17 @@ TEST_F(ObjectTest, DescriptorCompare) { Method* m4_2 = klass2->GetVirtualMethod(3); EXPECT_TRUE(m4_2->GetName()->Equals("m4")); - EXPECT_TRUE(m1_1->HasSameNameAndDescriptor(m1_2)); - EXPECT_TRUE(m1_2->HasSameNameAndDescriptor(m1_1)); + EXPECT_TRUE(m1_1->HasSameNameAndSignature(m1_2)); + EXPECT_TRUE(m1_2->HasSameNameAndSignature(m1_1)); - EXPECT_TRUE(m2_1->HasSameNameAndDescriptor(m2_2)); - EXPECT_TRUE(m2_2->HasSameNameAndDescriptor(m2_1)); + EXPECT_TRUE(m2_1->HasSameNameAndSignature(m2_2)); + EXPECT_TRUE(m2_2->HasSameNameAndSignature(m2_1)); - EXPECT_TRUE(m3_1->HasSameNameAndDescriptor(m3_2)); - EXPECT_TRUE(m3_2->HasSameNameAndDescriptor(m3_1)); + EXPECT_TRUE(m3_1->HasSameNameAndSignature(m3_2)); + EXPECT_TRUE(m3_2->HasSameNameAndSignature(m3_1)); - EXPECT_TRUE(m4_1->HasSameNameAndDescriptor(m4_2)); - EXPECT_TRUE(m4_2->HasSameNameAndDescriptor(m4_1)); + EXPECT_TRUE(m4_1->HasSameNameAndSignature(m4_2)); + EXPECT_TRUE(m4_2->HasSameNameAndSignature(m4_1)); } diff --git a/src/reflection.cc b/src/reflection.cc index 1294c0845f..86b4b095f8 100644 --- a/src/reflection.cc +++ b/src/reflection.cc @@ -70,6 +70,7 @@ jobject InvokeMethod(JNIEnv* env, jobject javaMethod, jobject javaReceiver, jobj // Find the actual implementation of the virtual method. m = receiver->GetClass()->FindVirtualMethodForVirtualOrInterface(m); + mid = reinterpret_cast<jmethodID>(m); } // Get our arrays of arguments and their types, and check they're the same size. @@ -310,13 +311,13 @@ bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_va Field* primitive_field = o->GetClass()->GetIFields()->Get(0); if (src_descriptor->Equals("Ljava/lang/Boolean;")) { src_class = class_linker->FindPrimitiveClass('Z'); - boxed_value.z = primitive_field->GetBoolean(o); + boxed_value.i = primitive_field->GetBoolean(o); // and extend read value to 32bits } else if (src_descriptor->Equals("Ljava/lang/Byte;")) { src_class = class_linker->FindPrimitiveClass('B'); - boxed_value.b = primitive_field->GetByte(o); + boxed_value.i = primitive_field->GetByte(o); // and extend read value to 32bits } else if (src_descriptor->Equals("Ljava/lang/Character;")) { src_class = class_linker->FindPrimitiveClass('C'); - boxed_value.c = primitive_field->GetChar(o); + boxed_value.i = primitive_field->GetChar(o); // and extend read value to 32bits } else if (src_descriptor->Equals("Ljava/lang/Float;")) { src_class = class_linker->FindPrimitiveClass('F'); boxed_value.f = primitive_field->GetFloat(o); @@ -331,7 +332,7 @@ bool UnboxPrimitive(JNIEnv* env, Object* o, Class* dst_class, JValue& unboxed_va boxed_value.j = primitive_field->GetLong(o); } else if (src_descriptor->Equals("Ljava/lang/Short;")) { src_class = class_linker->FindPrimitiveClass('S'); - boxed_value.s = primitive_field->GetShort(o); + boxed_value.i = primitive_field->GetShort(o); // and extend read value to 32bits } else { Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalArgumentException;", "%s is not a boxed primitive type", PrettyDescriptor(src_descriptor).c_str()); diff --git a/src/runtime_support.cc b/src/runtime_support.cc index ba3e1b4b1e..ccb8dccf98 100644 --- a/src/runtime_support.cc +++ b/src/runtime_support.cc @@ -901,14 +901,36 @@ extern "C" uint64_t artFindInterfaceMethodInCacheFromCode(uint32_t method_idx, return result; } +static void ThrowNewUndeclaredThrowableException(Thread* self, JNIEnv* env, Throwable* exception) { + ScopedLocalRef<jclass> jlr_UTE_class(env, + env->FindClass("java/lang/reflect/UndeclaredThrowableException")); + if (jlr_UTE_class.get() == NULL) { + LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\""; + } else { + jmethodID jlre_UTE_constructor = env->GetMethodID(jlr_UTE_class.get(), "<init>", + "(Ljava/lang/Throwable;)V"); + jthrowable jexception = AddLocalReference<jthrowable>(env, exception); + ScopedLocalRef<jthrowable> jlr_UTE(env, + reinterpret_cast<jthrowable>(env->NewObject(jlr_UTE_class.get(), jlre_UTE_constructor, + jexception))); + int rc = env->Throw(jlr_UTE.get()); + if (rc != JNI_OK) { + LOG(ERROR) << "Couldn't throw new \"java/lang/reflect/UndeclaredThrowableException\""; + } + } + CHECK(self->IsExceptionPending()); +} + // Handler for invocation on proxy methods. On entry a frame will exist for the proxy object method // which is responsible for recording callee save registers. We explicitly handlerize incoming // reference arguments (so they survive GC) and create a boxed argument array. Finally we invoke // the invocation handler which is a field within the proxy object receiver. extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, - byte* stack_args, Thread* self) { + Thread* self, byte* stack_args) { // Register the top of the managed stack - self->SetTopOfStack(reinterpret_cast<Method**>(stack_args + 8), 0); + Method** proxy_sp = reinterpret_cast<Method**>(stack_args - 12); + DCHECK_EQ(*proxy_sp, proxy_method); + self->SetTopOfStack(proxy_sp, 0); // TODO: ARM specific DCHECK_EQ(proxy_method->GetFrameSizeInBytes(), 48u); // Start new JNI local reference state @@ -941,7 +963,7 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, param_index++; } // Placing into local references incoming arguments from the caller's stack arguments - cur_arg += 5; // skip LR, Method* and spills for R1 to R3 + cur_arg += 11; // skip callee saves, LR, Method* and out arg spills for R1 to R3 while (param_index < num_params) { if (proxy_method->IsParamAReference(param_index)) { Object* obj = *reinterpret_cast<Object**>(stack_args + (cur_arg * kPointerSize)); @@ -951,23 +973,31 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, cur_arg = cur_arg + (proxy_method->IsParamALongOrDouble(param_index) ? 2 : 1); param_index++; } - // Create args array - ObjectArray<Object>* args = - Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1); - if(args == NULL) { - CHECK(self->IsExceptionPending()); - return; - } // Set up arguments array and place in local IRT during boxing (which may allocate/GC) jvalue args_jobj[3]; args_jobj[0].l = rcvr_jobj; args_jobj[1].l = proxy_method_jobj; - args_jobj[2].l = AddLocalReference<jobjectArray>(env, args); + // Args array, if no arguments then NULL (don't include receiver in argument count) + args_jobj[2].l = NULL; + ObjectArray<Object>* args = NULL; + if ((num_params - 1) > 0) { + args = Runtime::Current()->GetClassLinker()->AllocObjectArray<Object>(num_params - 1); + if(args == NULL) { + CHECK(self->IsExceptionPending()); + return; + } + args_jobj[2].l = AddLocalReference<jobjectArray>(env, args); + } + // Convert proxy method into expected interface method + Method* interface_method = proxy_method->FindOverriddenMethod(); + CHECK(interface_method != NULL); + args_jobj[1].l = AddLocalReference<jobject>(env, interface_method); + LOG(INFO) << "Interface method is " << PrettyMethod(interface_method, true); // Box arguments cur_arg = 0; // reset stack location to read to start // reset index, will index into param type array which doesn't include the receiver param_index = 0; - ObjectArray<Class>* param_types = proxy_method->GetJavaParameterTypes(); + ObjectArray<Class>* param_types = interface_method->GetJavaParameterTypes(); CHECK(param_types != NULL); // Check number of parameter types agrees with number from the Method - less 1 for the receiver. CHECK_EQ(static_cast<size_t>(param_types->GetLength()), num_params - 1); @@ -980,8 +1010,7 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, JValue val = *reinterpret_cast<JValue*>(stack_args + (cur_arg * kPointerSize)); if (cur_arg == 1 && (param_type->IsPrimitiveLong() || param_type->IsPrimitiveDouble())) { // long/double split over regs and stack, mask in high half from stack arguments - // (7 = 2 reg args + LR + Method* + 3 arg reg spill slots) - uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args + (7 * kPointerSize)); + uint64_t high_half = *reinterpret_cast<uint32_t*>(stack_args + (13 * kPointerSize)); val.j = (val.j & 0xffffffffULL) | (high_half << 32); } BoxPrimitive(env, param_type, val); @@ -995,8 +1024,8 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, param_index++; } // Placing into local references incoming arguments from the caller's stack arguments - cur_arg += 5; // skip LR, Method* and spills for R1 to R3 - while (param_index < num_params) { + cur_arg += 11; // skip callee saves, LR, Method* and out arg spills for R1 to R3 + while (param_index < (num_params - 1)) { Class* param_type = param_types->Get(param_index); Object* obj; if (!param_type->IsPrimitive()) { @@ -1017,13 +1046,13 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, static jmethodID inv_hand_invoke_mid = NULL; static jfieldID proxy_inv_hand_fid = NULL; if (proxy_inv_hand_fid == NULL) { - ScopedLocalRef<jclass> proxy(env, env->FindClass("java.lang.reflect.Proxy")); + ScopedLocalRef<jclass> proxy(env, env->FindClass("java/lang/reflect/Proxy")); proxy_inv_hand_fid = env->GetFieldID(proxy.get(), "h", "Ljava/lang/reflect/InvocationHandler;"); - ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java.lang.reflect.InvocationHandler")); + ScopedLocalRef<jclass> inv_hand_class(env, env->FindClass("java/lang/reflect/InvocationHandler")); inv_hand_invoke_mid = env->GetMethodID(inv_hand_class.get(), "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); } - DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java.lang.reflect.Proxy"))); + DCHECK(env->IsInstanceOf(rcvr_jobj, env->FindClass("java/lang/reflect/Proxy"))); jobject inv_hand = env->GetObjectField(rcvr_jobj, proxy_inv_hand_fid); // Call InvocationHandler.invoke jobject result = env->CallObjectMethodA(inv_hand, inv_hand_invoke_mid, args_jobj); @@ -1032,11 +1061,32 @@ extern "C" void artProxyInvokeHandler(Method* proxy_method, Object* receiver, Object* result_ref = self->DecodeJObject(result); if (result_ref != NULL) { JValue result_unboxed; - UnboxPrimitive(env, result_ref, proxy_method->GetReturnType(), result_unboxed); + UnboxPrimitive(env, result_ref, interface_method->GetReturnType(), result_unboxed); *reinterpret_cast<JValue*>(stack_args) = result_unboxed; } else { *reinterpret_cast<jobject*>(stack_args) = NULL; } + } else { + // In the case of checked exceptions that aren't declared, the exception must be wrapped by + // a UndeclaredThrowableException. + Throwable* exception = self->GetException(); + self->ClearException(); + if (!exception->IsCheckedException()) { + self->SetException(exception); + } else { + ObjectArray<Class>* declared_exceptions = proxy_method->GetExceptionTypes(); + Class* exception_class = exception->GetClass(); + bool declares_exception = false; + for (int i = 0; i < declared_exceptions->GetLength() && !declares_exception; i++) { + Class* declared_exception = declared_exceptions->Get(i); + declares_exception = declared_exception->IsAssignableFrom(exception_class); + } + if (declares_exception) { + self->SetException(exception); + } else { + ThrowNewUndeclaredThrowableException(self, env, exception); + } + } } } diff --git a/src/runtime_support.h b/src/runtime_support.h index de41f47763..ba0b0f1d80 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -32,6 +32,8 @@ extern int64_t F2L(float f); /* Helper for both JNI and regular compiled code */ extern "C" void art_deliver_exception_from_code(void*); +extern "C" void art_proxy_invoke_handler(); + #if defined(__arm__) /* Compiler helpers */ extern "C" void* art_alloc_object_from_code(uint32_t type_idx, void* method); diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S index 2076fad8a9..8ef3da8109 100644 --- a/src/runtime_support_asm.S +++ b/src/runtime_support_asm.S @@ -485,8 +485,9 @@ art_proxy_invoke_handler: add r3, sp, #12 @ pointer to r2/r3/LR/caller's Method**/out-args as second arg blx artProxyInvokeHandler @ (Method* proxy method, receiver, Thread*, args...) ldr r12, [r9, #THREAD_EXCEPTION_OFFSET] @ load Thread::Current()->exception_ - ldrd r0, [sp, #12] @ load r0/r1 from r2/r3 that were overwritten with the out args - RESTORE_REF_AND_ARGS_CALLEE_SAVE_FRAME + ldr lr, [sp, #44] @ restore lr + ldrd r0, [sp, #12] @ load r0/r1 from r2/r3 that were overwritten with the out args + add sp, #48 @ pop frame cmp r12, #0 @ success if no exception is pending bxeq lr @ return on success DELIVER_PENDING_EXCEPTION @@ -588,4 +589,9 @@ art_deliver_exception_from_code: call artDeliverExceptionFromCode // artDeliverExceptionFromCode(Throwable*, Thread*, SP) int3 + // TODO + .global art_proxy_invoke_handler +art_proxy_invoke_handler: + int3 + #endif diff --git a/src/thread.cc b/src/thread.cc index 62b3290e04..e143af5869 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -1091,15 +1091,17 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray(JNIEnv* env, job Method* method = down_cast<Method*>(method_trace->Get(i)); uint32_t native_pc = pc_trace->Get(i); Class* klass = method->GetDeclaringClass(); - const DexFile& dex_file = class_linker->FindDexFile(klass->GetDexCache()); std::string class_name(PrettyDescriptor(klass->GetDescriptor())); - + int32_t line_number = -1; + DexCache* dex_cache = klass->GetDexCache(); + if (dex_cache != NULL) { + const DexFile& dex_file = class_linker->FindDexFile(dex_cache); + line_number = dex_file.GetLineNumFromPC(method, method->ToDexPC(native_pc)); + } // Allocate element, potentially triggering GC StackTraceElement* obj = StackTraceElement::Alloc(String::AllocFromModifiedUtf8(class_name.c_str()), - method->GetName(), - klass->GetSourceFile(), - dex_file.GetLineNumFromPC(method, method->ToDexPC(native_pc))); + method->GetName(), klass->GetSourceFile(), line_number); if (obj == NULL) { return NULL; } diff --git a/test/044-proxy/expected.txt b/test/044-proxy/expected.txt index 4be26cf714..27771ce7c5 100644 --- a/test/044-proxy/expected.txt +++ b/test/044-proxy/expected.txt @@ -1,3 +1,7 @@ +ReturnsAndArgPassing.testProxyReturns RUNNING +ReturnsAndArgPassing.testProxyReturns PASSED +ReturnsAndArgPassing.testProxyArgPassing RUNNING +ReturnsAndArgPassing.testProxyArgPassing PASSED Invoke public abstract void Shapes.circle(int) 0: 3 --- circle 3 @@ -45,10 +49,10 @@ Invoke public abstract void Shapes.upCheck() throws java.lang.InterruptedExcepti (no args) Got expected ie -Proxy methods: [public native boolean $Proxy0.equals(java.lang.Object), public native int $Proxy0.hashCode(), public native java.lang.String $Proxy0.toString(), public native int $Proxy0.rectangle(int,int), public native int $Proxy0.square(int,int), public native int $Proxy0.trapezoid(int,double,int), public native java.lang.String $Proxy0.blob(), public native void $Proxy0.circle(int), public native void $Proxy0.upCheck(), public native void $Proxy0.upChuck(), public native double $Proxy0.blue(int), public native R0aa $Proxy0.checkMe(), public native int $Proxy0.green(double), public native int $Proxy0.mauve(java.lang.String), public native int $Proxy0.red(float)] +Proxy methods: [public final java.lang.String $Proxy1.blob(), public final double $Proxy1.blue(int), public final R0a $Proxy1.checkMe(), public final R0base $Proxy1.checkMe(), public final R0aa $Proxy1.checkMe(), public final void $Proxy1.circle(int), public final boolean $Proxy1.equals(java.lang.Object), public final int $Proxy1.green(double), public final int $Proxy1.hashCode(), public final int $Proxy1.mauve(java.lang.String), public final int $Proxy1.rectangle(int,int), public final int $Proxy1.red(float), public final int $Proxy1.square(int,int), public final java.lang.String $Proxy1.toString(), public final int $Proxy1.trapezoid(int,double,int), public final void $Proxy1.upCheck() throws java.lang.InterruptedException, public final void $Proxy1.upChuck()] Decl annos: [] -Param annos (1) : [[]] -Proxy fields: [private static java.lang.Throwable[][] $Proxy0.throws] +Param annos (0) : [] +Proxy fields: [private static java.lang.reflect.Method $Proxy1.m0, private static java.lang.reflect.Method $Proxy1.m1, private static java.lang.reflect.Method $Proxy1.m10, private static java.lang.reflect.Method $Proxy1.m11, private static java.lang.reflect.Method $Proxy1.m12, private static java.lang.reflect.Method $Proxy1.m13, private static java.lang.reflect.Method $Proxy1.m14, private static java.lang.reflect.Method $Proxy1.m15, private static java.lang.reflect.Method $Proxy1.m16, private static java.lang.reflect.Method $Proxy1.m2, private static java.lang.reflect.Method $Proxy1.m3, private static java.lang.reflect.Method $Proxy1.m4, private static java.lang.reflect.Method $Proxy1.m5, private static java.lang.reflect.Method $Proxy1.m6, private static java.lang.reflect.Method $Proxy1.m7, private static java.lang.reflect.Method $Proxy1.m8, private static java.lang.reflect.Method $Proxy1.m9] Dupe threw expected exception Clash threw expected exception Clash2 threw expected exception diff --git a/test/044-proxy/src/BasicTest.java b/test/044-proxy/src/BasicTest.java index 2a453c47f7..fa1896fd9a 100644 --- a/test/044-proxy/src/BasicTest.java +++ b/test/044-proxy/src/BasicTest.java @@ -22,6 +22,7 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Comparator; /** * Do some basic tests. @@ -70,6 +71,11 @@ public class BasicTest { */ System.out.println(""); Method[] methods = proxy.getClass().getDeclaredMethods(); + Arrays.sort(methods, new Comparator<Method>() { + public int compare(Method o1, Method o2) { + return o1.getName().compareTo(o2.getName()); + } + }); System.out.println("Proxy methods: " + Arrays.deepToString(methods)); Method meth = methods[methods.length -1]; System.out.println("Decl annos: " + Arrays.deepToString(meth.getDeclaredAnnotations())); @@ -77,6 +83,11 @@ public class BasicTest { System.out.println("Param annos (" + paramAnnos.length + ") : " + Arrays.deepToString(paramAnnos)); Field[] fields = proxy.getClass().getDeclaredFields(); + Arrays.sort(fields, new Comparator<Field>() { + public int compare(Field o1, Field o2) { + return o1.getName().compareTo(o2.getName()); + } + }); System.out.println("Proxy fields: " + Arrays.deepToString(fields)); } diff --git a/test/044-proxy/src/Main.java b/test/044-proxy/src/Main.java index 01926af2c2..5396ab8842 100644 --- a/test/044-proxy/src/Main.java +++ b/test/044-proxy/src/Main.java @@ -19,6 +19,7 @@ */ public class Main { public static void main(String[] args) { + ReturnsAndArgPassing.main(null); BasicTest.main(null); Clash.main(null); Clash2.main(null); diff --git a/test/044-proxy/src/ReturnsAndArgPassing.java b/test/044-proxy/src/ReturnsAndArgPassing.java new file mode 100644 index 0000000000..6a706cbf6c --- /dev/null +++ b/test/044-proxy/src/ReturnsAndArgPassing.java @@ -0,0 +1,206 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * 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. + */ +import java.lang.reflect.*; + +/** + * @author irogers@google.com (Ian Rogers) + */ +public class ReturnsAndArgPassing { + + public static final String testName = "ReturnsAndArgPassing"; + + static void check(boolean x) { + if (!x) { + throw new AssertionError(testName + " Check failed"); + } + } + + interface MyInterface { + void voidFoo(); + void voidBar(); + boolean booleanFoo(); + boolean booleanBar(); + byte byteFoo(); + byte byteBar(); + char charFoo(); + char charBar(); + short shortFoo(); + short shortBar(); + int intFoo(); + int intBar(); + long longFoo(); + long longBar(); + float floatFoo(); + float floatBar(); + double doubleFoo(); + double doubleBar(); + Object selectArg(int select, int a, long b, float c, double d, Object x); + } + + static int fooInvocations = 0; + static int barInvocations = 0; + + static class MyInvocationHandler implements InvocationHandler { + public Object invoke(Object proxy, Method method, Object[] args) { + check(proxy instanceof Proxy); + check(method.getDeclaringClass() == MyInterface.class); + String name = method.getName(); + if (name.endsWith("Foo")) { + check(args == null); + fooInvocations++; + } else if (name.endsWith("Bar")) { + check(args == null); + barInvocations++; + } + if (name.equals("voidFoo")) { return null; } + else if (name.equals("voidBar")) { return null; } + else if (name.equals("booleanFoo")) { return true; } + else if (name.equals("booleanBar")) { return false; } + else if (name.equals("byteFoo")) { return Byte.MAX_VALUE; } + else if (name.equals("byteBar")) { return Byte.MIN_VALUE; } + else if (name.equals("charFoo")) { return Character.MAX_VALUE; } + else if (name.equals("charBar")) { return Character.MIN_VALUE; } + else if (name.equals("shortFoo")) { return Short.MAX_VALUE; } + else if (name.equals("shortBar")) { return Short.MIN_VALUE; } + else if (name.equals("intFoo")) { return Integer.MAX_VALUE; } + else if (name.equals("intBar")) { return Integer.MIN_VALUE; } + else if (name.equals("longFoo")) { return Long.MAX_VALUE; } + else if (name.equals("longBar")) { return Long.MIN_VALUE; } + else if (name.equals("floatFoo")) { return Float.MAX_VALUE; } + else if (name.equals("floatBar")) { return Float.MIN_VALUE; } + else if (name.equals("doubleFoo")) { return Double.MAX_VALUE; } + else if (name.equals("doubleBar")) { return Double.MIN_VALUE; } + else if (name.equals("selectArg")) { + check(args.length == 6); + int select = (Integer)args[0]; + return args[select]; + } else { + throw new AssertionError("Unexpect method " + method); + } + } + } + + static void testProxyReturns() { + System.out.println(testName + ".testProxyReturns RUNNING"); + MyInvocationHandler myHandler = new MyInvocationHandler(); + MyInterface proxyMyInterface = + (MyInterface)Proxy.newProxyInstance(ReturnsAndArgPassing.class.getClassLoader(), + new Class[] { MyInterface.class }, + myHandler); + check(fooInvocations == 0); + proxyMyInterface.voidFoo(); + check(fooInvocations == 1); + + check(barInvocations == 0); + proxyMyInterface.voidBar(); + check(barInvocations == 1); + + check(fooInvocations == 1); + check(proxyMyInterface.booleanFoo() == true); + check(fooInvocations == 2); + + check(barInvocations == 1); + check(proxyMyInterface.booleanBar() == false); + check(barInvocations == 2); + + check(fooInvocations == 2); + check(proxyMyInterface.byteFoo() == Byte.MAX_VALUE); + check(fooInvocations == 3); + + check(barInvocations == 2); + check(proxyMyInterface.byteBar() == Byte.MIN_VALUE); + check(barInvocations == 3); + + check(fooInvocations == 3); + check(proxyMyInterface.charFoo() == Character.MAX_VALUE); + check(fooInvocations == 4); + + check(barInvocations == 3); + check(proxyMyInterface.charBar() == Character.MIN_VALUE); + check(barInvocations == 4); + + check(fooInvocations == 4); + check(proxyMyInterface.shortFoo() == Short.MAX_VALUE); + check(fooInvocations == 5); + + check(barInvocations == 4); + check(proxyMyInterface.shortBar() == Short.MIN_VALUE); + check(barInvocations == 5); + + check(fooInvocations == 5); + check(proxyMyInterface.intFoo() == Integer.MAX_VALUE); + check(fooInvocations == 6); + + check(barInvocations == 5); + check(proxyMyInterface.intBar() == Integer.MIN_VALUE); + check(barInvocations == 6); + + check(fooInvocations == 6); + check(proxyMyInterface.longFoo() == Long.MAX_VALUE); + check(fooInvocations == 7); + + check(barInvocations == 6); + check(proxyMyInterface.longBar() == Long.MIN_VALUE); + check(barInvocations == 7); + + check(fooInvocations == 7); + check(proxyMyInterface.floatFoo() == Float.MAX_VALUE); + check(fooInvocations == 8); + + check(barInvocations == 7); + check(proxyMyInterface.floatBar() == Float.MIN_VALUE); + check(barInvocations == 8); + + check(fooInvocations == 8); + check(proxyMyInterface.doubleFoo() == Double.MAX_VALUE); + check(fooInvocations == 9); + + check(barInvocations == 8); + check(proxyMyInterface.doubleBar() == Double.MIN_VALUE); + check(barInvocations == 9); + + System.out.println(testName + ".testProxyReturns PASSED"); + } + + static void testProxyArgPassing() { + System.out.println(testName + ".testProxyArgPassing RUNNING"); + MyInvocationHandler myHandler = new MyInvocationHandler(); + MyInterface proxyMyInterface = + (MyInterface)Proxy.newProxyInstance(ReturnsAndArgPassing.class.getClassLoader(), + new Class[] { MyInterface.class }, + myHandler); + + check((Integer)proxyMyInterface.selectArg(0, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == 0); + check((Integer)proxyMyInterface.selectArg(1, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == Integer.MAX_VALUE); + check((Long)proxyMyInterface.selectArg(2, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == Long.MAX_VALUE); + check((Float)proxyMyInterface.selectArg(3, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == Float.MAX_VALUE); + check((Double)proxyMyInterface.selectArg(4, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == Double.MAX_VALUE); + check(proxyMyInterface.selectArg(5, Integer.MAX_VALUE, Long.MAX_VALUE, + Float.MAX_VALUE, Double.MAX_VALUE, Object.class) == Object.class); + + System.out.println(testName + ".testProxyArgPassing PASSED"); + } + + public static void main(String args[]) { + testProxyReturns(); + testProxyArgPassing(); + } +} |