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