1 /*
   2  * Copyright (c) 2018, 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  * @test
  26  * @bug 8046171
  27  * @summary Test method selection process for private/public nestmate invocation
  28  * @compile TestMethodSelection.java
  29  * @compile PB_A.jcod \
  30  *          PC_B_A.jcod \
  31  *          PC_B_PA.jcod \
  32  *          PC_PB_A.jcod
  33  * @run main/othervm TestMethodSelection
  34  * @run main/othervm -Dsun.reflect.noInflation=true TestMethodSelection
  35  */
  36 
  37 // The first run will use NativeMethodAccessor and due to the limited number
  38 // of calls we will not reach the inflation threshold.
  39 // The second run disables inflation so we will use the GeneratedMethodAccessor
  40 // instead. In this way both sets of Reflection classes are tested.
  41 
  42 /*
  43 We are setting up a basic test structure as follows:
  44 
  45 class A {
  46   ?? String m() { return "A::m"; }
  47 }
  48 class B extends A {
  49   ?? String m() { return "B::m"; }
  50 }
  51 class C extends B {
  52   ?? String m() { return "C::m"; }
  53 }
  54 
  55 where the access modifier of m() is either public or private in all combinations.
  56 The only cases of interest here are private and non-private, so we use public for
  57 the non-private case.
  58 
  59 We then have a test function:
  60 
  61 void test(B target, String expected) {
  62   check(target.m() == expected);
  63 }
  64 
  65 where the call to target.m() is expressed as an invokevirtual B::m on target. We
  66 then pass either a B instance or a C instance and check that the expected method
  67 is invoked. In all cases the resolved method is B::m, so we are effectively
  68 testing the method selection rules. We are not testing resolution here.
  69 
  70 The expected behaviour is as follows (where P means m() is private and - means
  71 m() is public).
  72 
  73 Target  A.m  B.m  C.m     Result   Reason
  74 ------------------------------------------
  75  B       P    P   n/a      B.m       [1]
  76  B       P    -   n/a      B.m       [2]
  77  B       -    P   n/a      B.m       [1]
  78  B       -    -   n/a      B.m       [2]
  79  C       P    P    P       B.m       [1]
  80  C       P    P    -       B.m       [1]
  81  C       P    -    P       B.m       [3]
  82  C       P    -    -       C.m       [2]
  83  c       -    P    P       B.m       [1]
  84  C       -    P    -       B.m       [1]
  85  C       -    -    P       B.m       [3]
  86  C       -    -    -       C.m       [2]
  87 
  88 [1] Resolved method is private => selected method == resolved method
  89 [2] target-type.m() can override B.m => selected method == target-type.m()
  90 [3] private C.m does not override resolved public method B.m, but
  91     C has a superclass B, with B.m that (trivially) overrides resolved B.m
  92     => selected method = B.m
  93 
  94 To allow us to do this in source code we encode the inheritance hierarchy in the
  95 class name, and we use plain A (for example) when m() is public and PA when m()
  96 is private. So class C_B_A defines a public m() and inherits public m() from
  97 both B and A. While PC_PB_PA defines a private m() and also has private m()
  98 defined in its superclasses PB and PA.
  99 
 100 For cases where the subclass makes a public method private we can't write this
 101 directly in Java source code so we have to have jcod versions that change
 102 the access modifier to private. This occurs for:
 103 
 104 - PC_B_A
 105 - PB_A
 106 - PC_B_PA
 107 - PC_PB_A
 108 
 109 We test direct invocation from Java source, MethodHandle invocation and core
 110 reflection invocation. For MH and reflection we look for the method in "B" to
 111 maintain the same resolution process as in the direct case.
 112 */
 113 
 114 import java.lang.invoke.*;
 115 import static java.lang.invoke.MethodHandles.*;
 116 import static java.lang.invoke.MethodType.*;
 117 import java.lang.reflect.InvocationTargetException;
 118 
 119 public class TestMethodSelection {
 120 
 121     static final MethodType M_T = MethodType.methodType(String.class);
 122 
 123     static class A {
 124         public String m() { return "A::m"; }
 125     }
 126     static class PA {
 127         private String m() { return "PA::m"; }
 128     }
 129 
 130     static class B_A extends A {
 131         public String m() { return "B_A::m"; }
 132     }
 133     static class B_PA extends PA {
 134         public String m() { return "B_PA::m"; }
 135     }
 136     // jcod version will rewrite this to have private m()
 137     static class PB_A extends A {
 138         public String m() { return "PB_A::m"; }
 139     }
 140     static class PB_PA extends PA {
 141         private String m() { return "PB_PA::m"; }
 142     }
 143 
 144     static class C_B_A extends B_A {
 145         public String m() { return "C_B_A::m"; }
 146     }
 147     // jcod version will rewrite this to have private m()
 148     static class PC_B_A extends B_A {
 149         public String m() { return "PC_B_A"; }
 150     }
 151     static class C_PB_A extends PB_A {
 152         public String m() { return "C_PB_A::m"; }
 153     }
 154     // jcod version will rewrite this to have private m()
 155     static class PC_PB_A extends PB_A {
 156         public String m() { return "PC_PB_A"; }
 157     }
 158     static class C_B_PA extends B_PA {
 159         public String m() { return "C_B_PA::m"; }
 160     }
 161     // jcod version will rewrite this to have private m()
 162     static class PC_B_PA extends B_PA {
 163         public String m() { return "PC_B_PA"; }
 164     }
 165     static class C_PB_PA extends PB_PA {
 166         public String m() { return "C_PB_PA::m"; }
 167     }
 168     static class PC_PB_PA extends PB_PA {
 169         private String m() { return "PC_PB_PA::m"; }
 170     }
 171 
 172     // Need a test function for each of the "B" classes
 173 
 174     static void doInvoke(B_A target, String expected) throws Throwable {
 175         // Direct
 176         check(target.m(), expected);
 177         // MethodHandle
 178         MethodHandle mh = lookup().findVirtual(B_A.class, "m", M_T);
 179         check((String)mh.invoke(target), expected);
 180         // Reflection
 181         check((String)B_A.class.getDeclaredMethod("m", new Class<?>[0]).
 182               invoke(target, new Object[0]), expected);
 183     }
 184     static void doInvoke(B_PA target, String expected) throws Throwable {
 185         // Direct
 186         check(target.m(), expected);
 187         // MethodHandle
 188         MethodHandle mh = lookup().findVirtual(B_PA.class, "m", M_T);
 189         check((String)mh.invoke(target), expected);
 190         // Reflection
 191         check((String)B_PA.class.getDeclaredMethod("m", new Class<?>[0]).
 192               invoke(target, new Object[0]), expected);
 193     }
 194     static void doInvoke(PB_A target, String expected) throws Throwable {
 195         // Direct
 196         check(target.m(), expected);
 197         // MethodHandle
 198         MethodHandle mh = lookup().findVirtual(PB_A.class, "m", M_T);
 199         check((String)mh.invoke(target), expected);
 200         // Reflection
 201         check((String)PB_A.class.getDeclaredMethod("m", new Class<?>[0]).
 202               invoke(target, new Object[0]), expected);
 203     }
 204     static void doInvoke(PB_PA target, String expected) throws Throwable {
 205         // Direct
 206         check(target.m(), expected);
 207         // MethodHandle
 208         MethodHandle mh = lookup().findVirtual(PB_PA.class, "m", M_T);
 209         check((String)mh.invoke(target), expected);
 210         // Reflection
 211         check((String)PB_PA.class.getDeclaredMethod("m", new Class<?>[0]).
 212               invoke(target, new Object[0]), expected);
 213     }
 214 
 215     static void check(String actual, String expected) {
 216         if (!actual.equals(expected)) {
 217                 throw new Error("Selection error: expected " + expected +
 218                                 " but got " + actual);
 219         }
 220     }
 221 
 222     public static void main(String[] args) throws Throwable {
 223         // First pass a suitable "B" instance
 224         doInvoke(new PB_PA(), "PB_PA::m");
 225         doInvoke(new B_PA(),  "B_PA::m");
 226         doInvoke(new PB_A(),  "PB_A::m");
 227         doInvoke(new B_A(),   "B_A::m");
 228         // Now a "C" instance
 229         doInvoke(new PC_PB_PA(), "PB_PA::m");
 230         doInvoke(new C_PB_PA(),  "PB_PA::m");
 231         doInvoke(new PC_B_PA(),  "B_PA::m");
 232         doInvoke(new C_B_PA(),   "C_B_PA::m");
 233         doInvoke(new PC_PB_A(),  "PB_A::m");
 234         doInvoke(new C_PB_A(),   "PB_A::m");
 235         doInvoke(new PC_B_A(),   "B_A::m");
 236         doInvoke(new C_B_A(),    "C_B_A::m");
 237     }
 238 }
 239 
 240 
 241 
 242