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  * @library /test/lib
27  * @modules java.base/jdk.internal.misc
28  *          jdk.compiler
29  * @compile HiddenClassSigTest.java
30  * @run main/othervm/native -agentlib:HiddenClassSigTest P.Q.HiddenClassSigTest
31  */
32 
33 package P.Q;
34 
35 import java.io.ByteArrayOutputStream;
36 import java.io.File;
37 import java.io.FileInputStream;
38 
39 import java.lang.invoke.MethodHandles;
40 import java.lang.invoke.MethodHandles.Lookup;
41 import java.nio.file.Files;
42 import java.nio.file.Path;
43 import java.nio.file.Paths;
44 
45 import jdk.test.lib.Utils;
46 
47 
48 interface HCInterf<T> {
hcMethod(T t)49     String hcMethod(T t);
50 }
51 
52 class HiddenClassSig<T> implements HCInterf<T> {
realTest()53     private String realTest() { return "HiddenClassSig: "; }
54 
hcMethod(T t)55     public String hcMethod(T t) {
56         String str = realTest();
57         return str + t.toString();
58     }
59 }
60 
61 public class HiddenClassSigTest {
log(String str)62     private static void log(String str) { System.out.println(str); }
63 
64     private static final String HCName = "P/Q/HiddenClassSig.class";
65     private static final Path CLASSES_DIR = Paths.get(Utils.TEST_CLASSES);
66     private static final String LOG_PREFIX = "HiddenClassSigTest: ";
67 
checkHiddenClass(Class klass, String sig)68     static native void checkHiddenClass(Class klass, String sig);
checkHiddenClassArray(Class array, String sig)69     static native void checkHiddenClassArray(Class array, String sig);
checkFailed()70     static native boolean checkFailed(); // get native agent failing status
71 
72     static {
73         try {
74             System.loadLibrary("HiddenClassSigTest");
75         } catch (UnsatisfiedLinkError ule) {
76             System.err.println("Could not load HiddenClassSigTest library");
77             System.err.println("java.library.path: "
78                 + System.getProperty("java.library.path"));
79             throw ule;
80         }
81     }
82 
defineHiddenClass(String classFileName)83     static Class<?> defineHiddenClass(String classFileName) throws Exception {
84         Lookup lookup = MethodHandles.lookup();
85         byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve(classFileName));
86         Class<?> hc = lookup.defineHiddenClass(bytes, false).lookupClass();
87         return hc;
88     }
89 
90     // print all name variations
logClassInfo(Class<?> klass)91     static void logClassInfo(Class<?> klass) {
92         log("\n### Testing class: " + klass);
93         log(LOG_PREFIX + "isHidden:  " + klass.isHidden());
94         log(LOG_PREFIX + "getName:   " + klass.getName());
95         log(LOG_PREFIX + "typeName:  " + klass.getTypeName());
96         log(LOG_PREFIX + "toString:  " + klass.toString());
97         log(LOG_PREFIX + "toGenStr:  " + klass.toGenericString());
98         log(LOG_PREFIX + "elem type: " + klass.componentType());
99     }
100 
101     private static final String HC_NAME = "P.Q.HiddenClassSig";
102     private static final String HC_SUFFIX_REGEX = "0x[0-9a-f]+";
103 
checkName(Class<?> klass, String name, String toString)104     static boolean checkName(Class<?> klass, String name, String toString) {
105         boolean failed = false;
106         String regex = "";
107         Class<?> c = klass;
108 
109         // for an array add the prefix "[" for each dimension
110         while (c.isArray()) {
111             regex = "\\[" + regex;
112             c = c.componentType();
113         }
114         // construct the expected name
115         if (klass.isArray()) {
116             regex += "L" + HC_NAME + "/" + HC_SUFFIX_REGEX + ";";
117         } else {
118             regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
119         }
120         // check the name matches the expected
121         if (!name.matches(regex)) {
122             log("Test FAIL: result of Class::getName" + " \"" + name + "\" does not match " + regex);
123             failed = true;
124         }
125         // check the string name matches the expected
126         if (!toString.matches("class " + regex)) {
127             log("Test FAIL: result of Class::toString" + " \"" + name + "\" does not match " + regex);
128             failed = true;
129         }
130         return failed;
131     }
132 
checkTypeName(Class<?> klass, String name)133     static boolean checkTypeName(Class<?> klass, String name) {
134         boolean failed = false;
135         // construct the expected type name
136         String regex = HC_NAME + "/" + HC_SUFFIX_REGEX;
137         Class<?> c = klass;
138 
139         // for an array add the suffix "[]" for each dimension
140         while (c.isArray()) {
141             c = c.componentType();
142             regex = regex + "\\[\\]";
143         }
144         // check the type name matches the expected
145         if (!name.matches(regex)) {
146             log("Test FAIL: result of Class::getTypeName" + " \"" + name + "\" does not match " + regex);
147             failed = true;
148         }
149         return failed;
150     }
151 
checkGenericString(Class<?> klass, String name)152     static boolean checkGenericString(Class<?> klass, String name) {
153         boolean failed = false;
154         Class<?> c = klass;
155         // construct the expected generic string
156         String regex = HC_NAME + "/" + HC_SUFFIX_REGEX + "<T>";
157 
158         // add the expected name prefix for a non-array class
159         if (!klass.isArray()) {
160             regex = "class " + regex;
161         }
162         // for an array get the bottom component class
163         while (c.isArray()) {
164             c = c.componentType();
165             regex = regex + "\\[\\]";
166         }
167         // check the generic string matches the expected
168         if (!name.matches(regex)) {
169             log("Test FAIL: result of Class::toGenericString" + " \"" + name + "\" does not match " + regex);
170             failed = true;
171         }
172         return failed;
173     }
174 
checkDescriptorString(Class<?> klass, String name)175     static boolean checkDescriptorString(Class<?> klass, String name) {
176         boolean failed = false;
177         // construct the expected descriptor string
178         String regex = "L" + HC_NAME.replace('.', '/') + "." + HC_SUFFIX_REGEX + ";";
179         Class<?> c = klass;
180 
181         // for array get the bottom component class
182         while (c.isArray()) {
183             regex = "\\[" + regex;
184             c = c.componentType();
185         }
186         // check the descriptor string matches the expected
187         if (!name.matches(regex)) {
188             log("Test FAIL: result of Class::descriptorString" + " \"" + name + "\" does not match " + regex);
189             failed = true;
190         }
191         return failed;
192     }
193 
testClass(Class<?> klass)194     static boolean testClass(Class<?> klass) {
195         boolean failed = false;
196         logClassInfo(klass);
197 
198         // verify all name variations
199         failed |= checkName(klass, klass.getName(), klass.toString());
200         failed |= checkTypeName(klass, klass.getTypeName());
201         failed |= checkGenericString(klass, klass.toGenericString());
202         failed |= checkDescriptorString(klass, klass.descriptorString());
203 
204         // an array class is never hidden
205         if (klass.isArray() && klass.isHidden()) {
206             log("Test FAIL: an array class is never hidden");
207             failed = true;
208         }
209 
210         // verify hidden class array or class by the native agent
211         if (klass.isArray()) {
212             checkHiddenClassArray(klass, klass.descriptorString());
213         } else {
214             checkHiddenClass(klass, klass.descriptorString());
215         }
216         return failed;
217     }
218 
main(String args[])219     public static void main(String args[]) throws Exception {
220         log(LOG_PREFIX + "started");
221 
222         // define a hidden class to test
223         Class<?> hc = defineHiddenClass(HCName);
224 
225         // allocate a hidden class instance to test
226         HCInterf<String> testObj = (HCInterf<String>)hc.newInstance();
227 
228         String str = testObj.hcMethod("Test generic hidden class");
229         log(LOG_PREFIX + "hc.hcMethod() returned string: " + str);
230 
231         // test all hidden class name/signature variations
232         boolean failed = testClass(hc);
233 
234         // test all hidden class array name/signature variations
235         Class<?> hcArr = hc.arrayType();
236         failed |= testClass(hcArr);
237 
238         // test all hidden class double array name/signature variations
239         Class<?> hcArrArr = hcArr.arrayType();
240         failed |= testClass(hcArrArr);
241 
242         if (failed) { // check the java part failing status
243           throw new RuntimeException("FAIL: failed status from java part");
244         }
245         if (checkFailed()) { // check the native agent failing status
246           throw new RuntimeException("FAIL: failed status from native agent");
247         }
248         log(LOG_PREFIX + "finished");
249     }
250 }
251