Merge "Take hidden API into account during getDeclaredMethod()"
diff --git a/runtime/hidden_api.h b/runtime/hidden_api.h
index e15e9f3..0bdb5c8 100644
--- a/runtime/hidden_api.h
+++ b/runtime/hidden_api.h
@@ -354,7 +354,7 @@
// This function might print warnings into the log if the member is hidden.
template<typename T>
inline bool ShouldDenyAccessToMember(T* member,
- std::function<AccessContext()> fn_get_access_context,
+ const std::function<AccessContext()>& fn_get_access_context,
AccessMethod access_method)
REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK(member != nullptr);
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 72a8727..7a1b7eb 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -181,17 +181,21 @@
return param->AsString();
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(
+ ShadowFrame* frame) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) {
+ return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
+ };
+}
+
template<typename T>
static ALWAYS_INLINE bool ShouldDenyAccessToMember(T* member, ShadowFrame* frame)
REQUIRES_SHARED(Locks::mutator_lock_) {
// All uses in this file are from reflection
constexpr hiddenapi::AccessMethod kAccessMethod = hiddenapi::AccessMethod::kReflection;
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::AccessContext(frame->GetMethod()->GetDeclaringClass());
- },
- kAccessMethod);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(frame),
+ kAccessMethod);
}
void UnstartedRuntime::UnstartedClassForNameCommon(Thread* self,
@@ -390,22 +394,23 @@
Runtime* runtime = Runtime::Current();
bool transaction = runtime->IsActiveTransaction();
PointerSize pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
+ auto fn_hiddenapi_access_context = GetHiddenapiAccessContextFunction(shadow_frame);
ObjPtr<mirror::Method> method;
if (transaction) {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
} else {
if (pointer_size == PointerSize::k64) {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
} else {
method = mirror::Class::GetDeclaredMethodInternal<PointerSize::k32, false>(
- self, klass, name, args);
+ self, klass, name, args, fn_hiddenapi_access_context);
}
}
if (method != nullptr && ShouldDenyAccessToMember(method->GetArtMethod(), shadow_frame)) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 53c9cc7..fcd3714 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -35,6 +35,7 @@
#include "gc/accounting/card_table-inl.h"
#include "gc/heap-inl.h"
#include "handle_scope-inl.h"
+#include "hidden_api.h"
#include "subtype_check.h"
#include "method.h"
#include "object-inl.h"
@@ -1252,18 +1253,50 @@
return (type_id == nullptr) ? dex::TypeIndex() : dex_file.GetIndexForTypeId(*type_id);
}
+ALWAYS_INLINE
+static bool IsMethodPreferredOver(ArtMethod* orig_method,
+ bool orig_method_hidden,
+ ArtMethod* new_method,
+ bool new_method_hidden) {
+ DCHECK(new_method != nullptr);
+
+ // Is this the first result?
+ if (orig_method == nullptr) {
+ return true;
+ }
+
+ // Original method is hidden, the new one is not?
+ if (orig_method_hidden && !new_method_hidden) {
+ return true;
+ }
+
+ // We iterate over virtual methods first and then over direct ones,
+ // so we can never be in situation where `orig_method` is direct and
+ // `new_method` is virtual.
+ DCHECK(!orig_method->IsDirect() || new_method->IsDirect());
+
+ // Original method is synthetic, the new one is not?
+ if (orig_method->IsSynthetic() && !new_method->IsSynthetic()) {
+ return true;
+ }
+
+ return false;
+}
+
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Method> Class::GetDeclaredMethodInternal(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args) {
- // Covariant return types permit the class to define multiple
- // methods with the same name and parameter types. Prefer to
- // return a non-synthetic method in such situations. We may
- // still return a synthetic method to handle situations like
- // escalated visibility. We never return miranda methods that
- // were synthesized by the runtime.
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context) {
+ // Covariant return types (or smali) permit the class to define
+ // multiple methods with the same name and parameter types.
+ // Prefer (in decreasing order of importance):
+ // 1) non-hidden method over hidden
+ // 2) virtual methods over direct
+ // 3) non-synthetic methods over synthetic
+ // We never return miranda methods that were synthesized by the runtime.
StackHandleScope<3> hs(self);
auto h_method_name = hs.NewHandle(name);
if (UNLIKELY(h_method_name == nullptr)) {
@@ -1272,8 +1305,13 @@
}
auto h_args = hs.NewHandle(args);
Handle<Class> h_klass = hs.NewHandle(klass);
+ constexpr hiddenapi::AccessMethod access_method = hiddenapi::AccessMethod::kNone;
ArtMethod* result = nullptr;
+ bool result_hidden = false;
for (auto& m : h_klass->GetDeclaredVirtualMethods(kPointerSize)) {
+ if (m.IsMiranda()) {
+ continue;
+ }
auto* np_method = m.GetInterfaceMethodIfProxy(kPointerSize);
// May cause thread suspension.
ObjPtr<String> np_name = np_method->ResolveNameString();
@@ -1283,14 +1321,24 @@
}
continue;
}
- if (!m.IsMiranda()) {
- if (!m.IsSynthetic()) {
- return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
- }
- result = &m; // Remember as potential result if it's not a miranda method.
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, virtual, non-synthetic. Best possible result, exit early.
+ return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
}
- if (result == nullptr) {
+
+ if ((result != nullptr) && !result_hidden) {
+ // We have not found a non-hidden, virtual, non-synthetic method, but
+ // if we have found a non-hidden, virtual, synthetic method, we cannot
+ // do better than that later.
+ DCHECK(!result->IsDirect());
+ DCHECK(result->IsSynthetic());
+ } else {
for (auto& m : h_klass->GetDirectMethods(kPointerSize)) {
auto modifiers = m.GetAccessFlags();
if ((modifiers & kAccConstructor) != 0) {
@@ -1310,12 +1358,20 @@
continue;
}
DCHECK(!m.IsMiranda()); // Direct methods cannot be miranda methods.
- if ((modifiers & kAccSynthetic) == 0) {
+ bool m_hidden = hiddenapi::ShouldDenyAccessToMember(&m, fn_get_access_context, access_method);
+ if (!m_hidden && !m.IsSynthetic()) {
+ // Non-hidden, direct, non-synthetic. Any virtual result could only have been
+ // hidden, therefore this is the best possible match. Exit now.
+ DCHECK((result == nullptr) || result_hidden);
return Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, &m);
+ } else if (IsMethodPreferredOver(result, result_hidden, &m, m_hidden)) {
+ // Remember as potential result.
+ result = &m;
+ result_hidden = m_hidden;
}
- result = &m; // Remember as potential result.
}
}
+
return result != nullptr
? Method::CreateFromArtMethod<kPointerSize, kTransactionActive>(self, result)
: nullptr;
@@ -1326,25 +1382,29 @@
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k32, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, false>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template
ObjPtr<Method> Class::GetDeclaredMethodInternal<PointerSize::k64, true>(
Thread* self,
ObjPtr<Class> klass,
ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args);
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context);
template <PointerSize kPointerSize, bool kTransactionActive>
ObjPtr<Constructor> Class::GetDeclaredConstructorInternal(
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index 981ecf1..c9c542d 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -37,6 +37,10 @@
class TypeList;
} // namespace dex
+namespace hiddenapi {
+class AccessContext;
+} // namespace hiddenapi
+
template<typename T> class ArraySlice;
class ArtField;
class ArtMethod;
@@ -702,10 +706,12 @@
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
- static ObjPtr<Method> GetDeclaredMethodInternal(Thread* self,
- ObjPtr<Class> klass,
- ObjPtr<String> name,
- ObjPtr<ObjectArray<Class>> args)
+ static ObjPtr<Method> GetDeclaredMethodInternal(
+ Thread* self,
+ ObjPtr<Class> klass,
+ ObjPtr<String> name,
+ ObjPtr<ObjectArray<Class>> args,
+ const std::function<hiddenapi::AccessContext()>& fn_get_access_context)
REQUIRES_SHARED(Locks::mutator_lock_);
template <PointerSize kPointerSize, bool kTransactionActive>
diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc
index 4115791..db62475 100644
--- a/runtime/native/java_lang_Class.cc
+++ b/runtime/native/java_lang_Class.cc
@@ -113,15 +113,18 @@
: hiddenapi::AccessContext(caller);
}
+static std::function<hiddenapi::AccessContext()> GetHiddenapiAccessContextFunction(Thread* self) {
+ return [=]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); };
+}
+
// Returns true if the first non-ClassClass caller up the stack should not be
// allowed access to `member`.
template<typename T>
ALWAYS_INLINE static bool ShouldDenyAccessToMember(T* member, Thread* self)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return hiddenapi::ShouldDenyAccessToMember(
- member,
- [&]() REQUIRES_SHARED(Locks::mutator_lock_) { return GetReflectionCaller(self); },
- hiddenapi::AccessMethod::kReflection);
+ return hiddenapi::ShouldDenyAccessToMember(member,
+ GetHiddenapiAccessContextFunction(self),
+ hiddenapi::AccessMethod::kReflection);
}
// Returns true if a class member should be discoverable with reflection given
@@ -563,7 +566,8 @@
soa.Self(),
DecodeClass(soa, javaThis),
soa.Decode<mirror::String>(name),
- soa.Decode<mirror::ObjectArray<mirror::Class>>(args)));
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(args),
+ GetHiddenapiAccessContextFunction(soa.Self())));
if (result == nullptr || ShouldDenyAccessToMember(result->GetArtMethod(), soa.Self())) {
return nullptr;
}
diff --git a/test/690-hiddenapi-same-name-methods/build b/test/690-hiddenapi-same-name-methods/build
new file mode 100644
index 0000000..c364b3b
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2019 The Android Open Source Project
+#
+# 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.
+
+USE_HIDDENAPI=true ./default-build "$@"
diff --git a/test/690-hiddenapi-same-name-methods/expected.txt b/test/690-hiddenapi-same-name-methods/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
new file mode 100644
index 0000000..001ab80
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/hiddenapi-flags.csv
@@ -0,0 +1,9 @@
+LSpecificClass;->foo()Ljava/lang/Double;,blacklist
+LDirectMethods;->foo()Ljava/lang/Integer;,blacklist
+LDirectMethods;->foo()Ljava/lang/Boolean;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Integer;,blacklist
+LVirtualMethods;->foo()Ljava/lang/Boolean;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Integer;,blacklist
+LNonSyntheticMethods;->foo()Ljava/lang/Boolean;,blacklist
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/info.txt b/test/690-hiddenapi-same-name-methods/info.txt
new file mode 100644
index 0000000..be5b195
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/info.txt
@@ -0,0 +1 @@
+Test that Class::GetDeclaredMethodInternal() takes hidden API into account.
\ No newline at end of file
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
new file mode 100644
index 0000000..8564976
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/DirectMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+#
+
+.class LDirectMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method private foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method private foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
new file mode 100644
index 0000000..f47219f
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/NonSyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+#
+
+.class LNonSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
new file mode 100644
index 0000000..afb4d33
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/SyntheticMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+#
+
+.class LSyntheticMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, virtual method.
+
+# Non-hidden methods
+.method public synthetic foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public synthetic foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method private synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
new file mode 100644
index 0000000..fb26c74
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/smali-ex/VirtualMethods.smali
@@ -0,0 +1,46 @@
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# 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.
+#
+
+.class LVirtualMethods;
+.super Ljava/lang/Object;
+
+# Expect to choose the non-hidden, non-synthetic method.
+
+# Non-hidden methods
+.method public foo()Ljava/lang/Number;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Double;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+# Hidden methods
+.method public foo()Ljava/lang/Integer;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
+
+.method public synthetic foo()Ljava/lang/Boolean;
+ .registers 1
+ const/4 v0, 0x0
+ return-object v0
+.end method
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
new file mode 100644
index 0000000..c404402
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/GenericInterface.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+public interface GenericInterface<T extends Number> {
+ public T foo();
+}
diff --git a/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
new file mode 100644
index 0000000..dd3a835
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src-ex/SpecificClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.
+ */
+
+public class SpecificClass implements GenericInterface<Double> {
+ public Double foo() {
+ return 42.0;
+ }
+}
diff --git a/test/690-hiddenapi-same-name-methods/src/Main.java b/test/690-hiddenapi-same-name-methods/src/Main.java
new file mode 100644
index 0000000..12cfdd7
--- /dev/null
+++ b/test/690-hiddenapi-same-name-methods/src/Main.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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.io.File;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Main {
+ public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
+ System.loadLibrary(args[0]);
+
+ // Run the initialization routine. This will enable hidden API checks in
+ // the runtime, in case they are not enabled by default.
+ init();
+
+ // Load the '-ex' APK and attach it to the boot class path.
+ appendToBootClassLoader(DEX_EXTRA, /* isCorePlatform */ false);
+
+ // All test classes contain just methods named "foo" with different return types
+ // and access flags. Check that:
+ // (a) only the non-hidden ones are returned from getDeclaredMethods
+ // (they have return types Number and Double), and
+ // (b) getDeclaredMethod picks virtual/non-synthetic methods over direct/synthetic
+ // (the right one always has return type Number).
+ Class<?> covariantClass = Class.forName(JAVA_CLASS_NAME, true, BOOT_CLASS_LOADER);
+ checkMethodList(covariantClass, /* expectedLength= */ 1);
+ checkMethod(covariantClass);
+
+ String[] classes = new String[] {
+ "VirtualMethods",
+ "DirectMethods",
+ "SyntheticMethods",
+ "NonSyntheticMethods"
+ };
+ for (String className : classes) {
+ Class<?> klass = Class.forName(className, true, BOOT_CLASS_LOADER);
+ checkMethodList(klass, /* expectedLength= */ 2);
+ checkMethod(klass);
+ }
+ }
+
+ private static void checkMethodList(Class<?> klass, int expectedLength) {
+ String className = klass.getName();
+ Method[] methods = klass.getDeclaredMethods();
+ if (methods.length != expectedLength) {
+ throw new RuntimeException(className + ": expected " + expectedLength +
+ " declared method(s), got " + methods.length);
+ }
+ boolean hasNumberReturnType = false;
+ boolean hasDoubleReturnType = false;
+ for (Method method : methods) {
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ }
+ if (Number.class == method.getReturnType()) {
+ hasNumberReturnType = true;
+ } else if (Double.class == method.getReturnType()) {
+ hasDoubleReturnType = true;
+ }
+ }
+ if (methods.length >= 1 && !hasNumberReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Number\"");
+ }
+ if (methods.length >= 2 && !hasDoubleReturnType) {
+ throw new RuntimeException(className + ": expected a method with return type \"Double\"");
+ }
+ }
+
+ private static void checkMethod(Class<?> klass) throws NoSuchMethodException {
+ String className = klass.getName();
+ Method method = klass.getDeclaredMethod(METHOD_NAME);
+ if (!METHOD_NAME.equals(method.getName())) {
+ throw new RuntimeException(className + ": expected declared method name: \"" + METHOD_NAME +
+ "\", got: \"" + method.getName() + "\"");
+ } else if (Number.class != method.getReturnType()) {
+ throw new RuntimeException(className + ": expected method return type: \"Number\", got \"" +
+ method.getReturnType().toString() + "\"");
+ }
+ }
+
+ private static final String DEX_EXTRA = new File(System.getenv("DEX_LOCATION"),
+ "690-hiddenapi-same-name-methods-ex.jar").getAbsolutePath();
+
+ private static ClassLoader BOOT_CLASS_LOADER = Object.class.getClassLoader();
+
+ private static final String JAVA_CLASS_NAME = "SpecificClass";
+ private static final String METHOD_NAME = "foo";
+
+ // Native functions. Note that these are implemented in 674-hiddenapi/hiddenapi.cc.
+ private static native void appendToBootClassLoader(String dexPath, boolean isCorePlatform);
+ private static native void init();
+}
diff --git a/test/etc/default-build b/test/etc/default-build
index 5eba804..d203698 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -80,6 +80,12 @@
HAS_JASMIN_MULTIDEX=false
fi
+if [ -d smali-ex ]; then
+ HAS_SMALI_EX=true
+else
+ HAS_SMALI_EX=false
+fi
+
if [ -d src-ex ]; then
HAS_SRC_EX=true
else
@@ -454,13 +460,25 @@
javac_with_bootclasspath -d classes-tmp-for-ex `find src-art -name '*.java'`
src_tmp_for_ex="-cp classes-tmp-for-ex"
fi
- mkdir classes-ex
+ mkdir -p classes-ex
javac_with_bootclasspath -d classes-ex $src_tmp_for_ex `find src-ex -name '*.java'`
fi
if [[ -d classes-ex ]] && [ ${NEED_DEX} = "true" ]; then
make_dex classes-ex
+fi
+if [ "${HAS_SMALI_EX}" = "true" -a ${NEED_DEX} = "true" ]; then
+ # Compile Smali classes
+ ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes-ex.dex `find smali-ex -name '*.smali'`
+ if [[ ! -s smali_classes-ex.dex ]] ; then
+ fail "${SMALI} produced no output."
+ fi
+ # Merge smali files into classes-ex.dex.
+ make_dexmerge classes-ex.dex smali_classes-ex.dex
+fi
+
+if [[ -f classes-ex.dex ]]; then
# Apply hiddenapi on the dex files if the test has API list file(s).
if [ ${USE_HIDDENAPI} = "true" -a ${HAS_HIDDENAPI_SPEC} = "true" ]; then
make_hiddenapi classes-ex.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index a1a038a..cf6e69c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -534,8 +534,9 @@
"097-duplicate-method",
"138-duplicate-classes-check2",
"159-app-image-fields",
- "674-hiddenapi",
"649-vdex-duplicate-method",
+ "674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"804-class-extends-itself",
"921-hello-failure",
"999-redefine-hiddenapi"
@@ -555,6 +556,7 @@
"629-vdex-speed",
"647-jni-get-field-id",
"674-hiddenapi",
+ "690-hiddenapi-same-name-methods",
"944-transform-classloaders",
"999-redefine-hiddenapi"
],
@@ -1085,6 +1087,7 @@
"678-quickening",
"679-locks",
"688-shared-library",
+ "690-hiddenapi-same-name-methods",
"999-redefine-hiddenapi",
"1000-non-moving-space-stress",
"1001-app-image-regions",