Merge "Another bug fix for line numbers. Line uses signed in advancing." into dalvik-dev
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 9fd8f0b..53bcf7e 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 @@
   "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 @@
   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 @@
 }
 
 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 @@
 }
 
 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_);
+  // 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));
 
-  // 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);
+  // 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 @@
       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 @@
       // 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 @@
       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 37802ae..91d6259 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -227,7 +227,7 @@
   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 @@
   }
 
   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 @@
     kJavaLangReflectConstructor,
     kJavaLangReflectField,
     kJavaLangReflectMethod,
+    kJavaLangReflectProxy,
     kJavaLangClassLoader,
     kDalvikSystemBaseDexClassLoader,
     kDalvikSystemPathClassLoader,
diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc
index 34e02f0..e604ca0 100644
--- a/src/class_linker_test.cc
+++ b/src/class_linker_test.cc
@@ -864,8 +864,8 @@
 
   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 @@
   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 821bdd8..55ecd08 100644
--- a/src/java_lang_reflect_Proxy.cc
+++ b/src/java_lang_reflect_Proxy.cc
@@ -29,7 +29,7 @@
   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 7e75dff..7ce29d4 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -93,6 +93,7 @@
 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 aeaa19b..e8aeecd 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -201,12 +201,12 @@
   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 @@
   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 @@
   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 @@
   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::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<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 @@
   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 a4e9c7a..8f6f75d 100644
--- a/src/object.h
+++ b/src/object.h
@@ -447,8 +447,8 @@
   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 @@
   }
 
   // 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 @@
 
   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 @@
   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 @@
     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 @@
 
   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 @@
   // 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 @@
     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 @@
       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 @@
   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 @@
                  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 @@
                    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 a180d5e..e5af663 100644
--- a/src/object_test.cc
+++ b/src/object_test.cc
@@ -278,17 +278,17 @@
   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 1294c08..86b4b09 100644
--- a/src/reflection.cc
+++ b/src/reflection.cc
@@ -70,6 +70,7 @@
 
     // 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 @@
   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 @@
     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 ba3e1b4..ccb8dcc 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -901,14 +901,36 @@
   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 @@
     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 @@
     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 @@
       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 @@
     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 @@
   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 @@
     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 de41f47..ba0b0f1 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -32,6 +32,8 @@
 /* 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 2076fad..8ef3da8 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -485,8 +485,9 @@
     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 @@
     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 62b3290..e143af5 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1091,15 +1091,17 @@
     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 4be26cf..27771ce 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 @@
  (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 2a453c4..fa1896f 100644
--- a/test/044-proxy/src/BasicTest.java
+++ b/test/044-proxy/src/BasicTest.java
@@ -22,6 +22,7 @@
 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 @@
          */
         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 @@
         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 01926af..5396ab8 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 0000000..6a706cb
--- /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();
+  }
+}