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 ---