Add access checks to Method and Field reflection.

Art side of this change. Has a corresponding libcore change.

Bug: 13620925
Change-Id: Ie67f802a2a400e8212b489b9a261b7028422d8ba
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 24d16c4..4b6d82b 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -175,6 +175,12 @@
   va_end(args);
 }
 
+// IllegalAccessException
+
+void ThrowIllegalAccessException(const ThrowLocation* throw_location, const char* msg) {
+  ThrowException(throw_location, "Ljava/lang/IllegalAccessException;", NULL, msg);
+}
+
 // IllegalArgumentException
 
 void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg) {
diff --git a/runtime/common_throws.h b/runtime/common_throws.h
index 792cdef..c06763e 100644
--- a/runtime/common_throws.h
+++ b/runtime/common_throws.h
@@ -92,6 +92,11 @@
     __attribute__((__format__(__printf__, 2, 3)))
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
 
+// IllegalAccessException
+
+void ThrowIllegalAccessException(const ThrowLocation* throw_location, const char* msg)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) COLD_ATTR;
+
 // IllegalArgumentException
 
 void ThrowIllegalArgumentException(const ThrowLocation* throw_location, const char* msg)
diff --git a/runtime/native/java_lang_reflect_Constructor.cc b/runtime/native/java_lang_reflect_Constructor.cc
index b7e8ac2..d28ebd0 100644
--- a/runtime/native/java_lang_reflect_Constructor.cc
+++ b/runtime/native/java_lang_reflect_Constructor.cc
@@ -34,7 +34,8 @@
  * check.  We can also safely assume the constructor isn't associated
  * with an interface, array, or primitive class.
  */
-static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs) {
+static jobject Constructor_newInstance(JNIEnv* env, jobject javaMethod, jobjectArray javaArgs,
+                                       jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod);
   SirtRef<mirror::Class> c(soa.Self(), m->GetDeclaringClass());
@@ -67,14 +68,14 @@
   }
 
   jobject javaReceiver = soa.AddLocalReference<jobject>(receiver);
-  InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
+  InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, accessible);
 
   // Constructors are ()V methods, so we shouldn't touch the result of InvokeMethod.
   return javaReceiver;
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;)Ljava/lang/Object;"),
+  NATIVE_METHOD(Constructor, newInstance, "!([Ljava/lang/Object;Z)Ljava/lang/Object;"),
 };
 
 void register_java_lang_reflect_Constructor(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 48b58bf..755708e 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -20,6 +20,7 @@
 #include "dex_file-inl.h"
 #include "jni_internal.h"
 #include "mirror/art_field-inl.h"
+#include "mirror/art_method-inl.h"
 #include "mirror/class-inl.h"
 #include "object_utils.h"
 #include "reflection.h"
@@ -27,6 +28,21 @@
 
 namespace art {
 
+static bool ValidateFieldAccess(mirror::ArtField* field, mirror::Object* obj, bool is_set)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  if (field->IsFinal() && is_set) {
+    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot set final field: %s",
+                                                      PrettyField(field).c_str()).c_str());
+    return false;
+  }
+  if (!ValidateAccess(obj, field->GetDeclaringClass(), field->GetAccessFlags())) {
+    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access field: %s",
+                                                      PrettyField(field).c_str()).c_str());
+    return false;
+  }
+  return true;
+}
+
 static bool GetFieldValue(const ScopedFastNativeObjectAccess& soa, mirror::Object* o,
                           mirror::ArtField* f, Primitive::Type field_type, bool allow_references,
                           JValue* value)
@@ -98,7 +114,7 @@
   return true;
 }
 
-static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj) {
+static jobject Field_get(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   CHECK(!kMovingFields) << "CheckReceiver may trigger thread suspension for initialization";
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
@@ -107,6 +123,11 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return nullptr;
   }
+  // Validate access.
+  if (!accessible && !ValidateFieldAccess(f, o, false)) {
+    DCHECK(soa.Self()->IsExceptionPending());
+    return nullptr;
+  }
   // We now don't expect suspension unless an exception is thrown.
   // Get the field's value, boxing if necessary.
   Primitive::Type field_type = FieldHelper(f).GetTypeAsPrimitiveType();
@@ -119,7 +140,7 @@
 }
 
 static JValue GetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj,
