Fix invoke-virtual not throwing ICCE in some cases
Due to an oversight invoke-virtual on an interface method would
not cause an ICCE to be thrown if the target method is default. This
could potentially cause incorrect methods to be called at runtime.
Bug: 32201623
Test: mma test-art-host-run-test-978-virtual-interface
Change-Id: Ie565cf2fbe8602b17be0fb051e21d221a17b518f
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 73c6cf1..1aa6a00 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -227,9 +227,10 @@
case kDirect:
return !IsDirect() || IsStatic();
case kVirtual: {
- // We have an error if we are direct or a non-default, non-miranda interface method.
+ // We have an error if we are direct or a non-copied (i.e. not part of a real class) interface
+ // method.
mirror::Class* methods_class = GetDeclaringClass();
- return IsDirect() || (methods_class->IsInterface() && !IsDefault() && !IsMiranda());
+ return IsDirect() || (methods_class->IsInterface() && !IsCopied());
}
case kSuper:
// Constructors and static methods are called with invoke-direct.
diff --git a/test/978-virtual-interface/build b/test/978-virtual-interface/build
new file mode 100755
index 0000000..14230c2
--- /dev/null
+++ b/test/978-virtual-interface/build
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2015 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.
+
+# make us exit on a failure
+set -e
+
+./default-build "$@" --experimental default-methods
diff --git a/test/978-virtual-interface/expected.txt b/test/978-virtual-interface/expected.txt
new file mode 100644
index 0000000..99071b1
--- /dev/null
+++ b/test/978-virtual-interface/expected.txt
@@ -0,0 +1 @@
+Recieved expected ICCE error!
diff --git a/test/978-virtual-interface/info.txt b/test/978-virtual-interface/info.txt
new file mode 100644
index 0000000..0b8a39f
--- /dev/null
+++ b/test/978-virtual-interface/info.txt
@@ -0,0 +1,7 @@
+Smali-based regression test for b/32201623
+
+This test cannot be run with --jvm.
+
+This test checks that we correctly detect when one attempts to invoke an
+interface method via the invoke-virtual opcode and that correct exceptions are
+sent.
diff --git a/test/978-virtual-interface/smali/Iface.smali b/test/978-virtual-interface/smali/Iface.smali
new file mode 100644
index 0000000..9c3ef7a
--- /dev/null
+++ b/test/978-virtual-interface/smali/Iface.smali
@@ -0,0 +1,110 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# // Methods are sorted in alphabetical order in dex file. We need 10 padding
+# // methods to ensure the 11'th target lines up to the same vtable slot as the
+# // first Subtype virtual method (the other 10 are the java/lang/Object;
+# // methods).
+# interface Iface {
+# public default void fakeMethod_A() {}
+# public default void fakeMethod_B() {}
+# public default void fakeMethod_C() {}
+# public default void fakeMethod_D() {}
+# public default void fakeMethod_E() {}
+# public default void fakeMethod_F() {}
+# public default void fakeMethod_G() {}
+# public default void fakeMethod_H() {}
+# public default void fakeMethod_I() {}
+# public default void fakeMethod_J() {}
+# public default void fakeMethod_K() {}
+# public default void fakeMethod_Target() {}
+# }
+
+.class public abstract interface LIface;
+
+.super Ljava/lang/Object;
+
+# // 1
+.method public fakeMethod_A()V
+ .locals 0
+ return-void
+.end method
+
+# // 2
+.method public fakeMethod_B()V
+ .locals 0
+ return-void
+.end method
+
+# // 3
+.method public fakeMethod_C()V
+ .locals 0
+ return-void
+.end method
+
+# // 4
+.method public fakeMethod_D()V
+ .locals 0
+ return-void
+.end method
+
+# // 5
+.method public fakeMethod_E()V
+ .locals 0
+ return-void
+.end method
+
+# // 5
+.method public fakeMethod_F()V
+ .locals 0
+ return-void
+.end method
+
+# // 6
+.method public fakeMethod_G()V
+ .locals 0
+ return-void
+.end method
+
+# // 7
+.method public fakeMethod_H()V
+ .locals 0
+ return-void
+.end method
+
+# // 8
+.method public fakeMethod_I()V
+ .locals 0
+ return-void
+.end method
+
+# // 9
+.method public fakeMethod_J()V
+ .locals 0
+ return-void
+.end method
+
+# // 10
+.method public fakeMethod_K()V
+ .locals 0
+ return-void
+.end method
+
+# // 11
+.method public fakeMethod_Target()V
+ .locals 0
+ return-void
+.end method
diff --git a/test/978-virtual-interface/smali/Main.smali b/test/978-virtual-interface/smali/Main.smali
new file mode 100644
index 0000000..61b82f3
--- /dev/null
+++ b/test/978-virtual-interface/smali/Main.smali
@@ -0,0 +1,50 @@
+# /*
+# * Copyright (C) 2015 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 Main {
+# public static void main(String[] s) {
+# Subtype s = new Subtype();
+# try {
+# s.callPackage();
+# System.out.println("No error thrown!");
+# } catch (IncompatibleClassChangeError e) {
+# System.out.println("Recieved expected ICCE error!");
+# }
+# }
+# }
+
+.class public LMain;
+
+.super Ljava/lang/Object;
+
+.method public static main([Ljava/lang/String;)V
+ .locals 3
+
+ new-instance v0, LSubtype;
+ invoke-direct {v0}, LSubtype;-><init>()V
+ sget-object v2, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ :try_start
+ invoke-virtual {v0}, LSubtype;->callPackage()V
+ const-string v1, "No error thrown!"
+ invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+ :try_end
+ .catch Ljava/lang/IncompatibleClassChangeError; {:try_start .. :try_end} :error_start
+ :error_start
+ const-string v1, "Recieved expected ICCE error!"
+ invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+.end method
diff --git a/test/978-virtual-interface/smali/Subtype.smali b/test/978-virtual-interface/smali/Subtype.smali
new file mode 100644
index 0000000..f876cf9
--- /dev/null
+++ b/test/978-virtual-interface/smali/Subtype.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright (C) 2015 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 Subtype extends pkg.Target implements Iface{
+# public void callPackage() {
+# // Fake into a virtual call.
+# // ((Iface)this).fakeMethod_Target();
+# }
+# }
+
+.class public LSubtype;
+
+.super Lpkg/Target;
+
+.implements LIface;
+
+.method public constructor <init>()V
+ .locals 0
+ invoke-direct {p0}, Lpkg/Target;-><init>()V
+ return-void
+.end method
+
+.method public callPackage()V
+ .locals 0
+ invoke-virtual {p0}, LIface;->fakeMethod_Target()V
+ return-void
+.end method
diff --git a/test/978-virtual-interface/smali/Target.smali b/test/978-virtual-interface/smali/Target.smali
new file mode 100644
index 0000000..70108fb
--- /dev/null
+++ b/test/978-virtual-interface/smali/Target.smali
@@ -0,0 +1,40 @@
+# /*
+# * Copyright (C) 2015 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.
+# */
+#
+# package pkg;
+# public class Target {
+# public void packageMethod() {
+# System.out.println("Package method called!");
+# }
+# }
+
+.class public Lpkg/Target;
+
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .locals 0
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
+.method packageMethod()V
+ .locals 2
+ const-string v1, "Package method called!"
+ sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;
+ invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V
+ return-void
+.end method