| /* |
| * Copyright (c) 2015, 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_CDS_CLASSLISTPARSER_HPP |
| #define SHARE_CDS_CLASSLISTPARSER_HPP |
| |
| #include "utilities/exceptions.hpp" |
| #include "utilities/globalDefinitions.hpp" |
| #include "utilities/growableArray.hpp" |
| #include "utilities/istream.hpp" |
| #include "utilities/resizeableResourceHash.hpp" |
| |
| class constantPoolHandle; |
| class Thread; |
| |
| class CDSIndyInfo { |
| GrowableArray<const char*>* _items; |
| public: |
| CDSIndyInfo() : _items(nullptr) {} |
| void add_item(const char* item) { |
| if (_items == nullptr) { |
| _items = new GrowableArray<const char*>(9); |
| } |
| assert(_items != nullptr, "sanity"); |
| _items->append(item); |
| } |
| void add_ref_kind(int ref_kind) { |
| switch (ref_kind) { |
| case JVM_REF_getField : _items->append("REF_getField"); break; |
| case JVM_REF_getStatic : _items->append("REF_getStatic"); break; |
| case JVM_REF_putField : _items->append("REF_putField"); break; |
| case JVM_REF_putStatic : _items->append("REF_putStatic"); break; |
| case JVM_REF_invokeVirtual : _items->append("REF_invokeVirtual"); break; |
| case JVM_REF_invokeStatic : _items->append("REF_invokeStatic"); break; |
| case JVM_REF_invokeSpecial : _items->append("REF_invokeSpecial"); break; |
| case JVM_REF_newInvokeSpecial : _items->append("REF_newInvokeSpecial"); break; |
| case JVM_REF_invokeInterface : _items->append("REF_invokeInterface"); break; |
| default : ShouldNotReachHere(); |
| } |
| } |
| GrowableArray<const char*>* items() { |
| return _items; |
| } |
| }; |
| |
| class ClassListParser : public StackObj { |
| static const char* CONSTANT_POOL_TAG; |
| static const char* LAMBDA_FORM_TAG; |
| static const char* LAMBDA_PROXY_TAG; |
| |
| public: |
| enum ParseMode { |
| _parse_all, |
| _parse_lambda_forms_invokers_only, |
| }; |
| |
| private: |
| // Must be C_HEAP allocated -- we don't want nested resource allocations. |
| typedef ResizeableResourceHashtable<int, InstanceKlass*, |
| AnyObj::C_HEAP, mtClassShared> ID2KlassTable; |
| |
| enum { |
| _unspecified = -999, |
| }; |
| |
| // Use a small initial size in debug build to test resizing logic |
| static const int INITIAL_TABLE_SIZE = DEBUG_ONLY(17) NOT_DEBUG(1987); |
| static const int MAX_TABLE_SIZE = 61333; |
| static volatile Thread* _parsing_thread; // the thread that created _instance |
| static ClassListParser* _instance; // the singleton. |
| const char* _classlist_file; |
| |
| ID2KlassTable _id2klass_table; |
| |
| FileInput _file_input; |
| inputStream _input_stream; |
| char* _line; // The buffer that holds the current line. Some characters in |
| // the buffer may be overwritten by '\0' during parsing. |
| int _line_len; // Original length of the input line. |
| const char* _class_name; |
| GrowableArray<const char*>* _indy_items; // items related to invoke dynamic for archiving lambda proxy classes |
| int _id; |
| int _super; |
| GrowableArray<int>* _interfaces; |
| bool _interfaces_specified; |
| const char* _source; |
| ParseMode _parse_mode; |
| |
| bool parse_int_option(const char* option_name, int* value); |
| bool parse_uint_option(const char* option_name, int* value); |
| InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS); |
| ID2KlassTable* id2klass_table() { |
| return &_id2klass_table; |
| } |
| InstanceKlass* lookup_class_by_id(int id); |
| void print_specified_interfaces(); |
| void print_actual_interfaces(InstanceKlass *ik); |
| bool is_matching_cp_entry(const constantPoolHandle &pool, int cp_index, TRAPS); |
| |
| InstanceKlass* find_builtin_class_helper(JavaThread* current, Symbol* class_name_symbol, oop class_loader_oop); |
| InstanceKlass* find_builtin_class(JavaThread* current, const char* class_name); |
| |
| void resolve_indy(JavaThread* current, Symbol* class_name_symbol); |
| void resolve_indy_impl(Symbol* class_name_symbol, TRAPS); |
| void clean_up_input_line(); |
| void read_class_name_and_attributes(); |
| void parse_class_name_and_attributes(TRAPS); |
| Klass* load_current_class(Symbol* class_name_symbol, TRAPS); |
| void parse_constant_pool_tag(); |
| |
| size_t lineno() { return _input_stream.lineno(); } |
| FILE* do_open(const char* file); |
| ClassListParser(const char* file, ParseMode _parse_mode); |
| ~ClassListParser(); |
| void print_diagnostic_info(outputStream* st, const char* msg, va_list ap) ATTRIBUTE_PRINTF(3, 0); |
| void print_diagnostic_info(outputStream* st, const char* msg, ...) ATTRIBUTE_PRINTF(3, 0); |
| void constant_pool_resolution_warning(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0); |
| void error(const char* msg, ...) ATTRIBUTE_PRINTF(2, 0); |
| GrowableArray<InstanceKlass*> get_specified_interfaces(); |
| void check_supertype_obstruction(int specified_supertype_id, const InstanceKlass* specified_supertype, TRAPS); |
| |
| public: |
| static void parse_classlist(const char* classlist_path, ParseMode parse_mode, TRAPS); |
| |
| static bool is_parsing_thread(); |
| static ClassListParser* instance() { |
| assert(is_parsing_thread(), "call this only in the thread that created ClassListParsing::_instance"); |
| assert(_instance != nullptr, "must be"); |
| return _instance; |
| } |
| static const char* lambda_proxy_tag() { |
| return LAMBDA_PROXY_TAG; |
| } |
| static const char* lambda_form_tag() { |
| return LAMBDA_FORM_TAG; |
| } |
| |
| void parse(TRAPS); |
| void split_tokens_by_whitespace(int offset, GrowableArray<const char*>* items); |
| int split_at_tag_from_line(); |
| void parse_at_tags(TRAPS); |
| char* _token; |
| void parse_int(int* value); |
| void parse_uint(int* value); |
| bool try_parse_uint(int* value); |
| bool skip_token(const char* option_name); |
| void skip_whitespaces(); |
| void skip_non_whitespaces(); |
| |
| bool parse_lambda_forms_invokers_only() { |
| return _parse_mode == _parse_lambda_forms_invokers_only; |
| } |
| bool is_id_specified() { |
| return _id != _unspecified; |
| } |
| bool is_super_specified() { |
| return _super != _unspecified; |
| } |
| bool are_interfaces_specified() { |
| return _interfaces->length() > 0; |
| } |
| int id() { |
| assert(is_id_specified(), "do not query unspecified id"); |
| return _id; |
| } |
| int super() { |
| assert(is_super_specified(), "do not query unspecified super"); |
| return _super; |
| } |
| void check_already_loaded(const char* which, int id) { |
| if (!id2klass_table()->contains(id)) { |
| error("%s id %d is not yet loaded", which, id); |
| } |
| } |
| void check_class_name(const char* class_name); |
| |
| const char* current_class_name() { |
| return _class_name; |
| } |
| |
| bool is_loading_from_source(); |
| static void populate_cds_indy_info(const constantPoolHandle &pool, int cp_index, CDSIndyInfo* cii, TRAPS); |
| }; |
| #endif // SHARE_CDS_CLASSLISTPARSER_HPP |