-                                char dst_descriptor) {
+                                char dst_descriptor, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   CHECK(!kMovingFields) << "CheckReceiver may trigger thread suspension for initialization";
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
@@ -128,6 +149,13 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return JValue();
   }
+
+  // Validate access.
+  if (!accessible && !ValidateFieldAccess(f, o, false)) {
+    DCHECK(soa.Self()->IsExceptionPending());
+    return JValue();
+  }
+
   // We now don't expect suspension unless an exception is thrown.
   // Read the value.
   Primitive::Type field_type = FieldHelper(f).GetTypeAsPrimitiveType();
@@ -147,36 +175,38 @@
   return wide_value;
 }
 
-static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'Z').GetZ();
+static jboolean Field_getBoolean(JNIEnv* env, jobject javaField, jobject javaObj,
+                                 jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'Z', accessible).GetZ();
 }
 
-static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'B').GetB();
+static jbyte Field_getByte(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'B', accessible).GetB();
 }
 
-static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'C').GetC();
+static jchar Field_getChar(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'C', accessible).GetC();
 }
 
-static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'D').GetD();
+static jdouble Field_getDouble(JNIEnv* env, jobject javaField, jobject javaObj,
+                               jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'D', accessible).GetD();
 }
 
-static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'F').GetF();
+static jfloat Field_getFloat(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'F', accessible).GetF();
 }
 
-static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'I').GetI();
+static jint Field_getInt(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'I', accessible).GetI();
 }
 
-static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'J').GetJ();
+static jlong Field_getLong(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'J', accessible).GetJ();
 }
 
-static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj) {
-  return GetPrimitiveField(env, javaField, javaObj, 'S').GetS();
+static jshort Field_getShort(JNIEnv* env, jobject javaField, jobject javaObj, jboolean accessible) {
+  return GetPrimitiveField(env, javaField, javaObj, 'S', accessible).GetS();
 }
 
 static void SetFieldValue(ScopedFastNativeObjectAccess& soa, mirror::Object* o,
@@ -223,7 +253,8 @@
   }
 }
 
-static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue) {
+static void Field_set(JNIEnv* env, jobject javaField, jobject javaObj, jobject javaValue,
+                      jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   CHECK(!kMovingFields) << "CheckReceiver may trigger thread suspension for initialization";
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
@@ -260,11 +291,16 @@
     DCHECK(soa.Self()->IsExceptionPending());
     return;
   }
+  // Validate access.
+  if (!accessible && !ValidateFieldAccess(f, o, true)) {
+    DCHECK(soa.Self()->IsExceptionPending());
+    return;
+  }
   SetFieldValue(soa, o, f, field_prim_type, true, unboxed_value);
 }
 
 static void SetPrimitiveField(JNIEnv* env, jobject javaField, jobject javaObj, char src_descriptor,
-                              const JValue& new_value) {
+                              const JValue& new_value, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
   mirror::ArtField* f = mirror::ArtField::FromReflectedField(soa, javaField);
   mirror::Object* o = nullptr;
@@ -286,77 +322,91 @@
     return;
   }
 
+  // Validate access.
+  if (!accessible && !ValidateFieldAccess(f, o, true)) {
+    DCHECK(soa.Self()->IsExceptionPending());
+    return;
+  }
+
   // Write the value.
   SetFieldValue(soa, o, f, field_type, false, wide_value);
 }
 
