1 /*
  2  * Copyright (c) 2022, 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  * @test
 27  * @bug 8294609
 28  * @requires vm.compiler2.enabled & vm.flagless
 29  *
 30  * @library /test/lib
 31  *
 32  * @build compiler.c2.unloaded.TestInlineUnloaded
 33  *
 34  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar launcher.jar
 35  *                  compiler.c2.unloaded.TestInlineUnloaded
 36  *                  compiler.c2.unloaded.TestInlineUnloaded$Launcher
 37  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar parent.jar
 38  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent
 39  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$U
 40  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$TestCase
 41  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$Invoker
 42  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$TestNull
 43  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$TestLoadedRemotely
 44  *                  compiler.c2.unloaded.TestInlineUnloaded$Parent$TestUnloaded
 45  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar caller.jar
 46  *                  compiler.c2.unloaded.TestInlineUnloaded$Caller
 47  *                  compiler.c2.unloaded.TestInlineUnloaded$Caller$TestNull
 48  * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar callee.jar
 49  *                  compiler.c2.unloaded.TestInlineUnloaded$Callee
 50  *                  compiler.c2.unloaded.TestInlineUnloaded$Callee$TestNull
 51  *
 52  * @run driver compiler.c2.unloaded.TestInlineUnloaded
 53  */
 54 
 55 package compiler.c2.unloaded;
 56 
 57 import jdk.test.lib.JDKToolFinder;
 58 import jdk.test.lib.process.OutputAnalyzer;
 59 
 60 import java.io.IOException;
 61 import java.net.URL;
 62 import java.net.URLClassLoader;
 63 import java.util.function.Consumer;
 64 
 65 public class TestInlineUnloaded {
 66     static final String THIS_CLASS = TestInlineUnloaded.class.getName();
 67 
 68     public static class Parent {
 69         public class U {
 70         }
 71 
 72         public interface TestCase {
 73             U test(Invoker obj, U arg);
 74 
 75             void testArg(Invoker obj, U arg);
 76 
 77             U testRet(Invoker obj);
 78 
 79             void test(Invoker obj);
 80         }
 81 
 82         public interface Invoker {
 83             void invokeArg(U obj);
 84 
 85             U invokeRet();
 86 
 87             U invoke(U obj);
 88         }
 89 
 90         private static class TestNull implements Runnable {
 91             final TestCase test;
 92             final Invoker recv;
 93 
 94             public TestNull(TestCase test, Invoker recv) {
 95                 this.test = test;
 96                 this.recv = recv;
 97             }
 98 
 99             @Override
100             public void run() {
101                 test.testArg(recv, null);
102                 test.testRet(recv);
103                 test.test(recv, null);
104             }
105         }
106 
107         public static class TestLoadedRemotely extends TestNull {
108             public TestLoadedRemotely(TestCase test, Invoker recv) throws Exception {
109                 super(test, recv);
110                 Class.forName(U.class.getName()); // preload in parent context
111             }
112         }
113 
114         public static class TestUnloaded extends TestNull {
115             public TestUnloaded(TestCase test, Invoker recv) {
116                 super(test, recv);
117             }
118         }
119     }
120 
121     public static class Caller {
122         public static class TestNull implements Parent.TestCase {
123             public TestNull() {}
124 
125             public Parent.U test(Parent.Invoker obj, Parent.U arg) {
126                 return obj.invoke(arg);
127             }
128 
129             public void testArg(Parent.Invoker obj, Parent.U arg) {
130                 obj.invokeArg(arg);
131             }
132 
133             public Parent.U testRet(Parent.Invoker obj) {
134                 return obj.invokeRet();
135             }
136 
137             public void test(Parent.Invoker obj) {
138                 test(obj, null);
139             }
140         }
141     }
142 
143     public static class Callee {
144         public static class TestNull implements Parent.Invoker {
145             public void invokeArg(Parent.U obj) {}
146 
147             public Parent.U invokeRet() {
148                 return null;
149             }
150 
151             public Parent.U invoke(Parent.U obj) {
152                 return obj;
153             }
154         }
155     }
156 
157     public static class Launcher {
158         public static void main(String... args) throws Exception {
159             final String testName = args[0];
160 
161             URLClassLoader parentCL = new URLClassLoader("parent", new URL[] { new URL("file:parent.jar") }, ClassLoader.getSystemClassLoader());
162             URLClassLoader callerCL = new URLClassLoader("caller", new URL[] { new URL("file:caller.jar") }, parentCL);
163             URLClassLoader calleeCL = new URLClassLoader("callee", new URL[] { new URL("file:callee.jar") }, parentCL);
164 
165             Object caller = Class.forName(THIS_CLASS + "$Caller$TestNull", false, callerCL)
166                                  .getDeclaredConstructor().newInstance();
167             Object callee = Class.forName(THIS_CLASS + "$Callee$TestNull", false, calleeCL)
168                                  .getDeclaredConstructor().newInstance();
169 
170             Class<?> testClass = Class.forName(THIS_CLASS + "$Parent$TestCase", false, parentCL);
171             Class<?> invClass  = Class.forName(THIS_CLASS + "$Parent$Invoker",  false, parentCL);
172             Class<?> test      = Class.forName(THIS_CLASS + "$Parent$" + testName, false, parentCL);
173             Runnable r = (Runnable) test.getDeclaredConstructor(testClass, invClass)
174                                        .newInstance(caller, callee);
175 
176             for (int i = 0; i < 20_000; i ++) {
177                 r.run();
178             }
179         }
180     }
181 
182     static void run(String testCaseName, Consumer<OutputAnalyzer> processor) throws IOException {
183         ProcessBuilder pb = new ProcessBuilder();
184 
185         pb.command(JDKToolFinder.getJDKTool("java"),
186             "-cp", "launcher.jar",
187             "-XX:+IgnoreUnrecognizedVMOptions", "-showversion",
188             "-XX:-TieredCompilation", "-Xbatch",
189             "-XX:-InlineTypeReturnedAsFields", // TODO Remove this once 8284443 fixed handling of unloaded return types
190             "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
191             "-XX:CompileCommand=quiet", "-XX:CompileCommand=compileonly,*TestNull::run",
192             Launcher.class.getName(), testCaseName);
193 
194         System.out.println("Command line: [" + pb.command() + "]");
195 
196         OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
197 
198         analyzer.shouldHaveExitValue(0);
199 
200         // The test is applicable only to C2 (present in Server VM).
201         analyzer.stderrShouldContain("Server VM");
202 
203         analyzer.shouldContain("TestNull::run"); // ensure that relevant method is compiled
204 
205         processor.accept(analyzer); // test-specific checks
206     }
207 
208     public static void main(String[] args) throws Exception {
209         run("TestUnloaded", output -> {
210             output.shouldMatch("TestNull::testArg .* unloaded signature classes");
211             output.shouldMatch("TestNull::testRet .* unloaded signature classes");
212             output.shouldMatch("TestNull::test .* unloaded signature classes");
213 
214             output.shouldMatch("TestNull::testArg .* failed to inline");
215             output.shouldMatch("TestNull::testRet .* failed to inline");
216             output.shouldMatch("TestNull::test .* failed to inline");
217         });
218         run("TestLoadedRemotely", output -> {
219             output.shouldMatch("TestNull::testArg .* inline");
220             output.shouldMatch("TestNull::testRet .* inline");
221             output.shouldMatch("TestNull::test .* inline");
222 
223             output.shouldNotMatch("TestNull::testArg .* unloaded signature classes");
224             output.shouldNotMatch("TestNull::testRet .* unloaded signature classes");
225             output.shouldNotMatch("TestNull::test .* unloaded signature classes");
226         });
227     }
228 }