diff options
-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(); + } +} |