-static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z) {
+static void Field_setBoolean(JNIEnv* env, jobject javaField, jobject javaObj, jboolean z,
+                             jboolean accessible) {
   JValue value;
   value.SetZ(z);
-  SetPrimitiveField(env, javaField, javaObj, 'Z', value);
+  SetPrimitiveField(env, javaField, javaObj, 'Z', value, accessible);
 }
 
-static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b) {
+static void Field_setByte(JNIEnv* env, jobject javaField, jobject javaObj, jbyte b,
+                          jboolean accessible) {
   JValue value;
   value.SetB(b);
-  SetPrimitiveField(env, javaField, javaObj, 'B', value);
+  SetPrimitiveField(env, javaField, javaObj, 'B', value, accessible);
 }
 
-static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c) {
+static void Field_setChar(JNIEnv* env, jobject javaField, jobject javaObj, jchar c,
+                          jboolean accessible) {
   JValue value;
   value.SetC(c);
-  SetPrimitiveField(env, javaField, javaObj, 'C', value);
+  SetPrimitiveField(env, javaField, javaObj, 'C', value, accessible);
 }
 
-static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d) {
+static void Field_setDouble(JNIEnv* env, jobject javaField, jobject javaObj, jdouble d,
+                            jboolean accessible) {
   JValue value;
   value.SetD(d);
-  SetPrimitiveField(env, javaField, javaObj, 'D', value);
+  SetPrimitiveField(env, javaField, javaObj, 'D', value, accessible);
 }
 
-static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f) {
+static void Field_setFloat(JNIEnv* env, jobject javaField, jobject javaObj, jfloat f,
+                           jboolean accessible) {
   JValue value;
   value.SetF(f);
-  SetPrimitiveField(env, javaField, javaObj, 'F', value);
+  SetPrimitiveField(env, javaField, javaObj, 'F', value, accessible);
 }
 
-static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i) {
+static void Field_setInt(JNIEnv* env, jobject javaField, jobject javaObj, jint i,
+                         jboolean accessible) {
   JValue value;
   value.SetI(i);
-  SetPrimitiveField(env, javaField, javaObj, 'I', value);
+  SetPrimitiveField(env, javaField, javaObj, 'I', value, accessible);
 }
 
-static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j) {
+static void Field_setLong(JNIEnv* env, jobject javaField, jobject javaObj, jlong j,
+                          jboolean accessible) {
   JValue value;
   value.SetJ(j);
-  SetPrimitiveField(env, javaField, javaObj, 'J', value);
+  SetPrimitiveField(env, javaField, javaObj, 'J', value, accessible);
 }
 
-static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s) {
+static void Field_setShort(JNIEnv* env, jobject javaField, jobject javaObj, jshort s,
+                           jboolean accessible) {
   JValue value;
   value.SetS(s);
-  SetPrimitiveField(env, javaField, javaObj, 'S', value);
+  SetPrimitiveField(env, javaField, javaObj, 'S', value, accessible);
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Field, get,        "!(Ljava/lang/Object;)Ljava/lang/Object;"),
-  NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;)Z"),
-  NATIVE_METHOD(Field, getByte,    "!(Ljava/lang/Object;)B"),
-  NATIVE_METHOD(Field, getChar,    "!(Ljava/lang/Object;)C"),
-  NATIVE_METHOD(Field, getDouble,  "!(Ljava/lang/Object;)D"),
-  NATIVE_METHOD(Field, getFloat,   "!(Ljava/lang/Object;)F"),
-  NATIVE_METHOD(Field, getInt,     "!(Ljava/lang/Object;)I"),
-  NATIVE_METHOD(Field, getLong,    "!(Ljava/lang/Object;)J"),
-  NATIVE_METHOD(Field, getShort,   "!(Ljava/lang/Object;)S"),
-  NATIVE_METHOD(Field, set,        "!(Ljava/lang/Object;Ljava/lang/Object;)V"),
-  NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;Z)V"),
-  NATIVE_METHOD(Field, setByte,    "!(Ljava/lang/Object;B)V"),
-  NATIVE_METHOD(Field, setChar,    "!(Ljava/lang/Object;C)V"),
-  NATIVE_METHOD(Field, setDouble,  "!(Ljava/lang/Object;D)V"),
-  NATIVE_METHOD(Field, setFloat,   "!(Ljava/lang/Object;F)V"),
-  NATIVE_METHOD(Field, setInt,     "!(Ljava/lang/Object;I)V"),
-  NATIVE_METHOD(Field, setLong,    "!(Ljava/lang/Object;J)V"),
-  NATIVE_METHOD(Field, setShort,   "!(Ljava/lang/Object;S)V"),
+  NATIVE_METHOD(Field, get,        "!(Ljava/lang/Object;Z)Ljava/lang/Object;"),
+  NATIVE_METHOD(Field, getBoolean, "!(Ljava/lang/Object;Z)Z"),
+  NATIVE_METHOD(Field, getByte,    "!(Ljava/lang/Object;Z)B"),
+  NATIVE_METHOD(Field, getChar,    "!(Ljava/lang/Object;Z)C"),
+  NATIVE_METHOD(Field, getDouble,  "!(Ljava/lang/Object;Z)D"),
+  NATIVE_METHOD(Field, getFloat,   "!(Ljava/lang/Object;Z)F"),
+  NATIVE_METHOD(Field, getInt,     "!(Ljava/lang/Object;Z)I"),
+  NATIVE_METHOD(Field, getLong,    "!(Ljava/lang/Object;Z)J"),
+  NATIVE_METHOD(Field, getShort,   "!(Ljava/lang/Object;Z)S"),
+  NATIVE_METHOD(Field, set,        "!(Ljava/lang/Object;Ljava/lang/Object;Z)V"),
+  NATIVE_METHOD(Field, setBoolean, "!(Ljava/lang/Object;ZZ)V"),
+  NATIVE_METHOD(Field, setByte,    "!(Ljava/lang/Object;BZ)V"),
+  NATIVE_METHOD(Field, setChar,    "!(Ljava/lang/Object;CZ)V"),
+  NATIVE_METHOD(Field, setDouble,  "!(Ljava/lang/Object;DZ)V"),
+  NATIVE_METHOD(Field, setFloat,   "!(Ljava/lang/Object;FZ)V"),
+  NATIVE_METHOD(Field, setInt,     "!(Ljava/lang/Object;IZ)V"),
+  NATIVE_METHOD(Field, setLong,    "!(Ljava/lang/Object;JZ)V"),
+  NATIVE_METHOD(Field, setShort,   "!(Ljava/lang/Object;SZ)V"),
 };
 
 void register_java_lang_reflect_Field(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Method.cc b/runtime/native/java_lang_reflect_Method.cc
index abb73b6..c23f65c 100644
--- a/runtime/native/java_lang_reflect_Method.cc
+++ b/runtime/native/java_lang_reflect_Method.cc
@@ -29,10 +29,10 @@
 
 namespace art {
 
-static jobject Method_invoke(JNIEnv* env,
-                             jobject javaMethod, jobject javaReceiver, jobject javaArgs) {
+static jobject Method_invoke(JNIEnv* env, jobject javaMethod, jobject javaReceiver,
+                             jobject javaArgs, jboolean accessible) {
   ScopedFastNativeObjectAccess soa(env);
-  return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs);
+  return InvokeMethod(soa, javaMethod, javaReceiver, javaArgs, accessible);
 }
 
 static jobject Method_getExceptionTypesNative(JNIEnv* env, jobject javaMethod) {
@@ -56,7 +56,7 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"),
+  NATIVE_METHOD(Method, invoke, "!(Ljava/lang/Object;[Ljava/lang/Object;Z)Ljava/lang/Object;"),
   NATIVE_METHOD(Method, getExceptionTypesNative, "!()[Ljava/lang/Class;"),
 };
 
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 7f39e70..b38f9b4 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -26,6 +26,7 @@
 #include "mirror/class-inl.h"
 #include "mirror/object_array.h"
 #include "mirror/object_array-inl.h"
+#include "nth_caller_visitor.h"
 #include "object_utils.h"
 #include "scoped_thread_state_change.h"
 #include "stack.h"
@@ -461,7 +462,7 @@
 }
 
 jobject InvokeMethod(const ScopedObjectAccess& soa, jobject javaMethod,
-                     jobject javaReceiver, jobject javaArgs) {
+                     jobject javaReceiver, jobject javaArgs, bool accessible) {
   mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod);
 
   mirror::Class* declaring_class = m->GetDeclaringClass();
@@ -499,6 +500,13 @@
     return NULL;
   }
 
