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