rev 58768 : 8238358: Implementation of JEP 371: Hidden Classes
Reviewed-by: alanb, cjplummer, coleenp, dholmes, dlong, forax, jlahoda, psandoz, plevart, vromero
Contributed-by: mandy.chung@oracle.com, lois.foltan@oracle.com, david.holmes@oracle.com, harold.seigel@oracle.com, serguei.spitsyn@oracle.com, alex.buckley@oracle.com, jamsheed.c.m@oracle.com, jan.lahoda@oracle.com, amy.lu@oracle.com
rev 58769 : [mq]: type-descriptor-name
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 #include <string.h>
25 #include "jvmti.h"
26
27 extern "C" {
28
29 static const char* EXP_INTERF_SIG = "LP/Q/Test;";
30 static const char* SIG_START = "LP/Q/HiddenClassSig";
31 static const size_t SIG_START_LEN = strlen(SIG_START);
32 static const int ACC_INTERFACE = 0x0200; // Interface class modifiers bit
33
34 static jvmtiEnv *jvmti = NULL;
35 static jint class_load_count = 0;
36 static bool failed = false;
37
38 #define LOG0(str) { printf(str); fflush(stdout); }
39 #define LOG1(str, arg) { printf(str, arg); fflush(stdout); }
40 #define LOG2(str, arg1, arg2) { printf(str, arg1, arg2); fflush(stdout); }
41
42 #define CHECK_JVMTI_ERROR(jni, err, msg) \
43 if (err != JVMTI_ERROR_NONE) { \
44 LOG1("CHECK_JVMTI_ERROR: JVMTI function returned error: %d\n", err); \
45 jni->FatalError(msg); \
46 return; \
47 }
48
49 /* Return the jmethodID of j.l.Class.isHidden() method. */
50 static jmethodID
51 is_hidden_mid(JNIEnv* jni) {
52 char* csig = NULL;
53 jint count = 0;
54 jmethodID *methods = NULL;
55 jclass clazz = jni->FindClass("java/lang/Class");
56 if (clazz == NULL) {
57 jni->FatalError("is_hidden_mid: Error: FindClass returned NULL for java/lang/Class\n");
58 return NULL;
59 }
60
61 // find the jmethodID of j.l.Class.isHidden() method
62 jmethodID mid = jni->GetMethodID(clazz, "isHidden", "()Z");
63 if (mid == NULL) {
64 jni->FatalError("is_hidden_mid: Error in jni GetMethodID: Cannot find j.l.Class.isHidden method\n");
65 }
66 return mid;
67 }
68
69 /* Return true if the klass is hidden. */
70 static bool
71 is_hidden(JNIEnv* jni, jclass klass) {
72 static jmethodID is_hid_mid = NULL;
73
74 if (is_hid_mid == NULL) {
75 is_hid_mid = is_hidden_mid(jni);
76 }
77 // invoke j.l.Class.isHidden() method
78 return jni->CallBooleanMethod(klass, is_hid_mid);
79 }
80
81 /* Check the class signature matches the expected. */
82 static void
83 check_class_signature(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, bool is_hidden, const char* exp_sig) {
84 jint class_modifiers = 0;
85 char* sig = NULL;
86 char* gsig = NULL;
87 jvmtiError err;
88
89 err = jvmti->GetClassSignature(klass, &sig, &gsig);
90 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class: Error in JVMTI GetClassSignature");
91
92 LOG1("check_class_signature: class with sig: %s\n", sig);
93 LOG1("check_class_signature: class with gsig: %s\n", gsig);
94
95 if (strcmp(sig, exp_sig) != 0) {
96 LOG2("check_class_signature: FAIL: Hidden class signature %s does not match expected: %s\n", sig, exp_sig);
97 failed = true;
98 }
99 if (is_hidden && gsig == NULL) {
100 LOG0("check_class_signature: FAIL: unexpected NULL generic signature for hidden class\n");
101 failed = true;
102 }
103 }
104
105 /* Test hidden class flags: it should not be interface, array nor modifiable. */
106 static void
107 check_hidden_class_flags(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
108 jint modifiers = 0;
109 jboolean flag = false;
110 jvmtiError err;
111
112 err = jvmti->GetClassModifiers(klass, &modifiers);
113 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI GetClassModifiers");
114 LOG1("check_hidden_class_flags: hidden class modifiers: 0x%x\n", modifiers);
115 if ((modifiers & ACC_INTERFACE) != 0) {
116 LOG0("check_hidden_class_flags: FAIL: unexpected ACC_INTERFACE bit in hidden class modifiers\n");
117 failed = true;
118 return;
119 }
120
121 err = jvmti->IsInterface(klass, &flag);
122 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsInterface");
123 if (flag) {
124 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be interface\n");
125 failed = true;
126 return;
127 }
128
129 err = jvmti->IsArrayClass(klass, &flag);
130 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsArrayClass");
131 if (flag) {
132 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be array\n");
133 failed = true;
134 return;
135 }
136 err = jvmti->IsModifiableClass(klass, &flag);
137 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_flags: Error in JVMTI IsModifiableClass");
138 if (flag) {
139 LOG0("check_hidden_class_flags: FAIL: hidden class is not expected to be modifiable\n");
140 failed = true;
141 }
142 }
143
144 /* Test GetClassLoaderClasses: it should not return any hidden classes. */
145 static void
146 check_hidden_class_loader(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
147 jint count = 0;
148 jobject loader = NULL;
149 jclass* loader_classes = NULL;
150 jboolean found = false;
151 jvmtiError err;
152
153 err = jvmti->GetClassLoader(klass, &loader);
154 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoader");
155
156 err = jvmti->GetClassLoaderClasses(loader, &count, &loader_classes);
157 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassLoaderClasses");
158
159 for (int idx = 0; idx < count; idx++) {
160 char* sig = NULL;
161 jclass kls = loader_classes[idx];
162
163 if (!is_hidden(jni, kls)) {
164 continue;
165 }
166 err = jvmti->GetClassSignature(kls, &sig, NULL);
167 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_loader: Error in JVMTI GetClassSignature");
168
169 LOG1("check_hidden_class_loader: FAIL: JVMTI GetClassLoaderClasses returned hidden class: %s\n", sig);
170 failed = true;
171 return;
172 }
173 LOG0("check_hidden_class_loader: not found hidden class in its loader classes as expected\n");
174 }
175
176 /* Test the hidden class implements expected interface. */
177 static void
178 check_hidden_class_impl_interf(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass) {
179 char* sig = NULL;
180 jint count = 0;
181 jclass* interfaces = NULL;
182 jvmtiError err;
183
184 // check that hidden class implements just one interface
185 err = jvmti->GetImplementedInterfaces(klass, &count, &interfaces);
186 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetImplementedInterfaces");
187 if (count != 1) {
188 LOG1("check_hidden_class_impl_interf: FAIL: implemented interfaces count: %d, expected to be 1\n", count);
189 failed = true;
190 return;
191 }
192
193 // check the interface signature is matching the expected
194 err = jvmti->GetClassSignature(interfaces[0], &sig, NULL);
195 CHECK_JVMTI_ERROR(jni, err, "check_hidden_class_impl_interf: Error in JVMTI GetClassSignature for implemented interface");
196
197 if (strcmp(sig, EXP_INTERF_SIG) != 0) {
198 LOG2("check_hidden_class_impl_interf: FAIL: implemented interface signature: %s, expected to be: %s\n",
199 sig, EXP_INTERF_SIG);
200 failed = true;
201 }
202 }
203
204 /* Test hidden class. */
205 static void
206 check_hidden_class(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass, const char* exp_sig) {
207 char* source_file_name = NULL;
208
209 LOG1("\n### Native agent: check_hidden_class started: class: %s\n", exp_sig);
210
211 check_class_signature(jvmti, jni, klass, true /* not hidden */, exp_sig);
212 if (failed) return;
213
214 check_hidden_class_flags(jvmti, jni, klass);
215 if (failed) return;
216
217 check_hidden_class_loader(jvmti, jni, klass);
218 if (failed) return;
219
220 check_hidden_class_impl_interf(jvmti, jni, klass);
221 if (failed) return;
222
223 LOG0("### Native agent: check_hidden_class finished\n");
224 }
225
226 /* Test hidden class array. */
227 static void
228 check_hidden_class_array(jvmtiEnv* jvmti, JNIEnv* jni, jclass klass_array, const char* exp_sig) {
229 char* source_file_name = NULL;
230
231 LOG1("\n### Native agent: check_hidden_class_array started: array: %s\n", exp_sig);
232
233 check_class_signature(jvmti, jni, klass_array, false /* is hidden */, exp_sig);
234 if (failed) return;
235
236 LOG0("### Native agent: check_hidden_class_array finished\n");
237 }
238
239 /* Enable CLASS_LOAD event notification mode. */
240 static void JNICALL
241 VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
242 jvmtiError err;
243
244 printf("VMInit event: SIG_START: %s, SIG_START_LEN: %d\n", SIG_START, (int)SIG_START_LEN);
245 fflush(stdout);
246
247 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
248 CHECK_JVMTI_ERROR(jni, err, "VMInit event: Error in enabling ClassLoad events notification");
249 }
250
251 /* Check CLASS_LOAD event is generated for the given hidden class. */
252 static void JNICALL
253 ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
254 char* sig = NULL;
255 char* gsig = NULL;
256 char* src_name = NULL;
257 jvmtiError err;
258
259 err = jvmti->GetClassSignature(klass, &sig, &gsig);
260 CHECK_JVMTI_ERROR(jni, err, "ClassLoad event: Error in JVMTI GetClassSignature");
261
262 if (strlen(sig) > strlen(SIG_START) &&
263 strncmp(sig, SIG_START, SIG_START_LEN) == 0 &&
264 is_hidden(jni, klass)) {
265 class_load_count++;
266 if (gsig == NULL) {
267 LOG0("ClassLoad event: FAIL: GetClassSignature returned NULL generic signature for hidden class\n");
268 failed = true;
269 }
270 LOG1("ClassLoad event: hidden class with sig: %s\n", sig);
271 LOG1("ClassLoad event: hidden class with gsig: %s\n", gsig);
272 }
273 }
274
275 JNIEXPORT jint JNICALL
276 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) {
277 jvmtiEventCallbacks callbacks;
278 jvmtiCapabilities caps;
279 jvmtiError err;
280
281 LOG0("Agent_OnLoad: started\n");
282 if (jvm->GetEnv((void **) (&jvmti), JVMTI_VERSION) != JNI_OK) {
283 LOG0("Agent_OnLoad: Error in GetEnv in obtaining jvmtiEnv*\n");
284 failed = true;
285 return JNI_ERR;
286 }
287
288 // set required event callbacks
289 memset(&callbacks, 0, sizeof(callbacks));
290 callbacks.ClassLoad = &ClassLoad;
291 callbacks.VMInit = &VMInit;
292
293 err = jvmti->SetEventCallbacks(&callbacks, sizeof(jvmtiEventCallbacks));
294 if (err != JVMTI_ERROR_NONE) {
295 LOG1("Agent_OnLoad: Error in JVMTI SetEventCallbacks: %d\n", err);
296 failed = true;
297 return JNI_ERR;
298 }
299
300 // add required capabilities
301 memset(&caps, 0, sizeof(caps));
302 caps.can_get_source_file_name = 1;
303 err = jvmti->AddCapabilities(&caps);
304 if (err != JVMTI_ERROR_NONE) {
305 LOG1("Agent_OnLoad: Error in JVMTI AddCapabilities: %d\n", err);
306 failed = true;
307 return JNI_ERR;
308 }
309
310 // enable VM_INIT event notification mode
311 err = jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
312 if (err != JVMTI_ERROR_NONE) {
313 LOG1("Agent_OnLoad: Error in JVMTI SetEventNotificationMode: %d\n", err);
314 failed = true;
315 return JNI_ERR;
316 }
317
318 LOG0("Agent_OnLoad: finished\n");
319 return JNI_OK;
320 }
321
322 /* Native method: checkHiddenClass(). */
323 JNIEXPORT void JNICALL
324 Java_P_Q_HiddenClassSigTest_checkHiddenClass(JNIEnv *jni, jclass klass, jclass hidden_klass, jstring exp_sig_str) {
325 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL);
326
327 if (exp_sig == NULL) {
328 jni->FatalError("check_hidden_class: Error: JNI GetStringChars returned NULL for jstring\n");
329 return;
330 }
331 check_hidden_class(jvmti, jni, hidden_klass, exp_sig);
332
333 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
334 }
335
336 /* Native method: checkHiddenClassArray(). */
337 JNIEXPORT void JNICALL
338 Java_P_Q_HiddenClassSigTest_checkHiddenClassArray(JNIEnv *jni, jclass klass, jclass hidden_klass_array, jstring exp_sig_str) {
339 const char* exp_sig = jni->GetStringUTFChars(exp_sig_str, NULL);
340
341 if (exp_sig == NULL) {
342 jni->FatalError("check_hidden_class_array: Error: JNI GetStringChars returned NULL for jstring\n");
343 return;
344 }
345 check_hidden_class_array(jvmti, jni, hidden_klass_array, exp_sig);
346
347 jni->ReleaseStringUTFChars(exp_sig_str, exp_sig);
348 }
349
350 /* Native method: checkFailed(). */
351 JNIEXPORT jboolean JNICALL
352 Java_P_Q_HiddenClassSigTest_checkFailed(JNIEnv *jni, jclass klass) {
353 if (class_load_count == 0) {
354 LOG0("Native Agent: missed ClassLoad event for hidden class\n");
355 failed = true;
356 }
357 return failed;
358 }
359
360 } // extern "C"
--- EOF ---