+  // Validate access.
+  if (!accessible && !ValidateAccess(receiver, declaring_class, m->GetAccessFlags())) {
+    ThrowIllegalAccessException(nullptr, StringPrintf("Cannot access method: %s",
+                                                      PrettyMethod(m).c_str()).c_str());
+    return nullptr;
+  }
+
   // Invoke the method.
   JValue result;
   ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength());
@@ -786,4 +794,30 @@
   return UnboxPrimitive(&throw_location, o, dst_class, nullptr, unboxed_value);
 }
 
+bool ValidateAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags) {
+  NthCallerVisitor visitor(Thread::Current(), 2);
+  visitor.WalkStack();
+  mirror::Class* caller_class = visitor.caller->GetDeclaringClass();
+
+  if (((access_flags & kAccPublic) && declaring_class->IsPublic()) ||
+      caller_class == declaring_class) {
+    return true;
+  }
+  if (access_flags & kAccPrivate) {
+    return false;
+  }
+  if (access_flags & kAccProtected) {
+    if (obj != nullptr && !obj->InstanceOf(caller_class) &&
+        !declaring_class->IsInSamePackage(caller_class)) {
+      return false;
+    } else if (declaring_class->IsAssignableFrom(caller_class)) {
+      return true;
+    }
+  }
+  if (!declaring_class->IsInSamePackage(caller_class)) {
+    return false;
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/reflection.h b/runtime/reflection.h
index 325998f..5cc725f 100644
--- a/runtime/reflection.h
+++ b/runtime/reflection.h
@@ -68,12 +68,15 @@
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 jobject InvokeMethod(const ScopedObjectAccess& soa, jobject method, jobject receiver,
-                     jobject args)
+                     jobject args, bool accessible)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
 bool VerifyObjectIsClass(mirror::Object* o, mirror::Class* c)
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+bool ValidateAccess(mirror::Object* obj, mirror::Class* declaring_class, uint32_t access_flags)
+    SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_REFLECTION_H_
diff --git a/test/046-reflect/expected.txt b/test/046-reflect/expected.txt
index fcfaf2f..55b0dbe 100644
--- a/test/046-reflect/expected.txt
+++ b/test/046-reflect/expected.txt
@@ -81,7 +81,8 @@
  Field type is int
  Access flags are 0x11
   cantTouchThis is 77
-  cantTouchThis is now 99
+  as expected: set-final throws exception
+  cantTouchThis is still 77
   public final int Target.cantTouchThis accessible=false
   public final int Target.cantTouchThis accessible=true
   cantTouchThis is now 87
diff --git a/test/046-reflect/src/Main.java b/test/046-reflect/src/Main.java
index dfb0d8f..d60fcb4 100644
--- a/test/046-reflect/src/Main.java
+++ b/test/046-reflect/src/Main.java
@@ -335,11 +335,12 @@
             System.out.println("  cantTouchThis is " + intVal);
             try {
                 field.setInt(instance, 99);
+                System.out.println("ERROR: set-final did not throw exception");
             } catch (IllegalAccessException iae) {
-                System.out.println("ERROR: set-final failed");
+                System.out.println("  as expected: set-final throws exception");
             }
             intVal = field.getInt(instance);
-            System.out.println("  cantTouchThis is now " + intVal);
+            System.out.println("  cantTouchThis is still " + intVal);
 
             System.out.println("  " + field + " accessible=" + field.isAccessible());
             field.setAccessible(true);
diff --git a/test/064-field-access/src/Main.java b/test/064-field-access/src/Main.java
index 3b9a475..c9b93ba 100644
--- a/test/064-field-access/src/Main.java
+++ b/test/064-field-access/src/Main.java
@@ -16,6 +16,7 @@
 
 import other.PublicClass;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 
 /*
  * Test field access through reflection.
@@ -192,6 +193,11 @@
   /* package */ static float samePackagePackageFloatStaticField = 63.0f;
   /* package */ static double samePackagePackageDoubleStaticField = 64.0;
   /* package */ static Object samePackagePackageObjectStaticField = "65";
+
+  public void samePublicMethod() { }
+  protected void sameProtectedMethod() { }
+  private void samePrivateMethod() { }
+  /* package */ void samePackageMethod() { }
 }
 
 /*
@@ -510,23 +516,32 @@
     for (int round = 0; round < 3; round++) {
       Object validInst;
       Field[] fields;
+      Method[] methods;
       boolean same_package = false;
+      boolean protected_class = false;
       switch (round) {
         case 0:
           validInst = new SamePackage();
           fields = SamePackage.class.getDeclaredFields();
           check(fields.length == 72);
+          methods = SamePackage.class.getDeclaredMethods();
+          check(methods.length == 4);
           same_package = true;
           break;
         case 1:
           validInst = new PublicClass();
           fields = PublicClass.class.getDeclaredFields();
           check(fields.length == 72);
+          methods = PublicClass.class.getDeclaredMethods();
+          check(methods.length == 4);
           break;
         default:
           validInst = new PublicClass();
           fields = PublicClass.class.getSuperclass().getDeclaredFields();
           check(fields.length == 72);
+          methods = PublicClass.class.getSuperclass().getDeclaredMethods();
+          check(methods.length == 4);
+          protected_class = true;
           break;
       }
       for (Field f : fields) {
@@ -540,16 +555,15 @@
         // Check access or lack of to field.
         Class<?> subClassAccessExceptionClass = null;
         if (f.getName().contains("Private") ||
-            (!same_package && f.getName().contains("Package"))) {
-          // ART deliberately doesn't throw IllegalAccessException.
-          // subClassAccessExceptionClass = IllegalAccessException.class;
+            (!same_package && f.getName().contains("Package")) ||
+            (!same_package && f.getName().contains("Protected"))) {
+          subClassAccessExceptionClass = IllegalAccessException.class;
         }
         Class<?> mainClassAccessExceptionClass = null;
         if (f.getName().contains("Private") ||
             (!same_package && f.getName().contains("Package")) ||
             (!same_package && f.getName().contains("Protected"))) {
-          // ART deliberately doesn't throw IllegalAccessException.
-          // mainClassAccessExceptionClass = IllegalAccessException.class;
+          mainClassAccessExceptionClass = IllegalAccessException.class;
         }
 
         this.getValue(f, validInst, typeChar, subClassAccessExceptionClass);
@@ -588,6 +602,16 @@
           }
         }
       }
+
+      for (Method m : methods) {
+        Class<?> subClassAccessExceptionClass = null;
+        if (protected_class || m.getName().contains("Private") ||
+            (!same_package && m.getName().contains("Package")) ||
+            (!same_package && m.getName().contains("Protected"))) {
+          subClassAccessExceptionClass = IllegalAccessException.class;
+        }
+        this.invoke(m, validInst, subClassAccessExceptionClass);
+      }
     }
     System.out.println("good");
   }
@@ -598,7 +622,6 @@
    */
   public Object getValue(Field field, Object obj, char type,
       Class expectedException) {
-
     Object result = null;
     try {
       switch (type) {
@@ -657,4 +680,29 @@
 
     return result;
   }
+
+  public Object invoke(Method method, Object obj, Class expectedException) {
+    Object result = null;
+    try {
+      result = method.invoke(obj);
+      /* success; expected? */
+      if (expectedException != null) {
+        System.err.println("ERROR: call succeeded for method " + method + "', was expecting " +
+                           expectedException);
+        Thread.dumpStack();
+      }
+    } catch (Exception ex) {
+      if (expectedException == null) {
+        System.err.println("ERROR: call failed unexpectedly: " + ex.getClass());
+        ex.printStackTrace();
+      } else {
+        if (!expectedException.equals(ex.getClass())) {
+          System.err.println("ERROR: incorrect exception: wanted " + expectedException.getName() +
+                             ", got " + ex.getClass());
+          ex.printStackTrace();
+        }
+      }
+    }
+    return result;
+  }
 }
diff --git a/test/064-field-access/src/other/ProtectedClass.java b/test/064-field-access/src/other/ProtectedClass.java
index 779aa1d..756c97f 100644
--- a/test/064-field-access/src/other/ProtectedClass.java
+++ b/test/064-field-access/src/other/ProtectedClass.java
@@ -97,4 +97,10 @@
  /* package */ static float otherProtectedClassPackageFloatStaticField = 63.0f;
  /* package */ static double otherProtectedClassPackageDoubleStaticField = 64.0;
  /* package */ static Object otherProtectedClassPackageObjectStaticField = "65";
+
+    public void otherPublicMethod() { }
+    protected void otherProtectedMethod() { }
+    private void otherPrivateMethod() { }
+    /* package */ void otherPackageMethod() { }
+
 }
