< prev index next >

src/hotspot/share/oops/constantPool.cpp

Print this page
@@ -26,16 +26,19 @@
  #include "cds/archiveHeapWriter.hpp"
  #include "cds/archiveHeapLoader.hpp"
  #include "cds/archiveBuilder.hpp"
  #include "cds/cdsConfig.hpp"
  #include "cds/classPrelinker.hpp"
+ #include "cds/lambdaFormInvokers.inline.hpp"
  #include "cds/heapShared.hpp"
+ #include "classfile/classLoader.hpp"
  #include "classfile/classLoaderData.hpp"
  #include "classfile/javaClasses.inline.hpp"
  #include "classfile/metadataOnStackMark.hpp"
  #include "classfile/stringTable.hpp"
  #include "classfile/systemDictionary.hpp"
+ #include "classfile/systemDictionaryShared.hpp"
  #include "classfile/vmClasses.hpp"
  #include "classfile/vmSymbols.hpp"
  #include "code/codeCache.hpp"
  #include "interpreter/bootstrapInfo.hpp"
  #include "interpreter/linkResolver.hpp"

@@ -57,14 +60,16 @@
  #include "oops/objArrayOop.inline.hpp"
  #include "oops/oop.inline.hpp"
  #include "oops/typeArrayOop.inline.hpp"
  #include "prims/jvmtiExport.hpp"
  #include "runtime/atomic.hpp"
+ //#include "runtime/fieldDescriptor.inline.hpp"
  #include "runtime/handles.inline.hpp"
  #include "runtime/init.hpp"
  #include "runtime/javaCalls.hpp"
- #include "runtime/javaThread.hpp"
+ #include "runtime/javaThread.inline.hpp"
+ #include "runtime/perfData.hpp"
  #include "runtime/signature.hpp"
  #include "runtime/vframe.inline.hpp"
  #include "utilities/checkedCast.hpp"
  #include "utilities/copy.hpp"
  

@@ -296,25 +301,57 @@
      return nullptr;
    }
  
    objArrayOop rr = resolved_references();
    if (rr != nullptr) {
+     ResourceMark rm;
+     int rr_len = rr->length();
+     GrowableArray<bool> keep_resolved_refs(rr_len, rr_len, false);
      ConstantPool* orig_pool = ArchiveBuilder::current()->get_source_addr(this);
+ 
+     if (cache() != nullptr && ArchiveInvokeDynamic) {
+       Array<ResolvedIndyEntry>* indy_entries = cache()->resolved_indy_entries();
+       if (indy_entries != nullptr) {
+         for (int i = 0; i < indy_entries->length(); i++) {
+           ResolvedIndyEntry *rie = indy_entries->adr_at(i);
+           if (rie->is_resolved() && ClassPrelinker::is_indy_archivable(orig_pool, rie->constant_pool_index())) {
+             int rr_index = rie->resolved_references_index();
+             keep_resolved_refs.at_put(rr_index, true);
+           }
+         }
+       }
+ 
+       Array<ResolvedMethodEntry>* method_entries = cache()->resolved_method_entries();
+       if (method_entries != nullptr) {
+         for (int i = 0; i < method_entries->length(); i++) {
+           ResolvedMethodEntry* rme = method_entries->adr_at(i);
+           if (rme->is_resolved(Bytecodes::_invokehandle) && rme->has_appendix() &&
+               can_archive_resolved_method(rme)) {
+             int rr_index = rme->resolved_references_index();
+             keep_resolved_refs.at_put(rr_index, true);
+           }
+         }
+       }
+     }
+ 
      objArrayOop scratch_rr = HeapShared::scratch_resolved_references(orig_pool);
      Array<u2>* ref_map = reference_map();
      int ref_map_len = ref_map == nullptr ? 0 : ref_map->length();
-     int rr_len = rr->length();
      for (int i = 0; i < rr_len; i++) {
        oop obj = rr->obj_at(i);
        scratch_rr->obj_at_put(i, nullptr);
-       if (obj != nullptr && i < ref_map_len) {
-         int index = object_to_cp_index(i);
-         if (tag_at(index).is_string()) {
-           assert(java_lang_String::is_instance(obj), "must be");
-           if (!ArchiveHeapWriter::is_string_too_large_to_archive(obj)) {
-             scratch_rr->obj_at_put(i, obj);
+       if (obj != nullptr) {
+         if (i < ref_map_len) {
+           int index = object_to_cp_index(i);
+           if (tag_at(index).is_string()) {
+             assert(java_lang_String::is_instance(obj), "must be");
+             if (!ArchiveHeapWriter::is_string_too_large_to_archive(obj)) {
+               scratch_rr->obj_at_put(i, obj);
+             }
            }
+         } else if (keep_resolved_refs.at(i)) {
+           scratch_rr->obj_at_put(i, obj);
          }
        }
      }
      return scratch_rr;
    }

@@ -343,10 +380,13 @@
      return;
    }
    assert(is_constantPool(), "ensure C++ vtable is restored");
    assert(on_stack(), "should always be set for shared constant pools");
    assert(is_shared(), "should always be set for shared constant pools");
+   if (is_for_method_handle_intrinsic()) {
+     return;
+   }
    assert(_cache != nullptr, "constant pool _cache should not be null");
  
    // Only create the new resolved references array if it hasn't been attempted before
    if (resolved_references() != nullptr) return;
  

@@ -373,36 +413,64 @@
          Handle refs_handle(THREAD, stom);  // must handleize.
          set_resolved_references(loader_data->add_handle(refs_handle));
        }
      }
    }
