| 1 | /* | 
| 2 |  * Copyright (c) 2003, 2019, 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 | #include "precompiled.hpp" | 
| 26 | #include "classfile/classLoaderData.inline.hpp" | 
| 27 | #include "classfile/classLoaderDataGraph.hpp" | 
| 28 | #include "classfile/dictionary.hpp" | 
| 29 | #include "classfile/loaderConstraints.hpp" | 
| 30 | #include "logging/log.hpp" | 
| 31 | #include "memory/resourceArea.hpp" | 
| 32 | #include "oops/oop.inline.hpp" | 
| 33 | #include "runtime/handles.inline.hpp" | 
| 34 | #include "runtime/safepoint.hpp" | 
| 35 | #include "utilities/hashtable.inline.hpp" | 
| 36 |  | 
| 37 | void LoaderConstraintEntry::set_loader(int i, oop p) { | 
| 38 |   set_loader_data(i, ClassLoaderData::class_loader_data(p)); | 
| 39 | } | 
| 40 |  | 
| 41 | LoaderConstraintTable::LoaderConstraintTable(int table_size) | 
| 42 |   : Hashtable<InstanceKlass*, mtClass>(table_size, sizeof(LoaderConstraintEntry)) {}; | 
| 43 |  | 
| 44 |  | 
| 45 | LoaderConstraintEntry* LoaderConstraintTable::new_entry( | 
| 46 |                                  unsigned int hash, Symbol* name, | 
| 47 |                                  InstanceKlass* klass, int num_loaders, | 
| 48 |                                  int max_loaders) { | 
| 49 |   LoaderConstraintEntry* entry; | 
| 50 |   entry = (LoaderConstraintEntry*)Hashtable<InstanceKlass*, mtClass>::new_entry(hash, klass); | 
| 51 |   entry->set_name(name); | 
| 52 |   entry->set_num_loaders(num_loaders); | 
| 53 |   entry->set_max_loaders(max_loaders); | 
| 54 |   return entry; | 
| 55 | } | 
| 56 |  | 
| 57 | void LoaderConstraintTable::free_entry(LoaderConstraintEntry *entry) { | 
| 58 |   // decrement name refcount before freeing | 
| 59 |   entry->name()->decrement_refcount(); | 
| 60 |   Hashtable<InstanceKlass*, mtClass>::free_entry(entry); | 
| 61 | } | 
| 62 |  | 
| 63 | // The loaderConstraintTable must always be accessed with the | 
| 64 | // SystemDictionary lock held. This is true even for readers as | 
| 65 | // entries in the table could be being dynamically resized. | 
| 66 |  | 
| 67 | LoaderConstraintEntry** LoaderConstraintTable::find_loader_constraint( | 
| 68 |                                     Symbol* name, Handle loader) { | 
| 69 |   assert_lock_strong(SystemDictionary_lock); | 
| 70 |   unsigned int hash = compute_hash(name); | 
| 71 |   int index = hash_to_index(hash); | 
| 72 |   LoaderConstraintEntry** pp = bucket_addr(index); | 
| 73 |   ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader()); | 
| 74 |  | 
| 75 |   while (*pp) { | 
| 76 |     LoaderConstraintEntry* p = *pp; | 
| 77 |     if (p->hash() == hash) { | 
| 78 |       if (p->name() == name) { | 
| 79 |         for (int i = p->num_loaders() - 1; i >= 0; i--) { | 
| 80 |           if (p->loader_data(i) == loader_data && | 
| 81 |               // skip unloaded klasses | 
| 82 |               (p->klass() == NULL || | 
| 83 |                p->klass()->is_loader_alive())) { | 
| 84 |             return pp; | 
| 85 |           } | 
| 86 |         } | 
| 87 |       } | 
| 88 |     } | 
| 89 |     pp = p->next_addr(); | 
| 90 |   } | 
| 91 |   return pp; | 
| 92 | } | 
| 93 |  | 
| 94 |  | 
| 95 | void LoaderConstraintTable::purge_loader_constraints() { | 
| 96 |   assert_locked_or_safepoint(SystemDictionary_lock); | 
| 97 |   LogTarget(Info, class, loader, constraints) lt; | 
| 98 |   // Remove unloaded entries from constraint table | 
| 99 |   for (int index = 0; index < table_size(); index++) { | 
| 100 |     LoaderConstraintEntry** p = bucket_addr(index); | 
| 101 |     while(*p) { | 
| 102 |       LoaderConstraintEntry* probe = *p; | 
| 103 |       InstanceKlass* klass = probe->klass(); | 
| 104 |       // Remove klass that is no longer alive | 
| 105 |       if (klass != NULL && | 
| 106 |           !klass->is_loader_alive()) { | 
| 107 |         probe->set_klass(NULL); | 
| 108 |         if (lt.is_enabled()) { | 
| 109 |           ResourceMark rm; | 
| 110 |           lt.print("purging class object from constraint for name %s,"  | 
| 111 |                      " loader list:" , | 
| 112 |                      probe->name()->as_C_string()); | 
| 113 |           for (int i = 0; i < probe->num_loaders(); i++) { | 
| 114 |             lt.print("    [%d]: %s" , i, | 
| 115 |                           probe->loader_data(i)->loader_name_and_id()); | 
| 116 |           } | 
| 117 |         } | 
| 118 |       } | 
| 119 |       // Remove entries no longer alive from loader array | 
| 120 |       int n = 0; | 
| 121 |       while (n < probe->num_loaders()) { | 
| 122 |         if (probe->loader_data(n)->is_unloading()) { | 
| 123 |           if (lt.is_enabled()) { | 
| 124 |             ResourceMark rm; | 
| 125 |             lt.print("purging loader %s from constraint for name %s" , | 
| 126 |                      probe->loader_data(n)->loader_name_and_id(), | 
| 127 |                      probe->name()->as_C_string() | 
| 128 |                      ); | 
| 129 |           } | 
| 130 |  | 
| 131 |           // Compact array | 
| 132 |           int num = probe->num_loaders() - 1; | 
| 133 |           probe->set_num_loaders(num); | 
| 134 |           probe->set_loader_data(n, probe->loader_data(num)); | 
| 135 |           probe->set_loader_data(num, NULL); | 
| 136 |  | 
| 137 |           if (lt.is_enabled()) { | 
| 138 |             ResourceMark rm; | 
| 139 |             lt.print("new loader list:" ); | 
| 140 |             for (int i = 0; i < probe->num_loaders(); i++) { | 
| 141 |               lt.print("    [%d]: %s" , i, | 
| 142 |                             probe->loader_data(i)->loader_name_and_id()); | 
| 143 |             } | 
| 144 |           } | 
| 145 |  | 
| 146 |           continue;  // current element replaced, so restart without | 
| 147 |                      // incrementing n | 
| 148 |           } | 
| 149 |         n++; | 
| 150 |       } | 
| 151 |       // Check whether entry should be purged | 
| 152 |       if (probe->num_loaders() < 2) { | 
| 153 |             if (lt.is_enabled()) { | 
| 154 |               ResourceMark rm; | 
| 155 |               lt.print("purging complete constraint for name %s" , | 
| 156 |                          probe->name()->as_C_string()); | 
| 157 |             } | 
| 158 |  | 
| 159 |         // Purge entry | 
| 160 |         *p = probe->next(); | 
| 161 |         FREE_C_HEAP_ARRAY(oop, probe->loaders()); | 
| 162 |         free_entry(probe); | 
| 163 |       } else { | 
| 164 | #ifdef ASSERT | 
| 165 |         if (probe->klass() != NULL) { | 
| 166 |           assert(probe->klass()->is_loader_alive(), "klass should be live" ); | 
| 167 |         } | 
| 168 | #endif | 
| 169 |         // Go to next entry | 
| 170 |         p = probe->next_addr(); | 
| 171 |       } | 
| 172 |     } | 
| 173 |   } | 
| 174 | } | 
| 175 |  | 
| 176 | void log_ldr_constraint_msg(Symbol* class_name, const char* reason, | 
| 177 |                         Handle class_loader1, Handle class_loader2) { | 
| 178 |   LogTarget(Info, class, loader, constraints) lt; | 
| 179 |   if (lt.is_enabled()) { | 
| 180 |     ResourceMark rm; | 
| 181 |     lt.print("Failed to add constraint for name: %s, loader[0]: %s,"  | 
| 182 |                 " loader[1]: %s, Reason: %s" , | 
| 183 |                   class_name->as_C_string(), | 
| 184 |                   ClassLoaderData::class_loader_data(class_loader1())->loader_name_and_id(), | 
| 185 |                   ClassLoaderData::class_loader_data(class_loader2())->loader_name_and_id(), | 
| 186 |                   reason); | 
| 187 |   } | 
| 188 | } | 
| 189 |  | 
| 190 | bool LoaderConstraintTable::add_entry(Symbol* class_name, | 
| 191 |                                       InstanceKlass* klass1, Handle class_loader1, | 
| 192 |                                       InstanceKlass* klass2, Handle class_loader2) { | 
| 193 |   LogTarget(Info, class, loader, constraints) lt; | 
| 194 |   if (klass1 != NULL && klass2 != NULL) { | 
| 195 |     if (klass1 == klass2) { | 
| 196 |       // Same type already loaded in both places.  There is no need for any constraint. | 
| 197 |       return true; | 
| 198 |     } else { | 
| 199 |       log_ldr_constraint_msg(class_name, | 
| 200 |                              "The class objects presented by loader[0] and loader[1] "  | 
| 201 |                              "are different" , | 
| 202 |                              class_loader1, class_loader2); | 
| 203 |       return false; | 
| 204 |     } | 
| 205 |   } | 
| 206 |  | 
| 207 |   InstanceKlass* klass = klass1 != NULL ? klass1 : klass2; | 
| 208 |   LoaderConstraintEntry** pp1 = find_loader_constraint(class_name, class_loader1); | 
| 209 |   if (*pp1 != NULL && (*pp1)->klass() != NULL) { | 
| 210 |     if (klass != NULL) { | 
| 211 |       if (klass != (*pp1)->klass()) { | 
| 212 |         log_ldr_constraint_msg(class_name, | 
| 213 |                                "The class object presented by loader[0] does not match "  | 
| 214 |                                "the stored class object in the constraint" , | 
| 215 |                                class_loader1, class_loader2); | 
| 216 |         return false; | 
| 217 |       } | 
| 218 |     } else { | 
| 219 |       klass = (*pp1)->klass(); | 
| 220 |     } | 
| 221 |   } | 
| 222 |  | 
| 223 |   LoaderConstraintEntry** pp2 = find_loader_constraint(class_name, class_loader2); | 
| 224 |   if (*pp2 != NULL && (*pp2)->klass() != NULL) { | 
| 225 |     if (klass != NULL) { | 
| 226 |       if (klass != (*pp2)->klass()) { | 
| 227 |         log_ldr_constraint_msg(class_name, | 
| 228 |                                "The class object presented by loader[1] does not match "  | 
| 229 |                                "the stored class object in the constraint" , | 
| 230 |                                class_loader1, class_loader2); | 
| 231 |         return false; | 
| 232 |       } | 
| 233 |     } else { | 
| 234 |       klass = (*pp2)->klass(); | 
| 235 |     } | 
| 236 |   } | 
| 237 |  | 
| 238 |   if (*pp1 == NULL && *pp2 == NULL) { | 
| 239 |     unsigned int hash = compute_hash(class_name); | 
| 240 |     int index = hash_to_index(hash); | 
| 241 |     LoaderConstraintEntry* p; | 
| 242 |     p = new_entry(hash, class_name, klass, 2, 2); | 
| 243 |     p->set_loaders(NEW_C_HEAP_ARRAY(ClassLoaderData*, 2, mtClass)); | 
| 244 |     p->set_loader(0, class_loader1()); | 
| 245 |     p->set_loader(1, class_loader2()); | 
| 246 |     p->set_klass(klass); | 
| 247 |     p->set_next(bucket(index)); | 
| 248 |     set_entry(index, p); | 
| 249 |     if (lt.is_enabled()) { | 
| 250 |       ResourceMark rm; | 
| 251 |       lt.print("adding new constraint for name: %s, loader[0]: %s,"  | 
| 252 |                     " loader[1]: %s" , | 
| 253 |                     class_name->as_C_string(), | 
| 254 |                     ClassLoaderData::class_loader_data(class_loader1())->loader_name_and_id(), | 
| 255 |                     ClassLoaderData::class_loader_data(class_loader2())->loader_name_and_id() | 
| 256 |                     ); | 
| 257 |     } | 
| 258 |   } else if (*pp1 == *pp2) { | 
| 259 |     /* constraint already imposed */ | 
| 260 |     if ((*pp1)->klass() == NULL) { | 
| 261 |       (*pp1)->set_klass(klass); | 
| 262 |       if (lt.is_enabled()) { | 
| 263 |         ResourceMark rm; | 
| 264 |         lt.print("setting class object in existing constraint for"  | 
| 265 |                       " name: %s and loader %s" , | 
| 266 |                       class_name->as_C_string(), | 
| 267 |                       ClassLoaderData::class_loader_data(class_loader1())->loader_name_and_id() | 
| 268 |                       ); | 
| 269 |       } | 
| 270 |     } else { | 
| 271 |       assert((*pp1)->klass() == klass, "loader constraints corrupted" ); | 
| 272 |     } | 
| 273 |   } else if (*pp1 == NULL) { | 
| 274 |     extend_loader_constraint(*pp2, class_loader1, klass); | 
| 275 |   } else if (*pp2 == NULL) { | 
| 276 |     extend_loader_constraint(*pp1, class_loader2, klass); | 
| 277 |   } else { | 
| 278 |     merge_loader_constraints(pp1, pp2, klass); | 
| 279 |   } | 
| 280 |  | 
| 281 |   return true; | 
| 282 | } | 
| 283 |  | 
| 284 |  | 
| 285 | // return true if the constraint was updated, false if the constraint is | 
| 286 | // violated | 
| 287 | bool LoaderConstraintTable::check_or_update(InstanceKlass* k, | 
| 288 |                                             Handle loader, | 
| 289 |                                             Symbol* name) { | 
| 290 |   LogTarget(Info, class, loader, constraints) lt; | 
| 291 |   LoaderConstraintEntry* p = *(find_loader_constraint(name, loader)); | 
| 292 |   if (p && p->klass() != NULL && p->klass() != k) { | 
| 293 |     if (lt.is_enabled()) { | 
| 294 |       ResourceMark rm; | 
| 295 |       lt.print("constraint check failed for name %s, loader %s: "  | 
| 296 |                  "the presented class object differs from that stored" , | 
| 297 |                  name->as_C_string(), | 
| 298 |                  ClassLoaderData::class_loader_data(loader())->loader_name_and_id()); | 
| 299 |     } | 
| 300 |     return false; | 
| 301 |   } else { | 
| 302 |     if (p && p->klass() == NULL) { | 
| 303 |       p->set_klass(k); | 
| 304 |       if (lt.is_enabled()) { | 
| 305 |         ResourceMark rm; | 
| 306 |         lt.print("updating constraint for name %s, loader %s, "  | 
| 307 |                    "by setting class object" , | 
| 308 |                    name->as_C_string(), | 
| 309 |                    ClassLoaderData::class_loader_data(loader())->loader_name_and_id()); | 
| 310 |       } | 
| 311 |     } | 
| 312 |     return true; | 
| 313 |   } | 
| 314 | } | 
| 315 |  | 
| 316 | InstanceKlass* LoaderConstraintTable::find_constrained_klass(Symbol* name, | 
| 317 |                                                        Handle loader) { | 
| 318 |   LoaderConstraintEntry *p = *(find_loader_constraint(name, loader)); | 
| 319 |   if (p != NULL && p->klass() != NULL) { | 
| 320 |     assert(p->klass()->is_instance_klass(), "sanity" ); | 
| 321 |     if (!p->klass()->is_loaded()) { | 
| 322 |       // Only return fully loaded classes.  Classes found through the | 
| 323 |       // constraints might still be in the process of loading. | 
| 324 |       return NULL; | 
| 325 |     } | 
| 326 |     return p->klass(); | 
| 327 |   } | 
| 328 |  | 
| 329 |   // No constraints, or else no klass loaded yet. | 
| 330 |   return NULL; | 
| 331 | } | 
| 332 |  | 
| 333 | void LoaderConstraintTable::ensure_loader_constraint_capacity( | 
| 334 |                                                      LoaderConstraintEntry *p, | 
| 335 |                                                     int nfree) { | 
| 336 |     if (p->max_loaders() - p->num_loaders() < nfree) { | 
| 337 |         int n = nfree + p->num_loaders(); | 
| 338 |         ClassLoaderData** new_loaders = NEW_C_HEAP_ARRAY(ClassLoaderData*, n, mtClass); | 
| 339 |         memcpy(new_loaders, p->loaders(), sizeof(ClassLoaderData*) * p->num_loaders()); | 
| 340 |         p->set_max_loaders(n); | 
| 341 |         FREE_C_HEAP_ARRAY(ClassLoaderData*, p->loaders()); | 
| 342 |         p->set_loaders(new_loaders); | 
| 343 |     } | 
| 344 | } | 
| 345 |  | 
| 346 |  | 
| 347 | void LoaderConstraintTable::extend_loader_constraint(LoaderConstraintEntry* p, | 
| 348 |                                                      Handle loader, | 
| 349 |                                                      InstanceKlass* klass) { | 
| 350 |   ensure_loader_constraint_capacity(p, 1); | 
| 351 |   int num = p->num_loaders(); | 
| 352 |   p->set_loader(num, loader()); | 
| 353 |   p->set_num_loaders(num + 1); | 
| 354 |   LogTarget(Info, class, loader, constraints) lt; | 
| 355 |   if (lt.is_enabled()) { | 
| 356 |     ResourceMark rm; | 
| 357 |     lt.print("extending constraint for name %s by adding loader[%d]: %s %s" , | 
| 358 |                p->name()->as_C_string(), | 
| 359 |                num, | 
| 360 |                ClassLoaderData::class_loader_data(loader())->loader_name_and_id(), | 
| 361 |                (p->klass() == NULL ? " and setting class object"  : "" ) | 
| 362 |                ); | 
| 363 |   } | 
| 364 |   if (p->klass() == NULL) { | 
| 365 |     p->set_klass(klass); | 
| 366 |   } else { | 
| 367 |     assert(klass == NULL || p->klass() == klass, "constraints corrupted" ); | 
| 368 |   } | 
| 369 | } | 
| 370 |  | 
| 371 |  | 
| 372 | void LoaderConstraintTable::merge_loader_constraints( | 
| 373 |                                                    LoaderConstraintEntry** pp1, | 
| 374 |                                                    LoaderConstraintEntry** pp2, | 
| 375 |                                                    InstanceKlass* klass) { | 
| 376 |   // make sure *pp1 has higher capacity | 
| 377 |   if ((*pp1)->max_loaders() < (*pp2)->max_loaders()) { | 
| 378 |     LoaderConstraintEntry** tmp = pp2; | 
| 379 |     pp2 = pp1; | 
| 380 |     pp1 = tmp; | 
| 381 |   } | 
| 382 |  | 
| 383 |   LoaderConstraintEntry* p1 = *pp1; | 
| 384 |   LoaderConstraintEntry* p2 = *pp2; | 
| 385 |  | 
| 386 |   ensure_loader_constraint_capacity(p1, p2->num_loaders()); | 
| 387 |  | 
| 388 |   for (int i = 0; i < p2->num_loaders(); i++) { | 
| 389 |     int num = p1->num_loaders(); | 
| 390 |     p1->set_loader_data(num, p2->loader_data(i)); | 
| 391 |     p1->set_num_loaders(num + 1); | 
| 392 |   } | 
| 393 |  | 
| 394 |   LogTarget(Info, class, loader, constraints) lt; | 
| 395 |   if (lt.is_enabled()) { | 
| 396 |     ResourceMark rm; | 
| 397 |     lt.print("merged constraints for name %s, new loader list:" , | 
| 398 |                   p1->name()->as_C_string() | 
| 399 |                   ); | 
| 400 |  | 
| 401 |     for (int i = 0; i < p1->num_loaders(); i++) { | 
| 402 |       lt.print("    [%d]: %s" , i, | 
| 403 |                     p1->loader_data(i)->loader_name_and_id()); | 
| 404 |     } | 
| 405 |     if (p1->klass() == NULL) { | 
| 406 |       lt.print("... and setting class object" ); | 
| 407 |     } | 
| 408 |   } | 
| 409 |  | 
| 410 |   // p1->klass() will hold NULL if klass, p2->klass(), and old | 
| 411 |   // p1->klass() are all NULL.  In addition, all three must have | 
| 412 |   // matching non-NULL values, otherwise either the constraints would | 
| 413 |   // have been violated, or the constraints had been corrupted (and an | 
| 414 |   // assertion would fail). | 
| 415 |   if (p2->klass() != NULL) { | 
| 416 |     assert(p2->klass() == klass, "constraints corrupted" ); | 
| 417 |   } | 
| 418 |   if (p1->klass() == NULL) { | 
| 419 |     p1->set_klass(klass); | 
| 420 |   } else { | 
| 421 |     assert(p1->klass() == klass, "constraints corrupted" ); | 
| 422 |   } | 
| 423 |  | 
| 424 |   *pp2 = p2->next(); | 
| 425 |   FREE_C_HEAP_ARRAY(oop, p2->loaders()); | 
| 426 |   free_entry(p2); | 
| 427 |   return; | 
| 428 | } | 
| 429 |  | 
| 430 |  | 
| 431 | void LoaderConstraintTable::verify(PlaceholderTable* placeholders) { | 
| 432 |   Thread *thread = Thread::current(); | 
| 433 |   for (int cindex = 0; cindex < table_size(); cindex++) { | 
| 434 |     for (LoaderConstraintEntry* probe = bucket(cindex); | 
| 435 |                                 probe != NULL; | 
| 436 |                                 probe = probe->next()) { | 
| 437 |       if (probe->klass() != NULL) { | 
| 438 |         InstanceKlass* ik = probe->klass(); | 
| 439 |         guarantee(ik->name() == probe->name(), "name should match" ); | 
| 440 |         Symbol* name = ik->name(); | 
| 441 |         ClassLoaderData* loader_data = ik->class_loader_data(); | 
| 442 |         Dictionary* dictionary = loader_data->dictionary(); | 
| 443 |         unsigned int d_hash = dictionary->compute_hash(name); | 
| 444 |         int d_index = dictionary->hash_to_index(d_hash); | 
| 445 |         InstanceKlass* k = dictionary->find_class(d_index, d_hash, name); | 
| 446 |         if (k != NULL) { | 
| 447 |           // We found the class in the dictionary, so we should | 
| 448 |           // make sure that the Klass* matches what we already have. | 
| 449 |           guarantee(k == probe->klass(), "klass should be in dictionary" ); | 
| 450 |         } else { | 
| 451 |           // If we don't find the class in the dictionary, it | 
| 452 |           // has to be in the placeholders table. | 
| 453 |           unsigned int p_hash = placeholders->compute_hash(name); | 
| 454 |           int p_index = placeholders->hash_to_index(p_hash); | 
| 455 |           PlaceholderEntry* entry = placeholders->get_entry(p_index, p_hash, | 
| 456 |                                                             name, loader_data); | 
| 457 |  | 
| 458 |           // The InstanceKlass might not be on the entry, so the only | 
| 459 |           // thing we can check here is whether we were successful in | 
| 460 |           // finding the class in the placeholders table. | 
| 461 |           guarantee(entry != NULL, "klass should be in the placeholders" ); | 
| 462 |         } | 
| 463 |       } | 
| 464 |       for (int n = 0; n< probe->num_loaders(); n++) { | 
| 465 |         assert(ClassLoaderDataGraph::contains_loader_data(probe->loader_data(n)), "The loader is missing" ); | 
| 466 |       } | 
| 467 |     } | 
| 468 |   } | 
| 469 | } | 
| 470 |  | 
| 471 | // Called with the system dictionary lock held | 
| 472 | void LoaderConstraintTable::print_on(outputStream* st) const { | 
| 473 |   ResourceMark rm; | 
| 474 |   assert_locked_or_safepoint(SystemDictionary_lock); | 
| 475 |   st->print_cr("Java loader constraints (table_size=%d, constraints=%d)" , | 
| 476 |                table_size(), number_of_entries()); | 
| 477 |   for (int cindex = 0; cindex < table_size(); cindex++) { | 
| 478 |     for (LoaderConstraintEntry* probe = bucket(cindex); | 
| 479 |                                 probe != NULL; | 
| 480 |                                 probe = probe->next()) { | 
| 481 |       st->print("%4d: " , cindex); | 
| 482 |       probe->name()->print_on(st); | 
| 483 |       st->print(" , loaders:" ); | 
| 484 |       for (int n = 0; n < probe->num_loaders(); n++) { | 
| 485 |         probe->loader_data(n)->print_value_on(st); | 
| 486 |         st->print(", " ); | 
| 487 |       } | 
| 488 |       st->cr(); | 
| 489 |     } | 
| 490 |   } | 
| 491 | } | 
| 492 |  |