diff --git a/test/064-field-access/src/other/PublicClass.java b/test/064-field-access/src/other/PublicClass.java
index 1653973..dbcb4fd 100644
--- a/test/064-field-access/src/other/PublicClass.java
+++ b/test/064-field-access/src/other/PublicClass.java
@@ -97,4 +97,9 @@
  /* package */ static float otherPublicClassPackageFloatStaticField = -63.0f;
  /* package */ static double otherPublicClassPackageDoubleStaticField = -64.0;
  /* package */ static Object otherPublicClassPackageObjectStaticField = "-65";
+
+    public void otherPublicMethod() { }
+    protected void otherProtectedMethod() { }
+    private void otherPrivateMethod() { }
+    /* package */ void otherPackageMethod() { }
 }
diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt
index 3d87ebc..bed0689 100644
--- a/test/100-reflect2/expected.txt
+++ b/test/100-reflect2/expected.txt
@@ -22,10 +22,7 @@
 30
 62
 14
-java.lang.IllegalArgumentException: Invalid primitive conversion from int to short
-	at java.lang.reflect.Field.set(Native Method)
-	at Main.testFieldReflection(Main.java:121)
-	at Main.main(Main.java:269)
+got expected IllegalArgumentException
 true (class java.lang.Boolean)
 6 (class java.lang.Byte)
 z (class java.lang.Character)