+ 
+   if (CDSConfig::is_dumping_final_static_archive() && resolved_references() != nullptr) {
+     objArrayOop scratch_references = oopFactory::new_objArray(vmClasses::Object_klass(), resolved_references()->length(), CHECK);
+     HeapShared::add_scratch_resolved_references(this, scratch_references);
+   }
  }
  
  void ConstantPool::remove_unshareable_info() {
    // Shared ConstantPools are in the RO region, so the _flags cannot be modified.
    // The _on_stack flag is used to prevent ConstantPools from deallocation during
    // class redefinition. Since shared ConstantPools cannot be deallocated anyway,
    // we always set _on_stack to true to avoid having to change _flags during runtime.
    _flags |= (_on_stack | _is_shared);
  
-   if (!_pool_holder->is_linked() && !_pool_holder->verified_at_dump_time()) {
+   if (!ArchiveBuilder::current()->get_source_addr(_pool_holder)->is_linked()) {
      return;
    }
+ 
+   if (is_for_method_handle_intrinsic()) {
+     // This CP was created by Method::make_method_handle_intrinsic() and has nothing
+     // that need to be removed/restored. It has no cpCache since the intrinsic methods
+     // don't have any bytecodes.
+     assert(cache() == NULL, "must not have cpCache");
+     return;
+   }
+ 
    // Resolved references are not in the shared archive.
    // Save the length for restoration.  It is not necessarily the same length
    // as reference_map.length() if invokedynamic is saved. It is needed when
    // re-creating the resolved reference array if archived heap data cannot be map
    // at runtime.
    set_resolved_reference_length(
      resolved_references() != nullptr ? resolved_references()->length() : 0);
    set_resolved_references(OopHandle());
  
+   archive_entries();
+ }
+ 
+ void ConstantPool::archive_entries() {
+   InstanceKlass* src_holder = ArchiveBuilder::current()->get_source_addr(pool_holder());
+   assert(src_holder->is_linked(), "must be");
+   ResourceMark rm;
    bool archived = false;
+   bool preresolve = pool_holder()->is_shared_boot_class() || pool_holder()->is_shared_platform_class() ||
+                     pool_holder()->is_shared_app_class();
    for (int cp_index = 1; cp_index < length(); cp_index++) { // cp_index 0 is unused
-     switch (tag_at(cp_index).value()) {
+     int cp_tag = tag_at(cp_index).value();
+     switch (cp_tag) {
+     case JVM_CONSTANT_UnresolvedClass:
+       ArchiveBuilder::alloc_stats()->record_klass_cp_entry(false);
+       break;
      case JVM_CONSTANT_UnresolvedClassInError:
        tag_at_put(cp_index, JVM_CONSTANT_UnresolvedClass);
+       ArchiveBuilder::alloc_stats()->record_klass_cp_entry(false);
        break;
      case JVM_CONSTANT_MethodHandleInError:
        tag_at_put(cp_index, JVM_CONSTANT_MethodHandle);
        break;
      case JVM_CONSTANT_MethodTypeInError:

@@ -410,22 +478,69 @@
        break;
      case JVM_CONSTANT_DynamicInError:
        tag_at_put(cp_index, JVM_CONSTANT_Dynamic);
        break;
      case JVM_CONSTANT_Class:
-       archived = maybe_archive_resolved_klass_at(cp_index);
+       if (preresolve) {
+         archived = maybe_archive_resolved_klass_at(cp_index);
+       } else {
+         archived = false;
+       }
+       if (!archived) {
+         // This referenced class cannot be archived. Revert the tag to UnresolvedClass,
+         // so that the proper class loading and initialization can happen at runtime.
+         int resolved_klass_index = klass_slot_at(cp_index).resolved_klass_index();
+         resolved_klasses()->at_put(resolved_klass_index, nullptr);
+         tag_at_put(cp_index, JVM_CONSTANT_UnresolvedClass);
+       }
        ArchiveBuilder::alloc_stats()->record_klass_cp_entry(archived);
        break;
+     default:
+       break;
      }
    }
  
    if (cache() != nullptr) {
      // cache() is null if this class is not yet linked.
      cache()->remove_unshareable_info();
    }
  }
  
+ static const char* get_type(Klass* k) {
+   const char* type;
+   Klass* src_k;
+   if (ArchiveBuilder::is_active() && ArchiveBuilder::current()->is_in_buffer_space(k)) {
+     src_k = ArchiveBuilder::current()->get_source_addr(k);
+   } else {
+     src_k = k;
+   }
+ 
+   if (src_k->is_objArray_klass()) {
+     src_k = ObjArrayKlass::cast(src_k)->bottom_klass();
+     assert(!src_k->is_objArray_klass(), "sanity");
+   }
+ 
+   if (src_k->is_typeArray_klass()) {
+     type = "prim";
+   } else {
+     InstanceKlass* src_ik = InstanceKlass::cast(src_k);
+     oop loader = src_ik->class_loader();
+     if (loader == nullptr) {
+       type = "boot";
+     } else if (loader == SystemDictionary::java_platform_loader()) {
+       type = "plat";
+     } else if (loader == SystemDictionary::java_system_loader()) {
+       type = "app ";
+     } else {
+       type = "bad ";
+       assert(0, "shouldn't have resolved a type loaded by custom loader");
+     }
+   }
+ 
+   return type;
+ }
+ 
  bool ConstantPool::maybe_archive_resolved_klass_at(int cp_index) {
    assert(ArchiveBuilder::current()->is_in_buffer_space(this), "must be");
    assert(tag_at(cp_index).is_klass(), "must be resolved");
  
    if (pool_holder()->is_hidden() && cp_index == pool_holder()->this_class_index()) {

@@ -444,23 +559,125 @@
    if (k != nullptr) {
      ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
      if (ClassPrelinker::can_archive_resolved_klass(src_cp, cp_index)) {
        if (log_is_enabled(Debug, cds, resolve)) {
          ResourceMark rm;
-         log_debug(cds, resolve)("Resolved klass CP entry [%d]: %s => %s", cp_index,
-                                 pool_holder()->external_name(), k->external_name());
+         log_debug(cds, resolve)("archived klass  CP entry [%3d]: %s %s => %s %s%s", cp_index,
+                                 pool_holder()->name()->as_C_string(), get_type(pool_holder()),
+                                 k->name()->as_C_string(), get_type(k),
+                                 pool_holder()->is_subtype_of(k) ? "" : " (not supertype)");
        }
        return true;
      }
    }
- 
-   // This referenced class cannot be archived. Revert the tag to UnresolvedClass,
-   // so that the proper class loading and initialization can happen at runtime.
-   resolved_klasses()->at_put(resolved_klass_index, nullptr);
-   tag_at_put(cp_index, JVM_CONSTANT_UnresolvedClass);
    return false;
  }
+ 
+ bool ConstantPool::can_archive_invokehandle(ResolvedMethodEntry* rme) {
+   assert(rme->is_resolved(Bytecodes::_invokehandle), "sanity");
+ 
+   int cp_index = rme->constant_pool_index();
+   int klass_cp_index = uncached_klass_ref_index_at(cp_index);
+   Klass* resolved_klass = resolved_klass_at(klass_cp_index);
+   if (!resolved_klass->is_instance_klass()) {
+     // FIXME: can this ever happen?
+     return false;
+   }
+   // FIXME -- any class referenced by the archived CP entries should be added to ArchiveBuilder::classes, or should be
+   // filtered out.
+   return true;
+ }
+ 
+ bool ConstantPool::can_archive_resolved_method(ResolvedMethodEntry* method_entry) {
+   if (pool_holder()->is_hidden()) { // Not sure how to handle this yet ...
+     if (pool_holder()->name()->starts_with("java/lang/invoke/LambdaForm$")) {
+       // Hmmm, walking on thin ice here, but maybe we are OK :-)
+     } else {
+       return false;
+     }
+   }
+ 
+   if (!(pool_holder()->is_shared_boot_class() || pool_holder()->is_shared_platform_class() ||
+         pool_holder()->is_shared_app_class())) {
+     // Archiving resolved cp entries for classes from non-builtin loaders
+     // is not yet supported.
+     return false;
+   }
+ 
+   if (CDSConfig::is_dumping_dynamic_archive()) {
+     // InstanceKlass::methods() is has been resorted. We need to
+     // update the vtable_index in method_entry (not implemented)
+     return false;
+   }
+ 
+   if (method_entry->method() == nullptr) {
+     return false;
+   }
+   if (method_entry->method()->is_continuation_native_intrinsic()) {
+     return false; // FIXME: corresponding stub is generated on demand during method resolution (see LinkResolver::resolve_static_call).
+   }
+ 
+   int cp_index = method_entry->constant_pool_index();
+   ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
+   if (!src_cp->tag_at(cp_index).is_method() &&
+       !src_cp->tag_at(cp_index).is_interface_method()) {
+     return false;
+   }
+ 
+   if (!ClassPrelinker::can_archive_resolved_method(src_cp, cp_index)) {
+     return false;
+   }
+ 
+   int klass_cp_index = uncached_klass_ref_index_at(cp_index);
+   if (!src_cp->tag_at(klass_cp_index).is_klass()) {
+     return false;
+   }
+ 
+   Klass* resolved_klass = resolved_klass_at(klass_cp_index);
+   if (!resolved_klass->is_instance_klass()) {
+     // FIXME: is it valid to have a non-instance klass in method refs?
+     return false;
+   }
+   const char* is_static = "";
+   const char* is_interface = "";
+   if (method_entry->is_resolved(Bytecodes::_invokehandle)) {
+     if (!ArchiveInvokeDynamic) {
+       // FIXME We don't dump the MethodType tables. This somehow breaks stuff. Why???
+       return false;
+     } else if (!can_archive_invokehandle(method_entry)) {
+       return false;
+     }
+   } else if (method_entry->is_resolved(Bytecodes::_invokestatic)) {
+     is_static = " *** static";
+   } else if (method_entry->is_resolved(Bytecodes::_invokeinterface)) {
+     is_interface = "interface";
+   } else if (!method_entry->is_resolved(Bytecodes::_invokevirtual) &&
+              !method_entry->is_resolved(Bytecodes::_invokespecial)) {
+     return false;
+   }
+ 
+   if (resolved_klass->is_instance_klass()) {
+     InstanceKlass* ik = InstanceKlass::cast(resolved_klass);
+     if (SystemDictionaryShared::is_jfr_event_class(ik)) {
+       // Some methods in JRF event klasses may be redefined.
+       return false;
+     }
+     if (SystemDictionaryShared::has_been_redefined(ik)) {
+       return false;
+     }
+   }
+ 
+   if (log_is_enabled(Debug, cds, resolve)) {
+     ResourceMark rm;
+     Symbol* name = uncached_name_ref_at(cp_index);
+     Symbol* signature = uncached_signature_ref_at(cp_index);
+     log_debug(cds, resolve)("archived %s method CP entry [%3d]: %s => %s.%s:%s%s", is_interface, cp_index,
+                             pool_holder()->name()->as_C_string(), resolved_klass->name()->as_C_string(),
+                             name->as_C_string(), signature->as_C_string(), is_static);
+   }
+   return true;
+ }
  #endif // INCLUDE_CDS
  
  int ConstantPool::cp_to_object_index(int cp_index) {
    // this is harder don't do this so much.
    int i = reference_map()->find(checked_cast<u2>(cp_index));

@@ -1015,11 +1232,13 @@
        result_oop = resolved->java_mirror();
        break;
      }
  
    case JVM_CONSTANT_Dynamic:
-     {
+     { PerfTraceTimedEvent timer(ClassLoader::perf_resolve_invokedynamic_time(),
+                                 ClassLoader::perf_resolve_invokedynamic_count(),
+                                 THREAD->class_being_initialized() == nullptr);
        // Resolve the Dynamically-Computed constant to invoke the BSM in order to obtain the resulting oop.
        BootstrapInfo bootstrap_specifier(this_cp, cp_index);
  
        // The initial step in resolving an unresolved symbolic reference to a
        // dynamically-computed constant is to resolve the symbolic reference to a

@@ -1073,11 +1292,13 @@
      assert(cache_index != _no_index_sentinel, "should have been set");
      result_oop = string_at_impl(this_cp, cp_index, cache_index, CHECK_NULL);
      break;
  
    case JVM_CONSTANT_MethodHandle:
-     {
+     { PerfTraceTimedEvent timer(ClassLoader::perf_resolve_method_handle_time(),
+                                 ClassLoader::perf_resolve_method_handle_count(),
+                                 THREAD->class_being_initialized() == nullptr);
        int ref_kind                 = this_cp->method_handle_ref_kind_at(cp_index);
        int callee_index             = this_cp->method_handle_klass_index_at(cp_index);
        Symbol*  name =      this_cp->method_handle_name_ref_at(cp_index);
        Symbol*  signature = this_cp->method_handle_signature_ref_at(cp_index);
        constantTag m_tag  = this_cp->tag_at(this_cp->method_handle_index_at(cp_index));

@@ -1121,11 +1342,13 @@
        result_oop = value();
        break;
      }
  
    case JVM_CONSTANT_MethodType:
-     {
+     { PerfTraceTimedEvent timer(ClassLoader::perf_resolve_method_type_time(),
+                                 ClassLoader::perf_resolve_method_type_count(),
+                                 THREAD->class_being_initialized() == nullptr);
        Symbol*  signature = this_cp->method_type_signature_at(cp_index);
        { ResourceMark rm(THREAD);
          log_debug(class, resolve)("resolve JVM_CONSTANT_MethodType [%d/%d] %s",
                                cp_index, this_cp->method_type_index_at(cp_index),
                                signature->as_C_string());
< prev index next >