/* * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /** * @test * @summary Check that the verbose message of the AME is printed correctly. * @requires !(os.arch=="arm") * @library /test/lib / * @build sun.hotspot.WhiteBox * @run driver ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission * @compile AbstractMethodErrorTest.java * @compile AME1_E.jasm AME2_C.jasm AME3_C.jasm AME4_E.jasm AME5_B.jasm AME6_B.jasm * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -XX:-BackgroundCompilation -XX:-Inline * -XX:CompileCommand=exclude,AbstractMethodErrorTest::test_ame1 * AbstractMethodErrorTest */ import sun.hotspot.WhiteBox; import compiler.whitebox.CompilerWhiteBoxTest; import java.lang.reflect.Method; // This test assembles an errorneous installation of classes. // First, compile the test by @compile. This results in a legal set // of classes. // Then, with jasm, generate incompatible classes that overwrite // the class files in the build directory. // Last, call the real test throwing an AbstractMethodError and // check the message generated. public class AbstractMethodErrorTest { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); private static boolean enableChecks = true; public static void setup_test() { // Assure all exceptions are loaded. new AbstractMethodError(); new IncompatibleClassChangeError(); enableChecks = false; // Warmup System.out.println("warmup:"); test_ame5_compiled_vtable_stub(); test_ame6_compiled_itable_stub(); enableChecks = true; // Compile try { Method method = AbstractMethodErrorTest.class.getMethod("test_ame5_compiled_vtable_stub"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException(method.getName() + " is not compiled"); } method = AbstractMethodErrorTest.class.getMethod("test_ame6_compiled_itable_stub"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException(method.getName() + " is not compiled"); } method = AME5_C.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME5_C." + method.getName() + " is not compiled"); } method = AME5_D.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME5_D." + method.getName() + " is not compiled"); } method = AME5_E.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME5_E." + method.getName() + " is not compiled"); } method = AME6_C.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME6_C." + method.getName() + " is not compiled"); } method = AME6_D.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME6_D." + method.getName() + " is not compiled"); } method = AME6_E.class.getMethod("c"); WHITE_BOX.enqueueMethodForCompilation(method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION); if (!WHITE_BOX.isMethodCompiled(method)) { throw new RuntimeException("AME6_E." + method.getName() + " is not compiled"); } } catch (NoSuchMethodException e) { } } private static String expectedErrorMessageAME1_1 = "Missing implementation of resolved method abstract " + "anAbstractMethod()Ljava/lang/String; of abstract class AME1_B."; private static String expectedErrorMessageAME1_2 = "Receiver class AME1_E does not define or inherit an implementation of the " + "resolved method abstract aFunctionOfMyInterface()Ljava/lang/String; of " + "interface AME1_C."; public static void test_ame1() { AME1_B objectAbstract = new AME1_D(); AME1_C objectInterface = new AME1_D(); objectInterface.secondFunctionOfMyInterface(); objectAbstract.anAbstractMethod(); objectInterface.aFunctionOfMyInterface(); try { objectAbstract = new AME1_E(); // AbstractMethodError gets thrown in the interpreter at: // InterpreterGenerator::generate_abstract_entry objectAbstract.anAbstractMethod(); throw new RuntimeException("Expected AbstractRuntimeError was not thrown."); } catch (AbstractMethodError e) { String errorMsg = e.getMessage(); if (errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (!errorMsg.equals(expectedErrorMessageAME1_1)) { System.out.println("Expected: " + expectedErrorMessageAME1_1 + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } catch (RuntimeException e) { throw e; } catch (Throwable e) { throw new RuntimeException("Caught unexpected exception: " + e); } try { objectInterface = new AME1_E(); // AbstractMethodError gets thrown in: // TemplateTable::invokeinterface or C-Interpreter loop objectInterface.aFunctionOfMyInterface(); throw new RuntimeException("Expected AbstractRuntimeError was not thrown."); } catch (AbstractMethodError e) { String errorMsg = e.getMessage(); if (errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (!errorMsg.equals(expectedErrorMessageAME1_2)) { // Thrown via InterpreterRuntime::throw_AbstractMethodErrorVerbose(). System.out.println("Expected: " + expectedErrorMessageAME1_2 + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } catch (Throwable e) { throw new RuntimeException("Caught unexpected exception: " + e); } } private static String expectedErrorMessageAME2_Interpreted = "Missing implementation of resolved method abstract " + "aFunctionOfMyInterface()V of interface AME2_A."; private static String expectedErrorMessageAME2_Compiled = "Receiver class AME2_C does not define or inherit an implementation of the resolved method " + "abstract aFunctionOfMyInterface()V of interface AME2_A."; public AbstractMethodErrorTest() throws InstantiationException, IllegalAccessException { try { AME2_B myAbstract = new ImplementsAllFunctions(); myAbstract.fun2(); myAbstract.aFunctionOfMyInterface(); // AME2_C does not implement the method // aFunctionOfMyInterface(). Expected runtime behavior is // throwing an AbstractMethodError. // The error will be thrown via throw_AbstractMethodErrorWithMethod() // if the template interpreter calls an abstract method by // entering the abstract method entry. myAbstract = new AME2_C(); myAbstract.fun2(); myAbstract.aFunctionOfMyInterface(); } catch (SecurityException e) { e.printStackTrace(); } } // Loop so that method gets eventually compiled/osred. public static void test_ame2() throws Exception { boolean seenInterpreted = false; boolean seenCompiled = false; // Loop to test both, the interpreted and the compiled case. for (int i = 0; i < 10000 && !(seenInterpreted && seenCompiled); ++i) { try { // Supposed to throw AME with verbose message. new AbstractMethodErrorTest(); throw new RuntimeException("Expected AbstractMethodError was not thrown."); } catch (AbstractMethodError e) { String errorMsg = e.getMessage(); // Check the message obtained. if (errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (errorMsg.equals(expectedErrorMessageAME2_Interpreted)) { seenInterpreted = true; } else if (errorMsg.equals(expectedErrorMessageAME2_Compiled)) { // Sparc and the other platforms behave differently here: // Sparc throws the exception via SharedRuntime::handle_wrong_method_abstract(), // x86, ppc and s390 via LinkResolver::runtime_resolve_virtual_method(). Thus, // sparc misses the test case for LinkResolver::runtime_resolve_virtual_method(). seenCompiled = true; } else { System.out.println("Expected: " + expectedErrorMessageAME2_Interpreted + "\n" + "or: " + expectedErrorMessageAME2_Compiled + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } } if (!(seenInterpreted && seenCompiled)) { if (seenInterpreted) { System.out.println("Saw interpreted message."); } if (seenCompiled) { System.out.println("Saw compiled message."); } throw new RuntimeException("Test did not produce wrong error messages for AbstractMethodError, " + "but it did not test both cases (interpreted and compiled)."); } } private static String expectedErrorMessageAME3_1 = "Receiver class AME3_C does not define or inherit an implementation of the resolved method " + "ma()V of class AME3_A. Selected method is abstract AME3_B.ma()V."; // Testing abstract class that extends a class that has an implementation. // Loop so that method gets eventually compiled/osred. public static void test_ame3_1() throws Exception { AME3_A c = new AME3_C(); try { // Supposed to throw AME with verbose message. c.ma(); throw new RuntimeException("Expected AbstractMethodError was not thrown."); } catch (AbstractMethodError e) { String errorMsg = e.getMessage(); // Check the message obtained. if (errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (errorMsg.equals(expectedErrorMessageAME3_1)) { // Expected test case thrown via LinkResolver::runtime_resolve_virtual_method(). } else { System.out.println("Expected: " + expectedErrorMessageAME3_1 + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } } private static String expectedErrorMessageAME3_2 = "Receiver class AME3_C does not define or inherit an implementation of " + "the resolved method abstract ma()V of abstract class AME3_B."; // Testing abstract class that extends a class that has an implementation. // Loop so that method gets eventually compiled/osred. public static void test_ame3_2() throws Exception { AME3_C c = new AME3_C(); try { // Supposed to throw AME with verbose message. c.ma(); throw new RuntimeException("Expected AbstractMethodError was not thrown."); } catch (AbstractMethodError e) { String errorMsg = e.getMessage(); // Check the message obtained. if (errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (errorMsg.equals(expectedErrorMessageAME3_2)) { // Expected test case thrown via LinkResolver::runtime_resolve_virtual_method(). } else { System.out.println("Expected: " + expectedErrorMessageAME3_2 + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } } private static String expectedErrorMessageAME4 = "Missing implementation of resolved method abstract ma()V of " + "abstract class AME4_B."; // Testing abstract class that extends a class that has an implementation. public static void test_ame4() throws Exception { AME4_C c = new AME4_C(); AME4_D d = new AME4_D(); AME4_E e = new AME4_E(); // Errorneous. AME4_A a; try { // Test: calls errorneous e.ma() in the last iteration. final int iterations = 10; for (int i = 0; i < iterations; i++) { a = e; if (i % 2 == 0 && i < iterations - 1) { a = c; } if (i % 2 == 1 && i < iterations - 1) { a = d; } // AbstractMethodError gets thrown in the interpreter at: // InterpreterGenerator::generate_abstract_entry a.ma(); } throw new RuntimeException("Expected AbstractMethodError was not thrown."); } catch (AbstractMethodError exc) { System.out.println(); String errorMsg = exc.getMessage(); // Check the message obtained. if (enableChecks && errorMsg == null) { throw new RuntimeException("Caught AbstractMethodError with empty message."); } else if (errorMsg.equals(expectedErrorMessageAME4)) { // Expected test case. } else if (enableChecks) { System.out.println("Expected: " + expectedErrorMessageAME4 + "\n" + "but got: " + errorMsg); throw new RuntimeException("Wrong error message of AbstractMethodError."); } } } private static String expectedErrorMessageAME5_VtableStub = "Receiver class AME5_B does not define or inherit an implementation of the resolved method abstract mc()V " + "of abstract class AME5_A."; // AbstractMethodErrors detected in vtable stubs. // Note: How can we verify that we really stepped through the vtable stub? // - Bimorphic inlining should not happen since we have no profiling data when // we compile the method // - As a result, an inline cache call should be generated // - This inline cache call is patched into a real vtable call at the first // re-resolve, which happens constantly during the first 10 iterations of the loop. // => we should be fine! :-) public static void test_ame5_compiled_vtable_stub() { // Allocated the objects we need and call a valid method. boolean caught_ame = false; AME5_B b = new AME5_B(); AME5_C c = new AME5_C(); AME5_D d = new AME5_D(); AME5_E e = new AME5_E(); b.ma(); c.ma(); d.ma(); e.ma(); try { final int iterations = 10; // Test: calls b.c() in the last iteration. for (int i = 0; i < iterations; i++) { AME5_A a = b; if (i % 3 == 0 && i < iterations - 1) { a = c; } if (i % 3 == 1 && i < iterations - 1) { a = d; } if (i % 3 == 2 && i < iterations - 1) { a = e; } a.mc(); } System.out.println(); } catch (AbstractMethodError exc) { caught_ame = true; System.out.println(); String errorMsg = exc.getMessage(); if (enableChecks && errorMsg == null) { System.out.println(exc); throw new RuntimeException("Empty error message of AbstractMethodError."); } if (enableChecks && !errorMsg.equals(expectedErrorMessageAME5_VtableStub)) { // Thrown via SharedRuntime::handle_wrong_method_abstract(). System.out.println("Expected: " + expectedErrorMessageAME5_VtableStub + "\n" + "but got: " + errorMsg); System.out.println(exc); throw new RuntimeException("Wrong error message of AbstractMethodError."); } if (enableChecks) { System.out.println("Passed with message: " + errorMsg); } } catch (Throwable exc) { throw exc; } // Check that we got the exception at some point. if (enableChecks && !caught_ame) { throw new RuntimeException("Expected AbstractMethodError was not thrown."); } } private static String expectedErrorMessageAME6_ItableStub = "Receiver class AME6_B does not define or inherit an implementation of the resolved" + " method abstract mc()V of interface AME6_A."; // ------------------------------------------------------------------------- // AbstractMethodErrors detected in itable stubs. // Note: How can we verify that we really stepped through the itable stub? // - Bimorphic inlining should not happen since we have no profiling data when // we compile the method // - As a result, an inline cache call should be generated // - This inline cache call is patched into a real vtable call at the first // re-resolve, which happens constantly during the first 10 iterations of the loop. // => we should be fine! :-) public static void test_ame6_compiled_itable_stub() { // Allocated the objects we need and call a valid method. boolean caught_ame = false; AME6_B b = new AME6_B(); AME6_C c = new AME6_C(); AME6_D d = new AME6_D(); AME6_E e = new AME6_E(); b.ma(); c.ma(); d.ma(); e.ma(); try { final int iterations = 10; // Test: calls b.c() in the last iteration. for (int i = 0; i < iterations; i++) { AME6_A a = b; if (i % 3 == 0 && i < iterations - 1) { a = c; } if (i % 3 == 1 && i < iterations - 1) { a = d; } if (i % 3 == 2 && i < iterations - 1) { a = e; } a.mc(); } System.out.println(); } catch (AbstractMethodError exc) { caught_ame = true; System.out.println(); String errorMsg = exc.getMessage(); if (enableChecks && errorMsg == null) { System.out.println(exc); throw new RuntimeException("Empty error message of AbstractMethodError."); } if (enableChecks && !errorMsg.equals(expectedErrorMessageAME6_ItableStub)) { // Thrown via LinkResolver::runtime_resolve_interface_method(). System.out.println("Expected: " + expectedErrorMessageAME6_ItableStub + "\n" + "but got: " + errorMsg); System.out.println(exc); throw new RuntimeException("Wrong error message of AbstractMethodError."); } if (enableChecks) { System.out.println("Passed with message: " + errorMsg); } } catch (Throwable exc) { throw exc; } // Check that we got the exception at some point. if (enableChecks && !caught_ame) { throw new RuntimeException("Expected AbstractMethodError was not thrown."); } } public static void main(String[] args) throws Exception { setup_test(); test_ame1(); test_ame2(); test_ame3_1(); test_ame3_2(); test_ame4(); test_ame5_compiled_vtable_stub(); test_ame6_compiled_itable_stub(); } } // Helper classes to test abstract method error. // // Errorneous versions of these classes are implemented in java // assembler. // ------------------------------------------------------------------------- // This error should be detected interpreted. // // Class hierachy: // // C // interface, defines aFunctionOfMyInterface() // | // A | // interface // | | // B | // abstract class, defines anAbstractMethod() // \ / // E // errorneous class implementation lacks methods C::aFunctionOfMyInterface() // B::anAbstractMethod() interface AME1_A { public String firstFunctionOfMyInterface0(); public String secondFunctionOfMyInterface0(); } abstract class AME1_B implements AME1_A { abstract public String firstAbstractMethod(); abstract public String secondAbstractMethod(); abstract public String anAbstractMethod(); } interface AME1_C { public String firstFunctionOfMyInterface(); public String secondFunctionOfMyInterface(); public String aFunctionOfMyInterface(); } class AME1_D extends AME1_B implements AME1_C { public AME1_D() { } public String firstAbstractMethod() { return this.getClass().getName(); } public String secondAbstractMethod() { return this.getClass().getName(); } public String anAbstractMethod() { return this.getClass().getName(); } public String firstFunctionOfMyInterface0() { return this.getClass().getName(); } public String secondFunctionOfMyInterface0() { return this.getClass().getName(); } public String firstFunctionOfMyInterface() { return this.getClass().getName(); } public String secondFunctionOfMyInterface() { return this.getClass().getName(); } public String aFunctionOfMyInterface() { return this.getClass().getName(); } } class AME1_E extends AME1_B implements AME1_C { public AME1_E() { } public String firstAbstractMethod() { return this.getClass().getName(); } public String secondAbstractMethod() { return this.getClass().getName(); } // This method is missing in the .jasm implementation. public String anAbstractMethod() { return this.getClass().getName(); } public String firstFunctionOfMyInterface0() { return this.getClass().getName(); } public String secondFunctionOfMyInterface0() { return this.getClass().getName(); } public String firstFunctionOfMyInterface() { return this.getClass().getName(); } public String secondFunctionOfMyInterface() { return this.getClass().getName(); } // This method is missing in the .jasm implementation. public String aFunctionOfMyInterface() { return this.getClass().getName(); } } // ------------------------------------------------------------------------- // This error should be detected interpreted. // // Class hierachy: // // A // an interface declaring aFunctionOfMyInterface() // | // B // an abstract class // | // C // errorneous implementation lacks method A::aFunctionOfMyInterface() // interface AME2_A { public void aFunctionOfMyInterface(); } abstract class AME2_B implements AME2_A { abstract public void fun2(); } class ImplementsAllFunctions extends AME2_B { public ImplementsAllFunctions() {} public void fun2() { //System.out.print("You called public void ImplementsAllFunctions::fun2().\n"); } public void aFunctionOfMyInterface() { //System.out.print("You called public void ImplementsAllFunctions::aFunctionOfMyInterface()\n"); } } class AME2_C extends AME2_B { public AME2_C() {} public void fun2() { //System.out.print("You called public void AME2_C::fun2().\n"); } // This method is missing in the .jasm implementation. public void aFunctionOfMyInterface() { //System.out.print("You called public void AME2_C::aFunctionOfMyInterface()\n"); } } // ----------------------------------------------------------------------- // Test AbstractMethod error shadowing existing implementation. // // Class hierachy: // // A // a class implementing m() // | // B // an abstract class defining m() abstract // | // C // an errorneous class lacking an implementation of m() // class AME3_A { public void ma() { System.out.print("A.ma() "); } } abstract class AME3_B extends AME3_A { public abstract void ma(); } class AME3_C extends AME3_B { // This method is missing in the .jasm implementation. public void ma() { System.out.print("C.ma() "); } } // ----------------------------------------------------------------------- // Test AbstractMethod error shadowing existing implementation. In // this test there are several subclasses of the abstract class. // // Class hierachy: // // A // A: a class implementing ma() // | // B // B: an abstract class defining ma() abstract // / | \ // C D E // E: an errorneous class lacking an implementation of ma() // class AME4_A { public void ma() { System.out.print("A.ma() "); } } abstract class AME4_B extends AME4_A { public abstract void ma(); } class AME4_C extends AME4_B { public void ma() { System.out.print("C.ma() "); } } class AME4_D extends AME4_B { public void ma() { System.out.print("D.ma() "); } } class AME4_E extends AME4_B { // This method is missing in the .jasm implementation. public void ma() { System.out.print("E.ma() "); } } // ------------------------------------------------------------------------- // This error should be detected while processing the vtable stub. // // Class hierachy: // // A__ // abstract // /|\ \ // C D E \ // B // Bad class, missing method implementation. // // Test: // - Call D.mc() / E.mc() / F.mc() several times to force real vtable call constrution // - Call errorneous B.mc() in the end to raise the AbstraceMethodError abstract class AME5_A { abstract void ma(); abstract void mb(); abstract void mc(); } class AME5_B extends AME5_A { void ma() { System.out.print("B.ma() "); } void mb() { System.out.print("B.mb() "); } // This method is missing in the .jasm implementation. void mc() { System.out.print("B.mc() "); } } class AME5_C extends AME5_A { void ma() { System.out.print("C.ma() "); } void mb() { System.out.print("C.mb() "); } void mc() { System.out.print("C.mc() "); } } class AME5_D extends AME5_A { void ma() { System.out.print("D.ma() "); } void mb() { System.out.print("D.mb() "); } void mc() { System.out.print("D.mc() "); } } class AME5_E extends AME5_A { void ma() { System.out.print("E.ma() "); } void mb() { System.out.print("E.mb() "); } void mc() { System.out.print("E.mc() "); } } //------------------------------------------------------------------------- // Test AbstractMethod error detected while processing // the itable stub. // // Class hierachy: // // A__ (interface) // /|\ \ // C D E \ // B (bad class, missing method) // // Test: // - Call D.mc() / E.mc() / F.mc() several times to force real itable call constrution // - Call errorneous B.mc() in the end to raise the AbstraceMethodError interface AME6_A { abstract void ma(); abstract void mb(); abstract void mc(); } class AME6_B implements AME6_A { public void ma() { System.out.print("B.ma() "); } public void mb() { System.out.print("B.mb() "); } // This method is missing in the .jasm implementation. public void mc() { System.out.print("B.mc() "); } } class AME6_C implements AME6_A { public void ma() { System.out.print("C.ma() "); } public void mb() { System.out.print("C.mb() "); } public void mc() { System.out.print("C.mc() "); } } class AME6_D implements AME6_A { public void ma() { System.out.print("D.ma() "); } public void mb() { System.out.print("D.mb() "); } public void mc() { System.out.print("D.mc() "); } } class AME6_E implements AME6_A { public void ma() { System.out.print("E.ma() "); } public void mb() { System.out.print("E.mb() "); } public void mc() { System.out.print("E.mc() "); } }