test/hotspot/jtreg/runtime/InvocationTests/invokespecial/Generator.java
changeset 55497 d3a33953b936
equal deleted inserted replaced
55496:8e0ae3830fca 55497:d3a33953b936
       
     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 {
       
   250     public static void main (String[] args) throws Exception {
       
   251         new Generator(args).run();
       
   252     }
       
   253     public Generator(String[] args) {
       
   254         super(args);
       
   255     }
       
   256 
       
   257     protected Checker getChecker(Class paramClass, Class targetClass) {
       
   258         return new Checker(paramClass, targetClass);
       
   259     }
       
   260 
       
   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 }