Fix interface method linking for an edge case.
Make sure we select the implementation from the most-derived
class for an interface method if the superclass has more
than one method with the matching signature in its vtable.
Test: New tests in 182-method-linking.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --jvm
Bug: 211854716
Change-Id: Iee843e1acf56b576b864abbcd9c28a69b1862eeb
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 580ae03..64adc55 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -8096,10 +8096,22 @@
} else {
auto it2 = super_vtable_signatures.FindWithHash(interface_method, hash);
if (it2 != super_vtable_signatures.end()) {
- // FIXME: If there are multiple vtable methods with the same signature, the one
- // with the highest vtable index is not nessarily the one in most-derived class.
- // However, we're preserving old behavior for now. b/211854716
+ // If there are multiple vtable methods with the same signature, the one with
+ // the highest vtable index is not nessarily the one in most-derived class.
+ // Find the most-derived method. See b/211854716 .
vtable_method = super_vtable_accessor.GetVTableEntry(*it2);
+ if (UNLIKELY(!same_signature_vtable_lists.empty())) {
+ size_t current_index = *it2;
+ while (same_signature_vtable_lists[current_index] != dex::kDexNoIndex) {
+ DCHECK_LT(same_signature_vtable_lists[current_index], current_index);
+ current_index = same_signature_vtable_lists[current_index];
+ ArtMethod* current_method = super_vtable_accessor.GetVTableEntry(current_index);
+ ObjPtr<mirror::Class> current_class = current_method->GetDeclaringClass();
+ if (current_class->IsSubClass(vtable_method->GetDeclaringClass())) {
+ vtable_method = current_method;
+ }
+ }
+ }
found = true;
}
}
@@ -8107,6 +8119,7 @@
if (found) {
DCHECK(vtable_method != nullptr);
if (!vtable_method->IsAbstract() && !vtable_method->IsPublic()) {
+ // FIXME: Delay the exception until we actually try to call the method. b/211854716
sants.reset();
ThrowIllegalAccessErrorForImplementingMethod(klass, vtable_method, interface_method);
return 0u;
diff --git a/test/182-method-linking/expected-stdout.txt b/test/182-method-linking/expected-stdout.txt
index 712b0d7..56ad59d 100644
--- a/test/182-method-linking/expected-stdout.txt
+++ b/test/182-method-linking/expected-stdout.txt
@@ -32,3 +32,19 @@
pkg2.D2.foo
Calling pkg2.D2.foo on pkg2.D2
pkg2.D2.foo
+Calling pkg1.I1.foo on pkg1.C2I1
+pkg1.C2.foo
+Calling pkg2.I2.foo on pkg1.C2I2
+pkg1.C2.foo
+Calling pkg1.I1.foo on pkg2.D2I1
+pkg2.D2.foo
+Calling pkg2.I2.foo on pkg2.D2I2
+pkg2.D2.foo
+Calling pkg1.I1.foo on pkg1.CXI1
+Caught IllegalAccessError
+Calling pkg2.I2.foo on pkg1.CXI2
+Caught IllegalAccessError
+Calling pkg1.I1.foo on pkg2.DXI1
+Caught IllegalAccessError
+Calling pkg2.I2.foo on pkg2.DXI2
+Caught IllegalAccessError
diff --git a/test/182-method-linking/src/Main.java b/test/182-method-linking/src/Main.java
index 50102c1..3902956 100644
--- a/test/182-method-linking/src/Main.java
+++ b/test/182-method-linking/src/Main.java
@@ -17,12 +17,28 @@
import pkg1.A;
import pkg1.C;
import pkg1.C2;
+import pkg1.C2I1;
+import pkg1.C2I2;
+import pkg1.CXI1;
+import pkg1.CXI2;
+import pkg1.I1;
import pkg2.B;
import pkg2.D;
import pkg2.D2;
+import pkg2.D2I1;
+import pkg2.D2I2;
+import pkg2.DXI1;
+import pkg2.DXI2;
+import pkg2.I2;
public class Main {
public static void main(String args[]) {
+ try {
+ Class.forName("dalvik.system.PathClassLoader");
+ } catch (ClassNotFoundException e) {
+ usingRI = true;
+ }
+
// A single method signature can result in multiple vtable entries
// when package-private methods from different packages are involved.
// All classes here define the method `void foo()` but classes
@@ -63,5 +79,82 @@
d2.callBFoo(); // pkg2.D2.foo (overrides package-private pkg2.B.foo in the same package)
d2.callC2Foo(); // pkg2.D2.foo (overrides public pkg2.C2.foo)
d2.callD2Foo(); // pkg2.D2.foo
+
+ // Interface methods always target the method in the most-derived class with implementation
+ // even when package-private methods from different packages are involved.
+ //
+ // Test interface calls through the following interfaces:
+ // interface pkg1.I1 { ... }
+ // interface pkg2.I2 { ... }
+ // that declare a public `void foo()` for concrete classes
+ // class pkg1.C2I1 extends pkg1.C2 implements pkg1.I1 {}
+ // class pkg1.C2I2 extends pkg1.C2 implements pkg2.I2 {}
+ // class pkg2.D2I1 extends pkg2.D2 implements pkg1.I1 {}
+ // class pkg2.D2I2 extends pkg2.D2 implements pkg2.I2 {}
+ // class pkg1.CXI1 extends pkg1.CX implements pkg1.I1 {}
+ // class pkg1.CXI2 extends pkg1.CX implements pkg2.I2 {}
+ // class pkg2.DXI1 extends pkg2.DX implements pkg1.I1 {}
+ // class pkg2.DXI2 extends pkg2.DX implements pkg2.I2 {}
+ // with helper classes `pkg1.C2` and `pkg2.D2` from previous tests and helper class
+ // class pkg2.BX extends pkg1.A { ... }
+ // defining a public `void foo()` but helper classes
+ // class pkg1.CX extends pkg2.BX { ... }
+ // class pkg2.DX extends pkg1.CX { ... }
+ // defining a package-private `void foo()`. This is a compilation error in Java,
+ // so we're using different definitions for `pkg1.I1`, `pkg2.I2` and `pkg2.BX` in
+ // src/ for compiling other classes and in src2/ for their run-time definition.
+
+ C2I1 c2i1 = new C2I1();
+ I1.callI1Foo(c2i1); // pkg1.C2.foo
+
+ C2I2 c2i2 = new C2I2();
+ I2.callI2Foo(c2i2); // pkg1.C2.foo
+
+ D2I1 d2i1 = new D2I1();
+ I1.callI1Foo(d2i1); // pkg1.D2.foo
+
+ D2I2 d2i2 = new D2I2();
+ I2.callI2Foo(d2i2); // pkg1.D2.foo
+
+ try {
+ CXI1 cxi1 = new CXI1();
+ I1.callI1Foo(cxi1);
+ } catch (IllegalAccessError expected) {
+ printOnDalvik("Calling pkg1.I1.foo on pkg1.CXI1");
+ System.out.println("Caught IllegalAccessError");
+ }
+
+ try {
+ CXI2 cxi2 = new CXI2();
+ I2.callI2Foo(cxi2);
+ } catch (IllegalAccessError expected) {
+ printOnDalvik("Calling pkg2.I2.foo on pkg1.CXI2");
+ System.out.println("Caught IllegalAccessError");
+ }
+
+ try {
+ DXI1 dxi1 = new DXI1();
+ I1.callI1Foo(dxi1);
+ } catch (IllegalAccessError expected) {
+ printOnDalvik("Calling pkg1.I1.foo on pkg2.DXI1");
+ System.out.println("Caught IllegalAccessError");
+ }
+
+ try {
+ DXI2 dxi2 = new DXI2();
+ I2.callI2Foo(dxi2);
+ } catch (IllegalAccessError expected) {
+ printOnDalvik("Calling pkg2.I2.foo on pkg2.DXI2");
+ System.out.println("Caught IllegalAccessError");
+ }
}
+
+ private static void printOnDalvik(String line) {
+ if (!usingRI) {
+ // FIXME: Delay IAE until calling the method. Bug: 211854716
+ System.out.println(line);
+ }
+ }
+
+ private static boolean usingRI = false;
}
diff --git a/test/182-method-linking/src/pkg1/C2I1.java b/test/182-method-linking/src/pkg1/C2I1.java
new file mode 100644
index 0000000..de4f7a2
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/C2I1.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class C2I1 extends C2 implements I1 {
+}
diff --git a/test/182-method-linking/src/pkg1/C2I2.java b/test/182-method-linking/src/pkg1/C2I2.java
new file mode 100644
index 0000000..43464e7
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/C2I2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+import pkg2.I2;
+
+public class C2I2 extends C2 implements I2 {
+}
diff --git a/test/182-method-linking/src/pkg1/CX.java b/test/182-method-linking/src/pkg1/CX.java
new file mode 100644
index 0000000..ffb0d03
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/CX.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+import pkg2.BX;
+
+public class CX extends BX {
+ /*package-private*/ void foo() {
+ System.out.println("pkg1.CX.foo");
+ }
+}
diff --git a/test/182-method-linking/src/pkg1/CXI1.java b/test/182-method-linking/src/pkg1/CXI1.java
new file mode 100644
index 0000000..2884d3a
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/CXI1.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+public class CXI1 extends CX implements I1 {
+}
diff --git a/test/182-method-linking/src/pkg1/CXI2.java b/test/182-method-linking/src/pkg1/CXI2.java
new file mode 100644
index 0000000..1029e03
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/CXI2.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+import pkg2.I2;
+
+public class CXI2 extends CX implements I2 {
+}
diff --git a/test/182-method-linking/src/pkg1/I1.java b/test/182-method-linking/src/pkg1/I1.java
new file mode 100644
index 0000000..793dca5
--- /dev/null
+++ b/test/182-method-linking/src/pkg1/I1.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+// This definition is used for compiling but the interface used at runtime is in src2/.
+// The commented out code below is enabled in src2/.
+public interface I1 {
+ static void callI1Foo(I1 i1) {
+ System.out.println("Calling pkg1.I1.foo on " + i1.getClass().getName());
+ // i1.foo();
+ };
+
+ // void foo();
+}
diff --git a/test/182-method-linking/src/pkg2/BX.java b/test/182-method-linking/src/pkg2/BX.java
new file mode 100644
index 0000000..2265d24
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/BX.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+import pkg1.A;
+
+// This definition is used for compiling but the class used at runtime is in src2/.
+public class BX extends A {
+ /* public in src2/ */ void foo() {
+ System.out.println("pkg2.BX.foo");
+ }
+}
diff --git a/test/182-method-linking/src/pkg2/D2I1.java b/test/182-method-linking/src/pkg2/D2I1.java
new file mode 100644
index 0000000..0b18758
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/D2I1.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+import pkg1.I1;
+
+public class D2I1 extends D2 implements I1 {
+}
diff --git a/test/182-method-linking/src/pkg2/D2I2.java b/test/182-method-linking/src/pkg2/D2I2.java
new file mode 100644
index 0000000..9e58f44
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/D2I2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class D2I2 extends D2 implements I2 {
+}
diff --git a/test/182-method-linking/src/pkg2/DX.java b/test/182-method-linking/src/pkg2/DX.java
new file mode 100644
index 0000000..4927cda
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/DX.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+import pkg1.CX;
+
+public class DX extends CX {
+ /*package-private*/ void foo() {
+ System.out.println("pkg1.DX.foo");
+ }
+}
diff --git a/test/182-method-linking/src/pkg2/DXI1.java b/test/182-method-linking/src/pkg2/DXI1.java
new file mode 100644
index 0000000..cb4ca8a
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/DXI1.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+import pkg1.I1;
+
+public class DXI1 extends DX implements I1 {
+}
diff --git a/test/182-method-linking/src/pkg2/DXI2.java b/test/182-method-linking/src/pkg2/DXI2.java
new file mode 100644
index 0000000..de29a76
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/DXI2.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+public class DXI2 extends DX implements I2 {
+}
diff --git a/test/182-method-linking/src/pkg2/I2.java b/test/182-method-linking/src/pkg2/I2.java
new file mode 100644
index 0000000..abce043
--- /dev/null
+++ b/test/182-method-linking/src/pkg2/I2.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+// This definition is used for compiling but the interface used at runtime is in src2/.
+// The commented out code below is enabled in src2/.
+public interface I2 {
+ static void callI2Foo(I2 i2) {
+ System.out.println("Calling pkg2.I2.foo on " + i2.getClass().getName());
+ // i2.foo();
+ };
+
+ // void foo();
+}
diff --git a/test/182-method-linking/src2/pkg1/I1.java b/test/182-method-linking/src2/pkg1/I1.java
new file mode 100644
index 0000000..14cac9c
--- /dev/null
+++ b/test/182-method-linking/src2/pkg1/I1.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg1;
+
+// This is the interface class I1 used at runtime.
+public interface I1 {
+ static void callI1Foo(I1 i1) {
+ System.out.println("Calling pkg1.I1.foo on " + i1.getClass().getName());
+ i1.foo();
+ };
+
+ void foo();
+}
diff --git a/test/182-method-linking/src2/pkg2/BX.java b/test/182-method-linking/src2/pkg2/BX.java
new file mode 100644
index 0000000..7634563
--- /dev/null
+++ b/test/182-method-linking/src2/pkg2/BX.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+import pkg1.A;
+
+// This is the class BX used at runtime.
+public class BX extends A {
+ public void foo() {
+ System.out.println("pkg2.BX.foo");
+ }
+}
diff --git a/test/182-method-linking/src2/pkg2/I2.java b/test/182-method-linking/src2/pkg2/I2.java
new file mode 100644
index 0000000..4e004c5
--- /dev/null
+++ b/test/182-method-linking/src2/pkg2/I2.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2022 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 pkg2;
+
+// This is the interface class I2 used at runtime.
+public interface I2 {
+ static void callI2Foo(I2 i2) {
+ System.out.println("Calling pkg2.I2.foo on " + i2.getClass().getName());
+ i2.foo();
+ };
+
+ void foo();
+}