Stash the directly-implemented interfaces in Proxy so we can implement Class.getInterfaces.

Also extend test 044 to spot this (found by a libcore test).

Change-Id: I50019db49c549f045d94173c3ed9ae81f05ef858
diff --git a/src/class_linker.cc b/src/class_linker.cc
index c65cf7d..38ce30a 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -2166,13 +2166,21 @@
 
   klass->SetDexTypeIndex(DexFile::kDexNoIndex16);
 
-  // Create static field that holds throws, instance fields are inherited
-  klass->SetSFields(AllocObjectArray<Field>(1));
-  SirtRef<Field> sfield(AllocField());
-  klass->SetStaticField(0, sfield.get());
-  sfield->SetDexFieldIndex(-1);
-  sfield->SetDeclaringClass(klass.get());
-  sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+  // Instance fields are inherited, but we add a couple of static fields...
+  klass->SetSFields(AllocObjectArray<Field>(2));
+  // 1. Create a static field 'interfaces' that holds the _declared_ interfaces implemented by
+  // our proxy, so Class.getInterfaces doesn't return the flattened set.
+  SirtRef<Field> interfaces_sfield(AllocField());
+  klass->SetStaticField(0, interfaces_sfield.get());
+  interfaces_sfield->SetDexFieldIndex(0);
+  interfaces_sfield->SetDeclaringClass(klass.get());
+  interfaces_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
+  // 2. Create a static field 'throws' that holds exceptions thrown by our methods.
+  SirtRef<Field> throws_sfield(AllocField());
+  klass->SetStaticField(1, throws_sfield.get());
+  throws_sfield->SetDexFieldIndex(1);
+  throws_sfield->SetDeclaringClass(klass.get());
+  throws_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
 
   // Proxies have 1 direct method, the constructor
   klass->SetDirectMethods(AllocObjectArray<Method>(1));
@@ -2195,7 +2203,8 @@
     klass->SetStatus(Class::kStatusError);
     return NULL;
   }
-  sfield->SetObject(NULL, throws);    // initialize throws field
+  interfaces_sfield->SetObject(NULL, interfaces);
+  throws_sfield->SetObject(NULL, throws);
   klass->SetStatus(Class::kStatusInitialized);
 
   // sanity checks
@@ -2211,12 +2220,17 @@
       SirtRef<Method> prototype(methods->Get(i));
       CheckProxyMethod(klass->GetVirtualMethod(i), prototype);
     }
-    std::string throws_field_name("java.lang.Class[][] ");
-    throws_field_name += name->ToModifiedUtf8();
-    throws_field_name += ".throws";
-    CHECK(PrettyField(klass->GetStaticField(0)) == throws_field_name);
+
+    std::string interfaces_field_name(StringPrintf("java.lang.Class[] %s.interfaces",
+                                                   name->ToModifiedUtf8().c_str()));
+    CHECK_EQ(PrettyField(klass->GetStaticField(0)), interfaces_field_name);
+
+    std::string throws_field_name(StringPrintf("java.lang.Class[][] %s.throws",
+                                               name->ToModifiedUtf8().c_str()));
+    CHECK_EQ(PrettyField(klass->GetStaticField(1)), throws_field_name);
 
     SynthesizedProxyClass* synth_proxy_class = down_cast<SynthesizedProxyClass*>(klass.get());
+    CHECK_EQ(synth_proxy_class->GetInterfaces(), interfaces);
     CHECK_EQ(synth_proxy_class->GetThrows(), throws);
   }
   return klass.get();
diff --git a/src/java_lang_Class.cc b/src/java_lang_Class.cc
index 3dce559..567a067 100644
--- a/src/java_lang_Class.cc
+++ b/src/java_lang_Class.cc
@@ -288,6 +288,12 @@
   return AddLocalReference<jstring>(env, c->ComputeName());
 }
 
