Proxy implementation

This rounds out the proxy implementation by adding missing pieces to the
class linker, extending tests and fixing issues in the runtime support.
There are also some tweaks for performance and to clean up Method/Object
a little.
A unit test of the functionality is "art/test/run-test 044"

Change-Id: Id94102d10b81cd9b12b95ba8618f6187490204c4
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();
+  }
+}