| /* | |
| * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #ifndef SHARE_OOPS_CPCACHE_HPP | |
| #define SHARE_OOPS_CPCACHE_HPP | |
| #include "interpreter/bytecodes.hpp" | |
| #include "memory/allocation.hpp" | |
| #include "oops/array.hpp" | |
| #include "oops/oopHandle.hpp" | |
| #include "runtime/handles.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/constantTag.hpp" | |
| #include "utilities/growableArray.hpp" | |
| // The ConstantPoolCache is not a cache! It is the resolution table that the | |
| // interpreter uses to avoid going into the runtime and a way to access resolved | |
| // values. | |
| class CallInfo; | |
| class ResolvedFieldEntry; | |
| class ResolvedIndyEntry; | |
| class ResolvedMethodEntry; | |
| // A constant pool cache is a runtime data structure set aside to a constant pool. The cache | |
| // holds runtime information for all field access and invoke bytecodes. The cache | |
| // is created and initialized before a class is actively used (i.e., initialized), the indivi- | |
| // dual cache entries are filled at resolution (i.e., "link") time (see also: rewriter.*). | |
| class ConstantPoolCache: public MetaspaceObj { | |
| friend class VMStructs; | |
| friend class MetadataFactory; | |
| private: | |
| // If you add a new field that points to any metaspace object, you | |
| // must add this field to ConstantPoolCache::metaspace_pointers_do(). | |
| // The narrowOop pointer to the archived resolved_references. Set at CDS dump | |
| // time when caching java heap object is supported. | |
| CDS_JAVA_HEAP_ONLY(int _archived_references_index;) // Gap on LP64 | |
| ConstantPool* _constant_pool; // the corresponding constant pool | |
| // The following fields need to be modified at runtime, so they cannot be | |
| // stored in the ConstantPool, which is read-only. | |
| // Array of resolved objects from the constant pool and map from resolved | |
| // object index to original constant pool index | |
| OopHandle _resolved_references; | |
| Array<u2>* _reference_map; | |
| // RedefineClasses support | |
| uint64_t _gc_epoch; | |
| Array<ResolvedIndyEntry>* _resolved_indy_entries; | |
| Array<ResolvedFieldEntry>* _resolved_field_entries; | |
| Array<ResolvedMethodEntry>* _resolved_method_entries; | |
| // Sizing | |
| DEBUG_ONLY(friend class ClassVerifier;) | |
| public: | |
| // specific but defiinitions for ldc | |
| enum { | |
| // high order bits are the TosState corresponding to field type or method return type | |
| tos_state_bits = 4, | |
| tos_state_mask = right_n_bits(tos_state_bits), | |
| tos_state_shift = BitsPerInt - tos_state_bits, // see verify_tos_state_shift below | |
| // low order bits give field index (for FieldInfo) or method parameter size: | |
| field_index_bits = 16, | |
| field_index_mask = right_n_bits(field_index_bits), | |
| }; | |
| // Constructor | |
| ConstantPoolCache(const intStack& invokedynamic_references_map, | |
| Array<ResolvedIndyEntry>* indy_info, | |
| Array<ResolvedFieldEntry>* field_entries, | |
| Array<ResolvedMethodEntry>* mehtod_entries); | |
| // Initialization | |
| void initialize(const intArray& invokedynamic_references_map); | |
| public: | |
| static ConstantPoolCache* allocate(ClassLoaderData* loader_data, | |
| const intStack& invokedynamic_references_map, | |
| const GrowableArray<ResolvedIndyEntry> indy_entries, | |
| const GrowableArray<ResolvedFieldEntry> field_entries, | |
| const GrowableArray<ResolvedMethodEntry> method_entries, | |
| TRAPS); | |
| void metaspace_pointers_do(MetaspaceClosure* it); | |
| MetaspaceObj::Type type() const { return ConstantPoolCacheType; } | |
| oop archived_references() NOT_CDS_JAVA_HEAP_RETURN_(nullptr); | |
| void clear_archived_references() NOT_CDS_JAVA_HEAP_RETURN; | |
| inline objArrayOop resolved_references(); | |
| void set_resolved_references(OopHandle s) { _resolved_references = s; } | |
| Array<u2>* reference_map() const { return _reference_map; } | |
| void set_reference_map(Array<u2>* o) { _reference_map = o; } | |
| private: | |
| void set_direct_or_vtable_call( | |
| Bytecodes::Code invoke_code, // the bytecode used for invoking the method | |
| int method_index, // Index into the resolved method entry array | |
| const methodHandle& method, // the method/prototype if any (null, otherwise) | |
| int vtable_index, // the vtable index if any, else negative | |
| bool sender_is_interface | |
| ); | |
| public: | |
| void set_direct_call( // sets entry to exact concrete method entry | |
| Bytecodes::Code invoke_code, // the bytecode used for invoking the method | |
| int method_index, // Index into the resolved method entry array | |
| const methodHandle& method, // the method to call | |
| bool sender_is_interface | |
| ); | |
| void set_vtable_call( // sets entry to vtable index | |
| Bytecodes::Code invoke_code, // the bytecode used for invoking the method | |
| int method_index, // Index into the resolved method entry array | |
| const methodHandle& method, // resolved method which declares the vtable index | |
| int vtable_index // the vtable index | |
| ); | |
| void set_itable_call( | |
| Bytecodes::Code invoke_code, // the bytecode used; must be invokeinterface | |
| int method_index, // Index into the resolved method entry array | |
| Klass* referenced_klass, // the referenced klass in the InterfaceMethodref | |
| const methodHandle& method, // the resolved interface method | |
| int itable_index // index into itable for the method | |
| ); | |
| // The "appendix" is an optional call-site-specific parameter which is | |
| // pushed by the JVM at the end of the argument list. This argument may | |
| // be a MethodType for the MH.invokes and a CallSite for an invokedynamic | |
| // instruction. However, its exact type and use depends on the Java upcall, | |
| // which simply returns a compiled LambdaForm along with any reference | |
| // that LambdaForm needs to complete the call. If the upcall returns a | |
| // null appendix, the argument is not passed at all. | |
| // | |
| // The appendix is *not* represented in the signature of the symbolic | |
| // reference for the call site, but (if present) it *is* represented in | |
| // the Method* bound to the site. This means that static and dynamic | |
| // resolution logic needs to make slightly different assessments about the | |
| // number and types of arguments. | |
| ResolvedMethodEntry* set_method_handle( | |
| int method_index, | |
| const CallInfo &call_info // Call link information | |
| ); | |
| Method* method_if_resolved(int method_index) const; | |
| Array<ResolvedFieldEntry>* resolved_field_entries() { return _resolved_field_entries; } | |
| inline ResolvedFieldEntry* resolved_field_entry_at(int field_index) const; | |
| inline int resolved_field_entries_length() const; | |
| void print_resolved_field_entries(outputStream* st) const; | |
| Array<ResolvedIndyEntry>* resolved_indy_entries() { return _resolved_indy_entries; } | |
| inline ResolvedIndyEntry* resolved_indy_entry_at(int index) const; | |
| inline int resolved_indy_entries_length() const; | |
| void print_resolved_indy_entries(outputStream* st) const; | |
| Array<ResolvedMethodEntry>* resolved_method_entries() { return _resolved_method_entries; } | |
| inline ResolvedMethodEntry* resolved_method_entry_at(int method_index) const; | |
| inline int resolved_method_entries_length() const; | |
| void print_resolved_method_entries(outputStream* st) const; | |
| // Assembly code support | |
| static ByteSize resolved_references_offset() { return byte_offset_of(ConstantPoolCache, _resolved_references); } | |
| static ByteSize invokedynamic_entries_offset() { return byte_offset_of(ConstantPoolCache, _resolved_indy_entries); } | |
| static ByteSize field_entries_offset() { return byte_offset_of(ConstantPoolCache, _resolved_field_entries); } | |
| static ByteSize method_entries_offset() { return byte_offset_of(ConstantPoolCache, _resolved_method_entries); } | |
| #if INCLUDE_CDS | |
| void remove_unshareable_info(); | |
| #endif | |
| public: | |
| static int size() { return align_metadata_size(sizeof(ConstantPoolCache) / wordSize); } | |
| private: | |
| // Helpers | |
| ConstantPool** constant_pool_addr() { return &_constant_pool; } | |
| public: | |
| // Accessors | |
| void set_constant_pool(ConstantPool* pool) { _constant_pool = pool; } | |
| ConstantPool* constant_pool() const { return _constant_pool; } | |
| // Code generation | |
| static ByteSize base_offset() { return in_ByteSize(sizeof(ConstantPoolCache)); } | |
| #if INCLUDE_JVMTI | |
| // RedefineClasses() API support: | |
| // If any entry of this ConstantPoolCache points to any of | |
| // old_methods, replace it with the corresponding new_method. | |
| // trace_name_printed is set to true if the current call has | |
| // printed the klass name so that other routines in the adjust_* | |
| // group don't print the klass name. | |
| void adjust_method_entries(bool* trace_name_printed); | |
| bool check_no_old_or_obsolete_entries(); | |
| void dump_cache(); | |
| #endif // INCLUDE_JVMTI | |
| #if INCLUDE_CDS | |
| void remove_resolved_field_entries_if_non_deterministic(); | |
| void remove_resolved_indy_entries_if_non_deterministic(); | |
| void remove_resolved_method_entries_if_non_deterministic(); | |
| bool can_archive_resolved_method(ConstantPool* src_cp, ResolvedMethodEntry* method_entry); | |
| #endif | |
| // RedefineClasses support | |
| DEBUG_ONLY(bool on_stack() { return false; }) | |
| void deallocate_contents(ClassLoaderData* data); | |
| void record_gc_epoch(); | |
| uint64_t gc_epoch() { return _gc_epoch; } | |
| // Return TRUE if resolution failed and this thread got to record the failure | |
| // status. Return FALSE if another thread succeeded or failed in resolving | |
| // the method and recorded the success or failure before this thread had a | |
| // chance to record its failure. | |
| bool save_and_throw_indy_exc(const constantPoolHandle& cpool, int cpool_index, int index, constantTag tag, TRAPS); | |
| oop set_dynamic_call(const CallInfo &call_info, int index); | |
| oop appendix_if_resolved(int method_index) const; | |
| oop appendix_if_resolved(ResolvedMethodEntry* method_entry) const; | |
| // Printing | |
| void print_on(outputStream* st) const; | |
| void print_value_on(outputStream* st) const; | |
| const char* internal_name() const { return "{constant pool cache}"; } | |
| // Verify | |
| void verify_on(outputStream* st); | |
| }; | |
| #endif // SHARE_OOPS_CPCACHE_HPP |