@@ -66,12 +63,6 @@
 true 0 1 2.0  hello world 3.0 4 5 6
 null (null)
 []
-java.lang.reflect.InvocationTargetException
-	at java.lang.reflect.Method.invoke(Native Method)
-	at Main.testMethodReflection(Main.java:210)
-	at Main.main(Main.java:270)
-Caused by: java.lang.ArithmeticException: surprise!
-	at Main.thrower(Main.java:218)
-	... 3 more
+got expected InvocationTargetException
  (class java.lang.String)
 yz (class java.lang.String)
diff --git a/test/100-reflect2/src/Main.java b/test/100-reflect2/src/Main.java
index 0404591..0cc1488 100644
--- a/test/100-reflect2/src/Main.java
+++ b/test/100-reflect2/src/Main.java
@@ -119,8 +119,9 @@
     try {
       f = Main.class.getDeclaredField("s");
       f.set(null, Integer.valueOf(14));
+      System.out.println("************* should have thrown!");
     } catch (IllegalArgumentException expected) {
-      expected.printStackTrace();
+      System.out.println("got expected IllegalArgumentException");
     }
 
     f = Main.class.getDeclaredField("z");
@@ -209,8 +210,8 @@
       System.out.println(Arrays.toString(m.getParameterTypes()));
       show(m.invoke(null));
       System.out.println("************* should have thrown!");
-    } catch (Exception expected) {
-      expected.printStackTrace();
+    } catch (InvocationTargetException expected) {
+      System.out.println("got expected InvocationTargetException");
     }
   }