From a9660f1dc13b4d595b3f89b06dd5b70eeee18c43 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 29 Mar 2018 10:21:47 +0100 Subject: hidden_api: Call back into libcore on hidden api detection This change also removes some unnecessary RI specific logic for building src-ex since it isn't required. Bug: 73896556 Test: run-test --host 674-hiddenapi Test: StrictModeTest Co-Authored-By: Andreas Gampe (cherry picked from commit 757a9d0a2e97d43bafeb8a95cc3c51102be99586) Merged-In: Ib2b4dfad55c5d829630bfe2adb4a468124bea61c Change-Id: Ida0943990aa1b3bad0c674bc31ff46766ae493a6 --- runtime/native/java_lang_Class.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'runtime/native/java_lang_Class.cc') diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..1c17806b5e 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -535,18 +535,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, -- cgit v1.2.3-59-g8ed1b From 8c5de0f16444441c23a5ae807e4dd5cc0dd586a3 Mon Sep 17 00:00:00 2001 From: Orion Hodson Date: Tue, 3 Apr 2018 14:13:13 +0000 Subject: Revert "hidden_api: Call back into libcore on hidden api detection" This reverts commit 757a9d0a2e97d43bafeb8a95cc3c51102be99586. Reason for revert: Test failures with "art/test.py --host -t test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64" Bug: 73896556 Test: art/test.py --host -t test-art-host-run-test-debug-prebuild-interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64 (cherry picked from commit 9e68ade384abdb15714054feaed06cb38eb5432f) Merged-In: Ib2ad89c16ad797c37f6212bc7e5c0b6b92ce56b5 Change-Id: I11fa9b76da07162fde8773eb05cfc6a6514e0ca1 --- runtime/hidden_api.cc | 33 ------ runtime/native/java_lang_Class.cc | 7 +- runtime/well_known_classes.cc | 6 - runtime/well_known_classes.h | 3 - test/674-hiddenapi/build | 11 ++ test/674-hiddenapi/src-ex/ChildClass.java | 180 +++++++++++------------------- 6 files changed, 81 insertions(+), 159 deletions(-) (limited to 'runtime/native/java_lang_Class.cc') diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index aad3917f87..f0b36a090a 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,11 +16,7 @@ #include "hidden_api.h" -#include - #include "base/dumpable.h" -#include "thread-current-inl.h" -#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -140,35 +136,6 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access member_signature.WarnAboutAccess(access_method, HiddenApiAccessFlags::DecodeFromRuntime(member->GetAccessFlags())); - // We're now not in the boot classpath and have decided to warn, show - // a toast or deny access. Let strict mode know if a callback is set. - // - // For consistency of reasoning, we assume that a callback is never set - // when running unstarted with dex2oat. - if (access_method == kReflection && !runtime->IsAotCompiler()) { - ScopedObjectAccessUnchecked soa(Thread::Current()); - - ScopedLocalRef consumer_object(soa.Env(), - soa.Env()->GetStaticObjectField( - WellKnownClasses::dalvik_system_VMRuntime, - WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); - // If the consumer is non-null, we call back to it to let it know that we - // have encountered an API that's in one of our lists. - if (consumer_object != nullptr) { - std::ostringstream member_signature_str; - member_signature.Dump(member_signature_str); - - ScopedLocalRef signature_str( - soa.Env(), - soa.Env()->NewStringUTF(member_signature_str.str().c_str())); - - // Call through to Consumer.accept(String memberSignature); - soa.Env()->CallVoidMethod(consumer_object.get(), - WellKnownClasses::java_util_function_Consumer_accept, - signature_str.get()); - } - } - if (action == kDeny) { // Block access return true; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 1c17806b5e..ad05856eaf 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -535,19 +535,18 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); - StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - Handle result = hs.NewHandle( + ObjPtr result = mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args))); + soa.Decode>(args)); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result.Get()); + return soa.AddLocalReference(result); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 742e713774..bf36ccf0fa 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,7 +77,6 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; -jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -116,7 +115,6 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; -jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -127,7 +125,6 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; -jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -352,7 +349,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); - java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -383,7 +379,6 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "", "(JI)V"); - java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -394,7 +389,6 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); - dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index 7b1a2943d2..d5d7033132 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,7 +85,6 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; - static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -126,7 +125,6 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; - static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -137,7 +135,6 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; - static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 330a6def29..9012e8fd13 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,6 +16,15 @@ set -e +# Special build logic to handle src-ex .java files which have code that only builds on RI. +custom_build_logic() { + [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex + # src-ex uses code that can only build on RI. + ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') + # remove src-ex so that default-build doesn't try to build it. + [[ -d src-ex ]] && mv src-ex ignore.src-ex +} + # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -24,6 +33,7 @@ set -e # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true +custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -35,4 +45,5 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false +custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 822224c539..582e907ca3 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,7 +14,6 @@ * limitations under the License. */ -import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -22,7 +21,11 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import java.util.function.Consumer; + +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.type.PrimitiveType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeVisitor; public class ChildClass { enum PrimitiveType { @@ -133,47 +136,6 @@ public class ChildClass { } } - static final class RecordingConsumer implements Consumer { - public String recordedValue = null; - - @Override - public void accept(String value) { - recordedValue = value; - } - } - - private static void checkMemberCallback(Class klass, String name, - boolean isPublic, boolean isField) { - try { - RecordingConsumer consumer = new RecordingConsumer(); - VMRuntime.setNonSdkApiUsageConsumer(consumer); - try { - if (isPublic) { - if (isField) { - klass.getField(name); - } else { - klass.getMethod(name); - } - } else { - if (isField) { - klass.getDeclaredField(name); - } else { - klass.getDeclaredMethod(name); - } - } - } catch (NoSuchFieldException|NoSuchMethodException ignored) { - // We're not concerned whether an exception is thrown or not - we're - // only interested in whether the callback is invoked. - } - - if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { - throw new RuntimeException("No callback for member: " + name); - } - } finally { - VMRuntime.setNonSdkApiUsageConsumer(null); - } - } - private static void checkField(Class klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -212,52 +174,48 @@ public class ChildClass { // Finish here if we could not discover the field. - if (canDiscover) { - // Test that modifiers are unaffected. + if (!canDiscover) { + return; + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test that modifiers are unaffected. - // Test getters and setters when meaningful. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + // Test getters and setters when meaningful. - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); } - // Test that callbacks are invoked correctly. clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, true /* isField */); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); } } @@ -299,46 +257,42 @@ public class ChildClass { // Finish here if we could not discover the field. - if (canDiscover) { - // Test that modifiers are unaffected. + if (!canDiscover) { + return; + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test that modifiers are unaffected. - // Test whether we can invoke the method. This skips non-static interface methods. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + // Test whether we can invoke the method. This skips non-static interface methods. - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); - } + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); } - } - // Test that callbacks are invoked correctly. - clearWarning(); - if (setsWarning || !canDiscover) { - checkMemberCallback(klass, name, isPublic, false /* isField */); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } } -- cgit v1.2.3-59-g8ed1b From f5f1f80aa6c1c10c61b6723bbc52d5aec2eba2b9 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 3 Apr 2018 15:23:46 +0100 Subject: Revert^2 "hidden_api: Call back into libcore on hidden api detection"" This reverts commit bbe60d58496991c16e2943e174e26ab8a096b3d0. This CL deviates from the approach of the original change. Instead of calling back every time ShouldBlock.. was called, we explicitly call back in cases where it's safe to do so. Note that we only call back on reflective accesses for now, and not link time accesses. Coverage for the latter will be added in a follow up change. Bug: 73896556 Test: test-art-host Test: art/test.py --host -t test-art-host-run-test-debug-prebuild-\ interpreter-no-relocate-ntrace-gcstress-checkjni-picimage-pictest-\ ndebuggable-no-jvmti-cdex-fast-674-hiddenapi64 (cherry picked from commit e453a8dd87731f4b37b86a1284f7655d86c2a809) Merged-In: Ie99ac268a083af167accbdf955639da068bea950 Change-Id: I76860519d40b87032dbb8db38b04fcf79ef09723 --- runtime/class_linker.cc | 16 +-- runtime/hidden_api.cc | 56 ++++++++-- runtime/hidden_api.h | 46 ++++---- runtime/interpreter/unstarted_runtime.cc | 6 +- runtime/jni_internal.cc | 7 +- runtime/native/java_lang_Class.cc | 35 ++++-- runtime/well_known_classes.cc | 6 + runtime/well_known_classes.h | 3 + test/674-hiddenapi/build | 11 -- test/674-hiddenapi/src-ex/ChildClass.java | 180 +++++++++++++++++++----------- 10 files changed, 235 insertions(+), 131 deletions(-) (limited to 'runtime/native/java_lang_Class.cc') diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 5e69efea77..3f33f7976e 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -7860,8 +7860,8 @@ ArtMethod* ClassLinker::FindResolvedMethod(ObjPtr klass, } DCHECK(resolved == nullptr || resolved->GetDeclaringClassUnchecked() != nullptr); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader, dex_cache, hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader, dex_cache, hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { @@ -8003,8 +8003,8 @@ ArtMethod* ClassLinker::ResolveMethodWithoutInvokeType(uint32_t method_idx, resolved = klass->FindClassMethod(dex_cache.Get(), method_idx, image_pointer_size_); } if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } return resolved; @@ -8083,8 +8083,8 @@ ArtField* ClassLinker::ResolveField(uint32_t field_idx, } if (resolved == nullptr || - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { const char* name = dex_file.GetFieldName(field_id); const char* type = dex_file.GetFieldTypeDescriptor(field_id); ThrowNoSuchFieldError(is_static ? "static " : "instance ", klass, type, name); @@ -8117,8 +8117,8 @@ ArtField* ClassLinker::ResolveFieldJLS(uint32_t field_idx, StringPiece type(dex_file.GetFieldTypeDescriptor(field_id)); resolved = mirror::Class::FindField(self, klass, name, type); if (resolved != nullptr && - hiddenapi::ShouldBlockAccessToMember( - resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking)) { + hiddenapi::GetMemberAction( + resolved, class_loader.Get(), dex_cache.Get(), hiddenapi::kLinking) == hiddenapi::kDeny) { resolved = nullptr; } if (resolved != nullptr) { diff --git a/runtime/hidden_api.cc b/runtime/hidden_api.cc index f0b36a090a..0e72f275d2 100644 --- a/runtime/hidden_api.cc +++ b/runtime/hidden_api.cc @@ -16,7 +16,11 @@ #include "hidden_api.h" +#include + #include "base/dumpable.h" +#include "thread-current-inl.h" +#include "well_known_classes.h" namespace art { namespace hiddenapi { @@ -111,7 +115,7 @@ void MemberSignature::WarnAboutAccess(AccessMethod access_method, } template -bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access_method) { +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) { // Get the signature, we need it later. MemberSignature member_signature(member); @@ -138,7 +142,7 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access if (action == kDeny) { // Block access - return true; + return action; } // Allow access to this member but print a warning. @@ -156,17 +160,49 @@ bool ShouldBlockAccessToMemberImpl(T* member, Action action, AccessMethod access runtime->SetPendingHiddenApiWarning(true); } - return false; + return action; } // Need to instantiate this. -template bool ShouldBlockAccessToMemberImpl(ArtField* member, - Action action, - AccessMethod access_method); -template bool ShouldBlockAccessToMemberImpl(ArtMethod* member, - Action action, - AccessMethod access_method); - +template Action GetMemberActionImpl(ArtField* member, + Action action, + AccessMethod access_method); +template Action GetMemberActionImpl(ArtMethod* member, + Action action, + AccessMethod access_method); } // namespace detail + +template +void NotifyHiddenApiListener(T* member) { + Runtime* runtime = Runtime::Current(); + if (!runtime->IsAotCompiler()) { + ScopedObjectAccessUnchecked soa(Thread::Current()); + + ScopedLocalRef consumer_object(soa.Env(), + soa.Env()->GetStaticObjectField( + WellKnownClasses::dalvik_system_VMRuntime, + WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer)); + // If the consumer is non-null, we call back to it to let it know that we + // have encountered an API that's in one of our lists. + if (consumer_object != nullptr) { + detail::MemberSignature member_signature(member); + std::ostringstream member_signature_str; + member_signature.Dump(member_signature_str); + + ScopedLocalRef signature_str( + soa.Env(), + soa.Env()->NewStringUTF(member_signature_str.str().c_str())); + + // Call through to Consumer.accept(String memberSignature); + soa.Env()->CallVoidMethod(consumer_object.get(), + WellKnownClasses::java_util_function_Consumer_accept, + signature_str.get()); + } + } +} + +template void NotifyHiddenApiListener(ArtMethod* member); +template void NotifyHiddenApiListener(ArtField* member); + } // namespace hiddenapi } // namespace art diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h index cc6c146f00..ffdeacbfff 100644 --- a/runtime/hidden_api.h +++ b/runtime/hidden_api.h @@ -58,7 +58,7 @@ enum AccessMethod { kLinking, }; -inline Action GetMemberAction(uint32_t access_flags) { +inline Action GetActionFromAccessFlags(uint32_t access_flags) { EnforcementPolicy policy = Runtime::Current()->GetHiddenApiEnforcementPolicy(); if (policy == EnforcementPolicy::kNoChecks) { // Exit early. Nothing to enforce. @@ -108,9 +108,7 @@ class MemberSignature { }; template -bool ShouldBlockAccessToMemberImpl(T* member, - Action action, - AccessMethod access_method) +Action GetMemberActionImpl(T* member, Action action, AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_); // Returns true if the caller is either loaded by the boot strap class loader or comes from @@ -138,28 +136,28 @@ inline bool IsCallerInPlatformDex(ObjPtr caller_class_loade // return true if the caller is located in the platform. // This function might print warnings into the log if the member is hidden. template -inline bool ShouldBlockAccessToMember(T* member, - Thread* self, - std::function fn_caller_in_platform, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + Thread* self, + std::function fn_caller_in_platform, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { DCHECK(member != nullptr); - Action action = GetMemberAction(member->GetAccessFlags()); + Action action = GetActionFromAccessFlags(member->GetAccessFlags()); if (action == kAllow) { // Nothing to do. - return false; + return action; } // Member is hidden. Invoke `fn_caller_in_platform` and find the origin of the access. // This can be *very* expensive. Save it for last. if (fn_caller_in_platform(self)) { // Caller in the platform. Exit. - return false; + return kAllow; } // Member is hidden and caller is not in the platform. - return detail::ShouldBlockAccessToMemberImpl(member, action, access_method); + return detail::GetMemberActionImpl(member, action, access_method); } inline bool IsCallerInPlatformDex(ObjPtr caller) @@ -172,18 +170,26 @@ inline bool IsCallerInPlatformDex(ObjPtr caller) // `caller_class_loader`. // This function might print warnings into the log if the member is hidden. template -inline bool ShouldBlockAccessToMember(T* member, - ObjPtr caller_class_loader, - ObjPtr caller_dex_cache, - AccessMethod access_method) +inline Action GetMemberAction(T* member, + ObjPtr caller_class_loader, + ObjPtr caller_dex_cache, + AccessMethod access_method) REQUIRES_SHARED(Locks::mutator_lock_) { bool caller_in_platform = detail::IsCallerInPlatformDex(caller_class_loader, caller_dex_cache); - return ShouldBlockAccessToMember(member, - /* thread */ nullptr, - [caller_in_platform] (Thread*) { return caller_in_platform; }, - access_method); + return GetMemberAction(member, + /* thread */ nullptr, + [caller_in_platform] (Thread*) { return caller_in_platform; }, + access_method); } +// Calls back into managed code to notify VMRuntime.nonSdkApiUsageConsumer that +// |member| was accessed. This is usually called when an API is on the black, +// dark grey or light grey lists. Given that the callback can execute arbitrary +// code, a call to this method can result in thread suspension. +template void NotifyHiddenApiListener(T* member) + REQUIRES_SHARED(Locks::mutator_lock_); + + } // namespace hiddenapi } // namespace art diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc index 4c7a97dfa8..dd8d7dd3e2 100644 --- a/runtime/interpreter/unstarted_runtime.cc +++ b/runtime/interpreter/unstarted_runtime.cc @@ -181,11 +181,13 @@ static mirror::String* GetClassName(Thread* self, ShadowFrame* shadow_frame, siz template static ALWAYS_INLINE bool ShouldBlockAccessToMember(T* member, ShadowFrame* frame) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + // All uses in this file are from reflection + constexpr hiddenapi::AccessMethod access_method = hiddenapi::kReflection; + return hiddenapi::GetMemberAction( member, frame->GetMethod()->GetDeclaringClass()->GetClassLoader(), frame->GetMethod()->GetDeclaringClass()->GetDexCache(), - hiddenapi::kReflection); // all uses in this file are from reflection + access_method) == hiddenapi::kDeny; } void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self, diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index f309581735..9dbcded867 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -87,8 +87,13 @@ static bool IsCallerInPlatformDex(Thread* self) REQUIRES_SHARED(Locks::mutator_l template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kJNI); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index ad05856eaf..a8b203bff2 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -98,8 +98,13 @@ ALWAYS_INLINE static bool ShouldEnforceHiddenApi(Thread* self) template ALWAYS_INLINE static bool ShouldBlockAccessToMember(T* member, Thread* self) REQUIRES_SHARED(Locks::mutator_lock_) { - return hiddenapi::ShouldBlockAccessToMember( + hiddenapi::Action action = hiddenapi::GetMemberAction( member, self, IsCallerInPlatformDex, hiddenapi::kReflection); + if (action != hiddenapi::kAllow) { + hiddenapi::NotifyHiddenApiListener(member); + } + + return action == hiddenapi::kDeny; } // Returns true if a class member should be discoverable with reflection given @@ -113,7 +118,8 @@ ALWAYS_INLINE static bool IsDiscoverable(bool public_only, return false; } - if (enforce_hidden_api && hiddenapi::GetMemberAction(access_flags) == hiddenapi::kDeny) { + if (enforce_hidden_api && + hiddenapi::GetActionFromAccessFlags(access_flags) == hiddenapi::kDeny) { return false; } @@ -433,12 +439,14 @@ static jobject Class_getPublicFieldRecursive(JNIEnv* env, jobject javaThis, jstr return nullptr; } - mirror::Field* field = GetPublicFieldRecursive( - soa.Self(), DecodeClass(soa, javaThis), name_string); - if (field == nullptr || ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { + StackHandleScope<1> hs(soa.Self()); + Handle field = hs.NewHandle(GetPublicFieldRecursive( + soa.Self(), DecodeClass(soa, javaThis), name_string)); + if (field.Get() == nullptr || + ShouldBlockAccessToMember(field->GetArtField(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(field); + return soa.AddLocalReference(field.Get()); } static jobject Class_getDeclaredField(JNIEnv* env, jobject javaThis, jstring name) { @@ -477,15 +485,17 @@ static jobject Class_getDeclaredConstructorInternal( ScopedFastNativeObjectAccess soa(env); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + + StackHandleScope<1> hs(soa.Self()); + Handle result = hs.NewHandle( mirror::Class::GetDeclaredConstructorInternal( soa.Self(), DecodeClass(soa, javaThis), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static ALWAYS_INLINE inline bool MethodMatchesConstructor( @@ -535,18 +545,19 @@ static jobjectArray Class_getDeclaredConstructorsInternal( static jobject Class_getDeclaredMethodInternal(JNIEnv* env, jobject javaThis, jstring name, jobjectArray args) { ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); DCHECK_EQ(Runtime::Current()->GetClassLinker()->GetImagePointerSize(), kRuntimePointerSize); DCHECK(!Runtime::Current()->IsActiveTransaction()); - ObjPtr result = + Handle result = hs.NewHandle( mirror::Class::GetDeclaredMethodInternal( soa.Self(), DecodeClass(soa, javaThis), soa.Decode(name), - soa.Decode>(args)); + soa.Decode>(args))); if (result == nullptr || ShouldBlockAccessToMember(result->GetArtMethod(), soa.Self())) { return nullptr; } - return soa.AddLocalReference(result); + return soa.AddLocalReference(result.Get()); } static jobjectArray Class_getDeclaredMethodsUnchecked(JNIEnv* env, jobject javaThis, diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index bf36ccf0fa..742e713774 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -77,6 +77,7 @@ jclass WellKnownClasses::java_nio_ByteBuffer; jclass WellKnownClasses::java_nio_DirectByteBuffer; jclass WellKnownClasses::java_util_ArrayList; jclass WellKnownClasses::java_util_Collections; +jclass WellKnownClasses::java_util_function_Consumer; jclass WellKnownClasses::libcore_reflect_AnnotationFactory; jclass WellKnownClasses::libcore_reflect_AnnotationMember; jclass WellKnownClasses::libcore_util_EmptyArray; @@ -115,6 +116,7 @@ jmethodID WellKnownClasses::java_lang_Thread_run; jmethodID WellKnownClasses::java_lang_ThreadGroup_add; jmethodID WellKnownClasses::java_lang_ThreadGroup_removeThread; jmethodID WellKnownClasses::java_nio_DirectByteBuffer_init; +jmethodID WellKnownClasses::java_util_function_Consumer_accept; jmethodID WellKnownClasses::libcore_reflect_AnnotationFactory_createAnnotation; jmethodID WellKnownClasses::libcore_reflect_AnnotationMember_init; jmethodID WellKnownClasses::org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -125,6 +127,7 @@ jfieldID WellKnownClasses::dalvik_system_DexFile_fileName; jfieldID WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList; jfieldID WellKnownClasses::dalvik_system_DexPathList_dexElements; jfieldID WellKnownClasses::dalvik_system_DexPathList__Element_dexFile; +jfieldID WellKnownClasses::dalvik_system_VMRuntime_nonSdkApiUsageConsumer; jfieldID WellKnownClasses::java_lang_Thread_daemon; jfieldID WellKnownClasses::java_lang_Thread_group; jfieldID WellKnownClasses::java_lang_Thread_lock; @@ -349,6 +352,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_nio_DirectByteBuffer = CacheClass(env, "java/nio/DirectByteBuffer"); java_util_ArrayList = CacheClass(env, "java/util/ArrayList"); java_util_Collections = CacheClass(env, "java/util/Collections"); + java_util_function_Consumer = CacheClass(env, "java/util/function/Consumer"); libcore_reflect_AnnotationFactory = CacheClass(env, "libcore/reflect/AnnotationFactory"); libcore_reflect_AnnotationMember = CacheClass(env, "libcore/reflect/AnnotationMember"); libcore_util_EmptyArray = CacheClass(env, "libcore/util/EmptyArray"); @@ -379,6 +383,7 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_ThreadGroup_add = CacheMethod(env, java_lang_ThreadGroup, false, "add", "(Ljava/lang/Thread;)V"); java_lang_ThreadGroup_removeThread = CacheMethod(env, java_lang_ThreadGroup, false, "threadTerminated", "(Ljava/lang/Thread;)V"); java_nio_DirectByteBuffer_init = CacheMethod(env, java_nio_DirectByteBuffer, false, "", "(JI)V"); + java_util_function_Consumer_accept = CacheMethod(env, java_util_function_Consumer, false, "accept", "(Ljava/lang/Object;)V"); libcore_reflect_AnnotationFactory_createAnnotation = CacheMethod(env, libcore_reflect_AnnotationFactory, true, "createAnnotation", "(Ljava/lang/Class;[Llibcore/reflect/AnnotationMember;)Ljava/lang/annotation/Annotation;"); libcore_reflect_AnnotationMember_init = CacheMethod(env, libcore_reflect_AnnotationMember, false, "", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V"); org_apache_harmony_dalvik_ddmc_DdmServer_broadcast = CacheMethod(env, org_apache_harmony_dalvik_ddmc_DdmServer, true, "broadcast", "(I)V"); @@ -389,6 +394,7 @@ void WellKnownClasses::Init(JNIEnv* env) { dalvik_system_DexFile_fileName = CacheField(env, dalvik_system_DexFile, false, "mFileName", "Ljava/lang/String;"); dalvik_system_DexPathList_dexElements = CacheField(env, dalvik_system_DexPathList, false, "dexElements", "[Ldalvik/system/DexPathList$Element;"); dalvik_system_DexPathList__Element_dexFile = CacheField(env, dalvik_system_DexPathList__Element, false, "dexFile", "Ldalvik/system/DexFile;"); + dalvik_system_VMRuntime_nonSdkApiUsageConsumer = CacheField(env, dalvik_system_VMRuntime, true, "nonSdkApiUsageConsumer", "Ljava/util/function/Consumer;"); java_lang_Thread_daemon = CacheField(env, java_lang_Thread, false, "daemon", "Z"); java_lang_Thread_group = CacheField(env, java_lang_Thread, false, "group", "Ljava/lang/ThreadGroup;"); java_lang_Thread_lock = CacheField(env, java_lang_Thread, false, "lock", "Ljava/lang/Object;"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index d5d7033132..7b1a2943d2 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -85,6 +85,7 @@ struct WellKnownClasses { static jclass java_lang_Throwable; static jclass java_util_ArrayList; static jclass java_util_Collections; + static jclass java_util_function_Consumer; static jclass java_nio_ByteBuffer; static jclass java_nio_DirectByteBuffer; static jclass libcore_reflect_AnnotationFactory; @@ -125,6 +126,7 @@ struct WellKnownClasses { static jmethodID java_lang_ThreadGroup_add; static jmethodID java_lang_ThreadGroup_removeThread; static jmethodID java_nio_DirectByteBuffer_init; + static jmethodID java_util_function_Consumer_accept; static jmethodID libcore_reflect_AnnotationFactory_createAnnotation; static jmethodID libcore_reflect_AnnotationMember_init; static jmethodID org_apache_harmony_dalvik_ddmc_DdmServer_broadcast; @@ -135,6 +137,7 @@ struct WellKnownClasses { static jfieldID dalvik_system_DexFile_fileName; static jfieldID dalvik_system_DexPathList_dexElements; static jfieldID dalvik_system_DexPathList__Element_dexFile; + static jfieldID dalvik_system_VMRuntime_nonSdkApiUsageConsumer; static jfieldID java_lang_reflect_Executable_artMethod; static jfieldID java_lang_reflect_Proxy_h; static jfieldID java_lang_Thread_daemon; diff --git a/test/674-hiddenapi/build b/test/674-hiddenapi/build index 9012e8fd13..330a6def29 100644 --- a/test/674-hiddenapi/build +++ b/test/674-hiddenapi/build @@ -16,15 +16,6 @@ set -e -# Special build logic to handle src-ex .java files which have code that only builds on RI. -custom_build_logic() { - [[ -d ignore.src-ex ]] && mv ignore.src-ex src-ex - # src-ex uses code that can only build on RI. - ${JAVAC} -source 1.8 -target 1.8 -sourcepath src-ex -sourcepath src -d classes-ex $(find src-ex -name '*.java') - # remove src-ex so that default-build doesn't try to build it. - [[ -d src-ex ]] && mv src-ex ignore.src-ex -} - # Build the jars twice. First with applying hiddenapi, creating a boot jar, then # a second time without to create a normal jar. We need to do this because we # want to load the jar once as an app module and once as a member of the boot @@ -33,7 +24,6 @@ custom_build_logic() { # class path dex files, so the boot jar loads fine in the latter case. export USE_HIDDENAPI=true -custom_build_logic ./default-build "$@" # Move the jar file into the resource folder to be bundled with the test. @@ -45,5 +35,4 @@ mv ${TEST_NAME}.jar res/boot.jar rm -rf classes* export USE_HIDDENAPI=false -custom_build_logic ./default-build "$@" diff --git a/test/674-hiddenapi/src-ex/ChildClass.java b/test/674-hiddenapi/src-ex/ChildClass.java index 582e907ca3..822224c539 100644 --- a/test/674-hiddenapi/src-ex/ChildClass.java +++ b/test/674-hiddenapi/src-ex/ChildClass.java @@ -14,6 +14,7 @@ * limitations under the License. */ +import dalvik.system.VMRuntime; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -21,11 +22,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; - -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.type.PrimitiveType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeVisitor; +import java.util.function.Consumer; public class ChildClass { enum PrimitiveType { @@ -136,6 +133,47 @@ public class ChildClass { } } + static final class RecordingConsumer implements Consumer { + public String recordedValue = null; + + @Override + public void accept(String value) { + recordedValue = value; + } + } + + private static void checkMemberCallback(Class klass, String name, + boolean isPublic, boolean isField) { + try { + RecordingConsumer consumer = new RecordingConsumer(); + VMRuntime.setNonSdkApiUsageConsumer(consumer); + try { + if (isPublic) { + if (isField) { + klass.getField(name); + } else { + klass.getMethod(name); + } + } else { + if (isField) { + klass.getDeclaredField(name); + } else { + klass.getDeclaredMethod(name); + } + } + } catch (NoSuchFieldException|NoSuchMethodException ignored) { + // We're not concerned whether an exception is thrown or not - we're + // only interested in whether the callback is invoked. + } + + if (consumer.recordedValue == null || !consumer.recordedValue.contains(name)) { + throw new RuntimeException("No callback for member: " + name); + } + } finally { + VMRuntime.setNonSdkApiUsageConsumer(null); + } + } + private static void checkField(Class klass, String name, boolean isStatic, Visibility visibility, Behaviour behaviour) throws Exception { @@ -174,48 +212,52 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, true); + } - if (Reflection.canObserveFieldHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, true); - } + // Test getters and setters when meaningful. - // Test getters and setters when meaningful. + clearWarning(); + if (!Reflection.canGetField(klass, name)) { + throwAccessException(klass, name, true, "Field.getInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.getInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canGetField(klass, name)) { - throwAccessException(klass, name, true, "Field.getInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.getInt()", setsWarning); - } + clearWarning(); + if (!Reflection.canSetField(klass, name)) { + throwAccessException(klass, name, true, "Field.setInt()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "Field.setInt()", setsWarning); + } - clearWarning(); - if (!Reflection.canSetField(klass, name)) { - throwAccessException(klass, name, true, "Field.setInt()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "Field.setInt()", setsWarning); - } + clearWarning(); + if (!JNI.canGetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "getIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "getIntField", setsWarning); + } - clearWarning(); - if (!JNI.canGetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "getIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "getIntField", setsWarning); + clearWarning(); + if (!JNI.canSetField(klass, name, isStatic)) { + throwAccessException(klass, name, true, "setIntField"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, true, "setIntField", setsWarning); + } } + // Test that callbacks are invoked correctly. clearWarning(); - if (!JNI.canSetField(klass, name, isStatic)) { - throwAccessException(klass, name, true, "setIntField"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, true, "setIntField", setsWarning); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, true /* isField */); } } @@ -257,42 +299,46 @@ public class ChildClass { // Finish here if we could not discover the field. - if (!canDiscover) { - return; - } + if (canDiscover) { + // Test that modifiers are unaffected. - // Test that modifiers are unaffected. + if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { + throwModifiersException(klass, name, false); + } - if (Reflection.canObserveMethodHiddenAccessFlags(klass, name)) { - throwModifiersException(klass, name, false); - } + // Test whether we can invoke the method. This skips non-static interface methods. - // Test whether we can invoke the method. This skips non-static interface methods. + if (!klass.isInterface() || isStatic) { + clearWarning(); + if (!Reflection.canInvokeMethod(klass, name)) { + throwAccessException(klass, name, false, "invoke()"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "invoke()", setsWarning); + } - if (!klass.isInterface() || isStatic) { - clearWarning(); - if (!Reflection.canInvokeMethod(klass, name)) { - throwAccessException(klass, name, false, "invoke()"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "invoke()", setsWarning); - } + clearWarning(); + if (!JNI.canInvokeMethodA(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodA"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + } - clearWarning(); - if (!JNI.canInvokeMethodA(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodA"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodA()", setsWarning); + clearWarning(); + if (!JNI.canInvokeMethodV(klass, name, isStatic)) { + throwAccessException(klass, name, false, "CallMethodV"); + } + if (hasPendingWarning() != setsWarning) { + throwWarningException(klass, name, false, "CallMethodV()", setsWarning); + } } + } - clearWarning(); - if (!JNI.canInvokeMethodV(klass, name, isStatic)) { - throwAccessException(klass, name, false, "CallMethodV"); - } - if (hasPendingWarning() != setsWarning) { - throwWarningException(klass, name, false, "CallMethodV()", setsWarning); - } + // Test that callbacks are invoked correctly. + clearWarning(); + if (setsWarning || !canDiscover) { + checkMemberCallback(klass, name, isPublic, false /* isField */); } } -- cgit v1.2.3-59-g8ed1b