1 /*
2  * Copyright (c) 2019, 2020, 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 8227415 8254975 8270056
27  * @run testng/othervm p.ProtectedMethodInOtherPackage
28  * @summary method reference to a protected method inherited from its
29  *          superclass in a different runtime package where
30  *          lambda proxy class has no access to it.
31  */
32 
33 package p;
34 
35 import q.I;
36 import q.J;
37 
38 import java.io.IOException;
39 import java.io.UncheckedIOException;
40 import java.lang.invoke.MethodHandle;
41 import java.lang.invoke.MethodHandles;
42 import java.lang.invoke.MethodType;
43 import java.net.URL;
44 import java.net.URLClassLoader;
45 import java.nio.file.Files;
46 import java.nio.file.Path;
47 import java.nio.file.Paths;
48 import java.util.function.Function;
49 
50 import org.testng.annotations.Test;
51 import static org.testng.Assert.*;
52 
53 public class ProtectedMethodInOtherPackage  {
54     @Test
remotePackageSameLoader()55     public static void remotePackageSameLoader() {
56         Sub_I sub = new Sub_I();
57         sub.test(Paths.get("test"));
58     }
59 
60     public static class Sub_J extends J {
Sub_J(Function<Path,String> function)61         Sub_J(Function<Path,String> function) {
62             super(function);
63         }
64     }
65 
66     public static class Sub_I extends I {
test(Path path)67         public void test(Path path) {
68             /*
69              * The method reference to an inherited protected method
70              * in another package is desugared with REF_invokeVirtual on
71              * a bridge method to invoke protected q.I::filename method
72              */
73             Sub_J c = new Sub_J(this::filename);
74             c.check(path);
75         }
76     }
77 
78     @Test
splitPackage()79     public static void splitPackage() throws Throwable {
80         ClassLoader parent = new Loader("loader-A", null, A.class);
81         ClassLoader loader = new Loader("loader-B", parent, B.class);
82         Class<?> aClass = Class.forName(A.class.getName(), false, loader);
83         Class<?> bClass = Class.forName(B.class.getName(), false, loader);
84         assertTrue(aClass.getClassLoader() == parent);
85         assertTrue(bClass.getClassLoader() == loader);
86         assertEquals(aClass.getPackageName(), bClass.getPackageName());
87 
88         Object b = bClass.getDeclaredConstructor().newInstance();
89 
90         // verify subclass can access a protected member inherited from
91         // its superclass in a split package
92         MethodHandle test = MethodHandles.lookup()
93                 .findVirtual(bClass, "test", MethodType.methodType(void.class));
94         test.invoke(b);
95 
96         // verify lambda can access a protected member inherited from
97         // a superclass of the host class where the superclass is in
98         // a split package (not the same runtime package as the host class)
99         MethodHandle get = MethodHandles.lookup()
100                 .findVirtual(bClass, "get", MethodType.methodType(Runnable.class));
101         ((Runnable) get.invoke(b)).run();
102     }
103 
104     @Test
protectedStaticMethodInSplitPackage()105     public static void protectedStaticMethodInSplitPackage() throws Throwable {
106         ClassLoader parent = new Loader("loader-A1", null, A1.class);
107         ClassLoader loader = new Loader("loader-B1", parent, B1.class);
108         Class<?> aClass1 = Class.forName(A1.class.getName(), false, loader);
109         Class<?> bClass1 = Class.forName(B1.class.getName(), false, loader);
110         assertTrue(aClass1.getClassLoader() == parent);
111         assertTrue(bClass1.getClassLoader() == loader);
112         assertEquals(aClass1.getPackageName(), bClass1.getPackageName());
113 
114         // verify subclass can access a static protected method inherited from
115         // its superclass in a split package
116         MethodHandle test = MethodHandles.lookup()
117                 .findStatic(bClass1, "test", MethodType.methodType(void.class));
118         test.invoke();
119 
120         // verify lambda can access a static protected method inherited from
121         // a superclass of the host class where the superclass is in
122         // a split package (not the same runtime package as the host class)
123         MethodHandle get = MethodHandles.lookup()
124                 .findStatic(bClass1, "get", MethodType.methodType(Runnable.class));
125         ((Runnable) get.invoke()).run();
126     }
127 
128     static class Loader extends URLClassLoader {
129         static final Path CLASSES_DIR = Paths.get(System.getProperty("test.class.path"));
130         private final Class<?> c;
Loader(String name, ClassLoader parent, Class<?> c)131         Loader(String name, ClassLoader parent, Class<?> c) {
132             super(name, new URL[]{}, parent);
133             this.c = c;
134         }
135 
136         @Override
findClass(String name)137         protected Class<?> findClass(String name) throws ClassNotFoundException {
138             if (name.equals(c.getName())) {
139                 try {
140                     String path = name.replace('.', '/') + ".class";
141                     byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(path));
142                     return defineClass(name, bytes, 0, bytes.length);
143                 } catch (IOException e) {
144                     throw new UncheckedIOException(e);
145                 }
146             }
147 
148             return super.findClass(name);
149         }
150 
151     }
152 
153     public static class A {
func()154         protected void func() { }
155     }
156 
157     public static class B extends A {
get()158         public Runnable get() {
159             return this::func;
160         }
test()161         public void test() {
162             func();
163         }
164     }
165 
166     public static class A1 {
func()167         protected static void func() { }
168     }
169 
170     public static class B1 extends A1 {
get()171         public static Runnable get() {
172             return A1::func;
173         }
test()174         public static void test() {
175             func();
176         }
177     }
178 }
179