+static jobjectArray Class_getProxyInterfaces(JNIEnv* env, jobject javaThis) {
+  ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
+  SynthesizedProxyClass* c = down_cast<SynthesizedProxyClass*>(Decode<Class*>(env, javaThis));
+  return AddLocalReference<jobjectArray>(env, c->GetInterfaces()->Clone());
+}
+
 static jboolean Class_isAssignableFrom(JNIEnv* env, jobject javaLhs, jclass javaRhs) {
   ScopedThreadStateChange tsc(Thread::Current(), Thread::kRunnable);
   Class* lhs = Decode<Class*>(env, javaLhs);
@@ -410,6 +416,7 @@
   NATIVE_METHOD(Class, getDeclaredMethods, "(Z)[Ljava/lang/reflect/Method;"),
   NATIVE_METHOD(Class, getDex, "()Lcom/android/dex/Dex;"),
   NATIVE_METHOD(Class, getNameNative, "()Ljava/lang/String;"),
+  NATIVE_METHOD(Class, getProxyInterfaces, "()[Ljava/lang/Class;"),
   NATIVE_METHOD(Class, isAssignableFrom, "(Ljava/lang/Class;)Z"),
   NATIVE_METHOD(Class, isInstance, "(Ljava/lang/Object;)Z"),
   NATIVE_METHOD(Class, newInstanceImpl, "()Ljava/lang/Object;"),
diff --git a/src/object.h b/src/object.h
index 93493fd..fee4dd0 100644
--- a/src/object.h
+++ b/src/object.h
@@ -2508,10 +2508,16 @@
 
 class MANAGED SynthesizedProxyClass : public Class {
  public:
+  ObjectArray<Class>* GetInterfaces() {
+    return interfaces_;
+  }
+
   ObjectArray<ObjectArray<Class> >* GetThrows() {
     return throws_;
   }
+
  private:
+  ObjectArray<Class>* interfaces_;
   ObjectArray<ObjectArray<Class> >* throws_;
   DISALLOW_IMPLICIT_CONSTRUCTORS(SynthesizedProxyClass);
 };
diff --git a/src/object_utils.h b/src/object_utils.h
index b9d8a24..cbad7b0 100644
--- a/src/object_utils.h
+++ b/src/object_utils.h
@@ -249,32 +249,28 @@
   }
   const char* GetName() {
     uint32_t field_index = field_->GetDexFieldIndex();
-    if (field_index != DexFile::kDexNoIndex) {
+    if (!field_->GetDeclaringClass()->IsProxyClass()) {
       const DexFile& dex_file = GetDexFile();
       return dex_file.GetFieldName(dex_file.GetFieldId(field_index));
     } else {
-      // Proxy classes have a single static field called "throws"
-      CHECK(field_->GetDeclaringClass()->IsProxyClass());
       DCHECK(field_->IsStatic());
-      return "throws";
+      DCHECK_LT(field_index, 2U);
+      return field_index == 0 ? "interfaces" : "throws";
     }
   }
   String* GetNameAsString() {
     uint32_t field_index = field_->GetDexFieldIndex();
-    if (field_index != DexFile::kDexNoIndex) {
+    if (!field_->GetDeclaringClass()->IsProxyClass()) {
       const DexFile& dex_file = GetDexFile();
       const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
       return GetClassLinker()->ResolveString(dex_file, field_id.name_idx_, GetDexCache());
     } else {
-      // Proxy classes have a single static field called "throws"
-      CHECK(field_->GetDeclaringClass()->IsProxyClass());
-      DCHECK(field_->IsStatic());
-      return Runtime::Current()->GetInternTable()->InternStrong("throws");
+      return Runtime::Current()->GetInternTable()->InternStrong(GetName());
     }
   }
   Class* GetType() {
     uint32_t field_index = field_->GetDexFieldIndex();
-    if (field_index != DexFile::kDexNoIndex) {
+    if (!field_->GetDeclaringClass()->IsProxyClass()) {
       const DexFile& dex_file = GetDexFile();
       const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
       Class* type = GetDexCache()->GetResolvedType(field_id.type_idx_);
@@ -284,23 +280,20 @@
       }
       return type;
     } else {
-      // Proxy classes have a single static field called "throws" whose type is Class[][]
-      CHECK(field_->GetDeclaringClass()->IsProxyClass());
-      DCHECK(field_->IsStatic());
-      return GetClassLinker()->FindSystemClass("[[Ljava/lang/Class;");
+      return GetClassLinker()->FindSystemClass(GetTypeDescriptor());
     }
   }
   const char* GetTypeDescriptor() {
     uint32_t field_index = field_->GetDexFieldIndex();
-    if (field_index != DexFile::kDexNoIndex) {
+    if (!field_->GetDeclaringClass()->IsProxyClass()) {
       const DexFile& dex_file = GetDexFile();
       const DexFile::FieldId& field_id = dex_file.GetFieldId(field_index);
       return dex_file.GetFieldTypeDescriptor(field_id);
     } else {
-      // Proxy classes have a single static field called "throws" whose type is Class[][]
-      CHECK(field_->GetDeclaringClass()->IsProxyClass());
       DCHECK(field_->IsStatic());
-      return "[[Ljava/lang/Class;";
+      DCHECK_LT(field_index, 2U);
+      // 0 == Class[] interfaces; 1 == Class[][] throws;
+      return field_index == 0 ? "[Ljava/lang/Class;" : "[[Ljava/lang/Class;";
     }
   }
   Primitive::Type GetTypeAsPrimitiveType() {