1 /* 2 * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 /* 26 * INVOKESPECIAL EXPECTED RESULTS 27 * 28 * From JVMS 3rd edition: invokespecial instruction: 29 * 30 * Invoke instance method; special handling for superclass, private, and instance 31 * initialization method invocations 32 * 33 * The named method is resolved (5.4.3.3). Finally, if the resolved method is 34 * protected (4.7), and it is a member of a superclass of the current class, and 35 * the method is not declared in the same run-time package (5.3) as the current 36 * class, then the class of objectref must be either the current class or a 37 * subclass of the current class. 38 * 39 * Next, the resolved method is selected for invocation unless all of the 40 * following conditions are true: 41 * * The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class. 42 * * The class of the resolved method is a superclass of the current class. 43 * * The resolved method is not an instance initialization method (3.9). 44 * 45 * If the above conditions are true, the actual method to be invoked is selected 46 * by the following lookup procedure. Let C be the direct superclass of the 47 * current class: 48 * * If C contains a declaration for an instance method with the same name and 49 * descriptor as the resolved method, then this method will be invoked. 50 * The lookup procedure terminates. 51 * 52 * * Otherwise, if C has a superclass, this same lookup procedure is performed 53 * recursively using the direct superclass of C. The method to be invoked is 54 * the result of the recursive invocation of this lookup procedure. 55 * 56 * * Otherwise, an AbstractMethodError? is raised. 57 * 58 * During resolution of the symbolic reference to the method, any of the 59 * exceptions pertaining to method resolution documented in Section 5.4.3.3 can be 60 * thrown. 61 * 62 * Otherwise, if the resolved method is an instance initialization method, and the 63 * class in which it is declared is not the class symbolically referenced by the 64 * instruction, a NoSuchMethodError? is thrown. 65 * 66 * Otherwise, if the resolved method is a class (static) method, the invokespecial 67 * instruction throws an IncompatibleClassChangeError?. 68 * 69 * Otherwise, if no method matching the resolved name and descriptor is selected, 70 * invokespecial throws an AbstractMethodError?. 71 * 72 * Otherwise, if the selected method is abstract, invokespecial throws an 73 * AbstractMethodError?. 74 * 75 * RUNTIME EXCEPTIONS 76 * 77 * Otherwise, if objectref is null, the invokespecial instruction throws a NullPointerException?. 78 * 79 * Otherwise, if the selected method is native and the code that implements the 80 * method cannot be bound, invokespecial throws an UnsatisfiedLinkError?. 81 * 82 * NOTES 83 * 84 * The difference between the invokespecial and the invokevirtual instructions is 85 * that invokevirtual invokes a method based on the class of the object. The 86 * invokespecial instruction is used to invoke instance initialization methods 87 * (3.9) as well as private methods and methods of a superclass of the current 88 * class. 89 * 90 * ACC_SUPER: 91 * 92 * The setting of the ACC_SUPER flag indicates which of two alternative semantics 93 * for its invokespecial instruction the Java virtual machine is to express; the 94 * ACC_SUPER flag exists for backward compatibility for code compiled by Sun's 95 * older compilers for the Java programming language. All new implementations of 96 * the Java virtual machine should implement the semantics for invokespecial 97 * documented in this specification. All new compilers to the instruction set of 98 * the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers 99 * generated ClassFile? flags with ACC_SUPER unset. Sun's older Java virtual 100 * machine implementations ignore the flag if it is set. 101 * 102 * ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the 103 * invokespecial instruction. 104 * 105 * My Translation: 106 * 1. compile-time resolved class B 107 * 2. A,B,C direct superclass relationships 108 * 3. If B.m is protected 109 * - if the caller is in B 110 * then runtime resolved class must be in B or C 111 * - if the caller is in C 112 * then runtime resolved class must be in C 113 * TODO: otherwise what is thrown? <noWikiWord>AbstractMethodError? 114 * 4. If B.m is an instance initialization method, 115 * invoke B.m 116 * 5. If backward compatible caller does not set ACC_SUPER, 117 * invoke B.m 118 * 6. If B is not a superclass of the caller, e.g. A is caller, or unrelated X 119 * is the caller, invoke B.m 120 * 7. Otherwise: 121 * If superclass of caller contains name/sig match, use it 122 * Else, recursively through that superclass 123 * 8. If none found, throw AbstractMethodError 124 * 125 * Note: there is NO mention of overriding or accessibility in determining 126 * resolved method, except for if the compile-time type is protected. 127 * 128 * Case 1: B.m is protected 129 * Caller in A: if runtime resolved class in A.m, AbstractMethodError 130 * Caller in B: if runtime resolved class in A.m, AbstractMethodError 131 * Case 2: B.m is an instance initialization method 132 * Always invoke B.m 133 * Case 3: older javac, caller does not set ACC_SUPER 134 * Always invoke B.m 135 * Case 4: A or X (not in hierarchy) calls invokespecial on B.m, invoke B.m 136 * Case 5: Caller in B: 137 * if A.m exists, call it, else <noWikiWord>AbstractMethodError 138 * Caller in C: 139 * if B.m exists, call it 140 * if B.m does not exist, and A.m exists, call it 141 */ 142 143 // TODO: classes without ACC_SUPER attribute 144 // TODO: B.m is an instance initialization method 145 146 /* 147 * invokespecial <method-spec> 148 * 149 * invokespecial is used in certain special cases to invoke a method 150 * Specifically, invokespecial is used to invoke: 151 * - the instance initialization method, <init> 152 * - a private method of this 153 * - a method in a superclass of this 154 * 155 * The main use of invokespecial is to invoke an object's instance 156 * initialization method, <init>, during the construction phase for a new object. 157 * For example, when you write in Java: 158 * 159 * new StringBuffer() 160 * 161 * code like the following is generated: 162 * new java/lang/StringBuffer ; create a new StringBuffer 163 * dup ; make an extra reference to the new instance 164 * ; now call an instance initialization method 165 * invokespecial java/lang/StringBuffer/<init>()V 166 * ; stack now contains an initialized StringBuffer. 167 * 168 * invokespecial is also used by the Java language by the 'super' keyword to 169 * access a superclass's version of a method. For example, in the class: 170 * 171 * class Example { 172 * // override equals 173 * public boolean equals(Object x) { 174 * // call Object's version of equals 175 * return super.equals(x); 176 * } 177 * } 178 * 179 * the 'super.equals(x)' expression is compiled to: 180 * 181 * aload_0 ; push 'this' onto the stack 182 * aload_1 ; push the first argument (i.e. x) onto the stack 183 * ; now invoke Object's equals() method. 184 * invokespecial java/lang/Object/equals(Ljava/lang/Object;)Z 185 * 186 * Finally, invokespecial is used to invoke a private method. Remember that 187 * private methods are only visible to other methods belonging the same class as 188 * the private method. 189 * 190 * Before performing the method invocation, the class and the method identified 191 * by <method-spec> are resolved. See Chapter 9 for a description of how methods 192 * are resolved. 193 * 194 * invokespecial first looks at the descriptor given in <method-spec>, and 195 * determines how many argument words the method takes (this may be zero). It 196 * pops these arguments off the operand stack. Next it pops objectref (a 197 * reference to an object) off the operand stack. objectref must be an instance 198 * of the class named in <method-spec>, or one of its subclasses. The interpreter 199 * searches the list of methods defined by the class named in <method-spec>, 200 * looking for a method called methodname whose descriptor is descriptor. This 201 * search is not based on the runtime type of objectref, but on the compile time 202 * type given in <method-spec>. 203 * 204 * Once a method has been located, invokespecial calls the method. First, if 205 * the method is marked as synchronized, the monitor associated with objectref is 206 * entered. Next, a new stack frame structure is established on the call stack. 207 * Then the arguments for the method (which were popped off the current method's 208 * operand stack) are placed in local variables of the new stack frame structure. 209 * arg1 is stored in local variable 1, arg2 is stored in local variable 2 and so 210 * on. objectref is stored in local variable 0 (the local variable used for the 211 * special Java variable this). Finally, execution continues at the first 212 *instruction in the bytecode of the new method. 213 * 214 * Methods marked as native are handled slightly differently. For native 215 * methods, the runtime system locates the platform-specific code for the method, 216 * loading it and linking it into the JVM if necessary. Then the native method 217 * code is executed with the arguments popped from the operand stack. The exact 218 * mechanism used to invoke native methods is implementation-specific. 219 * 220 * When the method called by invokespecial returns, any single (or double) word 221 * return result is placed on the operand stack of the current method. If the 222 * invoked method was marked as synchronized, the monitor associated with 223 * objectref is exited. Execution continues at the instruction that follows 224 * invokespecial in the bytecode. 225 * 226 * Notes 227 * 228 * 1. In Java Virtual Machine implementations prior to version JDK 1.02, this 229 * instruction was called invokenonvirtual, and was less restrictive than 230 * invokespecial - it wasn't limited to invoking only superclass, private or 231 * <init> methods. The class access flag ACC_SUPER (see Chapter 4) is used to 232 * indicate which semantics are used by a class. In older class files, the 233 * ACC_SUPER flag is unset. In all new classes, the ACC_SUPER flag should be set, 234 * indicating that the restrictions enforced by invokespecial are obeyed. (In 235 * practice, all the common uses of invokenonvirtual continue to be supported 236 * by invokespecial, so this change should have little impact on JVM users). 237 * 238 */ 239 240 package invokespecial; 241 242 import static jdk.internal.org.objectweb.asm.Opcodes.*; 243 import shared.AbstractGenerator; 244 import shared.AccessType; 245 246 import java.util.HashMap; 247 import java.util.Map; 248 249 public class Generator extends AbstractGenerator { main(String[] args)250 public static void main (String[] args) throws Exception { 251 new Generator(args).run(); 252 } Generator(String[] args)253 public Generator(String[] args) { 254 super(args); 255 } 256 getChecker(Class paramClass, Class targetClass)257 protected Checker getChecker(Class paramClass, Class targetClass) { 258 return new Checker(paramClass, targetClass); 259 } 260 run()261 public void run() throws Exception { 262 // Specify package names 263 String pkg1 = "a."; 264 String pkg2 = "b."; 265 String[] packages = new String[] { "", pkg1, pkg2 }; 266 267 boolean isPassed = true; 268 269 // HIERARCHIES 270 // The following triples will be used during further 271 // hierarchy construction and will specify packages for A, B and C 272 String[][] packageSets = new String[][] { 273 { "", "", "" } 274 , { "", pkg1, pkg1 } 275 , { "", pkg1, pkg2 } 276 , { pkg1, "", pkg1 } 277 , { pkg1, "", pkg2 } 278 , { pkg1, pkg1, "" } 279 , { pkg1, pkg2, "" } 280 , { pkg1, pkg1, pkg1 } 281 , { pkg1, pkg1, pkg2 } 282 , { pkg1, pkg2, pkg1 } 283 , { pkg1, pkg2, pkg2 } 284 }; 285 286 String [] header = new String[] { 287 String.format("%30s %35s", "Method access modifiers", "Call site location") 288 , String.format("%4s %-10s %-10s %-10s %7s %7s %7s %7s %7s %7s %7s" 289 , " # " 290 , "A.m()" 291 , "B.m()" 292 , "C.m()" 293 , " A " 294 , "pkgA" 295 , " B " 296 , " pkgB" 297 , " C " 298 , "pkgC " 299 , " X " 300 ) 301 , "-----------------------------------------------------------------------------------------------------------" 302 }; 303 304 // Print header 305 for (String str : header) { 306 System.out.println(str); 307 } 308 309 // Iterate over all interesting package combinations 310 for (String[] pkgSet : packageSets) { 311 String packageA = pkgSet[0]; 312 String packageB = pkgSet[1]; 313 String packageC = pkgSet[2]; 314 315 String classNameA = packageA + "A"; 316 String classNameB = packageB + "B"; 317 String classNameC = packageC + "C"; 318 319 // For all possible access modifier combinations 320 for (AccessType accessFlagA : AccessType.values()) { 321 for (AccessType accessFlagB : AccessType.values()) { 322 for (AccessType accessFlagC : AccessType.values()) { 323 Map<String, byte[]> classes = new HashMap<String, byte[]>(); 324 325 String calleeClassName = classNameB; 326 int classFlags = ACC_PUBLIC; 327 328 // The following hierarhcy is created: 329 // c.C extends b.B extends a.A extends Object - base hierarchy 330 // X extends Object - external caller 331 // c.Caller, b.Caller, a.Caller extends Object - package callers 332 333 // Generate result storage 334 classes.put( 335 "Result" 336 , new ClassGenerator( 337 "Result" 338 , "java.lang.Object" 339 , ACC_PUBLIC 340 ) 341 .addField( 342 ACC_PUBLIC | ACC_STATIC 343 , "value" 344 , "java.lang.String" 345 ) 346 .getClassFile() 347 ); 348 349 // Generate class A 350 classes.put( 351 classNameA 352 , new ClassGenerator( 353 classNameA 354 , "java.lang.Object" 355 , classFlags 356 ) 357 .addTargetConstructor(accessFlagA) 358 .addTargetMethod(accessFlagA) 359 .addCaller(calleeClassName) 360 .getClassFile() 361 ); 362 363 // Generate class B 364 classes.put( 365 classNameB 366 , new ClassGenerator( 367 classNameB 368 , classNameA 369 , classFlags 370 ) 371 .addTargetConstructor(accessFlagB) 372 .addTargetMethod(accessFlagB) 373 .addCaller(calleeClassName) 374 .getClassFile() 375 ); 376 377 // Generate class C 378 classes.put( 379 classNameC 380 , new ClassGenerator( 381 classNameC 382 , classNameB 383 , classFlags 384 ) 385 .addTargetConstructor(accessFlagC) 386 .addTargetMethod(accessFlagC) 387 .addCaller(calleeClassName) 388 .getClassFile() 389 ); 390 391 // Generate class X 392 String classNameX = "x.X"; 393 classes.put( 394 classNameX 395 , new ClassGenerator( 396 classNameX 397 , "java.lang.Object" 398 , classFlags 399 ) 400 .addTargetMethod(accessFlagC) 401 .addCaller(calleeClassName) 402 .getClassFile() 403 ); 404 405 // Generate package callers 406 for (String pkg : packages) { 407 classes.put( 408 pkg+"Caller" 409 , new ClassGenerator( 410 pkg+"Caller" 411 , "java.lang.Object" 412 , classFlags 413 ) 414 .addCaller(calleeClassName) 415 .getClassFile() 416 ); 417 } 418 419 String[] callSites = new String[] { 420 classNameA 421 , packageA+"Caller" 422 , classNameB 423 , packageB+"Caller" 424 , classNameC 425 , packageC+"Caller" 426 , classNameX 427 }; 428 429 String caseDescription = String.format( 430 "%-10s %-10s %-10s| " 431 , classNameA + " " + accessFlagA 432 , classNameB + " " + accessFlagB 433 , classNameC + " " + accessFlagC 434 ); 435 436 boolean result = exec(classes, caseDescription, calleeClassName, classNameC, callSites); 437 isPassed = isPassed && result; 438 } 439 } 440 } 441 } 442 443 // Print footer 444 for (int i = header.length-1; i >= 0; i--) { 445 System.out.println(header[i]); 446 } 447 448 if (executeTests) { 449 System.out.printf("\nEXECUTION STATUS: %s\n", (isPassed? "PASSED" : "FAILED")); 450 } 451 } 452 } 453