image_classes);
}
for (auto& m : c->GetVirtualMethods(pointer_size)) {
- if (m.IsMiranda() || (true)) {
- StackHandleScope<1> hs2(self);
- MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
- }
+ StackHandleScope<1> hs2(self);
+ MaybeAddToImageClasses(hs2.NewHandle(m.GetDeclaringClass()), image_classes);
}
if (klass->IsArrayClass()) {
StackHandleScope<1> hs2(self);
// Copied methods may be held live by a class which was not an image class but have a
// declaring class which is an image class. Set it to the resolution method to be safe and
// prevent dangling pointers.
- if (method->MightBeCopied() || !KeepClass(declaring_class)) {
+ if (method->IsCopied() || !KeepClass(declaring_class)) {
mirror::DexCache::SetElementPtrSize(resolved_methods,
i,
resolution_method,
// TODO We should also check copied methods in this test.
for (auto& m : klass->GetDeclaredVirtualMethods(pointer_size)) {
if (!klass->IsInterface()) {
- EXPECT_FALSE(m.MightBeCopied());
+ EXPECT_FALSE(m.IsCopied());
}
CheckMethod(&m, oat_class.GetOatMethod(method_index), dex_file);
++method_index;
return (GetAccessFlags() & kAccFinal) != 0;
}
- // Returns true if this method might be copied from another class.
- bool MightBeCopied() {
- return IsMiranda() || IsDefault() || IsDefaultConflicting();
+ bool IsCopied() {
+ const bool copied = (GetAccessFlags() & kAccCopied) != 0;
+ // (IsMiranda() || IsDefaultConflicting()) implies copied
+ DCHECK(!(IsMiranda() || IsDefaultConflicting()) || copied)
+ << "Miranda or default-conflict methods must always be copied.";
+ return copied;
}
bool IsMiranda() {
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
CHECK(m->GetDeclaringClass() == nullptr) << PrettyMethod(m);
- } else if (m->MightBeCopied()) {
+ } else if (m->IsCopied()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*));
- const bool maybe_copied = method->MightBeCopied();
+ const bool is_copied = method->IsCopied();
if (resolved_types != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || maybe_copied) {
+ if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(maybe_copied || in_image_space)
+ DCHECK(is_copied || in_image_space)
<< resolved_types << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!maybe_copied || in_image_space) {
+ if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types),
sizeof(void*));
ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*));
if (resolved_methods != nullptr) {
bool in_image_space = false;
- if (kIsDebugBuild || maybe_copied) {
+ if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
- DCHECK(maybe_copied || in_image_space)
+ DCHECK(is_copied || in_image_space)
<< resolved_methods << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
- if (!maybe_copied || in_image_space) {
+ if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods),
sizeof(void*));
for (ArtMethod* mir_method : miranda_methods) {
ArtMethod& new_method = *out;
new_method.CopyFrom(mir_method, image_pointer_size_);
- new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda);
+ new_method.SetAccessFlags(new_method.GetAccessFlags() | kAccMiranda | kAccCopied);
DCHECK_NE(new_method.GetAccessFlags() & kAccAbstract, 0u)
<< "Miranda method should be abstract!";
move_table.emplace(mir_method, &new_method);
// yet it shouldn't have methods that are skipping access checks.
// TODO This is rather arbitrary. We should maybe support classes where only some of its
// methods are skip_access_checks.
- new_method.SetAccessFlags((new_method.GetAccessFlags() | kAccDefault) & ~kAccSkipAccessChecks);
+ constexpr uint32_t kSetFlags = kAccDefault | kAccCopied;
+ constexpr uint32_t kMaskFlags = ~kAccSkipAccessChecks;
+ new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
move_table.emplace(def_method, &new_method);
++out;
}
// this as a default, non-abstract method, since thats what it is. Also clear the
// kAccSkipAccessChecks bit since this class hasn't been verified yet it shouldn't have
// methods that are skipping access checks.
- constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict;
+ constexpr uint32_t kSetFlags = kAccDefault | kAccDefaultConflict | kAccCopied;
constexpr uint32_t kMaskFlags = ~(kAccAbstract | kAccSkipAccessChecks);
new_method.SetAccessFlags((new_method.GetAccessFlags() | kSetFlags) & kMaskFlags);
DCHECK(new_method.IsDefaultConflicting());
for (ArtMethod& method : klass->GetCopiedMethods(sizeof(void*))) {
AssertMethod(&method);
EXPECT_FALSE(method.IsDirect());
- EXPECT_TRUE(method.MightBeCopied());
+ EXPECT_TRUE(method.IsCopied());
EXPECT_TRUE(method.GetDeclaringClass()->IsInterface())
<< "declaring class: " << PrettyClass(method.GetDeclaringClass());
EXPECT_TRUE(method.GetDeclaringClass()->IsAssignableFrom(klass.Get()))
if (method->IsDirect()) {
return method;
}
- if (method->GetDeclaringClass()->IsInterface() && !method->IsMiranda()) {
+ if (method->GetDeclaringClass()->IsInterface() && !method->IsCopied()) {
return FindVirtualMethodForInterface(method, pointer_size);
}
return FindVirtualMethodForVirtual(method, pointer_size);
// Used by a class to denote that the verifier has attempted to check it at least once.
static constexpr uint32_t kAccVerificationAttempted = 0x00080000; // class (runtime)
static constexpr uint32_t kAccFastNative = 0x00080000; // method (dex only)
+// This is set by the class linker during LinkInterfaceMethods. It is used by a method to represent
+// that it was copied from its declaring class into another class. All methods marked kAccMiranda
+// and kAccDefaultConflict will have this bit set. Any kAccDefault method contained in the methods_
+// array of a concrete class will also have this bit set.
+static constexpr uint32_t kAccCopied = 0x00100000; // method (runtime)
static constexpr uint32_t kAccMiranda = 0x00200000; // method (dex only)
static constexpr uint32_t kAccDefault = 0x00400000; // method (runtime)
// This is set by the class linker during LinkInterfaceMethods. Prior to that point we do not know
RUNNING sub object, sub class, sub nonstatic
Subclass.nonstaticMethod
PASSED sub object, sub class, sub nonstatic
+Calling method ConcreteClass->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenDefaultMethodWithSuper on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethodWithSuper
+DefaultInterface.JniCallOverridenDefaultMethod
+Calling method ConcreteClass->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method ConcreteClass->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method ConcreteClass->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method DefaultInterface->JniCallNonOverridenDefaultMethod on object of type ConcreteClass
+DefaultInterface.JniCallNonOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenDefaultMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenDefaultMethod
+Calling method DefaultInterface->JniCallOverridenAbstractMethod on object of type ConcreteClass
+ConcreteClass.JniCallOverridenAbstractMethod
+Calling method DefaultInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
+Calling method DefaultInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method AbstractInterface->JniCallSoftConflictMethod on object of type ConcreteClass
+DefaultInterface.JniCallSoftConflictMethod
+Calling method ConflictInterface->JniCallConflictDefaultMethod on object of type ConcreteClass
+EXCEPTION OCCURED: java.lang.IncompatibleClassChangeError: Conflicting default method implementations void ConflictInterface.JniCallConflictDefaultMethod()
env->ReleasePrimitiveArrayCritical(array0, data0, 0);
}
}
+
+class JniCallDefaultMethodsTest {
+ public:
+ explicit JniCallDefaultMethodsTest(JNIEnv* env)
+ : env_(env), concrete_class_(env_->FindClass("ConcreteClass")) {
+ assert(!env_->ExceptionCheck());
+ assert(concrete_class_ != nullptr);
+ }
+
+ void Test() {
+ TestCalls("ConcreteClass", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethodWithSuper",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("DefaultInterface", { "JniCallNonOverridenDefaultMethod",
+ "JniCallOverridenDefaultMethod",
+ "JniCallOverridenAbstractMethod",
+ "JniCallConflictDefaultMethod",
+ "JniCallSoftConflictMethod" });
+ TestCalls("AbstractInterface", { "JniCallSoftConflictMethod" });
+ TestCalls("ConflictInterface", { "JniCallConflictDefaultMethod" });
+ }
+
+ private:
+ void TestCalls(const char* declaring_class, std::vector<const char*> methods) {
+ jmethodID new_method = env_->GetMethodID(concrete_class_, "<init>", "()V");
+ jobject obj = env_->NewObject(concrete_class_, new_method);
+ assert(!env_->ExceptionCheck());
+ assert(obj != nullptr);
+ jclass decl_class = env_->FindClass(declaring_class);
+ assert(!env_->ExceptionCheck());
+ assert(decl_class != nullptr);
+ for (const char* method : methods) {
+ jmethodID method_id = env_->GetMethodID(decl_class, method, "()V");
+ assert(!env_->ExceptionCheck());
+ printf("Calling method %s->%s on object of type ConcreteClass\n", declaring_class, method);
+ env_->CallVoidMethod(obj, method_id);
+ if (env_->ExceptionCheck()) {
+ jthrowable thrown = env_->ExceptionOccurred();
+ env_->ExceptionClear();
+ jmethodID to_string = env_->GetMethodID(
+ env_->FindClass("java/lang/Object"), "toString", "()Ljava/lang/String;");
+ jstring exception_string = (jstring) env_->CallObjectMethod(thrown, to_string);
+ assert(!env_->ExceptionCheck());
+ const char* exception_string_utf8 = env_->GetStringUTFChars(exception_string, nullptr);
+ assert(!env_->ExceptionCheck());
+ assert(exception_string_utf8 != nullptr);
+ printf("EXCEPTION OCCURED: %s\n", exception_string_utf8);
+ env_->ReleaseStringUTFChars(exception_string, exception_string_utf8);
+ }
+ }
+ }
+
+ JNIEnv* env_;
+ jclass concrete_class_;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testCallDefaultMethods(JNIEnv* env) {
+ JniCallDefaultMethodsTest(env).Test();
+}
--- /dev/null
+# /*
+# * Copyright 2016 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 public interface LAbstractInterface;
+.super Ljava/lang/Object;
+
+# public interface AbstractInterface {
+# public void JniCallSoftConflictMethod();
+# }
+
+.method public abstract JniCallSoftConflictMethod()V
+.end method
+
--- /dev/null
+# /*
+# * Copyright 2016 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 public LConcreteClass;
+.super Ljava/lang/Object;
+.implements LDefaultInterface;
+.implements LConflictInterface;
+.implements LAbstractInterface;
+
+# public class ConcreteClass implements DefaultInterface, ConflictInterface, AbstractInterface {
+# public void JniCallOverridenAbstractMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenAbstractMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethod() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenDefaultMethodWithSuper() {
+# System.out.println("ConcreteClass.JniCallOverridenDefaultMethodWithSuper");
+# DefaultInterface.super.JniCallOverridenDefaultMethod();
+# }
+# }
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method public JniCallOverridenAbstractMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenAbstractMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethodWithSuper()V
+ .locals 2
+
+ const-string v0, "ConcreteClass.JniCallOverridenDefaultMethodWithSuper"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+
+ invoke-super {p0}, LDefaultInterface;->JniCallOverridenDefaultMethod()V
+
+ return-void
+.end method
--- /dev/null
+# /*
+# * Copyright 2016 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 public interface LConflictInterface;
+.super Ljava/lang/Object;
+
+# public interface ConflictInterface {
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("ConflictInterface.JniCallConflictDefaultMethod");
+# }
+#
+# }
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "ConflictInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
--- /dev/null
+# /*
+# * Copyright 2016 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 public interface LDefaultInterface;
+.super Ljava/lang/Object;
+
+# public interface DefaultInterface {
+# public default void JniCallNonOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallNonOverridenDefaultMethod");
+# }
+#
+# public default void JniCallOverridenDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallOverridenDefaultMethod");
+# }
+#
+# public void JniCallOverridenAbstractMethod();
+#
+# public default void JniCallConflictDefaultMethod() {
+# System.out.println("DefaultInterface.JniCallConflictDefaultMethod");
+# }
+#
+# public default void JniCallSoftConflictMethod() {
+# System.out.println("DefaultInterface.JniCallSoftConflictMethod");
+# }
+# }
+
+.method public JniCallNonOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallNonOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallOverridenDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallOverridenDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public abstract JniCallOverridenAbstractMethod()V
+.end method
+
+.method public JniCallConflictDefaultMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallConflictDefaultMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
+
+.method public JniCallSoftConflictMethod()V
+ .locals 2
+
+ const-string v0, "DefaultInterface.JniCallSoftConflictMethod"
+ sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v1,v0}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V
+ return-void
+.end method
testRemoveLocalObject();
testProxyGetMethodID();
testJniCriticalSectionAndGc();
+ testCallDefaultMethods();
}
+ private static native void testCallDefaultMethods();
+
private static native void testFindClassOnAttachedNativeThread();
private static boolean testFindFieldOnAttachedNativeThreadField;
private static void testRemoveLocalObject() {
removeLocalObject(new Object());
}
-
+
private static native short shortMethod(short s1, short s2, short s3, short s4, short s5, short s6, short s7,
short s8, short s9, short s10);