/* * Copyright (c) 2009, 2019, Oracle and/or its affiliates. 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. * */ /* * INVOKESPECIAL EXPECTED RESULTS * * From JVMS 3rd edition: invokespecial instruction: * * Invoke instance method; special handling for superclass, private, and instance * initialization method invocations * * The named method is resolved (5.4.3.3). Finally, if the resolved method is * protected (4.7), and it is a member of a superclass of the current class, and * the method is not declared in the same run-time package (5.3) as the current * class, then the class of objectref must be either the current class or a * subclass of the current class. * * Next, the resolved method is selected for invocation unless all of the * following conditions are true: * * The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class. * * The class of the resolved method is a superclass of the current class. * * The resolved method is not an instance initialization method (3.9). * * If the above conditions are true, the actual method to be invoked is selected * by the following lookup procedure. Let C be the direct superclass of the * current class: * * If C contains a declaration for an instance method with the same name and * descriptor as the resolved method, then this method will be invoked. * The lookup procedure terminates. * * * Otherwise, if C has a superclass, this same lookup procedure is performed * recursively using the direct superclass of C. The method to be invoked is * the result of the recursive invocation of this lookup procedure. * * * Otherwise, an AbstractMethodError? is raised. * * During resolution of the symbolic reference to the method, any of the * exceptions pertaining to method resolution documented in Section 5.4.3.3 can be * thrown. * * Otherwise, if the resolved method is an instance initialization method, and the * class in which it is declared is not the class symbolically referenced by the * instruction, a NoSuchMethodError? is thrown. * * Otherwise, if the resolved method is a class (static) method, the invokespecial * instruction throws an IncompatibleClassChangeError?. * * Otherwise, if no method matching the resolved name and descriptor is selected, * invokespecial throws an AbstractMethodError?. * * Otherwise, if the selected method is abstract, invokespecial throws an * AbstractMethodError?. * * RUNTIME EXCEPTIONS * * Otherwise, if objectref is null, the invokespecial instruction throws a NullPointerException?. * * Otherwise, if the selected method is native and the code that implements the * method cannot be bound, invokespecial throws an UnsatisfiedLinkError?. * * NOTES * * The difference between the invokespecial and the invokevirtual instructions is * that invokevirtual invokes a method based on the class of the object. The * invokespecial instruction is used to invoke instance initialization methods * (3.9) as well as private methods and methods of a superclass of the current * class. * * ACC_SUPER: * * The setting of the ACC_SUPER flag indicates which of two alternative semantics * for its invokespecial instruction the Java virtual machine is to express; the * ACC_SUPER flag exists for backward compatibility for code compiled by Sun's * older compilers for the Java programming language. All new implementations of * the Java virtual machine should implement the semantics for invokespecial * documented in this specification. All new compilers to the instruction set of * the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers * generated ClassFile? flags with ACC_SUPER unset. Sun's older Java virtual * machine implementations ignore the flag if it is set. * * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the * invokespecial instruction. * * My Translation: * 1. compile-time resolved class B * 2. A,B,C direct superclass relationships * 3. If B.m is protected * - if the caller is in B * then runtime resolved class must be in B or C * - if the caller is in C * then runtime resolved class must be in C * TODO: otherwise what is thrown? AbstractMethodError? * 4. If B.m is an instance initialization method, * invoke B.m * 5. If backward compatible caller does not set ACC_SUPER, * invoke B.m * 6. If B is not a superclass of the caller, e.g. A is caller, or unrelated X * is the caller, invoke B.m * 7. Otherwise: * If superclass of caller contains name/sig match, use it * Else, recursively through that superclass * 8. If none found, throw AbstractMethodError * * Note: there is NO mention of overriding or accessibility in determining * resolved method, except for if the compile-time type is protected. * * Case 1: B.m is protected * Caller in A: if runtime resolved class in A.m, AbstractMethodError * Caller in B: if runtime resolved class in A.m, AbstractMethodError * Case 2: B.m is an instance initialization method * Always invoke B.m * Case 3: older javac, caller does not set ACC_SUPER * Always invoke B.m * Case 4: A or X (not in hierarchy) calls invokespecial on B.m, invoke B.m * Case 5: Caller in B: * if A.m exists, call it, else AbstractMethodError * Caller in C: * if B.m exists, call it * if B.m does not exist, and A.m exists, call it */ // TODO: classes without ACC_SUPER attribute // TODO: B.m is an instance initialization method /* * invokespecial * * invokespecial is used in certain special cases to invoke a method * Specifically, invokespecial is used to invoke: * - the instance initialization method, * - a private method of this * - a method in a superclass of this * * The main use of invokespecial is to invoke an object's instance * initialization method, , during the construction phase for a new object. * For example, when you write in Java: * * new StringBuffer() * * code like the following is generated: * new java/lang/StringBuffer ; create a new StringBuffer * dup ; make an extra reference to the new instance * ; now call an instance initialization method * invokespecial java/lang/StringBuffer/()V * ; stack now contains an initialized StringBuffer. * * invokespecial is also used by the Java language by the 'super' keyword to * access a superclass's version of a method. For example, in the class: * * class Example { * // override equals * public boolean equals(Object x) { * // call Object's version of equals * return super.equals(x); * } * } * * the 'super.equals(x)' expression is compiled to: * * aload_0 ; push 'this' onto the stack * aload_1 ; push the first argument (i.e. x) onto the stack * ; now invoke Object's equals() method. * invokespecial java/lang/Object/equals(Ljava/lang/Object;)Z * * Finally, invokespecial is used to invoke a private method. Remember that * private methods are only visible to other methods belonging the same class as * the private method. * * Before performing the method invocation, the class and the method identified * by are resolved. See Chapter 9 for a description of how methods * are resolved. * * invokespecial first looks at the descriptor given in , and * determines how many argument words the method takes (this may be zero). It * pops these arguments off the operand stack. Next it pops objectref (a * reference to an object) off the operand stack. objectref must be an instance * of the class named in , or one of its subclasses. The interpreter * searches the list of methods defined by the class named in , * looking for a method called methodname whose descriptor is descriptor. This * search is not based on the runtime type of objectref, but on the compile time * type given in . * * Once a method has been located, invokespecial calls the method. First, if * the method is marked as synchronized, the monitor associated with objectref is * entered. Next, a new stack frame structure is established on the call stack. * Then the arguments for the method (which were popped off the current method's * operand stack) are placed in local variables of the new stack frame structure. * arg1 is stored in local variable 1, arg2 is stored in local variable 2 and so * on. objectref is stored in local variable 0 (the local variable used for the * special Java variable this). Finally, execution continues at the first *instruction in the bytecode of the new method. * * Methods marked as native are handled slightly differently. For native * methods, the runtime system locates the platform-specific code for the method, * loading it and linking it into the JVM if necessary. Then the native method * code is executed with the arguments popped from the operand stack. The exact * mechanism used to invoke native methods is implementation-specific. * * When the method called by invokespecial returns, any single (or double) word * return result is placed on the operand stack of the current method. If the * invoked method was marked as synchronized, the monitor associated with * objectref is exited. Execution continues at the instruction that follows * invokespecial in the bytecode. * * Notes * * 1. In Java Virtual Machine implementations prior to version JDK 1.02, this * instruction was called invokenonvirtual, and was less restrictive than * invokespecial - it wasn't limited to invoking only superclass, private or * methods. The class access flag ACC_SUPER (see Chapter 4) is used to * indicate which semantics are used by a class. In older class files, the * ACC_SUPER flag is unset. In all new classes, the ACC_SUPER flag should be set, * indicating that the restrictions enforced by invokespecial are obeyed. (In * practice, all the common uses of invokenonvirtual continue to be supported * by invokespecial, so this change should have little impact on JVM users). * */ package invokespecial; import static jdk.internal.org.objectweb.asm.Opcodes.*; import shared.AbstractGenerator; import shared.AccessType; import java.util.HashMap; import java.util.Map; public class Generator extends AbstractGenerator { public static void main (String[] args) throws Exception { new Generator(args).run(); } public Generator(String[] args) { super(args); } protected Checker getChecker(Class paramClass, Class targetClass) { return new Checker(paramClass, targetClass); } public void run() throws Exception { // Specify package names String pkg1 = "a."; String pkg2 = "b."; String[] packages = new String[] { "", pkg1, pkg2 }; boolean isPassed = true; // HIERARCHIES // The following triples will be used during further // hierarchy construction and will specify packages for A, B and C String[][] packageSets = new String[][] { { "", "", "" } , { "", pkg1, pkg1 } , { "", pkg1, pkg2 } , { pkg1, "", pkg1 } , { pkg1, "", pkg2 } , { pkg1, pkg1, "" } , { pkg1, pkg2, "" } , { pkg1, pkg1, pkg1 } , { pkg1, pkg1, pkg2 } , { pkg1, pkg2, pkg1 } , { pkg1, pkg2, pkg2 } }; String [] header = new String[] { String.format("%30s %35s", "Method access modifiers", "Call site location") , String.format("%4s %-10s %-10s %-10s %7s %7s %7s %7s %7s %7s %7s" , " # " , "A.m()" , "B.m()" , "C.m()" , " A " , "pkgA" , " B " , " pkgB" , " C " , "pkgC " , " X " ) , "-----------------------------------------------------------------------------------------------------------" }; // Print header for (String str : header) { System.out.println(str); } // Iterate over all interesting package combinations for (String[] pkgSet : packageSets) { String packageA = pkgSet[0]; String packageB = pkgSet[1]; String packageC = pkgSet[2]; String classNameA = packageA + "A"; String classNameB = packageB + "B"; String classNameC = packageC + "C"; // For all possible access modifier combinations for (AccessType accessFlagA : AccessType.values()) { for (AccessType accessFlagB : AccessType.values()) { for (AccessType accessFlagC : AccessType.values()) { Map classes = new HashMap(); String calleeClassName = classNameB; int classFlags = ACC_PUBLIC; // The following hierarhcy is created: // c.C extends b.B extends a.A extends Object - base hierarchy // X extends Object - external caller // c.Caller, b.Caller, a.Caller extends Object - package callers // Generate result storage classes.put( "Result" , new ClassGenerator( "Result" , "java.lang.Object" , ACC_PUBLIC ) .addField( ACC_PUBLIC | ACC_STATIC , "value" , "java.lang.String" ) .getClassFile() ); // Generate class A classes.put( classNameA , new ClassGenerator( classNameA , "java.lang.Object" , classFlags ) .addTargetConstructor(accessFlagA) .addTargetMethod(accessFlagA) .addCaller(calleeClassName) .getClassFile() ); // Generate class B classes.put( classNameB , new ClassGenerator( classNameB , classNameA , classFlags ) .addTargetConstructor(accessFlagB) .addTargetMethod(accessFlagB) .addCaller(calleeClassName) .getClassFile() ); // Generate class C classes.put( classNameC , new ClassGenerator( classNameC , classNameB , classFlags ) .addTargetConstructor(accessFlagC) .addTargetMethod(accessFlagC) .addCaller(calleeClassName) .getClassFile() ); // Generate class X String classNameX = "x.X"; classes.put( classNameX , new ClassGenerator( classNameX , "java.lang.Object" , classFlags ) .addTargetMethod(accessFlagC) .addCaller(calleeClassName) .getClassFile() ); // Generate package callers for (String pkg : packages) { classes.put( pkg+"Caller" , new ClassGenerator( pkg+"Caller" , "java.lang.Object" , classFlags ) .addCaller(calleeClassName) .getClassFile() ); } String[] callSites = new String[] { classNameA , packageA+"Caller" , classNameB , packageB+"Caller" , classNameC , packageC+"Caller" , classNameX }; String caseDescription = String.format( "%-10s %-10s %-10s| " , classNameA + " " + accessFlagA , classNameB + " " + accessFlagB , classNameC + " " + accessFlagC ); boolean result = exec(classes, caseDescription, calleeClassName, classNameC, callSites); isPassed = isPassed && result; } } } } // Print footer for (int i = header.length-1; i >= 0; i--) { System.out.println(header[i]); } if (executeTests) { System.out.printf("\nEXECUTION STATUS: %s\n", (isPassed? "PASSED" : "FAILED")); } } }