/* * 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. */ public class Main { private static boolean usingDalvik = "Dalvik".equals(System.getProperty("java.vm.name")); public static void expectSameString(String expected, String actual) { if (expected != actual) { throw new Error("Expected " + expected + ", got " + actual + " (different object)"); } } public static void expectDefault(Super target) { String output = target.testMethod(); // invoke-virtual Super.testMethod() Abstract abstractTarget = target; String output2 = abstractTarget.testMethod(); // invoke-interface Abstract.testMethod() expectSameString(output, output2); System.out.println("Output from " + target.getClass().getName() + ": " + output); } public static void expectConflict(Super target) { try { String output = target.testMethod(); // invoke-virtual Super.testMethod() throw new Error("Unexpected success for " + target.getClass().getName() + " output: " + output); } catch (AbstractMethodError ame) { if (usingDalvik) { throw new Error("Unexpected AbstractMethodError", ame); } // else the AME is expected on RI. } catch (IncompatibleClassChangeError expected) { } try { Abstract abstractTarget = target; String output = abstractTarget.testMethod(); // invoke-interface Abstract.testMethod() throw new Error("Unexpected success for " + target.getClass().getName() + " output: " + output); } catch (AbstractMethodError ame) { if (usingDalvik) { throw new Error("Unexpected AbstractMethodError", ame); } // else the AME is expected on RI. } catch (IncompatibleClassChangeError expected) { } System.out.println("Conflict in class " + target.getClass().getName()); } public static void expectMiranda(Super target) { try { String output = target.testMethod(); // invoke-virtual Super.testMethod() throw new Error("Unexpected success for " + target.getClass().getName() + " output: " + output); } catch (AbstractMethodError expected) { } try { Abstract abstractTarget = target; String output = abstractTarget.testMethod(); // invoke-interface Abstract.testMethod() throw new Error("Unexpected success for " + target.getClass().getName() + " output: " + output); } catch (AbstractMethodError expected) { } System.out.println("Miranda in class " + target.getClass().getName()); } public static void main(String args[]) { // Basic tests that have the interfaces D with default method and/or // DM interfaces that mask the default method ordered by . expectMiranda(new Super()); expectDefault(new Default_D1()); expectDefault(new Default_D2()); expectDefault(new Default_D3()); expectMiranda(new Miranda_D1M()); expectMiranda(new Miranda_D2M()); expectMiranda(new Miranda_D3M()); expectConflict(new Conflict_D1_D2()); expectDefault(new Default_D1_D2M()); expectDefault(new Default_D1M_D2()); expectMiranda(new Miranda_D1M_D2M()); expectConflict(new Conflict_D1_D2_D3()); expectConflict(new Conflict_D1_D2_D3M()); expectConflict(new Conflict_D1_D2M_D3()); expectDefault(new Default_D1_D2M_D3M()); expectConflict(new Conflict_D1M_D2_D3()); expectDefault(new Default_D1M_D2_D3M()); expectDefault(new Default_D1M_D2M_D3()); expectMiranda(new Miranda_D1M_D2M_D3M()); // Cases where one interface masks the method in more than one superinterface. expectMiranda(new Miranda_D1D2M()); expectDefault(new Default_D1D2D()); expectMiranda(new Miranda_AD1D2M()); expectDefault(new Default_AD1D2D()); // Cases where the interface D2 is early in the interface table but masked by D2M later. expectDefault(new Default_D2_D1_D2M()); expectMiranda(new Miranda_D2_D1M_D2M()); // Cases that involve a superclass with miranda method in the vtable. // Note: The above cases also include a miranda method in the superclass vtable // anyway because we want to test `invoke-virtual Super.testMethod()` as well // as the `invoke-interface Abstract.testMethod()`. However, miranda methods in // superclass vtable mean that all default methods in superclass interfaces, // if any, have been masked by abstract method, so processing them is a no-op. expectDefault(new Default_D1M_x_D2()); expectMiranda(new Miranda_D1M_x_D2M()); expectConflict(new Conflict_D1M_x_D2_D3()); expectDefault(new Default_D1M_x_D2_D3M()); expectDefault(new Default_D1M_x_D2M_D3()); expectMiranda(new Miranda_D1M_x_D2M_D3M()); // Cases that involve a superclass with default method in the vtable. expectConflict(new Conflict_D1_x_D2()); expectDefault(new Default_D1_x_D2M()); expectDefault(new Default_D1_x_D1MD()); expectMiranda(new Miranda_D1_x_D1M()); expectConflict(new Conflict_D1_x_D2_D3()); expectConflict(new Conflict_D1_x_D2_D3M()); expectConflict(new Conflict_D1_x_D2M_D3()); expectDefault(new Default_D1_x_D2M_D3M()); expectConflict(new Conflict_D1_x_D1MD_D2()); expectDefault(new Default_D1_x_D1MD_D2M()); expectDefault(new Default_D1_x_D1M_D2()); expectMiranda(new Miranda_D1_x_D1M_D2M()); // Cases that involve a superclass with conflict method in the vtable. expectDefault(new Default_D1_D2_x_D2M()); expectDefault(new Default_D1_D2_x_D1M()); expectMiranda(new Miranda_D1_D2_x_D1M_D2M()); expectDefault(new Default_D1_D2_x_D1MD_D2M()); expectConflict(new Conflict_D1_D2_x_D1M_D3()); expectDefault(new Default_D1_D2_x_D1M_D3M()); expectConflict(new Conflict_D1_D2_x_D2M_D3()); expectDefault(new Default_D1_D2_x_D2M_D3M()); expectConflict(new Conflict_D1_D2_x_D1MD_D3()); expectConflict(new Conflict_D1_D2_x_D1MD_D3M()); expectDefault(new Default_D1_D2_D3M_x_D1MD_D2M()); expectMiranda(new Miranda_D1_D2_D3M_x_D1M_D2M()); expectDefault(new Default_D1_D2_x_D1D2D()); expectMiranda(new Miranda_D1_D2_x_D1D2M()); expectDefault(new Default_D1_D2_x_AD1D2D()); expectMiranda(new Miranda_D1_D2_x_AD1D2M()); expectConflict(new Conflict_D1_D2_D3_x_D1D2D()); expectDefault(new Default_D1_D2_D3_x_D1D2M()); expectConflict(new Conflict_D1_D2_D3_x_AD1D2D()); expectDefault(new Default_D1_D2_D3_x_AD1D2M()); expectDefault(new Default_D1_D2_D3M_x_D1D2D()); expectMiranda(new Miranda_D1_D2_D3M_x_D1D2M()); expectDefault(new Default_D1_D2_D3M_x_AD1D2D()); expectMiranda(new Miranda_D1_D2_D3M_x_AD1D2M()); regressionTestB215510819(); } static public void regressionTestB215510819() { // The failure to fill IMT correctly would have resulted in calling the wrong method, // or triggering a check when interpreting in debug mode. Abstract abstractTarget = new B215510819Test(); String result = abstractTarget.testMethod(); System.out.println("B215510819 test result: " + result); } } class Default_D1 extends Super implements Abstract, D1 {} class Default_D2 extends Super implements Abstract, D2 {} class Default_D3 extends Super implements Abstract, D3 {} class Miranda_D1M extends Super implements Abstract, D1M {} class Miranda_D2M extends Super implements Abstract, D2M {} class Miranda_D3M extends Super implements Abstract, D3M {} class Conflict_D1_D2 extends Super implements Abstract, D1, D2 {} class Default_D1_D2M extends Super implements Abstract, D1, D2M {} class Default_D1M_D2 extends Super implements Abstract, D1M, D2 {} class Miranda_D1M_D2M extends Super implements Abstract, D1M, D2M {} class Conflict_D1_D2_D3 extends Super implements Abstract, D1, D2, D3 {} class Conflict_D1_D2_D3M extends Super implements Abstract, D1, D2, D3M {} class Conflict_D1_D2M_D3 extends Super implements Abstract, D1, D2M, D3 {} class Default_D1_D2M_D3M extends Super implements Abstract, D1, D2M, D3M {} class Conflict_D1M_D2_D3 extends Super implements Abstract, D1M, D2, D3 {} class Default_D1M_D2_D3M extends Super implements Abstract, D1M, D2, D3M {} class Default_D1M_D2M_D3 extends Super implements Abstract, D1M, D2M, D3 {} class Miranda_D1M_D2M_D3M extends Super implements Abstract, D1M, D2M, D3M {} class Miranda_D1D2M extends Super implements D1D2M {} class Default_D1D2D extends Super implements D1D2D {} class Miranda_AD1D2M extends Super implements AD1D2M {} class Default_AD1D2D extends Super implements AD1D2D {} class Default_D2_D1_D2M extends Super implements Abstract, D1, D2M {} class Miranda_D2_D1M_D2M extends Super implements Abstract, D1M, D2M {} class Default_D1M_x_D2 extends Miranda_D1M implements D2 {} class Miranda_D1M_x_D2M extends Miranda_D1M implements D2M {} class Conflict_D1M_x_D2_D3 extends Miranda_D1M implements D2, D3 {} class Default_D1M_x_D2_D3M extends Miranda_D1M implements D2, D3M {} class Default_D1M_x_D2M_D3 extends Miranda_D1M implements D2M, D3 {} class Miranda_D1M_x_D2M_D3M extends Miranda_D1M implements D2M, D3M {} class Conflict_D1_x_D2 extends Default_D1 implements D2 {} class Default_D1_x_D2M extends Default_D1 implements D2M {} class Default_D1_x_D1MD extends Default_D1 implements D1MD {} class Miranda_D1_x_D1M extends Default_D1 implements D1M {} class Conflict_D1_x_D2_D3 extends Default_D1 implements D2, D3 {} class Conflict_D1_x_D2_D3M extends Default_D1 implements D2, D3M {} class Conflict_D1_x_D2M_D3 extends Default_D1 implements D2M, D3 {} class Default_D1_x_D2M_D3M extends Default_D1 implements D2M, D3M {} class Conflict_D1_x_D1MD_D2 extends Default_D1 implements D1MD, D2 {} class Default_D1_x_D1MD_D2M extends Default_D1 implements D1MD, D2M {} class Default_D1_x_D1M_D2 extends Default_D1 implements D1M, D2 {} class Miranda_D1_x_D1M_D2M extends Default_D1 implements D1M, D2M {} class Default_D1_D2_x_D2M extends Conflict_D1_D2 implements D2M {} class Default_D1_D2_x_D1M extends Conflict_D1_D2 implements D1M {} class Miranda_D1_D2_x_D1M_D2M extends Conflict_D1_D2 implements D1M, D2M {} class Default_D1_D2_x_D1MD_D2M extends Conflict_D1_D2 implements D1MD, D2M {} class Conflict_D1_D2_x_D1M_D3 extends Conflict_D1_D2 implements D1M, D3 {} class Default_D1_D2_x_D1M_D3M extends Conflict_D1_D2 implements D1M, D3M {} class Conflict_D1_D2_x_D2M_D3 extends Conflict_D1_D2 implements D2M, D3 {} class Default_D1_D2_x_D2M_D3M extends Conflict_D1_D2 implements D2M, D3M {} class Conflict_D1_D2_x_D1MD_D3 extends Conflict_D1_D2 implements D1MD, D3 {} class Conflict_D1_D2_x_D1MD_D3M extends Conflict_D1_D2 implements D1MD, D3M {} class Default_D1_D2_D3M_x_D1MD_D2M extends Conflict_D1_D2_D3M implements D1MD, D2M {} class Miranda_D1_D2_D3M_x_D1M_D2M extends Conflict_D1_D2_D3M implements D1M, D2M {} class Miranda_D1_D2_x_D1D2M extends Conflict_D1_D2 implements D1D2M {} class Default_D1_D2_x_D1D2D extends Conflict_D1_D2 implements D1D2D {} class Miranda_D1_D2_x_AD1D2M extends Conflict_D1_D2 implements AD1D2M {} class Default_D1_D2_x_AD1D2D extends Conflict_D1_D2 implements AD1D2D {} class Default_D1_D2_D3_x_D1D2M extends Conflict_D1_D2_D3 implements D1D2M {} class Conflict_D1_D2_D3_x_D1D2D extends Conflict_D1_D2_D3 implements D1D2D {} class Default_D1_D2_D3_x_AD1D2M extends Conflict_D1_D2_D3 implements AD1D2M {} class Conflict_D1_D2_D3_x_AD1D2D extends Conflict_D1_D2_D3 implements AD1D2D {} class Miranda_D1_D2_D3M_x_D1D2M extends Conflict_D1_D2_D3M implements D1D2M {} class Default_D1_D2_D3M_x_D1D2D extends Conflict_D1_D2_D3M implements D1D2D {} class Miranda_D1_D2_D3M_x_AD1D2M extends Conflict_D1_D2_D3M implements AD1D2M {} class Default_D1_D2_D3M_x_AD1D2D extends Conflict_D1_D2_D3M implements AD1D2D {} interface B215510819Iface { // The IMT size is currently 43 and we want to cover all 43 indexes with non-copied // implementations. The IMT index for abstract methods is calculated with a hash that // includes the method name, so 43 consecutive characters in the method name would be best. // (The fact that the name hash is multiplied by 16 is OK because the size of the IMT is a // prime number and thus GCD(16, 43) = 1.) However, we do not have a contiguous range of 43 // valid characters, so we need to rely on the `hash % 43` to mask out the difference when // we use `0`..`5` between `Z` and `a`. ('Z' + 1 - 43 = '0' and '5' + 1 + 43 = 'a'.) String method_A(); String method_B(); String method_C(); String method_D(); String method_E(); String method_F(); String method_G(); String method_H(); String method_I(); String method_J(); String method_K(); String method_L(); String method_M(); String method_N(); String method_O(); String method_P(); String method_Q(); String method_R(); String method_S(); String method_T(); String method_U(); String method_V(); String method_W(); String method_X(); String method_Y(); String method_Z(); String method_0(); String method_1(); String method_2(); String method_3(); String method_4(); String method_5(); String method_a(); String method_b(); String method_c(); String method_d(); String method_e(); String method_f(); String method_g(); String method_h(); String method_i(); String method_j(); String method_k(); } // Note: Marked as abstract to avoid IMT table in this class. abstract class B215510819Base extends Default_D1 implements B215510819Iface { public String method_A() { return "B215510819 - wrong method_A!"; } public String method_B() { return "B215510819 - wrong method_B!"; } public String method_C() { return "B215510819 - wrong method_C!"; } public String method_D() { return "B215510819 - wrong method_D!"; } public String method_E() { return "B215510819 - wrong method_E!"; } public String method_F() { return "B215510819 - wrong method_F!"; } public String method_G() { return "B215510819 - wrong method_G!"; } public String method_H() { return "B215510819 - wrong method_H!"; } public String method_I() { return "B215510819 - wrong method_I!"; } public String method_J() { return "B215510819 - wrong method_J!"; } public String method_K() { return "B215510819 - wrong method_K!"; } public String method_L() { return "B215510819 - wrong method_L!"; } public String method_M() { return "B215510819 - wrong method_M!"; } public String method_N() { return "B215510819 - wrong method_N!"; } public String method_O() { return "B215510819 - wrong method_O!"; } public String method_P() { return "B215510819 - wrong method_P!"; } public String method_Q() { return "B215510819 - wrong method_Q!"; } public String method_R() { return "B215510819 - wrong method_R!"; } public String method_S() { return "B215510819 - wrong method_S!"; } public String method_T() { return "B215510819 - wrong method_T!"; } public String method_U() { return "B215510819 - wrong method_U!"; } public String method_V() { return "B215510819 - wrong method_V!"; } public String method_W() { return "B215510819 - wrong method_W!"; } public String method_X() { return "B215510819 - wrong method_X!"; } public String method_Y() { return "B215510819 - wrong method_Y!"; } public String method_Z() { return "B215510819 - wrong method_Z!"; } public String method_0() { return "B215510819 - wrong method_0!"; } public String method_1() { return "B215510819 - wrong method_1!"; } public String method_2() { return "B215510819 - wrong method_2!"; } public String method_3() { return "B215510819 - wrong method_3!"; } public String method_4() { return "B215510819 - wrong method_4!"; } public String method_5() { return "B215510819 - wrong method_5!"; } public String method_a() { return "B215510819 - wrong method_a!"; } public String method_b() { return "B215510819 - wrong method_b!"; } public String method_c() { return "B215510819 - wrong method_c!"; } public String method_d() { return "B215510819 - wrong method_d!"; } public String method_e() { return "B215510819 - wrong method_e!"; } public String method_f() { return "B215510819 - wrong method_f!"; } public String method_g() { return "B215510819 - wrong method_g!"; } public String method_h() { return "B215510819 - wrong method_h!"; } public String method_i() { return "B215510819 - wrong method_i!"; } public String method_j() { return "B215510819 - wrong method_j!"; } public String method_k() { return "B215510819 - wrong method_k!"; } } // Regression test for bug 215510819 where we failed to properly fill the IMT table // when there were no new methods or interfaces and the superclass did not have an IMT // table, so we filled the IMT from the superclass IfTable and erroneously ignored // copied implementation methods in the process. Thus calls that should go to copied // methods via an IMT conflict resolution trampoline would just end up in unrelated // concrete method when called from compiled code or from interpreter in release mode. // In debug mode, interpreter would fail a debug check. class B215510819Test extends B215510819Base {} // No new interfaces or virtuals.