| /* | |
| * Copyright (c) 1997, 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. | |
| * | |
| */ | |
| #include "cds/cdsConfig.hpp" | |
| #include "classfile/classFileParser.hpp" | |
| #include "classfile/classFileStream.hpp" | |
| #include "classfile/classLoader.hpp" | |
| #include "classfile/classLoaderData.inline.hpp" | |
| #include "classfile/classLoadInfo.hpp" | |
| #include "classfile/defaultMethods.hpp" | |
| #include "classfile/fieldLayoutBuilder.hpp" | |
| #include "classfile/javaClasses.inline.hpp" | |
| #include "classfile/moduleEntry.hpp" | |
| #include "classfile/packageEntry.hpp" | |
| #include "classfile/symbolTable.hpp" | |
| #include "classfile/systemDictionary.hpp" | |
| #include "classfile/verificationType.hpp" | |
| #include "classfile/verifier.hpp" | |
| #include "classfile/vmClasses.hpp" | |
| #include "classfile/vmSymbols.hpp" | |
| #include "jvm.h" | |
| #include "logging/log.hpp" | |
| #include "logging/logStream.hpp" | |
| #include "memory/allocation.hpp" | |
| #include "memory/metadataFactory.hpp" | |
| #include "memory/oopFactory.hpp" | |
| #include "memory/resourceArea.hpp" | |
| #include "memory/universe.hpp" | |
| #include "oops/annotations.hpp" | |
| #include "oops/constantPool.inline.hpp" | |
| #include "oops/fieldInfo.hpp" | |
| #include "oops/fieldStreams.inline.hpp" | |
| #include "oops/instanceKlass.inline.hpp" | |
| #include "oops/instanceMirrorKlass.hpp" | |
| #include "oops/klass.inline.hpp" | |
| #include "oops/klassVtable.hpp" | |
| #include "oops/metadata.hpp" | |
| #include "oops/method.inline.hpp" | |
| #include "oops/oop.inline.hpp" | |
| #include "oops/recordComponent.hpp" | |
| #include "oops/symbol.hpp" | |
| #include "prims/jvmtiExport.hpp" | |
| #include "prims/jvmtiThreadState.hpp" | |
| #include "runtime/arguments.hpp" | |
| #include "runtime/fieldDescriptor.inline.hpp" | |
| #include "runtime/handles.inline.hpp" | |
| #include "runtime/javaCalls.hpp" | |
| #include "runtime/os.hpp" | |
| #include "runtime/perfData.hpp" | |
| #include "runtime/reflection.hpp" | |
| #include "runtime/safepointVerifiers.hpp" | |
| #include "runtime/signature.hpp" | |
| #include "runtime/timer.hpp" | |
| #include "services/classLoadingService.hpp" | |
| #include "services/threadService.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/bitMap.inline.hpp" | |
| #include "utilities/checkedCast.hpp" | |
| #include "utilities/copy.hpp" | |
| #include "utilities/formatBuffer.hpp" | |
| #include "utilities/exceptions.hpp" | |
| #include "utilities/globalDefinitions.hpp" | |
| #include "utilities/growableArray.hpp" | |
| #include "utilities/macros.hpp" | |
| #include "utilities/ostream.hpp" | |
| #include "utilities/resourceHash.hpp" | |
| #include "utilities/utf8.hpp" | |
| #if INCLUDE_CDS | |
| #include "classfile/systemDictionaryShared.hpp" | |
| #endif | |
| #if INCLUDE_JFR | |
| #include "jfr/support/jfrTraceIdExtension.hpp" | |
| #endif | |
| // We generally try to create the oops directly when parsing, rather than | |
| // allocating temporary data structures and copying the bytes twice. A | |
| // temporary area is only needed when parsing utf8 entries in the constant | |
| // pool and when parsing line number tables. | |
| // We add assert in debug mode when class format is not checked. | |
| #define JAVA_CLASSFILE_MAGIC 0xCAFEBABE | |
| #define JAVA_MIN_SUPPORTED_VERSION 45 | |
| #define JAVA_PREVIEW_MINOR_VERSION 65535 | |
| // Used for two backward compatibility reasons: | |
| // - to check for new additions to the class file format in JDK1.5 | |
| // - to check for bug fixes in the format checker in JDK1.5 | |
| #define JAVA_1_5_VERSION 49 | |
| // Used for backward compatibility reasons: | |
| // - to check for javac bug fixes that happened after 1.5 | |
| // - also used as the max version when running in jdk6 | |
| #define JAVA_6_VERSION 50 | |
| // Used for backward compatibility reasons: | |
| // - to disallow argument and require ACC_STATIC for <clinit> methods | |
| #define JAVA_7_VERSION 51 | |
| // Extension method support. | |
| #define JAVA_8_VERSION 52 | |
| #define JAVA_9_VERSION 53 | |
| #define JAVA_10_VERSION 54 | |
| #define JAVA_11_VERSION 55 | |
| #define JAVA_12_VERSION 56 | |
| #define JAVA_13_VERSION 57 | |
| #define JAVA_14_VERSION 58 | |
| #define JAVA_15_VERSION 59 | |
| #define JAVA_16_VERSION 60 | |
| #define JAVA_17_VERSION 61 | |
| #define JAVA_18_VERSION 62 | |
| #define JAVA_19_VERSION 63 | |
| #define JAVA_20_VERSION 64 | |
| #define JAVA_21_VERSION 65 | |
| #define JAVA_22_VERSION 66 | |
| #define JAVA_23_VERSION 67 | |
| #define JAVA_24_VERSION 68 | |
| #define JAVA_25_VERSION 69 | |
| void ClassFileParser::set_class_bad_constant_seen(short bad_constant) { | |
| assert((bad_constant == JVM_CONSTANT_Module || | |
| bad_constant == JVM_CONSTANT_Package) && _major_version >= JAVA_9_VERSION, | |
| "Unexpected bad constant pool entry"); | |
| if (_bad_constant_seen == 0) _bad_constant_seen = bad_constant; | |
| } | |
| void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const stream, | |
| ConstantPool* cp, | |
| const int length, | |
| TRAPS) { | |
| assert(stream != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| // Use a local copy of ClassFileStream. It helps the C++ compiler to optimize | |
| // this function (_current can be allocated in a register, with scalar | |
| // replacement of aggregates). The _current pointer is copied back to | |
| // stream() when this function returns. DON'T call another method within | |
| // this method that uses stream(). | |
| const ClassFileStream cfs1 = *stream; | |
| const ClassFileStream* const cfs = &cfs1; | |
| DEBUG_ONLY(const u1* const old_current = stream->current();) | |
| // Used for batching symbol allocations. | |
| const char* names[SymbolTable::symbol_alloc_batch_size]; | |
| int lengths[SymbolTable::symbol_alloc_batch_size]; | |
| int indices[SymbolTable::symbol_alloc_batch_size]; | |
| unsigned int hashValues[SymbolTable::symbol_alloc_batch_size]; | |
| int names_count = 0; | |
| // parsing Index 0 is unused | |
| for (int index = 1; index < length; index++) { | |
| // Each of the following case guarantees one more byte in the stream | |
| // for the following tag or the access_flags following constant pool, | |
| // so we don't need bounds-check for reading tag. | |
| const u1 tag = cfs->get_u1_fast(); | |
| switch (tag) { | |
| case JVM_CONSTANT_Class : { | |
| cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags | |
| const u2 name_index = cfs->get_u2_fast(); | |
| cp->klass_index_at_put(index, name_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_Fieldref: { | |
| cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
| const u2 class_index = cfs->get_u2_fast(); | |
| const u2 name_and_type_index = cfs->get_u2_fast(); | |
| cp->field_at_put(index, class_index, name_and_type_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_Methodref: { | |
| cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
| const u2 class_index = cfs->get_u2_fast(); | |
| const u2 name_and_type_index = cfs->get_u2_fast(); | |
| cp->method_at_put(index, class_index, name_and_type_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_InterfaceMethodref: { | |
| cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
| const u2 class_index = cfs->get_u2_fast(); | |
| const u2 name_and_type_index = cfs->get_u2_fast(); | |
| cp->interface_method_at_put(index, class_index, name_and_type_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_String : { | |
| cfs->guarantee_more(3, CHECK); // string_index, tag/access_flags | |
| const u2 string_index = cfs->get_u2_fast(); | |
| cp->string_index_at_put(index, string_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_MethodHandle : | |
| case JVM_CONSTANT_MethodType: { | |
| if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { | |
| classfile_parse_error( | |
| "Class file version does not support constant tag %u in class file %s", | |
| tag, THREAD); | |
| return; | |
| } | |
| if (tag == JVM_CONSTANT_MethodHandle) { | |
| cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags | |
| const u1 ref_kind = cfs->get_u1_fast(); | |
| const u2 method_index = cfs->get_u2_fast(); | |
| cp->method_handle_index_at_put(index, ref_kind, method_index); | |
| } | |
| else if (tag == JVM_CONSTANT_MethodType) { | |
| cfs->guarantee_more(3, CHECK); // signature_index, tag/access_flags | |
| const u2 signature_index = cfs->get_u2_fast(); | |
| cp->method_type_index_at_put(index, signature_index); | |
| } | |
| else { | |
| ShouldNotReachHere(); | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_Dynamic : { | |
| if (_major_version < Verifier::DYNAMICCONSTANT_MAJOR_VERSION) { | |
| classfile_parse_error( | |
| "Class file version does not support constant tag %u in class file %s", | |
| tag, THREAD); | |
| return; | |
| } | |
| cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags | |
| const u2 bootstrap_specifier_index = cfs->get_u2_fast(); | |
| const u2 name_and_type_index = cfs->get_u2_fast(); | |
| if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) { | |
| _max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later | |
| } | |
| cp->dynamic_constant_at_put(index, bootstrap_specifier_index, name_and_type_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_InvokeDynamic : { | |
| if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { | |
| classfile_parse_error( | |
| "Class file version does not support constant tag %u in class file %s", | |
| tag, THREAD); | |
| return; | |
| } | |
| cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags | |
| const u2 bootstrap_specifier_index = cfs->get_u2_fast(); | |
| const u2 name_and_type_index = cfs->get_u2_fast(); | |
| if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) { | |
| _max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later | |
| } | |
| cp->invoke_dynamic_at_put(index, bootstrap_specifier_index, name_and_type_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_Integer: { | |
| cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags | |
| const u4 bytes = cfs->get_u4_fast(); | |
| cp->int_at_put(index, (jint)bytes); | |
| break; | |
| } | |
| case JVM_CONSTANT_Float: { | |
| cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags | |
| const u4 bytes = cfs->get_u4_fast(); | |
| cp->float_at_put(index, *(jfloat*)&bytes); | |
| break; | |
| } | |
| case JVM_CONSTANT_Long: { | |
| // A mangled type might cause you to overrun allocated memory | |
| guarantee_property(index + 1 < length, | |
| "Invalid constant pool entry %u in class file %s", | |
| index, | |
| CHECK); | |
| cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags | |
| const u8 bytes = cfs->get_u8_fast(); | |
| cp->long_at_put(index, bytes); | |
| index++; // Skip entry following eigth-byte constant, see JVM book p. 98 | |
| break; | |
| } | |
| case JVM_CONSTANT_Double: { | |
| // A mangled type might cause you to overrun allocated memory | |
| guarantee_property(index+1 < length, | |
| "Invalid constant pool entry %u in class file %s", | |
| index, | |
| CHECK); | |
| cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags | |
| const u8 bytes = cfs->get_u8_fast(); | |
| cp->double_at_put(index, *(jdouble*)&bytes); | |
| index++; // Skip entry following eigth-byte constant, see JVM book p. 98 | |
| break; | |
| } | |
| case JVM_CONSTANT_NameAndType: { | |
| cfs->guarantee_more(5, CHECK); // name_index, signature_index, tag/access_flags | |
| const u2 name_index = cfs->get_u2_fast(); | |
| const u2 signature_index = cfs->get_u2_fast(); | |
| cp->name_and_type_at_put(index, name_index, signature_index); | |
| break; | |
| } | |
| case JVM_CONSTANT_Utf8 : { | |
| cfs->guarantee_more(2, CHECK); // utf8_length | |
| u2 utf8_length = cfs->get_u2_fast(); | |
| const u1* utf8_buffer = cfs->current(); | |
| assert(utf8_buffer != nullptr, "null utf8 buffer"); | |
| // Got utf8 string, guarantee utf8_length+1 bytes, set stream position forward. | |
| cfs->guarantee_more(utf8_length+1, CHECK); // utf8 string, tag/access_flags | |
| cfs->skip_u1_fast(utf8_length); | |
| // Before storing the symbol, make sure it's legal | |
| if (_need_verify) { | |
| verify_legal_utf8(utf8_buffer, utf8_length, CHECK); | |
| } | |
| unsigned int hash; | |
| Symbol* const result = SymbolTable::lookup_only((const char*)utf8_buffer, | |
| utf8_length, | |
| hash); | |
| if (result == nullptr) { | |
| names[names_count] = (const char*)utf8_buffer; | |
| lengths[names_count] = utf8_length; | |
| indices[names_count] = index; | |
| hashValues[names_count++] = hash; | |
| if (names_count == SymbolTable::symbol_alloc_batch_size) { | |
| SymbolTable::new_symbols(_loader_data, | |
| constantPoolHandle(THREAD, cp), | |
| names_count, | |
| names, | |
| lengths, | |
| indices, | |
| hashValues); | |
| names_count = 0; | |
| } | |
| } else { | |
| cp->symbol_at_put(index, result); | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_Module: | |
| case JVM_CONSTANT_Package: { | |
| // Record that an error occurred in these two cases but keep parsing so | |
| // that ACC_Module can be checked for in the access_flags. Need to | |
| // throw NoClassDefFoundError in that case. | |
| if (_major_version >= JAVA_9_VERSION) { | |
| cfs->guarantee_more(3, CHECK); | |
| cfs->get_u2_fast(); | |
| set_class_bad_constant_seen(tag); | |
| break; | |
| } | |
| } | |
| default: { | |
| classfile_parse_error("Unknown constant tag %u in class file %s", | |
| tag, | |
| THREAD); | |
| return; | |
| } | |
| } // end of switch(tag) | |
| } // end of for | |
| // Allocate the remaining symbols | |
| if (names_count > 0) { | |
| SymbolTable::new_symbols(_loader_data, | |
| constantPoolHandle(THREAD, cp), | |
| names_count, | |
| names, | |
| lengths, | |
| indices, | |
| hashValues); | |
| } | |
| // Copy _current pointer of local copy back to stream. | |
| assert(stream->current() == old_current, "non-exclusive use of stream"); | |
| stream->set_current(cfs1.current()); | |
| } | |
| static inline bool valid_cp_range(int index, int length) { | |
| return (index > 0 && index < length); | |
| } | |
| static inline Symbol* check_symbol_at(const ConstantPool* cp, int index) { | |
| assert(cp != nullptr, "invariant"); | |
| if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8()) { | |
| return cp->symbol_at(index); | |
| } | |
| return nullptr; | |
| } | |
| void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream, | |
| ConstantPool* const cp, | |
| const int length, | |
| TRAPS) { | |
| assert(cp != nullptr, "invariant"); | |
| assert(stream != nullptr, "invariant"); | |
| // parsing constant pool entries | |
| parse_constant_pool_entries(stream, cp, length, CHECK); | |
| if (class_bad_constant_seen() != 0) { | |
| // a bad CP entry has been detected previously so stop parsing and just return. | |
| return; | |
| } | |
| int index = 1; // declared outside of loops for portability | |
| int num_klasses = 0; | |
| // first verification pass - validate cross references | |
| // and fixup class and string constants | |
| for (index = 1; index < length; index++) { // Index 0 is unused | |
| const jbyte tag = cp->tag_at(index).value(); | |
| switch (tag) { | |
| case JVM_CONSTANT_Class: { | |
| ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present | |
| break; | |
| } | |
| case JVM_CONSTANT_Fieldref: | |
| // fall through | |
| case JVM_CONSTANT_Methodref: | |
| // fall through | |
| case JVM_CONSTANT_InterfaceMethodref: { | |
| if (!_need_verify) break; | |
| const int klass_ref_index = cp->uncached_klass_ref_index_at(index); | |
| const int name_and_type_ref_index = cp->uncached_name_and_type_ref_index_at(index); | |
| guarantee_property(valid_klass_reference_at(klass_ref_index), | |
| "Invalid constant pool index %u in class file %s", | |
| klass_ref_index, CHECK); | |
| guarantee_property(valid_cp_range(name_and_type_ref_index, length) && | |
| cp->tag_at(name_and_type_ref_index).is_name_and_type(), | |
| "Invalid constant pool index %u in class file %s", | |
| name_and_type_ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_String: { | |
| ShouldNotReachHere(); // Only JVM_CONSTANT_StringIndex should be present | |
| break; | |
| } | |
| case JVM_CONSTANT_Integer: | |
| break; | |
| case JVM_CONSTANT_Float: | |
| break; | |
| case JVM_CONSTANT_Long: | |
| case JVM_CONSTANT_Double: { | |
| index++; | |
| guarantee_property( | |
| (index < length && cp->tag_at(index).is_invalid()), | |
| "Improper constant pool long/double index %u in class file %s", | |
| index, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_NameAndType: { | |
| if (!_need_verify) break; | |
| const int name_ref_index = cp->name_ref_index_at(index); | |
| const int signature_ref_index = cp->signature_ref_index_at(index); | |
| guarantee_property(valid_symbol_at(name_ref_index), | |
| "Invalid constant pool index %u in class file %s", | |
| name_ref_index, CHECK); | |
| guarantee_property(valid_symbol_at(signature_ref_index), | |
| "Invalid constant pool index %u in class file %s", | |
| signature_ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_Utf8: | |
| break; | |
| case JVM_CONSTANT_UnresolvedClass: // fall-through | |
| case JVM_CONSTANT_UnresolvedClassInError: { | |
| ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present | |
| break; | |
| } | |
| case JVM_CONSTANT_ClassIndex: { | |
| const int class_index = cp->klass_index_at(index); | |
| guarantee_property(valid_symbol_at(class_index), | |
| "Invalid constant pool index %u in class file %s", | |
| class_index, CHECK); | |
| cp->unresolved_klass_at_put(index, class_index, num_klasses++); | |
| break; | |
| } | |
| case JVM_CONSTANT_StringIndex: { | |
| const int string_index = cp->string_index_at(index); | |
| guarantee_property(valid_symbol_at(string_index), | |
| "Invalid constant pool index %u in class file %s", | |
| string_index, CHECK); | |
| Symbol* const sym = cp->symbol_at(string_index); | |
| cp->unresolved_string_at_put(index, sym); | |
| break; | |
| } | |
| case JVM_CONSTANT_MethodHandle: { | |
| const int ref_index = cp->method_handle_index_at(index); | |
| guarantee_property(valid_cp_range(ref_index, length), | |
| "Invalid constant pool index %u in class file %s", | |
| ref_index, CHECK); | |
| const constantTag tag = cp->tag_at(ref_index); | |
| const int ref_kind = cp->method_handle_ref_kind_at(index); | |
| switch (ref_kind) { | |
| case JVM_REF_getField: | |
| case JVM_REF_getStatic: | |
| case JVM_REF_putField: | |
| case JVM_REF_putStatic: { | |
| guarantee_property( | |
| tag.is_field(), | |
| "Invalid constant pool index %u in class file %s (not a field)", | |
| ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_REF_invokeVirtual: | |
| case JVM_REF_newInvokeSpecial: { | |
| guarantee_property( | |
| tag.is_method(), | |
| "Invalid constant pool index %u in class file %s (not a method)", | |
| ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_REF_invokeStatic: | |
| case JVM_REF_invokeSpecial: { | |
| guarantee_property( | |
| tag.is_method() || | |
| ((_major_version >= JAVA_8_VERSION) && tag.is_interface_method()), | |
| "Invalid constant pool index %u in class file %s (not a method)", | |
| ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_REF_invokeInterface: { | |
| guarantee_property( | |
| tag.is_interface_method(), | |
| "Invalid constant pool index %u in class file %s (not an interface method)", | |
| ref_index, CHECK); | |
| break; | |
| } | |
| default: { | |
| classfile_parse_error( | |
| "Bad method handle kind at constant pool index %u in class file %s", | |
| index, THREAD); | |
| return; | |
| } | |
| } // switch(refkind) | |
| // Keep the ref_index unchanged. It will be indirected at link-time. | |
| break; | |
| } // case MethodHandle | |
| case JVM_CONSTANT_MethodType: { | |
| const int ref_index = cp->method_type_index_at(index); | |
| guarantee_property(valid_symbol_at(ref_index), | |
| "Invalid constant pool index %u in class file %s", | |
| ref_index, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_Dynamic: { | |
| const int name_and_type_ref_index = | |
| cp->bootstrap_name_and_type_ref_index_at(index); | |
| guarantee_property(valid_cp_range(name_and_type_ref_index, length) && | |
| cp->tag_at(name_and_type_ref_index).is_name_and_type(), | |
| "Invalid constant pool index %u in class file %s", | |
| name_and_type_ref_index, CHECK); | |
| // bootstrap specifier index must be checked later, | |
| // when BootstrapMethods attr is available | |
| // Mark the constant pool as having a CONSTANT_Dynamic_info structure | |
| cp->set_has_dynamic_constant(); | |
| break; | |
| } | |
| case JVM_CONSTANT_InvokeDynamic: { | |
| const int name_and_type_ref_index = | |
| cp->bootstrap_name_and_type_ref_index_at(index); | |
| guarantee_property(valid_cp_range(name_and_type_ref_index, length) && | |
| cp->tag_at(name_and_type_ref_index).is_name_and_type(), | |
| "Invalid constant pool index %u in class file %s", | |
| name_and_type_ref_index, CHECK); | |
| // bootstrap specifier index must be checked later, | |
| // when BootstrapMethods attr is available | |
| break; | |
| } | |
| default: { | |
| fatal("bad constant pool tag value %u", cp->tag_at(index).value()); | |
| ShouldNotReachHere(); | |
| break; | |
| } | |
| } // switch(tag) | |
| } // end of for | |
| cp->allocate_resolved_klasses(_loader_data, num_klasses, CHECK); | |
| if (!_need_verify) { | |
| return; | |
| } | |
| // second verification pass - checks the strings are of the right format. | |
| // but not yet to the other entries | |
| for (index = 1; index < length; index++) { | |
| const jbyte tag = cp->tag_at(index).value(); | |
| switch (tag) { | |
| case JVM_CONSTANT_UnresolvedClass: { | |
| const Symbol* const class_name = cp->klass_name_at(index); | |
| // check the name | |
| verify_legal_class_name(class_name, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_NameAndType: { | |
| if (_need_verify) { | |
| const int sig_index = cp->signature_ref_index_at(index); | |
| const int name_index = cp->name_ref_index_at(index); | |
| const Symbol* const name = cp->symbol_at(name_index); | |
| const Symbol* const sig = cp->symbol_at(sig_index); | |
| guarantee_property(sig->utf8_length() != 0, | |
| "Illegal zero length constant pool entry at %d in class %s", | |
| sig_index, CHECK); | |
| guarantee_property(name->utf8_length() != 0, | |
| "Illegal zero length constant pool entry at %d in class %s", | |
| name_index, CHECK); | |
| if (Signature::is_method(sig)) { | |
| // Format check method name and signature | |
| verify_legal_method_name(name, CHECK); | |
| verify_legal_method_signature(name, sig, CHECK); | |
| } else { | |
| // Format check field name and signature | |
| verify_legal_field_name(name, CHECK); | |
| verify_legal_field_signature(name, sig, CHECK); | |
| } | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_Dynamic: { | |
| const int name_and_type_ref_index = | |
| cp->uncached_name_and_type_ref_index_at(index); | |
| // already verified to be utf8 | |
| const int name_ref_index = | |
| cp->name_ref_index_at(name_and_type_ref_index); | |
| // already verified to be utf8 | |
| const int signature_ref_index = | |
| cp->signature_ref_index_at(name_and_type_ref_index); | |
| const Symbol* const name = cp->symbol_at(name_ref_index); | |
| const Symbol* const signature = cp->symbol_at(signature_ref_index); | |
| if (_need_verify) { | |
| // CONSTANT_Dynamic's name and signature are verified above, when iterating NameAndType_info. | |
| // Need only to be sure signature is the right type. | |
| if (Signature::is_method(signature)) { | |
| throwIllegalSignature("CONSTANT_Dynamic", name, signature, CHECK); | |
| } | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_InvokeDynamic: | |
| case JVM_CONSTANT_Fieldref: | |
| case JVM_CONSTANT_Methodref: | |
| case JVM_CONSTANT_InterfaceMethodref: { | |
| const int name_and_type_ref_index = | |
| cp->uncached_name_and_type_ref_index_at(index); | |
| // already verified to be utf8 | |
| const int name_ref_index = | |
| cp->name_ref_index_at(name_and_type_ref_index); | |
| // already verified to be utf8 | |
| const int signature_ref_index = | |
| cp->signature_ref_index_at(name_and_type_ref_index); | |
| const Symbol* const name = cp->symbol_at(name_ref_index); | |
| const Symbol* const signature = cp->symbol_at(signature_ref_index); | |
| if (tag == JVM_CONSTANT_Fieldref) { | |
| if (_need_verify) { | |
| // Field name and signature are verified above, when iterating NameAndType_info. | |
| // Need only to be sure signature is non-zero length and the right type. | |
| if (Signature::is_method(signature)) { | |
| throwIllegalSignature("Field", name, signature, CHECK); | |
| } | |
| } | |
| } else { | |
| if (_need_verify) { | |
| // Method name and signature are individually verified above, when iterating | |
| // NameAndType_info. Need to check here that signature is non-zero length and | |
| // the right type. | |
| if (!Signature::is_method(signature)) { | |
| throwIllegalSignature("Method", name, signature, CHECK); | |
| } | |
| } | |
| // If a class method name begins with '<', it must be "<init>" and have void signature. | |
| const unsigned int name_len = name->utf8_length(); | |
| if (tag == JVM_CONSTANT_Methodref && name_len != 0 && | |
| name->char_at(0) == JVM_SIGNATURE_SPECIAL) { | |
| if (name != vmSymbols::object_initializer_name()) { | |
| classfile_parse_error( | |
| "Bad method name at constant pool index %u in class file %s", | |
| name_ref_index, THREAD); | |
| return; | |
| } else if (!Signature::is_void_method(signature)) { // must have void signature. | |
| throwIllegalSignature("Method", name, signature, CHECK); | |
| } | |
| } | |
| } | |
| break; | |
| } | |
| case JVM_CONSTANT_MethodHandle: { | |
| const int ref_index = cp->method_handle_index_at(index); | |
| const int ref_kind = cp->method_handle_ref_kind_at(index); | |
| switch (ref_kind) { | |
| case JVM_REF_invokeVirtual: | |
| case JVM_REF_invokeStatic: | |
| case JVM_REF_invokeSpecial: | |
| case JVM_REF_newInvokeSpecial: { | |
| const int name_and_type_ref_index = | |
| cp->uncached_name_and_type_ref_index_at(ref_index); | |
| const int name_ref_index = | |
| cp->name_ref_index_at(name_and_type_ref_index); | |
| const Symbol* const name = cp->symbol_at(name_ref_index); | |
| if (ref_kind == JVM_REF_newInvokeSpecial) { | |
| if (name != vmSymbols::object_initializer_name()) { | |
| classfile_parse_error( | |
| "Bad constructor name at constant pool index %u in class file %s", | |
| name_ref_index, THREAD); | |
| return; | |
| } | |
| } else { | |
| if (name == vmSymbols::object_initializer_name()) { | |
| classfile_parse_error( | |
| "Bad method name at constant pool index %u in class file %s", | |
| name_ref_index, THREAD); | |
| return; | |
| } | |
| } | |
| break; | |
| } | |
| // Other ref_kinds are already fully checked in previous pass. | |
| } // switch(ref_kind) | |
| break; | |
| } | |
| case JVM_CONSTANT_MethodType: { | |
| const Symbol* const no_name = vmSymbols::type_name(); // place holder | |
| const Symbol* const signature = cp->method_type_signature_at(index); | |
| verify_legal_method_signature(no_name, signature, CHECK); | |
| break; | |
| } | |
| case JVM_CONSTANT_Utf8: { | |
| assert(cp->symbol_at(index)->refcount() != 0, "count corrupted"); | |
| } | |
| } // switch(tag) | |
| } // end of for | |
| } | |
| class NameSigHash: public ResourceObj { | |
| public: | |
| const Symbol* _name; // name | |
| const Symbol* _sig; // signature | |
| static const int HASH_ROW_SIZE = 256; | |
| NameSigHash(Symbol* name, Symbol* sig) : | |
| _name(name), | |
| _sig(sig) {} | |
| static unsigned int hash(NameSigHash const& namesig) { | |
| return namesig._name->identity_hash() ^ namesig._sig->identity_hash(); | |
| } | |
| static bool equals(NameSigHash const& e0, NameSigHash const& e1) { | |
| return (e0._name == e1._name) && | |
| (e0._sig == e1._sig); | |
| } | |
| }; | |
| using NameSigHashtable = ResourceHashtable<NameSigHash, int, | |
| NameSigHash::HASH_ROW_SIZE, | |
| AnyObj::RESOURCE_AREA, mtInternal, | |
| &NameSigHash::hash, &NameSigHash::equals>; | |
| // Side-effects: populates the _local_interfaces field | |
| void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, | |
| const int itfs_len, | |
| ConstantPool* const cp, | |
| bool* const has_nonstatic_concrete_methods, | |
| TRAPS) { | |
| assert(stream != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| assert(has_nonstatic_concrete_methods != nullptr, "invariant"); | |
| if (itfs_len == 0) { | |
| _local_interfaces = Universe::the_empty_instance_klass_array(); | |
| } else { | |
| assert(itfs_len > 0, "only called for len>0"); | |
| _local_interfaces = MetadataFactory::new_array<InstanceKlass*>(_loader_data, itfs_len, nullptr, CHECK); | |
| int index; | |
| for (index = 0; index < itfs_len; index++) { | |
| const u2 interface_index = stream->get_u2(CHECK); | |
| Klass* interf; | |
| guarantee_property( | |
| valid_klass_reference_at(interface_index), | |
| "Interface name has bad constant pool index %u in class file %s", | |
| interface_index, CHECK); | |
| if (cp->tag_at(interface_index).is_klass()) { | |
| interf = cp->resolved_klass_at(interface_index); | |
| } else { | |
| Symbol* const unresolved_klass = cp->klass_name_at(interface_index); | |
| // Don't need to check legal name because it's checked when parsing constant pool. | |
| // But need to make sure it's not an array type. | |
| guarantee_property(unresolved_klass->char_at(0) != JVM_SIGNATURE_ARRAY, | |
| "Bad interface name in class file %s", CHECK); | |
| // Call resolve on the interface class name with class circularity checking | |
| interf = SystemDictionary::resolve_super_or_fail(_class_name, | |
| unresolved_klass, | |
| Handle(THREAD, _loader_data->class_loader()), | |
| false, CHECK); | |
| } | |
| if (!interf->is_interface()) { | |
| THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), | |
| err_msg("class %s can not implement %s, because it is not an interface (%s)", | |
| _class_name->as_klass_external_name(), | |
| interf->external_name(), | |
| interf->class_in_module_of_loader())); | |
| } | |
| if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) { | |
| *has_nonstatic_concrete_methods = true; | |
| } | |
| _local_interfaces->at_put(index, InstanceKlass::cast(interf)); | |
| } | |
| if (!_need_verify || itfs_len <= 1) { | |
| return; | |
| } | |
| // Check if there's any duplicates in interfaces | |
| ResourceMark rm(THREAD); | |
| // Set containing interface names | |
| ResourceHashtable<Symbol*, int>* interface_names = new ResourceHashtable<Symbol*, int>(); | |
| for (index = 0; index < itfs_len; index++) { | |
| const InstanceKlass* const k = _local_interfaces->at(index); | |
| Symbol* interface_name = k->name(); | |
| // If no duplicates, add (name, nullptr) in hashtable interface_names. | |
| if (!interface_names->put(interface_name, 0)) { | |
| classfile_parse_error("Duplicate interface name \"%s\" in class file %s", | |
| interface_name->as_C_string(), THREAD); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| void ClassFileParser::verify_constantvalue(const ConstantPool* const cp, | |
| int constantvalue_index, | |
| int signature_index, | |
| TRAPS) const { | |
| // Make sure the constant pool entry is of a type appropriate to this field | |
| guarantee_property( | |
| (constantvalue_index > 0 && | |
| constantvalue_index < cp->length()), | |
| "Bad initial value index %u in ConstantValue attribute in class file %s", | |
| constantvalue_index, CHECK); | |
| const constantTag value_type = cp->tag_at(constantvalue_index); | |
| switch(cp->basic_type_for_signature_at(signature_index)) { | |
| case T_LONG: { | |
| guarantee_property(value_type.is_long(), | |
| "Inconsistent constant value type in class file %s", | |
| CHECK); | |
| break; | |
| } | |
| case T_FLOAT: { | |
| guarantee_property(value_type.is_float(), | |
| "Inconsistent constant value type in class file %s", | |
| CHECK); | |
| break; | |
| } | |
| case T_DOUBLE: { | |
| guarantee_property(value_type.is_double(), | |
| "Inconsistent constant value type in class file %s", | |
| CHECK); | |
| break; | |
| } | |
| case T_BYTE: | |
| case T_CHAR: | |
| case T_SHORT: | |
| case T_BOOLEAN: | |
| case T_INT: { | |
| guarantee_property(value_type.is_int(), | |
| "Inconsistent constant value type in class file %s", | |
| CHECK); | |
| break; | |
| } | |
| case T_OBJECT: { | |
| guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;") | |
| && value_type.is_string()), | |
| "Bad string initial value in class file %s", | |
| CHECK); | |
| break; | |
| } | |
| default: { | |
| classfile_parse_error("Unable to set initial value %u in class file %s", | |
| constantvalue_index, | |
| THREAD); | |
| } | |
| } | |
| } | |
| class AnnotationCollector : public ResourceObj{ | |
| public: | |
| enum Location { _in_field, _in_method, _in_class }; | |
| enum ID { | |
| _unknown = 0, | |
| _method_CallerSensitive, | |
| _method_ForceInline, | |
| _method_DontInline, | |
| _method_ChangesCurrentThread, | |
| _method_JvmtiHideEvents, | |
| _method_JvmtiMountTransition, | |
| _method_InjectedProfile, | |
| _method_LambdaForm_Compiled, | |
| _method_Hidden, | |
| _method_Scoped, | |
| _method_IntrinsicCandidate, | |
| _jdk_internal_vm_annotation_Contended, | |
| _field_Stable, | |
| _jdk_internal_vm_annotation_ReservedStackAccess, | |
| _jdk_internal_ValueBased, | |
| _java_lang_Deprecated, | |
| _java_lang_Deprecated_for_removal, | |
| _annotation_LIMIT | |
| }; | |
| const Location _location; | |
| int _annotations_present; | |
| u2 _contended_group; | |
| AnnotationCollector(Location location) | |
| : _location(location), _annotations_present(0), _contended_group(0) | |
| { | |
| assert((int)_annotation_LIMIT <= (int)sizeof(_annotations_present) * BitsPerByte, ""); | |
| } | |
| // If this annotation name has an ID, report it (or _none). | |
| ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name, bool can_access_vm_annotations); | |
| // Set the annotation name: | |
| void set_annotation(ID id) { | |
| assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); | |
| _annotations_present |= (int)nth_bit((int)id); | |
| } | |
| void remove_annotation(ID id) { | |
| assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); | |
| _annotations_present &= (int)~nth_bit((int)id); | |
| } | |
| // Report if the annotation is present. | |
| bool has_any_annotations() const { return _annotations_present != 0; } | |
| bool has_annotation(ID id) const { return (nth_bit((int)id) & _annotations_present) != 0; } | |
| void set_contended_group(u2 group) { _contended_group = group; } | |
| u2 contended_group() const { return _contended_group; } | |
| bool is_contended() const { return has_annotation(_jdk_internal_vm_annotation_Contended); } | |
| void set_stable(bool stable) { set_annotation(_field_Stable); } | |
| bool is_stable() const { return has_annotation(_field_Stable); } | |
| }; | |
| // This class also doubles as a holder for metadata cleanup. | |
| class ClassFileParser::FieldAnnotationCollector : public AnnotationCollector { | |
| private: | |
| ClassLoaderData* _loader_data; | |
| AnnotationArray* _field_annotations; | |
| AnnotationArray* _field_type_annotations; | |
| public: | |
| FieldAnnotationCollector(ClassLoaderData* loader_data) : | |
| AnnotationCollector(_in_field), | |
| _loader_data(loader_data), | |
| _field_annotations(nullptr), | |
| _field_type_annotations(nullptr) {} | |
| ~FieldAnnotationCollector(); | |
| void apply_to(FieldInfo* f); | |
| AnnotationArray* field_annotations() { return _field_annotations; } | |
| AnnotationArray* field_type_annotations() { return _field_type_annotations; } | |
| void set_field_annotations(AnnotationArray* a) { _field_annotations = a; } | |
| void set_field_type_annotations(AnnotationArray* a) { _field_type_annotations = a; } | |
| }; | |
| class MethodAnnotationCollector : public AnnotationCollector{ | |
| public: | |
| MethodAnnotationCollector() : AnnotationCollector(_in_method) { } | |
| void apply_to(const methodHandle& m); | |
| }; | |
| class ClassFileParser::ClassAnnotationCollector : public AnnotationCollector{ | |
| public: | |
| ClassAnnotationCollector() : AnnotationCollector(_in_class) { } | |
| void apply_to(InstanceKlass* ik); | |
| }; | |
| static int skip_annotation_value(const u1*, int, int); // fwd decl | |
| // Safely increment index by val if does not pass limit | |
| #define SAFE_ADD(index, limit, val) \ | |
| if (index >= limit - val) return limit; \ | |
| index += val; | |
| // Skip an annotation. Return >=limit if there is any problem. | |
| static int skip_annotation(const u1* buffer, int limit, int index) { | |
| assert(buffer != nullptr, "invariant"); | |
| // annotation := atype:u2 do(nmem:u2) {member:u2 value} | |
| // value := switch (tag:u1) { ... } | |
| SAFE_ADD(index, limit, 4); // skip atype and read nmem | |
| int nmem = Bytes::get_Java_u2((address)buffer + index - 2); | |
| while (--nmem >= 0 && index < limit) { | |
| SAFE_ADD(index, limit, 2); // skip member | |
| index = skip_annotation_value(buffer, limit, index); | |
| } | |
| return index; | |
| } | |
| // Skip an annotation value. Return >=limit if there is any problem. | |
| static int skip_annotation_value(const u1* buffer, int limit, int index) { | |
| assert(buffer != nullptr, "invariant"); | |
| // value := switch (tag:u1) { | |
| // case B, C, I, S, Z, D, F, J, c: con:u2; | |
| // case e: e_class:u2 e_name:u2; | |
| // case s: s_con:u2; | |
| // case [: do(nval:u2) {value}; | |
| // case @: annotation; | |
| // case s: s_con:u2; | |
| // } | |
| SAFE_ADD(index, limit, 1); // read tag | |
| const u1 tag = buffer[index - 1]; | |
| switch (tag) { | |
| case 'B': | |
| case 'C': | |
| case 'I': | |
| case 'S': | |
| case 'Z': | |
| case 'D': | |
| case 'F': | |
| case 'J': | |
| case 'c': | |
| case 's': | |
| SAFE_ADD(index, limit, 2); // skip con or s_con | |
| break; | |
| case 'e': | |
| SAFE_ADD(index, limit, 4); // skip e_class, e_name | |
| break; | |
| case '[': | |
| { | |
| SAFE_ADD(index, limit, 2); // read nval | |
| int nval = Bytes::get_Java_u2((address)buffer + index - 2); | |
| while (--nval >= 0 && index < limit) { | |
| index = skip_annotation_value(buffer, limit, index); | |
| } | |
| } | |
| break; | |
| case '@': | |
| index = skip_annotation(buffer, limit, index); | |
| break; | |
| default: | |
| return limit; // bad tag byte | |
| } | |
| return index; | |
| } | |
| // Sift through annotations, looking for those significant to the VM: | |
| static void parse_annotations(const ConstantPool* const cp, | |
| const u1* buffer, int limit, | |
| AnnotationCollector* coll, | |
| ClassLoaderData* loader_data, | |
| const bool can_access_vm_annotations) { | |
| assert(cp != nullptr, "invariant"); | |
| assert(buffer != nullptr, "invariant"); | |
| assert(coll != nullptr, "invariant"); | |
| assert(loader_data != nullptr, "invariant"); | |
| // annotations := do(nann:u2) {annotation} | |
| int index = 2; // read nann | |
| if (index >= limit) return; | |
| int nann = Bytes::get_Java_u2((address)buffer + index - 2); | |
| enum { // initial annotation layout | |
| atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;' | |
| count_off = 2, // u2 such as 1 (one value) | |
| member_off = 4, // utf8 such as 'value' | |
| tag_off = 6, // u1 such as 'c' (type) or 'e' (enum) | |
| e_tag_val = 'e', | |
| e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;' | |
| e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME' | |
| e_size = 11, // end of 'e' annotation | |
| c_tag_val = 'c', // payload is type | |
| c_con_off = 7, // utf8 payload, such as 'I' | |
| c_size = 9, // end of 'c' annotation | |
| s_tag_val = 's', // payload is String | |
| s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;' | |
| s_size = 9, | |
| b_tag_val = 'Z', // payload is boolean | |
| min_size = 6 // smallest possible size (zero members) | |
| }; | |
| // Cannot add min_size to index in case of overflow MAX_INT | |
| while ((--nann) >= 0 && (index - 2 <= limit - min_size)) { | |
| int index0 = index; | |
| index = skip_annotation(buffer, limit, index); | |
| const u1* const abase = buffer + index0; | |
| const int atype = Bytes::get_Java_u2((address)abase + atype_off); | |
| const int count = Bytes::get_Java_u2((address)abase + count_off); | |
| const Symbol* const aname = check_symbol_at(cp, atype); | |
| if (aname == nullptr) break; // invalid annotation name | |
| const Symbol* member = nullptr; | |
| if (count >= 1) { | |
| const int member_index = Bytes::get_Java_u2((address)abase + member_off); | |
| member = check_symbol_at(cp, member_index); | |
| if (member == nullptr) break; // invalid member name | |
| } | |
| // Here is where parsing particular annotations will take place. | |
| AnnotationCollector::ID id = coll->annotation_index(loader_data, aname, can_access_vm_annotations); | |
| if (AnnotationCollector::_unknown == id) continue; | |
| coll->set_annotation(id); | |
| if (AnnotationCollector::_java_lang_Deprecated == id) { | |
| // @Deprecated can specify forRemoval=true, which we need | |
| // to record for JFR to use. If the annotation is not well-formed | |
| // then we may not be able to determine that. | |
| const u1* offset = abase + member_off; | |
| // There are only 2 members in @Deprecated. | |
| int n_members = MIN2(count, 2); | |
| for (int i = 0; i < n_members; ++i) { | |
| int member_index = Bytes::get_Java_u2((address)offset); | |
| offset += 2; | |
| member = check_symbol_at(cp, member_index); | |
| if (member == vmSymbols::since() && | |
| (*((address)offset) == s_tag_val)) { | |
| // Found `since` first so skip over it | |
| offset += 3; | |
| } | |
| else if (member == vmSymbols::for_removal() && | |
| (*((address)offset) == b_tag_val)) { | |
| const u2 boolean_value_index = Bytes::get_Java_u2((address)offset + 1); | |
| // No guarantee the entry is valid so check it refers to an int in the CP. | |
| if (cp->is_within_bounds(boolean_value_index) && | |
| cp->tag_at(boolean_value_index).is_int() && | |
| cp->int_at(boolean_value_index) == 1) { | |
| // forRemoval == true | |
| coll->set_annotation(AnnotationCollector::_java_lang_Deprecated_for_removal); | |
| } | |
| break; // no need to check further | |
| } | |
| else { | |
| // This @Deprecated annotation is malformed so we don't try to | |
| // determine whether forRemoval is set. | |
| break; | |
| } | |
| } | |
| continue; // proceed to next annotation | |
| } | |
| if (AnnotationCollector::_jdk_internal_vm_annotation_Contended == id) { | |
| // @Contended can optionally specify the contention group. | |
| // | |
| // Contended group defines the equivalence class over the fields: | |
| // the fields within the same contended group are not treated distinct. | |
| // The only exception is default group, which does not incur the | |
| // equivalence. Naturally, contention group for classes is meaningless. | |
| // | |
| // While the contention group is specified as String, annotation | |
| // values are already interned, and we might as well use the constant | |
| // pool index as the group tag. | |
| // | |
| u2 group_index = 0; // default contended group | |
| if (count == 1 | |
| && s_size == (index - index0) // match size | |
| && s_tag_val == *(abase + tag_off) | |
| && member == vmSymbols::value_name()) { | |
| group_index = Bytes::get_Java_u2((address)abase + s_con_off); | |
| // No guarantee the group_index is valid so check it refers to a | |
| // symbol in the CP. | |
| if (cp->is_within_bounds(group_index) && | |
| cp->tag_at(group_index).is_utf8()) { | |
| // Seems valid, so check for empty string and reset | |
| if (cp->symbol_at(group_index)->utf8_length() == 0) { | |
| group_index = 0; // default contended group | |
| } | |
| } else { | |
| // Not valid so use the default | |
| group_index = 0; | |
| } | |
| } | |
| coll->set_contended_group(group_index); | |
| continue; // proceed to next annotation | |
| } | |
| } | |
| } | |
| // Parse attributes for a field. | |
| void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, | |
| u2 attributes_count, | |
| bool is_static, u2 signature_index, | |
| u2* const constantvalue_index_addr, | |
| bool* const is_synthetic_addr, | |
| u2* const generic_signature_index_addr, | |
| ClassFileParser::FieldAnnotationCollector* parsed_annotations, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(constantvalue_index_addr != nullptr, "invariant"); | |
| assert(is_synthetic_addr != nullptr, "invariant"); | |
| assert(generic_signature_index_addr != nullptr, "invariant"); | |
| assert(parsed_annotations != nullptr, "invariant"); | |
| assert(attributes_count > 0, "attributes_count should be greater than 0"); | |
| u2 constantvalue_index = 0; | |
| u2 generic_signature_index = 0; | |
| bool is_synthetic = false; | |
| const u1* runtime_visible_annotations = nullptr; | |
| int runtime_visible_annotations_length = 0; | |
| const u1* runtime_visible_type_annotations = nullptr; | |
| int runtime_visible_type_annotations_length = 0; | |
| bool runtime_invisible_annotations_exists = false; | |
| bool runtime_invisible_type_annotations_exists = false; | |
| const ConstantPool* const cp = _cp; | |
| while (attributes_count--) { | |
| cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length | |
| const u2 attribute_name_index = cfs->get_u2_fast(); | |
| const u4 attribute_length = cfs->get_u4_fast(); | |
| guarantee_property(valid_symbol_at(attribute_name_index), | |
| "Invalid field attribute index %u in class file %s", | |
| attribute_name_index, | |
| CHECK); | |
| const Symbol* const attribute_name = cp->symbol_at(attribute_name_index); | |
| if (is_static && attribute_name == vmSymbols::tag_constant_value()) { | |
| // ignore if non-static | |
| if (constantvalue_index != 0) { | |
| classfile_parse_error("Duplicate ConstantValue attribute in class file %s", THREAD); | |
| return; | |
| } | |
| guarantee_property( | |
| attribute_length == 2, | |
| "Invalid ConstantValue field attribute length %u in class file %s", | |
| attribute_length, CHECK); | |
| constantvalue_index = cfs->get_u2(CHECK); | |
| if (_need_verify) { | |
| verify_constantvalue(cp, constantvalue_index, signature_index, CHECK); | |
| } | |
| } else if (attribute_name == vmSymbols::tag_synthetic()) { | |
| if (attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Synthetic field attribute length %u in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| is_synthetic = true; | |
| } else if (attribute_name == vmSymbols::tag_deprecated()) { // 4276120 | |
| if (attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Deprecated field attribute length %u in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| } else if (_major_version >= JAVA_1_5_VERSION) { | |
| if (attribute_name == vmSymbols::tag_signature()) { | |
| if (generic_signature_index != 0) { | |
| classfile_parse_error( | |
| "Multiple Signature attributes for field in class file %s", THREAD); | |
| return; | |
| } | |
| if (attribute_length != 2) { | |
| classfile_parse_error( | |
| "Wrong size %u for field's Signature attribute in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| generic_signature_index = parse_generic_signature_attribute(cfs, CHECK); | |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { | |
| if (runtime_visible_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleAnnotations attributes for field in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_visible_annotations_length = attribute_length; | |
| runtime_visible_annotations = cfs->current(); | |
| assert(runtime_visible_annotations != nullptr, "null visible annotations"); | |
| cfs->guarantee_more(runtime_visible_annotations_length, CHECK); | |
| parse_annotations(cp, | |
| runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| parsed_annotations, | |
| _loader_data, | |
| _can_access_vm_annotations); | |
| cfs->skip_u1_fast(runtime_visible_annotations_length); | |
| } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { | |
| if (runtime_invisible_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleAnnotations attributes for field in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_invisible_annotations_exists = true; | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { | |
| if (runtime_visible_type_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleTypeAnnotations attributes for field in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_visible_type_annotations_length = attribute_length; | |
| runtime_visible_type_annotations = cfs->current(); | |
| assert(runtime_visible_type_annotations != nullptr, "null visible type annotations"); | |
| cfs->skip_u1(runtime_visible_type_annotations_length, CHECK); | |
| } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { | |
| if (runtime_invisible_type_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleTypeAnnotations attributes for field in class file %s", THREAD); | |
| return; | |
| } else { | |
| runtime_invisible_type_annotations_exists = true; | |
| } | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } else { | |
| cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes | |
| } | |
| } else { | |
| cfs->skip_u1(attribute_length, CHECK); // Skip unknown attributes | |
| } | |
| } | |
| *constantvalue_index_addr = constantvalue_index; | |
| *is_synthetic_addr = is_synthetic; | |
| *generic_signature_index_addr = generic_signature_index; | |
| AnnotationArray* a = allocate_annotations(runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| CHECK); | |
| parsed_annotations->set_field_annotations(a); | |
| a = allocate_annotations(runtime_visible_type_annotations, | |
| runtime_visible_type_annotations_length, | |
| CHECK); | |
| parsed_annotations->set_field_type_annotations(a); | |
| return; | |
| } | |
| // Side-effects: populates the _fields, _fields_annotations, | |
| // _fields_type_annotations fields | |
| void ClassFileParser::parse_fields(const ClassFileStream* const cfs, | |
| bool is_interface, | |
| ConstantPool* cp, | |
| const int cp_size, | |
| u2* const java_fields_count_ptr, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| assert(java_fields_count_ptr != nullptr, "invariant"); | |
| assert(nullptr == _fields_annotations, "invariant"); | |
| assert(nullptr == _fields_type_annotations, "invariant"); | |
| cfs->guarantee_more(2, CHECK); // length | |
| const u2 length = cfs->get_u2_fast(); | |
| *java_fields_count_ptr = length; | |
| int num_injected = 0; | |
| const InjectedField* const injected = JavaClasses::get_injected(_class_name, | |
| &num_injected); | |
| const int total_fields = length + num_injected; | |
| // Allocate a temporary resource array to collect field data. | |
| // After parsing all fields, data are stored in a UNSIGNED5 compressed stream. | |
| _temp_field_info = new GrowableArray<FieldInfo>(total_fields); | |
| ResourceMark rm(THREAD); | |
| for (int n = 0; n < length; n++) { | |
| // access_flags, name_index, descriptor_index, attributes_count | |
| cfs->guarantee_more(8, CHECK); | |
| AccessFlags access_flags; | |
| const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; | |
| verify_legal_field_modifiers(flags, is_interface, CHECK); | |
| access_flags.set_flags(flags); | |
| FieldInfo::FieldFlags fieldFlags(0); | |
| const u2 name_index = cfs->get_u2_fast(); | |
| guarantee_property(valid_symbol_at(name_index), | |
| "Invalid constant pool index %u for field name in class file %s", | |
| name_index, CHECK); | |
| const Symbol* const name = cp->symbol_at(name_index); | |
| verify_legal_field_name(name, CHECK); | |
| const u2 signature_index = cfs->get_u2_fast(); | |
| guarantee_property(valid_symbol_at(signature_index), | |
| "Invalid constant pool index %u for field signature in class file %s", | |
| signature_index, CHECK); | |
| const Symbol* const sig = cp->symbol_at(signature_index); | |
| verify_legal_field_signature(name, sig, CHECK); | |
| u2 constantvalue_index = 0; | |
| bool is_synthetic = false; | |
| u2 generic_signature_index = 0; | |
| const bool is_static = access_flags.is_static(); | |
| FieldAnnotationCollector parsed_annotations(_loader_data); | |
| const u2 attributes_count = cfs->get_u2_fast(); | |
| if (attributes_count > 0) { | |
| parse_field_attributes(cfs, | |
| attributes_count, | |
| is_static, | |
| signature_index, | |
| &constantvalue_index, | |
| &is_synthetic, | |
| &generic_signature_index, | |
| &parsed_annotations, | |
| CHECK); | |
| if (parsed_annotations.field_annotations() != nullptr) { | |
| if (_fields_annotations == nullptr) { | |
| _fields_annotations = MetadataFactory::new_array<AnnotationArray*>( | |
| _loader_data, length, nullptr, | |
| CHECK); | |
| } | |
| _fields_annotations->at_put(n, parsed_annotations.field_annotations()); | |
| parsed_annotations.set_field_annotations(nullptr); | |
| } | |
| if (parsed_annotations.field_type_annotations() != nullptr) { | |
| if (_fields_type_annotations == nullptr) { | |
| _fields_type_annotations = | |
| MetadataFactory::new_array<AnnotationArray*>(_loader_data, | |
| length, | |
| nullptr, | |
| CHECK); | |
| } | |
| _fields_type_annotations->at_put(n, parsed_annotations.field_type_annotations()); | |
| parsed_annotations.set_field_type_annotations(nullptr); | |
| } | |
| if (is_synthetic) { | |
| access_flags.set_is_synthetic(); | |
| } | |
| if (generic_signature_index != 0) { | |
| fieldFlags.update_generic(true); | |
| } | |
| } | |
| const BasicType type = cp->basic_type_for_signature_at(signature_index); | |
| // Update number of static oop fields. | |
| if (is_static && is_reference_type(type)) { | |
| _static_oop_count++; | |
| } | |
| FieldInfo fi(access_flags, name_index, signature_index, constantvalue_index, fieldFlags); | |
| fi.set_index(n); | |
| if (fieldFlags.is_generic()) { | |
| fi.set_generic_signature_index(generic_signature_index); | |
| } | |
| parsed_annotations.apply_to(&fi); | |
| if (fi.field_flags().is_contended()) { | |
| _has_contended_fields = true; | |
| } | |
| _temp_field_info->append(fi); | |
| } | |
| assert(_temp_field_info->length() == length, "Must be"); | |
| int index = length; | |
| if (num_injected != 0) { | |
| for (int n = 0; n < num_injected; n++) { | |
| // Check for duplicates | |
| if (injected[n].may_be_java) { | |
| const Symbol* const name = injected[n].name(); | |
| const Symbol* const signature = injected[n].signature(); | |
| bool duplicate = false; | |
| for (int i = 0; i < length; i++) { | |
| const FieldInfo* const f = _temp_field_info->adr_at(i); | |
| if (name == cp->symbol_at(f->name_index()) && | |
| signature == cp->symbol_at(f->signature_index())) { | |
| // Symbol is desclared in Java so skip this one | |
| duplicate = true; | |
| break; | |
| } | |
| } | |
| if (duplicate) { | |
| // These will be removed from the field array at the end | |
| continue; | |
| } | |
| } | |
| // Injected field | |
| FieldInfo::FieldFlags fflags(0); | |
| fflags.update_injected(true); | |
| AccessFlags aflags; | |
| FieldInfo fi(aflags, (u2)(injected[n].name_index), (u2)(injected[n].signature_index), 0, fflags); | |
| fi.set_index(index); | |
| _temp_field_info->append(fi); | |
| index++; | |
| } | |
| } | |
| assert(_temp_field_info->length() == index, "Must be"); | |
| if (_need_verify && length > 1) { | |
| // Check duplicated fields | |
| ResourceMark rm(THREAD); | |
| // Set containing name-signature pairs | |
| NameSigHashtable* names_and_sigs = new NameSigHashtable(); | |
| for (int i = 0; i < _temp_field_info->length(); i++) { | |
| NameSigHash name_and_sig(_temp_field_info->adr_at(i)->name(_cp), | |
| _temp_field_info->adr_at(i)->signature(_cp)); | |
| // If no duplicates, add name/signature in hashtable names_and_sigs. | |
| if(!names_and_sigs->put(name_and_sig, 0)) { | |
| classfile_parse_error("Duplicate field name \"%s\" with signature \"%s\" in class file %s", | |
| name_and_sig._name->as_C_string(), name_and_sig._sig->as_klass_external_name(), THREAD); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| const ClassFileParser::unsafe_u2* ClassFileParser::parse_exception_table(const ClassFileStream* const cfs, | |
| u4 code_length, | |
| u4 exception_table_length, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| const unsafe_u2* const exception_table_start = cfs->current(); | |
| assert(exception_table_start != nullptr, "null exception table"); | |
| cfs->guarantee_more(8 * exception_table_length, CHECK_NULL); // start_pc, | |
| // end_pc, | |
| // handler_pc, | |
| // catch_type_index | |
| // Will check legal target after parsing code array in verifier. | |
| if (_need_verify) { | |
| for (unsigned int i = 0; i < exception_table_length; i++) { | |
| const u2 start_pc = cfs->get_u2_fast(); | |
| const u2 end_pc = cfs->get_u2_fast(); | |
| const u2 handler_pc = cfs->get_u2_fast(); | |
| const u2 catch_type_index = cfs->get_u2_fast(); | |
| guarantee_property((start_pc < end_pc) && (end_pc <= code_length), | |
| "Illegal exception table range in class file %s", | |
| CHECK_NULL); | |
| guarantee_property(handler_pc < code_length, | |
| "Illegal exception table handler in class file %s", | |
| CHECK_NULL); | |
| if (catch_type_index != 0) { | |
| guarantee_property(valid_klass_reference_at(catch_type_index), | |
| "Catch type in exception table has bad constant type in class file %s", CHECK_NULL); | |
| } | |
| } | |
| } else { | |
| cfs->skip_u2_fast(exception_table_length * 4); | |
| } | |
| return exception_table_start; | |
| } | |
| void ClassFileParser::parse_linenumber_table(u4 code_attribute_length, | |
| u4 code_length, | |
| CompressedLineNumberWriteStream**const write_stream, | |
| TRAPS) { | |
| const ClassFileStream* const cfs = _stream; | |
| unsigned int num_entries = cfs->get_u2(CHECK); | |
| // Each entry is a u2 start_pc, and a u2 line_number | |
| const unsigned int length_in_bytes = num_entries * (sizeof(u2) * 2); | |
| // Verify line number attribute and table length | |
| guarantee_property( | |
| code_attribute_length == sizeof(u2) + length_in_bytes, | |
| "LineNumberTable attribute has wrong length in class file %s", CHECK); | |
| cfs->guarantee_more(length_in_bytes, CHECK); | |
| if ((*write_stream) == nullptr) { | |
| if (length_in_bytes > fixed_buffer_size) { | |
| (*write_stream) = new CompressedLineNumberWriteStream(length_in_bytes); | |
| } else { | |
| (*write_stream) = new CompressedLineNumberWriteStream( | |
| _linenumbertable_buffer, fixed_buffer_size); | |
| } | |
| } | |
| while (num_entries-- > 0) { | |
| const u2 bci = cfs->get_u2_fast(); // start_pc | |
| const u2 line = cfs->get_u2_fast(); // line_number | |
| guarantee_property(bci < code_length, | |
| "Invalid pc in LineNumberTable in class file %s", CHECK); | |
| (*write_stream)->write_pair(bci, line); | |
| } | |
| } | |
| class LVT_Hash : public AllStatic { | |
| public: | |
| static bool equals(LocalVariableTableElement const& e0, LocalVariableTableElement const& e1) { | |
| /* | |
| * 3-tuple start_bci/length/slot has to be unique key, | |
| * so the following comparison seems to be redundant: | |
| * && elem->name_cp_index == entry->_elem->name_cp_index | |
| */ | |
| return (e0.start_bci == e1.start_bci && | |
| e0.length == e1.length && | |
| e0.name_cp_index == e1.name_cp_index && | |
| e0.slot == e1.slot); | |
| } | |
| static unsigned int hash(LocalVariableTableElement const& e0) { | |
| unsigned int raw_hash = e0.start_bci; | |
| raw_hash = e0.length + raw_hash * 37; | |
| raw_hash = e0.name_cp_index + raw_hash * 37; | |
| raw_hash = e0.slot + raw_hash * 37; | |
| return raw_hash; | |
| } | |
| }; | |
| // Class file LocalVariableTable elements. | |
| class Classfile_LVT_Element { | |
| public: | |
| u2 start_bci; | |
| u2 length; | |
| u2 name_cp_index; | |
| u2 descriptor_cp_index; | |
| u2 slot; | |
| }; | |
| static void copy_lvt_element(const Classfile_LVT_Element* const src, | |
| LocalVariableTableElement* const lvt) { | |
| lvt->start_bci = Bytes::get_Java_u2((u1*) &src->start_bci); | |
| lvt->length = Bytes::get_Java_u2((u1*) &src->length); | |
| lvt->name_cp_index = Bytes::get_Java_u2((u1*) &src->name_cp_index); | |
| lvt->descriptor_cp_index = Bytes::get_Java_u2((u1*) &src->descriptor_cp_index); | |
| lvt->signature_cp_index = 0; | |
| lvt->slot = Bytes::get_Java_u2((u1*) &src->slot); | |
| } | |
| // Function is used to parse both attributes: | |
| // LocalVariableTable (LVT) and LocalVariableTypeTable (LVTT) | |
| const ClassFileParser::unsafe_u2* ClassFileParser::parse_localvariable_table(const ClassFileStream* cfs, | |
| u4 code_length, | |
| u2 max_locals, | |
| u4 code_attribute_length, | |
| u2* const localvariable_table_length, | |
| bool isLVTT, | |
| TRAPS) { | |
| const char* const tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable"; | |
| *localvariable_table_length = cfs->get_u2(CHECK_NULL); | |
| const unsigned int size = checked_cast<unsigned>( | |
| (*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2)); | |
| const ConstantPool* const cp = _cp; | |
| // Verify local variable table attribute has right length | |
| if (_need_verify) { | |
| guarantee_property(code_attribute_length == (sizeof(*localvariable_table_length) + size * sizeof(u2)), | |
| "%s has wrong length in class file %s", tbl_name, CHECK_NULL); | |
| } | |
| const unsafe_u2* const localvariable_table_start = cfs->current(); | |
| assert(localvariable_table_start != nullptr, "null local variable table"); | |
| if (!_need_verify) { | |
| cfs->skip_u2_fast(size); | |
| } else { | |
| cfs->guarantee_more(size * 2, CHECK_NULL); | |
| for(int i = 0; i < (*localvariable_table_length); i++) { | |
| const u2 start_pc = cfs->get_u2_fast(); | |
| const u2 length = cfs->get_u2_fast(); | |
| const u2 name_index = cfs->get_u2_fast(); | |
| const u2 descriptor_index = cfs->get_u2_fast(); | |
| const u2 index = cfs->get_u2_fast(); | |
| // Assign to a u4 to avoid overflow | |
| const u4 end_pc = (u4)start_pc + (u4)length; | |
| if (start_pc >= code_length) { | |
| classfile_parse_error( | |
| "Invalid start_pc %u in %s in class file %s", | |
| start_pc, tbl_name, THREAD); | |
| return nullptr; | |
| } | |
| if (end_pc > code_length) { | |
| classfile_parse_error( | |
| "Invalid length %u in %s in class file %s", | |
| length, tbl_name, THREAD); | |
| return nullptr; | |
| } | |
| const int cp_size = cp->length(); | |
| guarantee_property(valid_symbol_at(name_index), | |
| "Name index %u in %s has bad constant type in class file %s", | |
| name_index, tbl_name, CHECK_NULL); | |
| guarantee_property(valid_symbol_at(descriptor_index), | |
| "Signature index %u in %s has bad constant type in class file %s", | |
| descriptor_index, tbl_name, CHECK_NULL); | |
| const Symbol* const name = cp->symbol_at(name_index); | |
| const Symbol* const sig = cp->symbol_at(descriptor_index); | |
| verify_legal_field_name(name, CHECK_NULL); | |
| u2 extra_slot = 0; | |
| if (!isLVTT) { | |
| verify_legal_field_signature(name, sig, CHECK_NULL); | |
| // 4894874: check special cases for double and long local variables | |
| if (sig == vmSymbols::type_signature(T_DOUBLE) || | |
| sig == vmSymbols::type_signature(T_LONG)) { | |
| extra_slot = 1; | |
| } | |
| } | |
| guarantee_property((index + extra_slot) < max_locals, | |
| "Invalid index %u in %s in class file %s", | |
| index, tbl_name, CHECK_NULL); | |
| } | |
| } | |
| return localvariable_table_start; | |
| } | |
| static const u1* parse_stackmap_table(const ClassFileStream* const cfs, | |
| u4 code_attribute_length, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| if (0 == code_attribute_length) { | |
| return nullptr; | |
| } | |
| const u1* const stackmap_table_start = cfs->current(); | |
| assert(stackmap_table_start != nullptr, "null stackmap table"); | |
| // check code_attribute_length | |
| cfs->skip_u1(code_attribute_length, CHECK_NULL); | |
| return stackmap_table_start; | |
| } | |
| const ClassFileParser::unsafe_u2* ClassFileParser::parse_checked_exceptions(const ClassFileStream* const cfs, | |
| u2* const checked_exceptions_length, | |
| u4 method_attribute_length, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(checked_exceptions_length != nullptr, "invariant"); | |
| cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length | |
| *checked_exceptions_length = cfs->get_u2_fast(); | |
| const unsigned int size = | |
| (*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2); | |
| const unsafe_u2* const checked_exceptions_start = cfs->current(); | |
| assert(checked_exceptions_start != nullptr, "null checked exceptions"); | |
| if (!_need_verify) { | |
| cfs->skip_u2_fast(size); | |
| } else { | |
| // Verify each value in the checked exception table | |
| u2 checked_exception; | |
| const u2 len = *checked_exceptions_length; | |
| cfs->guarantee_more(2 * len, CHECK_NULL); | |
| for (int i = 0; i < len; i++) { | |
| checked_exception = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_klass_reference_at(checked_exception), | |
| "Exception name has bad type at constant pool %u in class file %s", | |
| checked_exception, CHECK_NULL); | |
| } | |
| } | |
| // check exceptions attribute length | |
| if (_need_verify) { | |
| guarantee_property(method_attribute_length == (sizeof(*checked_exceptions_length) + | |
| sizeof(u2) * size), | |
| "Exceptions attribute has wrong length in class file %s", CHECK_NULL); | |
| } | |
| return checked_exceptions_start; | |
| } | |
| void ClassFileParser::throwIllegalSignature(const char* type, | |
| const Symbol* name, | |
| const Symbol* sig, | |
| TRAPS) const { | |
| assert(name != nullptr, "invariant"); | |
| assert(sig != nullptr, "invariant"); | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow(THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "%s \"%s\" in class %s has illegal signature \"%s\"", type, | |
| name->as_C_string(), _class_name->as_C_string(), sig->as_C_string()); | |
| } | |
| AnnotationCollector::ID | |
| AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, | |
| const Symbol* name, | |
| const bool can_access_vm_annotations) { | |
| const vmSymbolID sid = vmSymbols::find_sid(name); | |
| // Privileged code can use all annotations. Other code silently drops some. | |
| const bool privileged = loader_data->is_boot_class_loader_data() || | |
| loader_data->is_platform_class_loader_data() || | |
| can_access_vm_annotations; | |
| switch (sid) { | |
| case VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_CallerSensitive; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ForceInline_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_ForceInline; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_DontInline_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_DontInline; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ChangesCurrentThread_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_ChangesCurrentThread; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_JvmtiHideEvents_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_JvmtiHideEvents; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_JvmtiMountTransition_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_JvmtiMountTransition; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(java_lang_invoke_InjectedProfile_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_InjectedProfile; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_LambdaForm_Compiled; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Hidden_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_Hidden; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_misc_Scoped_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_Scoped; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_IntrinsicCandidate_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (!privileged) break; // only allow in privileged code | |
| return _method_IntrinsicCandidate; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Stable_signature): { | |
| if (_location != _in_field) break; // only allow for fields | |
| if (!privileged) break; // only allow in privileged code | |
| return _field_Stable; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { | |
| if (_location != _in_field && _location != _in_class) { | |
| break; // only allow for fields and classes | |
| } | |
| if (!EnableContended || (RestrictContended && !privileged)) { | |
| break; // honor privileges | |
| } | |
| return _jdk_internal_vm_annotation_Contended; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ReservedStackAccess_signature): { | |
| if (_location != _in_method) break; // only allow for methods | |
| if (RestrictReservedStack && !privileged) break; // honor privileges | |
| return _jdk_internal_vm_annotation_ReservedStackAccess; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(jdk_internal_ValueBased_signature): { | |
| if (_location != _in_class) break; // only allow for classes | |
| if (!privileged) break; // only allow in privileged code | |
| return _jdk_internal_ValueBased; | |
| } | |
| case VM_SYMBOL_ENUM_NAME(java_lang_Deprecated): { | |
| return _java_lang_Deprecated; | |
| } | |
| default: { | |
| break; | |
| } | |
| } | |
| return AnnotationCollector::_unknown; | |
| } | |
| void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) { | |
| if (is_contended()) | |
| // Setting the contended group also sets the contended bit in field flags | |
| f->set_contended_group(contended_group()); | |
| if (is_stable()) | |
| (f->field_flags_addr())->update_stable(true); | |
| } | |
| ClassFileParser::FieldAnnotationCollector::~FieldAnnotationCollector() { | |
| // If there's an error deallocate metadata for field annotations | |
| MetadataFactory::free_array<u1>(_loader_data, _field_annotations); | |
| MetadataFactory::free_array<u1>(_loader_data, _field_type_annotations); | |
| } | |
| void MethodAnnotationCollector::apply_to(const methodHandle& m) { | |
| if (has_annotation(_method_CallerSensitive)) | |
| m->set_caller_sensitive(); | |
| if (has_annotation(_method_ForceInline)) | |
| m->set_force_inline(); | |
| if (has_annotation(_method_DontInline)) | |
| m->set_dont_inline(); | |
| if (has_annotation(_method_ChangesCurrentThread)) | |
| m->set_changes_current_thread(); | |
| if (has_annotation(_method_JvmtiHideEvents)) | |
| m->set_jvmti_hide_events(); | |
| if (has_annotation(_method_JvmtiMountTransition)) | |
| m->set_jvmti_mount_transition(); | |
| if (has_annotation(_method_InjectedProfile)) | |
| m->set_has_injected_profile(); | |
| if (has_annotation(_method_LambdaForm_Compiled) && m->intrinsic_id() == vmIntrinsics::_none) | |
| m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm); | |
| if (has_annotation(_method_Hidden)) | |
| m->set_is_hidden(); | |
| if (has_annotation(_method_Scoped)) | |
| m->set_scoped(); | |
| if (has_annotation(_method_IntrinsicCandidate) && !m->is_synthetic()) | |
| m->set_intrinsic_candidate(); | |
| if (has_annotation(_jdk_internal_vm_annotation_ReservedStackAccess)) | |
| m->set_has_reserved_stack_access(); | |
| if (has_annotation(_java_lang_Deprecated)) | |
| m->set_deprecated(); | |
| if (has_annotation(_java_lang_Deprecated_for_removal)) | |
| m->set_deprecated_for_removal(); | |
| } | |
| void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { | |
| assert(ik != nullptr, "invariant"); | |
| if (has_annotation(_jdk_internal_vm_annotation_Contended)) { | |
| ik->set_is_contended(is_contended()); | |
| } | |
| if (has_annotation(_jdk_internal_ValueBased)) { | |
| ik->set_has_value_based_class_annotation(); | |
| if (DiagnoseSyncOnValueBasedClasses) { | |
| ik->set_is_value_based(); | |
| } | |
| } | |
| if (has_annotation(_java_lang_Deprecated)) { | |
| Array<Method*>* methods = ik->methods(); | |
| int length = ik->methods()->length(); | |
| for (int i = 0; i < length; i++) { | |
| Method* m = methods->at(i); | |
| m->set_deprecated(); | |
| } | |
| } | |
| if (has_annotation(_java_lang_Deprecated_for_removal)) { | |
| Array<Method*>* methods = ik->methods(); | |
| int length = ik->methods()->length(); | |
| for (int i = 0; i < length; i++) { | |
| Method* m = methods->at(i); | |
| m->set_deprecated_for_removal(); | |
| } | |
| } | |
| } | |
| #define MAX_ARGS_SIZE 255 | |
| #define MAX_CODE_SIZE 65535 | |
| #define INITIAL_MAX_LVT_NUMBER 256 | |
| /* Copy class file LVT's/LVTT's into the HotSpot internal LVT. | |
| * | |
| * Rules for LVT's and LVTT's are: | |
| * - There can be any number of LVT's and LVTT's. | |
| * - If there are n LVT's, it is the same as if there was just | |
| * one LVT containing all the entries from the n LVT's. | |
| * - There may be no more than one LVT entry per local variable. | |
| * Two LVT entries are 'equal' if these fields are the same: | |
| * start_pc, length, name, slot | |
| * - There may be no more than one LVTT entry per each LVT entry. | |
| * Each LVTT entry has to match some LVT entry. | |
| * - HotSpot internal LVT keeps natural ordering of class file LVT entries. | |
| */ | |
| void ClassFileParser::copy_localvariable_table(const ConstMethod* cm, | |
| int lvt_cnt, | |
| u2* const localvariable_table_length, | |
| const unsafe_u2** const localvariable_table_start, | |
| int lvtt_cnt, | |
| u2* const localvariable_type_table_length, | |
| const unsafe_u2** const localvariable_type_table_start, | |
| TRAPS) { | |
| ResourceMark rm(THREAD); | |
| typedef ResourceHashtable<LocalVariableTableElement, LocalVariableTableElement*, | |
| 256, AnyObj::RESOURCE_AREA, mtInternal, | |
| &LVT_Hash::hash, &LVT_Hash::equals> LVT_HashTable; | |
| LVT_HashTable* const table = new LVT_HashTable(); | |
| // To fill LocalVariableTable in | |
| const Classfile_LVT_Element* cf_lvt; | |
| LocalVariableTableElement* lvt = cm->localvariable_table_start(); | |
| for (int tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { | |
| cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; | |
| for (int idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { | |
| copy_lvt_element(&cf_lvt[idx], lvt); | |
| // If no duplicates, add LVT elem in hashtable. | |
| if (table->put(*lvt, lvt) == false | |
| && _need_verify | |
| && _major_version >= JAVA_1_5_VERSION) { | |
| classfile_parse_error("Duplicated LocalVariableTable attribute " | |
| "entry for '%s' in class file %s", | |
| _cp->symbol_at(lvt->name_cp_index)->as_utf8(), | |
| THREAD); | |
| return; | |
| } | |
| } | |
| } | |
| // To merge LocalVariableTable and LocalVariableTypeTable | |
| const Classfile_LVT_Element* cf_lvtt; | |
| LocalVariableTableElement lvtt_elem; | |
| for (int tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { | |
| cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; | |
| for (int idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { | |
| copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); | |
| LocalVariableTableElement** entry = table->get(lvtt_elem); | |
| if (entry == nullptr) { | |
| if (_need_verify) { | |
| classfile_parse_error("LVTT entry for '%s' in class file %s " | |
| "does not match any LVT entry", | |
| _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), | |
| THREAD); | |
| return; | |
| } | |
| } else if ((*entry)->signature_cp_index != 0 && _need_verify) { | |
| classfile_parse_error("Duplicated LocalVariableTypeTable attribute " | |
| "entry for '%s' in class file %s", | |
| _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), | |
| THREAD); | |
| return; | |
| } else { | |
| // to add generic signatures into LocalVariableTable | |
| (*entry)->signature_cp_index = lvtt_elem.descriptor_cp_index; | |
| } | |
| } | |
| } | |
| } | |
| void ClassFileParser::copy_method_annotations(ConstMethod* cm, | |
| const u1* runtime_visible_annotations, | |
| int runtime_visible_annotations_length, | |
| const u1* runtime_visible_parameter_annotations, | |
| int runtime_visible_parameter_annotations_length, | |
| const u1* runtime_visible_type_annotations, | |
| int runtime_visible_type_annotations_length, | |
| const u1* annotation_default, | |
| int annotation_default_length, | |
| TRAPS) { | |
| AnnotationArray* a; | |
| if (runtime_visible_annotations_length > 0) { | |
| a = allocate_annotations(runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| CHECK); | |
| cm->set_method_annotations(a); | |
| } | |
| if (runtime_visible_parameter_annotations_length > 0) { | |
| a = allocate_annotations(runtime_visible_parameter_annotations, | |
| runtime_visible_parameter_annotations_length, | |
| CHECK); | |
| cm->set_parameter_annotations(a); | |
| } | |
| if (annotation_default_length > 0) { | |
| a = allocate_annotations(annotation_default, | |
| annotation_default_length, | |
| CHECK); | |
| cm->set_default_annotations(a); | |
| } | |
| if (runtime_visible_type_annotations_length > 0) { | |
| a = allocate_annotations(runtime_visible_type_annotations, | |
| runtime_visible_type_annotations_length, | |
| CHECK); | |
| cm->set_type_annotations(a); | |
| } | |
| } | |
| // Note: the parse_method below is big and clunky because all parsing of the code and exceptions | |
| // attribute is inlined. This is cumbersome to avoid since we inline most of the parts in the | |
| // Method* to save footprint, so we only know the size of the resulting Method* when the | |
| // entire method attribute is parsed. | |
| // | |
| // The has_localvariable_table parameter is used to pass up the value to InstanceKlass. | |
| Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, | |
| bool is_interface, | |
| const ConstantPool* cp, | |
| bool* const has_localvariable_table, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| assert(has_localvariable_table != nullptr, "invariant"); | |
| ResourceMark rm(THREAD); | |
| // Parse fixed parts: | |
| // access_flags, name_index, descriptor_index, attributes_count | |
| cfs->guarantee_more(8, CHECK_NULL); | |
| u2 flags = cfs->get_u2_fast(); | |
| const u2 name_index = cfs->get_u2_fast(); | |
| const int cp_size = cp->length(); | |
| guarantee_property( | |
| valid_symbol_at(name_index), | |
| "Illegal constant pool index %u for method name in class file %s", | |
| name_index, CHECK_NULL); | |
| const Symbol* const name = cp->symbol_at(name_index); | |
| verify_legal_method_name(name, CHECK_NULL); | |
| const u2 signature_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_symbol_at(signature_index), | |
| "Illegal constant pool index %u for method signature in class file %s", | |
| signature_index, CHECK_NULL); | |
| const Symbol* const signature = cp->symbol_at(signature_index); | |
| if (name == vmSymbols::class_initializer_name()) { | |
| // We ignore the other access flags for a valid class initializer. | |
| // (JVM Spec 2nd ed., chapter 4.6) | |
| if (_major_version < 51) { // backward compatibility | |
| flags = JVM_ACC_STATIC; | |
| } else if ((flags & JVM_ACC_STATIC) == JVM_ACC_STATIC) { | |
| flags &= JVM_ACC_STATIC | (_major_version <= JAVA_16_VERSION ? JVM_ACC_STRICT : 0); | |
| } else { | |
| classfile_parse_error("Method <clinit> is not static in class file %s", THREAD); | |
| return nullptr; | |
| } | |
| } else { | |
| verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL); | |
| } | |
| if (name == vmSymbols::object_initializer_name() && is_interface) { | |
| classfile_parse_error("Interface cannot have a method named <init>, class file %s", THREAD); | |
| return nullptr; | |
| } | |
| int args_size = -1; // only used when _need_verify is true | |
| if (_need_verify) { | |
| verify_legal_name_with_signature(name, signature, CHECK_NULL); | |
| args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) + | |
| verify_legal_method_signature(name, signature, CHECK_NULL); | |
| if (args_size > MAX_ARGS_SIZE) { | |
| classfile_parse_error("Too many arguments in method signature in class file %s", THREAD); | |
| return nullptr; | |
| } | |
| } | |
| AccessFlags access_flags(flags & JVM_RECOGNIZED_METHOD_MODIFIERS); | |
| // Default values for code and exceptions attribute elements | |
| u2 max_stack = 0; | |
| u2 max_locals = 0; | |
| u4 code_length = 0; | |
| const u1* code_start = nullptr; | |
| u2 exception_table_length = 0; | |
| const unsafe_u2* exception_table_start = nullptr; // (potentially unaligned) pointer to array of u2 elements | |
| Array<int>* exception_handlers = Universe::the_empty_int_array(); | |
| u2 checked_exceptions_length = 0; | |
| const unsafe_u2* checked_exceptions_start = nullptr; // (potentially unaligned) pointer to array of u2 elements | |
| CompressedLineNumberWriteStream* linenumber_table = nullptr; | |
| int linenumber_table_length = 0; | |
| int total_lvt_length = 0; | |
| u2 lvt_cnt = 0; | |
| u2 lvtt_cnt = 0; | |
| bool lvt_allocated = false; | |
| u2 max_lvt_cnt = INITIAL_MAX_LVT_NUMBER; | |
| u2 max_lvtt_cnt = INITIAL_MAX_LVT_NUMBER; | |
| u2* localvariable_table_length = nullptr; | |
| const unsafe_u2** localvariable_table_start = nullptr; // (potentially unaligned) pointer to array of LVT attributes | |
| u2* localvariable_type_table_length = nullptr; | |
| const unsafe_u2** localvariable_type_table_start = nullptr; // (potentially unaligned) pointer to LVTT attributes | |
| int method_parameters_length = -1; | |
| const u1* method_parameters_data = nullptr; | |
| bool method_parameters_seen = false; | |
| bool parsed_code_attribute = false; | |
| bool parsed_checked_exceptions_attribute = false; | |
| bool parsed_stackmap_attribute = false; | |
| // stackmap attribute - JDK1.5 | |
| const u1* stackmap_data = nullptr; | |
| int stackmap_data_length = 0; | |
| u2 generic_signature_index = 0; | |
| MethodAnnotationCollector parsed_annotations; | |
| const u1* runtime_visible_annotations = nullptr; | |
| int runtime_visible_annotations_length = 0; | |
| const u1* runtime_visible_parameter_annotations = nullptr; | |
| int runtime_visible_parameter_annotations_length = 0; | |
| const u1* runtime_visible_type_annotations = nullptr; | |
| int runtime_visible_type_annotations_length = 0; | |
| bool runtime_invisible_annotations_exists = false; | |
| bool runtime_invisible_type_annotations_exists = false; | |
| bool runtime_invisible_parameter_annotations_exists = false; | |
| const u1* annotation_default = nullptr; | |
| int annotation_default_length = 0; | |
| // Parse code and exceptions attribute | |
| u2 method_attributes_count = cfs->get_u2_fast(); | |
| while (method_attributes_count--) { | |
| cfs->guarantee_more(6, CHECK_NULL); // method_attribute_name_index, method_attribute_length | |
| const u2 method_attribute_name_index = cfs->get_u2_fast(); | |
| const u4 method_attribute_length = cfs->get_u4_fast(); | |
| guarantee_property( | |
| valid_symbol_at(method_attribute_name_index), | |
| "Invalid method attribute name index %u in class file %s", | |
| method_attribute_name_index, CHECK_NULL); | |
| const Symbol* const method_attribute_name = cp->symbol_at(method_attribute_name_index); | |
| if (method_attribute_name == vmSymbols::tag_code()) { | |
| // Parse Code attribute | |
| if (_need_verify) { | |
| guarantee_property( | |
| !access_flags.is_native() && !access_flags.is_abstract(), | |
| "Code attribute in native or abstract methods in class file %s", | |
| CHECK_NULL); | |
| } | |
| if (parsed_code_attribute) { | |
| classfile_parse_error("Multiple Code attributes in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| parsed_code_attribute = true; | |
| // Stack size, locals size, and code size | |
| cfs->guarantee_more(8, CHECK_NULL); | |
| max_stack = cfs->get_u2_fast(); | |
| max_locals = cfs->get_u2_fast(); | |
| code_length = cfs->get_u4_fast(); | |
| if (_need_verify) { | |
| guarantee_property(args_size <= max_locals, | |
| "Arguments can't fit into locals in class file %s", | |
| CHECK_NULL); | |
| guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE, | |
| "Invalid method Code length %u in class file %s", | |
| code_length, CHECK_NULL); | |
| } | |
| // Code pointer | |
| code_start = cfs->current(); | |
| assert(code_start != nullptr, "null code start"); | |
| cfs->guarantee_more(code_length, CHECK_NULL); | |
| cfs->skip_u1_fast(code_length); | |
| // Exception handler table | |
| cfs->guarantee_more(2, CHECK_NULL); // exception_table_length | |
| exception_table_length = cfs->get_u2_fast(); | |
| if (exception_table_length > 0) { | |
| exception_table_start = parse_exception_table(cfs, | |
| code_length, | |
| exception_table_length, | |
| CHECK_NULL); | |
| } | |
| // Parse additional attributes in code attribute | |
| cfs->guarantee_more(2, CHECK_NULL); // code_attributes_count | |
| u2 code_attributes_count = cfs->get_u2_fast(); | |
| unsigned int calculated_attribute_length = 0; | |
| calculated_attribute_length = | |
| sizeof(max_stack) + sizeof(max_locals) + sizeof(code_length); | |
| calculated_attribute_length += checked_cast<unsigned int>( | |
| code_length + | |
| sizeof(exception_table_length) + | |
| sizeof(code_attributes_count) + | |
| exception_table_length * | |
| ( sizeof(u2) + // start_pc | |
| sizeof(u2) + // end_pc | |
| sizeof(u2) + // handler_pc | |
| sizeof(u2) )); // catch_type_index | |
| while (code_attributes_count--) { | |
| cfs->guarantee_more(6, CHECK_NULL); // code_attribute_name_index, code_attribute_length | |
| const u2 code_attribute_name_index = cfs->get_u2_fast(); | |
| const u4 code_attribute_length = cfs->get_u4_fast(); | |
| calculated_attribute_length += code_attribute_length + | |
| (unsigned)sizeof(code_attribute_name_index) + | |
| (unsigned)sizeof(code_attribute_length); | |
| guarantee_property(valid_symbol_at(code_attribute_name_index), | |
| "Invalid code attribute name index %u in class file %s", | |
| code_attribute_name_index, | |
| CHECK_NULL); | |
| if (LoadLineNumberTables && | |
| cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) { | |
| // Parse and compress line number table | |
| parse_linenumber_table(code_attribute_length, | |
| code_length, | |
| &linenumber_table, | |
| CHECK_NULL); | |
| } else if (LoadLocalVariableTables && | |
| cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { | |
| // Parse local variable table | |
| if (!lvt_allocated) { | |
| localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
| lvt_allocated = true; | |
| } | |
| if (lvt_cnt == max_lvt_cnt) { | |
| max_lvt_cnt <<= 1; | |
| localvariable_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt); | |
| localvariable_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); | |
| } | |
| localvariable_table_start[lvt_cnt] = | |
| parse_localvariable_table(cfs, | |
| code_length, | |
| max_locals, | |
| code_attribute_length, | |
| &localvariable_table_length[lvt_cnt], | |
| false, // is not LVTT | |
| CHECK_NULL); | |
| total_lvt_length += localvariable_table_length[lvt_cnt]; | |
| lvt_cnt++; | |
| } else if (LoadLocalVariableTypeTables && | |
| _major_version >= JAVA_1_5_VERSION && | |
| cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_type_table()) { | |
| if (!lvt_allocated) { | |
| localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
| localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
| THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
| lvt_allocated = true; | |
| } | |
| // Parse local variable type table | |
| if (lvtt_cnt == max_lvtt_cnt) { | |
| max_lvtt_cnt <<= 1; | |
| localvariable_type_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); | |
| localvariable_type_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); | |
| } | |
| localvariable_type_table_start[lvtt_cnt] = | |
| parse_localvariable_table(cfs, | |
| code_length, | |
| max_locals, | |
| code_attribute_length, | |
| &localvariable_type_table_length[lvtt_cnt], | |
| true, // is LVTT | |
| CHECK_NULL); | |
| lvtt_cnt++; | |
| } else if (_major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION && | |
| cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { | |
| // Stack map is only needed by the new verifier in JDK1.5. | |
| if (parsed_stackmap_attribute) { | |
| classfile_parse_error("Multiple StackMapTable attributes in class file %s", THREAD); | |
| return nullptr; | |
| } | |
| stackmap_data = parse_stackmap_table(cfs, code_attribute_length, CHECK_NULL); | |
| stackmap_data_length = code_attribute_length; | |
| parsed_stackmap_attribute = true; | |
| } else { | |
| // Skip unknown attributes | |
| cfs->skip_u1(code_attribute_length, CHECK_NULL); | |
| } | |
| } | |
| // check method attribute length | |
| if (_need_verify) { | |
| guarantee_property(method_attribute_length == calculated_attribute_length, | |
| "Code segment has wrong length in class file %s", | |
| CHECK_NULL); | |
| } | |
| } else if (method_attribute_name == vmSymbols::tag_exceptions()) { | |
| // Parse Exceptions attribute | |
| if (parsed_checked_exceptions_attribute) { | |
| classfile_parse_error("Multiple Exceptions attributes in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| parsed_checked_exceptions_attribute = true; | |
| checked_exceptions_start = | |
| parse_checked_exceptions(cfs, | |
| &checked_exceptions_length, | |
| method_attribute_length, | |
| CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_method_parameters()) { | |
| // reject multiple method parameters | |
| if (method_parameters_seen) { | |
| classfile_parse_error("Multiple MethodParameters attributes in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| method_parameters_seen = true; | |
| method_parameters_length = cfs->get_u1_fast(); | |
| const u4 real_length = (method_parameters_length * 4u) + 1u; | |
| if (method_attribute_length != real_length) { | |
| classfile_parse_error( | |
| "Invalid MethodParameters method attribute length %u in class file", | |
| method_attribute_length, THREAD); | |
| return nullptr; | |
| } | |
| method_parameters_data = cfs->current(); | |
| cfs->skip_u2_fast(method_parameters_length); | |
| cfs->skip_u2_fast(method_parameters_length); | |
| // ignore this attribute if it cannot be reflected | |
| if (!vmClasses::Parameter_klass_loaded()) | |
| method_parameters_length = -1; | |
| } else if (method_attribute_name == vmSymbols::tag_synthetic()) { | |
| if (method_attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Synthetic method attribute length %u in class file %s", | |
| method_attribute_length, THREAD); | |
| return nullptr; | |
| } | |
| // Should we check that there hasn't already been a synthetic attribute? | |
| access_flags.set_is_synthetic(); | |
| } else if (method_attribute_name == vmSymbols::tag_deprecated()) { // 4276120 | |
| if (method_attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Deprecated method attribute length %u in class file %s", | |
| method_attribute_length, THREAD); | |
| return nullptr; | |
| } | |
| } else if (_major_version >= JAVA_1_5_VERSION) { | |
| if (method_attribute_name == vmSymbols::tag_signature()) { | |
| if (generic_signature_index != 0) { | |
| classfile_parse_error( | |
| "Multiple Signature attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| if (method_attribute_length != 2) { | |
| classfile_parse_error( | |
| "Invalid Signature attribute length %u in class file %s", | |
| method_attribute_length, THREAD); | |
| return nullptr; | |
| } | |
| generic_signature_index = parse_generic_signature_attribute(cfs, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { | |
| if (runtime_visible_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_visible_annotations_length = method_attribute_length; | |
| runtime_visible_annotations = cfs->current(); | |
| assert(runtime_visible_annotations != nullptr, "null visible annotations"); | |
| cfs->guarantee_more(runtime_visible_annotations_length, CHECK_NULL); | |
| parse_annotations(cp, | |
| runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| &parsed_annotations, | |
| _loader_data, | |
| _can_access_vm_annotations); | |
| cfs->skip_u1_fast(runtime_visible_annotations_length); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { | |
| if (runtime_invisible_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_invisible_annotations_exists = true; | |
| cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { | |
| if (runtime_visible_parameter_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleParameterAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_visible_parameter_annotations_length = method_attribute_length; | |
| runtime_visible_parameter_annotations = cfs->current(); | |
| assert(runtime_visible_parameter_annotations != nullptr, "null visible parameter annotations"); | |
| cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) { | |
| if (runtime_invisible_parameter_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleParameterAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_invisible_parameter_annotations_exists = true; | |
| cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { | |
| if (annotation_default != nullptr) { | |
| classfile_parse_error( | |
| "Multiple AnnotationDefault attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| annotation_default_length = method_attribute_length; | |
| annotation_default = cfs->current(); | |
| assert(annotation_default != nullptr, "null annotation default"); | |
| cfs->skip_u1(annotation_default_length, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { | |
| if (runtime_visible_type_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleTypeAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_visible_type_annotations_length = method_attribute_length; | |
| runtime_visible_type_annotations = cfs->current(); | |
| assert(runtime_visible_type_annotations != nullptr, "null visible type annotations"); | |
| // No need for the VM to parse Type annotations | |
| cfs->skip_u1(runtime_visible_type_annotations_length, CHECK_NULL); | |
| } else if (method_attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { | |
| if (runtime_invisible_type_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| runtime_invisible_type_annotations_exists = true; | |
| cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
| } else { | |
| // Skip unknown attributes | |
| cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
| } | |
| } else { | |
| // Skip unknown attributes | |
| cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
| } | |
| } | |
| if (linenumber_table != nullptr) { | |
| linenumber_table->write_terminator(); | |
| linenumber_table_length = linenumber_table->position(); | |
| } | |
| // Make sure there's at least one Code attribute in non-native/non-abstract method | |
| if (_need_verify) { | |
| guarantee_property(access_flags.is_native() || | |
| access_flags.is_abstract() || | |
| parsed_code_attribute, | |
| "Absent Code attribute in method that is not native or abstract in class file %s", | |
| CHECK_NULL); | |
| } | |
| // All sizing information for a Method* is finally available, now create it | |
| InlineTableSizes sizes( | |
| total_lvt_length, | |
| linenumber_table_length, | |
| exception_table_length, | |
| checked_exceptions_length, | |
| method_parameters_length, | |
| generic_signature_index, | |
| runtime_visible_annotations_length, | |
| runtime_visible_parameter_annotations_length, | |
| runtime_visible_type_annotations_length, | |
| annotation_default_length, | |
| 0); | |
| Method* const m = Method::allocate(_loader_data, | |
| code_length, | |
| access_flags, | |
| &sizes, | |
| ConstMethod::NORMAL, | |
| _cp->symbol_at(name_index), | |
| CHECK_NULL); | |
| ClassLoadingService::add_class_method_size(m->size()*wordSize); | |
| // Fill in information from fixed part (access_flags already set) | |
| m->set_constants(_cp); | |
| m->set_name_index(name_index); | |
| m->set_signature_index(signature_index); | |
| m->constMethod()->compute_from_signature(cp->symbol_at(signature_index), access_flags.is_static()); | |
| assert(args_size < 0 || args_size == m->size_of_parameters(), ""); | |
| // Fill in code attribute information | |
| m->set_max_stack(max_stack); | |
| m->set_max_locals(max_locals); | |
| if (stackmap_data != nullptr) { | |
| m->constMethod()->copy_stackmap_data(_loader_data, | |
| (u1*)stackmap_data, | |
| stackmap_data_length, | |
| CHECK_NULL); | |
| } | |
| // Copy byte codes | |
| m->set_code((u1*)code_start); | |
| // Copy line number table | |
| if (linenumber_table != nullptr) { | |
| memcpy(m->compressed_linenumber_table(), | |
| linenumber_table->buffer(), | |
| linenumber_table_length); | |
| } | |
| // Copy exception table | |
| if (exception_table_length > 0) { | |
| Copy::conjoint_swap_if_needed<Endian::JAVA>(exception_table_start, | |
| m->exception_table_start(), | |
| exception_table_length * sizeof(ExceptionTableElement), | |
| sizeof(u2)); | |
| } | |
| // Copy method parameters | |
| if (method_parameters_length > 0) { | |
| MethodParametersElement* elem = m->constMethod()->method_parameters_start(); | |
| for (int i = 0; i < method_parameters_length; i++) { | |
| elem[i].name_cp_index = Bytes::get_Java_u2((address)method_parameters_data); | |
| method_parameters_data += 2; | |
| elem[i].flags = Bytes::get_Java_u2((address)method_parameters_data); | |
| method_parameters_data += 2; | |
| } | |
| } | |
| // Copy checked exceptions | |
| if (checked_exceptions_length > 0) { | |
| Copy::conjoint_swap_if_needed<Endian::JAVA>(checked_exceptions_start, | |
| m->checked_exceptions_start(), | |
| checked_exceptions_length * sizeof(CheckedExceptionElement), | |
| sizeof(u2)); | |
| } | |
| // Copy class file LVT's/LVTT's into the HotSpot internal LVT. | |
| if (total_lvt_length > 0) { | |
| *has_localvariable_table = true; | |
| copy_localvariable_table(m->constMethod(), | |
| lvt_cnt, | |
| localvariable_table_length, | |
| localvariable_table_start, | |
| lvtt_cnt, | |
| localvariable_type_table_length, | |
| localvariable_type_table_start, | |
| CHECK_NULL); | |
| } | |
| if (parsed_annotations.has_any_annotations()) | |
| parsed_annotations.apply_to(methodHandle(THREAD, m)); | |
| if (is_hidden()) { // Mark methods in hidden classes as 'hidden'. | |
| m->set_is_hidden(); | |
| } | |
| // Copy annotations | |
| copy_method_annotations(m->constMethod(), | |
| runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| runtime_visible_parameter_annotations, | |
| runtime_visible_parameter_annotations_length, | |
| runtime_visible_type_annotations, | |
| runtime_visible_type_annotations_length, | |
| annotation_default, | |
| annotation_default_length, | |
| CHECK_NULL); | |
| if (InstanceKlass::is_finalization_enabled() && | |
| name == vmSymbols::finalize_method_name() && | |
| signature == vmSymbols::void_method_signature()) { | |
| if (m->is_empty_method()) { | |
| _has_empty_finalizer = true; | |
| } else { | |
| _has_finalizer = true; | |
| } | |
| } | |
| NOT_PRODUCT(m->verify()); | |
| return m; | |
| } | |
| // Side-effects: populates the _methods field in the parser | |
| void ClassFileParser::parse_methods(const ClassFileStream* const cfs, | |
| bool is_interface, | |
| bool* const has_localvariable_table, | |
| bool* has_final_method, | |
| bool* declares_nonstatic_concrete_methods, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(has_localvariable_table != nullptr, "invariant"); | |
| assert(has_final_method != nullptr, "invariant"); | |
| assert(declares_nonstatic_concrete_methods != nullptr, "invariant"); | |
| assert(nullptr == _methods, "invariant"); | |
| cfs->guarantee_more(2, CHECK); // length | |
| const u2 length = cfs->get_u2_fast(); | |
| if (length == 0) { | |
| _methods = Universe::the_empty_method_array(); | |
| } else { | |
| _methods = MetadataFactory::new_array<Method*>(_loader_data, | |
| length, | |
| nullptr, | |
| CHECK); | |
| for (int index = 0; index < length; index++) { | |
| Method* method = parse_method(cfs, | |
| is_interface, | |
| _cp, | |
| has_localvariable_table, | |
| CHECK); | |
| if (method->is_final()) { | |
| *has_final_method = true; | |
| } | |
| // declares_nonstatic_concrete_methods: declares concrete instance methods, any access flags | |
| // used for interface initialization, and default method inheritance analysis | |
| if (is_interface && !(*declares_nonstatic_concrete_methods) | |
| && !method->is_abstract() && !method->is_static()) { | |
| *declares_nonstatic_concrete_methods = true; | |
| } | |
| _methods->at_put(index, method); | |
| } | |
| if (_need_verify && length > 1) { | |
| // Check duplicated methods | |
| ResourceMark rm(THREAD); | |
| // Set containing name-signature pairs | |
| NameSigHashtable* names_and_sigs = new NameSigHashtable(); | |
| for (int i = 0; i < length; i++) { | |
| const Method* const m = _methods->at(i); | |
| NameSigHash name_and_sig(m->name(), m->signature()); | |
| // If no duplicates, add name/signature in hashtable names_and_sigs. | |
| if(!names_and_sigs->put(name_and_sig, 0)) { | |
| classfile_parse_error("Duplicate method name \"%s\" with signature \"%s\" in class file %s", | |
| name_and_sig._name->as_C_string(), name_and_sig._sig->as_klass_external_name(), THREAD); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| static const intArray* sort_methods(Array<Method*>* methods) { | |
| const int length = methods->length(); | |
| // If JVMTI original method ordering or sharing is enabled we have to | |
| // remember the original class file ordering. | |
| // We temporarily use the vtable_index field in the Method* to store the | |
| // class file index, so we can read in after calling qsort. | |
| // Put the method ordering in the shared archive. | |
| if (JvmtiExport::can_maintain_original_method_order() || CDSConfig::is_dumping_archive()) { | |
| for (int index = 0; index < length; index++) { | |
| Method* const m = methods->at(index); | |
| assert(!m->valid_vtable_index(), "vtable index should not be set"); | |
| m->set_vtable_index(index); | |
| } | |
| } | |
| // Sort method array by ascending method name (for faster lookups & vtable construction) | |
| // Note that the ordering is not alphabetical, see Symbol::fast_compare | |
| Method::sort_methods(methods); | |
| intArray* method_ordering = nullptr; | |
| // If JVMTI original method ordering or sharing is enabled construct int | |
| // array remembering the original ordering | |
| if (JvmtiExport::can_maintain_original_method_order() || CDSConfig::is_dumping_archive()) { | |
| method_ordering = new intArray(length, length, -1); | |
| for (int index = 0; index < length; index++) { | |
| Method* const m = methods->at(index); | |
| const int old_index = m->vtable_index(); | |
| assert(old_index >= 0 && old_index < length, "invalid method index"); | |
| method_ordering->at_put(index, old_index); | |
| m->set_vtable_index(Method::invalid_vtable_index); | |
| } | |
| } | |
| return method_ordering; | |
| } | |
| // Parse generic_signature attribute for methods and fields | |
| u2 ClassFileParser::parse_generic_signature_attribute(const ClassFileStream* const cfs, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| cfs->guarantee_more(2, CHECK_0); // generic_signature_index | |
| const u2 generic_signature_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_symbol_at(generic_signature_index), | |
| "Invalid Signature attribute at constant pool index %u in class file %s", | |
| generic_signature_index, CHECK_0); | |
| return generic_signature_index; | |
| } | |
| void ClassFileParser::parse_classfile_sourcefile_attribute(const ClassFileStream* const cfs, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| cfs->guarantee_more(2, CHECK); // sourcefile_index | |
| const u2 sourcefile_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_symbol_at(sourcefile_index), | |
| "Invalid SourceFile attribute at constant pool index %u in class file %s", | |
| sourcefile_index, CHECK); | |
| set_class_sourcefile_index(sourcefile_index); | |
| } | |
| void ClassFileParser::parse_classfile_source_debug_extension_attribute(const ClassFileStream* const cfs, | |
| int length, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| const u1* const sde_buffer = cfs->current(); | |
| assert(sde_buffer != nullptr, "null sde buffer"); | |
| // Don't bother storing it if there is no way to retrieve it | |
| if (JvmtiExport::can_get_source_debug_extension()) { | |
| assert((length+1) > length, "Overflow checking"); | |
| u1* const sde = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, u1, length+1); | |
| for (int i = 0; i < length; i++) { | |
| sde[i] = sde_buffer[i]; | |
| } | |
| sde[length] = '\0'; | |
| set_class_sde_buffer((const char*)sde, length); | |
| } | |
| // Got utf8 string, set stream position forward | |
| cfs->skip_u1(length, CHECK); | |
| } | |
| // Inner classes can be static, private or protected (classic VM does this) | |
| #define RECOGNIZED_INNER_CLASS_MODIFIERS ( JVM_RECOGNIZED_CLASS_MODIFIERS | \ | |
| JVM_ACC_PRIVATE | \ | |
| JVM_ACC_PROTECTED | \ | |
| JVM_ACC_STATIC \ | |
| ) | |
| // Find index of the InnerClasses entry for the specified inner_class_info_index. | |
| // Return -1 if none is found. | |
| static int inner_classes_find_index(const Array<u2>* inner_classes, int inner, const ConstantPool* cp, int length) { | |
| Symbol* cp_klass_name = cp->klass_name_at(inner); | |
| for (int idx = 0; idx < length; idx += InstanceKlass::inner_class_next_offset) { | |
| int idx_inner = inner_classes->at(idx + InstanceKlass::inner_class_inner_class_info_offset); | |
| if (cp->klass_name_at(idx_inner) == cp_klass_name) { | |
| return idx; | |
| } | |
| } | |
| return -1; | |
| } | |
| // Return the outer_class_info_index for the InnerClasses entry containing the | |
| // specified inner_class_info_index. Return -1 if no InnerClasses entry is found. | |
| static int inner_classes_jump_to_outer(const Array<u2>* inner_classes, int inner, const ConstantPool* cp, int length) { | |
| if (inner == 0) return -1; | |
| int idx = inner_classes_find_index(inner_classes, inner, cp, length); | |
| if (idx == -1) return -1; | |
| int result = inner_classes->at(idx + InstanceKlass::inner_class_outer_class_info_offset); | |
| return result; | |
| } | |
| // Return true if circularity is found, false if no circularity is found. | |
| // Use Floyd's cycle finding algorithm. | |
| static bool inner_classes_check_loop_through_outer(const Array<u2>* inner_classes, int idx, const ConstantPool* cp, int length) { | |
| int slow = inner_classes->at(idx + InstanceKlass::inner_class_inner_class_info_offset); | |
| int fast = inner_classes->at(idx + InstanceKlass::inner_class_outer_class_info_offset); | |
| while (fast != -1 && fast != 0) { | |
| if (slow != 0 && (cp->klass_name_at(slow) == cp->klass_name_at(fast))) { | |
| return true; // found a circularity | |
| } | |
| fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); | |
| if (fast == -1) return false; | |
| fast = inner_classes_jump_to_outer(inner_classes, fast, cp, length); | |
| if (fast == -1) return false; | |
| slow = inner_classes_jump_to_outer(inner_classes, slow, cp, length); | |
| assert(slow != -1, "sanity check"); | |
| } | |
| return false; | |
| } | |
| // Loop through each InnerClasses entry checking for circularities and duplications | |
| // with other entries. If duplicate entries are found then throw CFE. Otherwise, | |
| // return true if a circularity or entries with duplicate inner_class_info_indexes | |
| // are found. | |
| bool ClassFileParser::check_inner_classes_circularity(const ConstantPool* cp, int length, TRAPS) { | |
| // Loop through each InnerClasses entry. | |
| for (int idx = 0; idx < length; idx += InstanceKlass::inner_class_next_offset) { | |
| // Return true if there are circular entries. | |
| if (inner_classes_check_loop_through_outer(_inner_classes, idx, cp, length)) { | |
| return true; | |
| } | |
| // Check if there are duplicate entries or entries with the same inner_class_info_index. | |
| for (int y = idx + InstanceKlass::inner_class_next_offset; y < length; | |
| y += InstanceKlass::inner_class_next_offset) { | |
| // 4347400: make sure there's no duplicate entry in the classes array | |
| if (_major_version >= JAVA_1_5_VERSION) { | |
| guarantee_property((_inner_classes->at(idx) != _inner_classes->at(y) || | |
| _inner_classes->at(idx+1) != _inner_classes->at(y+1) || | |
| _inner_classes->at(idx+2) != _inner_classes->at(y+2) || | |
| _inner_classes->at(idx+3) != _inner_classes->at(y+3)), | |
| "Duplicate entry in InnerClasses attribute in class file %s", | |
| CHECK_(true)); | |
| } | |
| // Return true if there are two entries with the same inner_class_info_index. | |
| if (_inner_classes->at(y) == _inner_classes->at(idx)) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| // Return number of classes in the inner classes attribute table | |
| u2 ClassFileParser::parse_classfile_inner_classes_attribute(const ClassFileStream* const cfs, | |
| const ConstantPool* cp, | |
| const u1* const inner_classes_attribute_start, | |
| bool parsed_enclosingmethod_attribute, | |
| u2 enclosing_method_class_index, | |
| u2 enclosing_method_method_index, | |
| TRAPS) { | |
| const u1* const current_mark = cfs->current(); | |
| u2 length = 0; | |
| if (inner_classes_attribute_start != nullptr) { | |
| cfs->set_current(inner_classes_attribute_start); | |
| cfs->guarantee_more(2, CHECK_0); // length | |
| length = cfs->get_u2_fast(); | |
| } | |
| // 4-tuples of shorts of inner classes data and 2 shorts of enclosing | |
| // method data: | |
| // [inner_class_info_index, | |
| // outer_class_info_index, | |
| // inner_name_index, | |
| // inner_class_access_flags, | |
| // ... | |
| // enclosing_method_class_index, | |
| // enclosing_method_method_index] | |
| const int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0); | |
| Array<u2>* inner_classes = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0); | |
| _inner_classes = inner_classes; | |
| int index = 0; | |
| cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2 | |
| for (int n = 0; n < length; n++) { | |
| // Inner class index | |
| const u2 inner_class_info_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_klass_reference_at(inner_class_info_index), | |
| "inner_class_info_index %u has bad constant type in class file %s", | |
| inner_class_info_index, CHECK_0); | |
| // Outer class index | |
| const u2 outer_class_info_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| outer_class_info_index == 0 || | |
| valid_klass_reference_at(outer_class_info_index), | |
| "outer_class_info_index %u has bad constant type in class file %s", | |
| outer_class_info_index, CHECK_0); | |
| if (outer_class_info_index != 0) { | |
| const Symbol* const outer_class_name = cp->klass_name_at(outer_class_info_index); | |
| char* bytes = (char*)outer_class_name->bytes(); | |
| guarantee_property(bytes[0] != JVM_SIGNATURE_ARRAY, | |
| "Outer class is an array class in class file %s", CHECK_0); | |
| } | |
| // Inner class name | |
| const u2 inner_name_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| inner_name_index == 0 || valid_symbol_at(inner_name_index), | |
| "inner_name_index %u has bad constant type in class file %s", | |
| inner_name_index, CHECK_0); | |
| if (_need_verify) { | |
| guarantee_property(inner_class_info_index != outer_class_info_index, | |
| "Class is both outer and inner class in class file %s", CHECK_0); | |
| } | |
| // Access flags | |
| u2 flags; | |
| // JVM_ACC_MODULE is defined in JDK-9 and later. | |
| if (_major_version >= JAVA_9_VERSION) { | |
| flags = cfs->get_u2_fast() & (RECOGNIZED_INNER_CLASS_MODIFIERS | JVM_ACC_MODULE); | |
| } else { | |
| flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_MODIFIERS; | |
| } | |
| if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) { | |
| // Set abstract bit for old class files for backward compatibility | |
| flags |= JVM_ACC_ABSTRACT; | |
| } | |
| verify_legal_class_modifiers(flags, CHECK_0); | |
| AccessFlags inner_access_flags(flags); | |
| inner_classes->at_put(index++, inner_class_info_index); | |
| inner_classes->at_put(index++, outer_class_info_index); | |
| inner_classes->at_put(index++, inner_name_index); | |
| inner_classes->at_put(index++, inner_access_flags.as_unsigned_short()); | |
| } | |
| // Check for circular and duplicate entries. | |
| bool has_circularity = false; | |
| if (_need_verify) { | |
| has_circularity = check_inner_classes_circularity(cp, length * 4, CHECK_0); | |
| if (has_circularity) { | |
| // If circularity check failed then ignore InnerClasses attribute. | |
| MetadataFactory::free_array<u2>(_loader_data, _inner_classes); | |
| index = 0; | |
| if (parsed_enclosingmethod_attribute) { | |
| inner_classes = MetadataFactory::new_array<u2>(_loader_data, 2, CHECK_0); | |
| _inner_classes = inner_classes; | |
| } else { | |
| _inner_classes = Universe::the_empty_short_array(); | |
| } | |
| } | |
| } | |
| // Set EnclosingMethod class and method indexes. | |
| if (parsed_enclosingmethod_attribute) { | |
| inner_classes->at_put(index++, enclosing_method_class_index); | |
| inner_classes->at_put(index++, enclosing_method_method_index); | |
| } | |
| assert(index == size || has_circularity, "wrong size"); | |
| // Restore buffer's current position. | |
| cfs->set_current(current_mark); | |
| return length; | |
| } | |
| u2 ClassFileParser::parse_classfile_nest_members_attribute(const ClassFileStream* const cfs, | |
| const u1* const nest_members_attribute_start, | |
| TRAPS) { | |
| const u1* const current_mark = cfs->current(); | |
| u2 length = 0; | |
| if (nest_members_attribute_start != nullptr) { | |
| cfs->set_current(nest_members_attribute_start); | |
| cfs->guarantee_more(2, CHECK_0); // length | |
| length = cfs->get_u2_fast(); | |
| } | |
| const int size = length; | |
| Array<u2>* const nest_members = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0); | |
| _nest_members = nest_members; | |
| int index = 0; | |
| cfs->guarantee_more(2 * length, CHECK_0); | |
| for (int n = 0; n < length; n++) { | |
| const u2 class_info_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_klass_reference_at(class_info_index), | |
| "Nest member class_info_index %u has bad constant type in class file %s", | |
| class_info_index, CHECK_0); | |
| nest_members->at_put(index++, class_info_index); | |
| } | |
| assert(index == size, "wrong size"); | |
| // Restore buffer's current position. | |
| cfs->set_current(current_mark); | |
| return length; | |
| } | |
| u2 ClassFileParser::parse_classfile_permitted_subclasses_attribute(const ClassFileStream* const cfs, | |
| const u1* const permitted_subclasses_attribute_start, | |
| TRAPS) { | |
| const u1* const current_mark = cfs->current(); | |
| u2 length = 0; | |
| if (permitted_subclasses_attribute_start != nullptr) { | |
| cfs->set_current(permitted_subclasses_attribute_start); | |
| cfs->guarantee_more(2, CHECK_0); // length | |
| length = cfs->get_u2_fast(); | |
| } | |
| const int size = length; | |
| Array<u2>* const permitted_subclasses = MetadataFactory::new_array<u2>(_loader_data, size, CHECK_0); | |
| _permitted_subclasses = permitted_subclasses; | |
| if (length > 0) { | |
| int index = 0; | |
| cfs->guarantee_more(2 * length, CHECK_0); | |
| for (int n = 0; n < length; n++) { | |
| const u2 class_info_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_klass_reference_at(class_info_index), | |
| "Permitted subclass class_info_index %u has bad constant type in class file %s", | |
| class_info_index, CHECK_0); | |
| permitted_subclasses->at_put(index++, class_info_index); | |
| } | |
| assert(index == size, "wrong size"); | |
| } | |
| // Restore buffer's current position. | |
| cfs->set_current(current_mark); | |
| return length; | |
| } | |
| // Record { | |
| // u2 attribute_name_index; | |
| // u4 attribute_length; | |
| // u2 components_count; | |
| // component_info components[components_count]; | |
| // } | |
| // component_info { | |
| // u2 name_index; | |
| // u2 descriptor_index | |
| // u2 attributes_count; | |
| // attribute_info_attributes[attributes_count]; | |
| // } | |
| u4 ClassFileParser::parse_classfile_record_attribute(const ClassFileStream* const cfs, | |
| const ConstantPool* cp, | |
| const u1* const record_attribute_start, | |
| TRAPS) { | |
| const u1* const current_mark = cfs->current(); | |
| int components_count = 0; | |
| unsigned int calculate_attr_size = 0; | |
| if (record_attribute_start != nullptr) { | |
| cfs->set_current(record_attribute_start); | |
| cfs->guarantee_more(2, CHECK_0); // num of components | |
| components_count = (int)cfs->get_u2_fast(); | |
| calculate_attr_size = 2; | |
| } | |
| Array<RecordComponent*>* const record_components = | |
| MetadataFactory::new_array<RecordComponent*>(_loader_data, components_count, nullptr, CHECK_0); | |
| _record_components = record_components; | |
| for (int x = 0; x < components_count; x++) { | |
| cfs->guarantee_more(6, CHECK_0); // name_index, descriptor_index, attributes_count | |
| const u2 name_index = cfs->get_u2_fast(); | |
| guarantee_property(valid_symbol_at(name_index), | |
| "Invalid constant pool index %u for name in Record attribute in class file %s", | |
| name_index, CHECK_0); | |
| const Symbol* const name = cp->symbol_at(name_index); | |
| verify_legal_field_name(name, CHECK_0); | |
| const u2 descriptor_index = cfs->get_u2_fast(); | |
| guarantee_property(valid_symbol_at(descriptor_index), | |
| "Invalid constant pool index %u for descriptor in Record attribute in class file %s", | |
| descriptor_index, CHECK_0); | |
| const Symbol* const descr = cp->symbol_at(descriptor_index); | |
| verify_legal_field_signature(name, descr, CHECK_0); | |
| const u2 attributes_count = cfs->get_u2_fast(); | |
| calculate_attr_size += 6; | |
| u2 generic_sig_index = 0; | |
| const u1* runtime_visible_annotations = nullptr; | |
| int runtime_visible_annotations_length = 0; | |
| bool runtime_invisible_annotations_exists = false; | |
| const u1* runtime_visible_type_annotations = nullptr; | |
| int runtime_visible_type_annotations_length = 0; | |
| bool runtime_invisible_type_annotations_exists = false; | |
| // Expected attributes for record components are Signature, Runtime(In)VisibleAnnotations, | |
| // and Runtime(In)VisibleTypeAnnotations. Other attributes are ignored. | |
| for (int y = 0; y < attributes_count; y++) { | |
| cfs->guarantee_more(6, CHECK_0); // attribute_name_index, attribute_length | |
| const u2 attribute_name_index = cfs->get_u2_fast(); | |
| const u4 attribute_length = cfs->get_u4_fast(); | |
| calculate_attr_size += 6; | |
| guarantee_property( | |
| valid_symbol_at(attribute_name_index), | |
| "Invalid Record attribute name index %u in class file %s", | |
| attribute_name_index, CHECK_0); | |
| const Symbol* const attribute_name = cp->symbol_at(attribute_name_index); | |
| if (attribute_name == vmSymbols::tag_signature()) { | |
| if (generic_sig_index != 0) { | |
| classfile_parse_error( | |
| "Multiple Signature attributes for Record component in class file %s", | |
| THREAD); | |
| return 0; | |
| } | |
| if (attribute_length != 2) { | |
| classfile_parse_error( | |
| "Invalid Signature attribute length %u in Record component in class file %s", | |
| attribute_length, THREAD); | |
| return 0; | |
| } | |
| generic_sig_index = parse_generic_signature_attribute(cfs, CHECK_0); | |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { | |
| if (runtime_visible_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleAnnotations attributes for Record component in class file %s", THREAD); | |
| return 0; | |
| } | |
| runtime_visible_annotations_length = attribute_length; | |
| runtime_visible_annotations = cfs->current(); | |
| assert(runtime_visible_annotations != nullptr, "null record component visible annotation"); | |
| cfs->guarantee_more(runtime_visible_annotations_length, CHECK_0); | |
| cfs->skip_u1_fast(runtime_visible_annotations_length); | |
| } else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { | |
| if (runtime_invisible_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleAnnotations attributes for Record component in class file %s", THREAD); | |
| return 0; | |
| } | |
| runtime_invisible_annotations_exists = true; | |
| cfs->skip_u1(attribute_length, CHECK_0); | |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { | |
| if (runtime_visible_type_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleTypeAnnotations attributes for Record component in class file %s", THREAD); | |
| return 0; | |
| } | |
| runtime_visible_type_annotations_length = attribute_length; | |
| runtime_visible_type_annotations = cfs->current(); | |
| assert(runtime_visible_type_annotations != nullptr, "null record component visible type annotation"); | |
| cfs->guarantee_more(runtime_visible_type_annotations_length, CHECK_0); | |
| cfs->skip_u1_fast(runtime_visible_type_annotations_length); | |
| } else if (attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { | |
| if (runtime_invisible_type_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleTypeAnnotations attributes for Record component in class file %s", THREAD); | |
| return 0; | |
| } | |
| runtime_invisible_type_annotations_exists = true; | |
| cfs->skip_u1(attribute_length, CHECK_0); | |
| } else { | |
| // Skip unknown attributes | |
| cfs->skip_u1(attribute_length, CHECK_0); | |
| } | |
| calculate_attr_size += attribute_length; | |
| } // End of attributes For loop | |
| AnnotationArray* annotations = allocate_annotations(runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| CHECK_0); | |
| AnnotationArray* type_annotations = allocate_annotations(runtime_visible_type_annotations, | |
| runtime_visible_type_annotations_length, | |
| CHECK_0); | |
| RecordComponent* record_component = | |
| RecordComponent::allocate(_loader_data, name_index, descriptor_index, generic_sig_index, | |
| annotations, type_annotations, CHECK_0); | |
| record_components->at_put(x, record_component); | |
| } // End of component processing loop | |
| // Restore buffer's current position. | |
| cfs->set_current(current_mark); | |
| return calculate_attr_size; | |
| } | |
| void ClassFileParser::parse_classfile_synthetic_attribute() { | |
| set_class_synthetic_flag(true); | |
| } | |
| void ClassFileParser::parse_classfile_signature_attribute(const ClassFileStream* const cfs, TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| const u2 signature_index = cfs->get_u2(CHECK); | |
| guarantee_property( | |
| valid_symbol_at(signature_index), | |
| "Invalid constant pool index %u in Signature attribute in class file %s", | |
| signature_index, CHECK); | |
| set_class_generic_signature_index(signature_index); | |
| } | |
| void ClassFileParser::parse_classfile_bootstrap_methods_attribute(const ClassFileStream* const cfs, | |
| ConstantPool* cp, | |
| u4 attribute_byte_length, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| const u1* const current_start = cfs->current(); | |
| guarantee_property(attribute_byte_length >= sizeof(u2), | |
| "Invalid BootstrapMethods attribute length %u in class file %s", | |
| attribute_byte_length, | |
| CHECK); | |
| cfs->guarantee_more(attribute_byte_length, CHECK); | |
| const int attribute_array_length = cfs->get_u2_fast(); | |
| guarantee_property(_max_bootstrap_specifier_index < attribute_array_length, | |
| "Short length on BootstrapMethods in class file %s", | |
| CHECK); | |
| // The attribute contains a counted array of counted tuples of shorts, | |
| // represending bootstrap specifiers: | |
| // length*{bootstrap_method_index, argument_count*{argument_index}} | |
| const unsigned int operand_count = (attribute_byte_length - (unsigned)sizeof(u2)) / (unsigned)sizeof(u2); | |
| // operand_count = number of shorts in attr, except for leading length | |
| // The attribute is copied into a short[] array. | |
| // The array begins with a series of short[2] pairs, one for each tuple. | |
| const int index_size = (attribute_array_length * 2); | |
| Array<u2>* const operands = | |
| MetadataFactory::new_array<u2>(_loader_data, index_size + operand_count, CHECK); | |
| // Eagerly assign operands so they will be deallocated with the constant | |
| // pool if there is an error. | |
| cp->set_operands(operands); | |
| int operand_fill_index = index_size; | |
| const int cp_size = cp->length(); | |
| for (int n = 0; n < attribute_array_length; n++) { | |
| // Store a 32-bit offset into the header of the operand array. | |
| ConstantPool::operand_offset_at_put(operands, n, operand_fill_index); | |
| // Read a bootstrap specifier. | |
| cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc | |
| const u2 bootstrap_method_index = cfs->get_u2_fast(); | |
| const u2 argument_count = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_cp_range(bootstrap_method_index, cp_size) && | |
| cp->tag_at(bootstrap_method_index).is_method_handle(), | |
| "bootstrap_method_index %u has bad constant type in class file %s", | |
| bootstrap_method_index, | |
| CHECK); | |
| guarantee_property((operand_fill_index + 1 + argument_count) < operands->length(), | |
| "Invalid BootstrapMethods num_bootstrap_methods or num_bootstrap_arguments value in class file %s", | |
| CHECK); | |
| operands->at_put(operand_fill_index++, bootstrap_method_index); | |
| operands->at_put(operand_fill_index++, argument_count); | |
| cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc] | |
| for (int j = 0; j < argument_count; j++) { | |
| const u2 argument_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_cp_range(argument_index, cp_size) && | |
| cp->tag_at(argument_index).is_loadable_constant(), | |
| "argument_index %u has bad constant type in class file %s", | |
| argument_index, | |
| CHECK); | |
| operands->at_put(operand_fill_index++, argument_index); | |
| } | |
| } | |
| guarantee_property(current_start + attribute_byte_length == cfs->current(), | |
| "Bad length on BootstrapMethods in class file %s", | |
| CHECK); | |
| } | |
| void ClassFileParser::parse_classfile_attributes(const ClassFileStream* const cfs, | |
| ConstantPool* cp, | |
| ClassFileParser::ClassAnnotationCollector* parsed_annotations, | |
| TRAPS) { | |
| assert(cfs != nullptr, "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| assert(parsed_annotations != nullptr, "invariant"); | |
| // Set inner classes attribute to default sentinel | |
| _inner_classes = Universe::the_empty_short_array(); | |
| // Set nest members attribute to default sentinel | |
| _nest_members = Universe::the_empty_short_array(); | |
| // Set _permitted_subclasses attribute to default sentinel | |
| _permitted_subclasses = Universe::the_empty_short_array(); | |
| cfs->guarantee_more(2, CHECK); // attributes_count | |
| u2 attributes_count = cfs->get_u2_fast(); | |
| bool parsed_sourcefile_attribute = false; | |
| bool parsed_innerclasses_attribute = false; | |
| bool parsed_nest_members_attribute = false; | |
| bool parsed_permitted_subclasses_attribute = false; | |
| bool parsed_nest_host_attribute = false; | |
| bool parsed_record_attribute = false; | |
| bool parsed_enclosingmethod_attribute = false; | |
| bool parsed_bootstrap_methods_attribute = false; | |
| const u1* runtime_visible_annotations = nullptr; | |
| int runtime_visible_annotations_length = 0; | |
| const u1* runtime_visible_type_annotations = nullptr; | |
| int runtime_visible_type_annotations_length = 0; | |
| bool runtime_invisible_type_annotations_exists = false; | |
| bool runtime_invisible_annotations_exists = false; | |
| bool parsed_source_debug_ext_annotations_exist = false; | |
| const u1* inner_classes_attribute_start = nullptr; | |
| u4 inner_classes_attribute_length = 0; | |
| u2 enclosing_method_class_index = 0; | |
| u2 enclosing_method_method_index = 0; | |
| const u1* nest_members_attribute_start = nullptr; | |
| u4 nest_members_attribute_length = 0; | |
| const u1* record_attribute_start = nullptr; | |
| u4 record_attribute_length = 0; | |
| const u1* permitted_subclasses_attribute_start = nullptr; | |
| u4 permitted_subclasses_attribute_length = 0; | |
| // Iterate over attributes | |
| while (attributes_count--) { | |
| cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length | |
| const u2 attribute_name_index = cfs->get_u2_fast(); | |
| const u4 attribute_length = cfs->get_u4_fast(); | |
| guarantee_property( | |
| valid_symbol_at(attribute_name_index), | |
| "Attribute name has bad constant pool index %u in class file %s", | |
| attribute_name_index, CHECK); | |
| const Symbol* const tag = cp->symbol_at(attribute_name_index); | |
| if (tag == vmSymbols::tag_source_file()) { | |
| // Check for SourceFile tag | |
| if (_need_verify) { | |
| guarantee_property(attribute_length == 2, "Wrong SourceFile attribute length in class file %s", CHECK); | |
| } | |
| if (parsed_sourcefile_attribute) { | |
| classfile_parse_error("Multiple SourceFile attributes in class file %s", THREAD); | |
| return; | |
| } else { | |
| parsed_sourcefile_attribute = true; | |
| } | |
| parse_classfile_sourcefile_attribute(cfs, CHECK); | |
| } else if (tag == vmSymbols::tag_source_debug_extension()) { | |
| // Check for SourceDebugExtension tag | |
| if (parsed_source_debug_ext_annotations_exist) { | |
| classfile_parse_error( | |
| "Multiple SourceDebugExtension attributes in class file %s", THREAD); | |
| return; | |
| } | |
| parsed_source_debug_ext_annotations_exist = true; | |
| parse_classfile_source_debug_extension_attribute(cfs, (int)attribute_length, CHECK); | |
| } else if (tag == vmSymbols::tag_inner_classes()) { | |
| // Check for InnerClasses tag | |
| if (parsed_innerclasses_attribute) { | |
| classfile_parse_error("Multiple InnerClasses attributes in class file %s", THREAD); | |
| return; | |
| } else { | |
| parsed_innerclasses_attribute = true; | |
| } | |
| inner_classes_attribute_start = cfs->current(); | |
| inner_classes_attribute_length = attribute_length; | |
| cfs->skip_u1(inner_classes_attribute_length, CHECK); | |
| } else if (tag == vmSymbols::tag_synthetic()) { | |
| // Check for Synthetic tag | |
| // Shouldn't we check that the synthetic flags wasn't already set? - not required in spec | |
| if (attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Synthetic classfile attribute length %u in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| parse_classfile_synthetic_attribute(); | |
| } else if (tag == vmSymbols::tag_deprecated()) { | |
| // Check for Deprecated tag - 4276120 | |
| if (attribute_length != 0) { | |
| classfile_parse_error( | |
| "Invalid Deprecated classfile attribute length %u in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| } else if (_major_version >= JAVA_1_5_VERSION) { | |
| if (tag == vmSymbols::tag_signature()) { | |
| if (_generic_signature_index != 0) { | |
| classfile_parse_error( | |
| "Multiple Signature attributes in class file %s", THREAD); | |
| return; | |
| } | |
| if (attribute_length != 2) { | |
| classfile_parse_error( | |
| "Wrong Signature attribute length %u in class file %s", | |
| attribute_length, THREAD); | |
| return; | |
| } | |
| parse_classfile_signature_attribute(cfs, CHECK); | |
| } else if (tag == vmSymbols::tag_runtime_visible_annotations()) { | |
| if (runtime_visible_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleAnnotations attributes in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_visible_annotations_length = attribute_length; | |
| runtime_visible_annotations = cfs->current(); | |
| assert(runtime_visible_annotations != nullptr, "null visible annotations"); | |
| cfs->guarantee_more(runtime_visible_annotations_length, CHECK); | |
| parse_annotations(cp, | |
| runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| parsed_annotations, | |
| _loader_data, | |
| _can_access_vm_annotations); | |
| cfs->skip_u1_fast(runtime_visible_annotations_length); | |
| } else if (tag == vmSymbols::tag_runtime_invisible_annotations()) { | |
| if (runtime_invisible_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleAnnotations attributes in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_invisible_annotations_exists = true; | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } else if (tag == vmSymbols::tag_enclosing_method()) { | |
| if (parsed_enclosingmethod_attribute) { | |
| classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", THREAD); | |
| return; | |
| } else { | |
| parsed_enclosingmethod_attribute = true; | |
| } | |
| guarantee_property(attribute_length == 4, | |
| "Wrong EnclosingMethod attribute length %u in class file %s", | |
| attribute_length, CHECK); | |
| cfs->guarantee_more(4, CHECK); // class_index, method_index | |
| enclosing_method_class_index = cfs->get_u2_fast(); | |
| enclosing_method_method_index = cfs->get_u2_fast(); | |
| if (enclosing_method_class_index == 0) { | |
| classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", THREAD); | |
| return; | |
| } | |
| // Validate the constant pool indices and types | |
| guarantee_property(valid_klass_reference_at(enclosing_method_class_index), | |
| "Invalid or out-of-bounds class index in EnclosingMethod attribute in class file %s", CHECK); | |
| if (enclosing_method_method_index != 0 && | |
| (!cp->is_within_bounds(enclosing_method_method_index) || | |
| !cp->tag_at(enclosing_method_method_index).is_name_and_type())) { | |
| classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", THREAD); | |
| return; | |
| } | |
| } else if (tag == vmSymbols::tag_bootstrap_methods() && | |
| _major_version >= Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { | |
| if (parsed_bootstrap_methods_attribute) { | |
| classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", THREAD); | |
| return; | |
| } | |
| parsed_bootstrap_methods_attribute = true; | |
| parse_classfile_bootstrap_methods_attribute(cfs, cp, attribute_length, CHECK); | |
| } else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) { | |
| if (runtime_visible_type_annotations != nullptr) { | |
| classfile_parse_error( | |
| "Multiple RuntimeVisibleTypeAnnotations attributes in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_visible_type_annotations_length = attribute_length; | |
| runtime_visible_type_annotations = cfs->current(); | |
| assert(runtime_visible_type_annotations != nullptr, "null visible type annotations"); | |
| // No need for the VM to parse Type annotations | |
| cfs->skip_u1(runtime_visible_type_annotations_length, CHECK); | |
| } else if (tag == vmSymbols::tag_runtime_invisible_type_annotations()) { | |
| if (runtime_invisible_type_annotations_exists) { | |
| classfile_parse_error( | |
| "Multiple RuntimeInvisibleTypeAnnotations attributes in class file %s", THREAD); | |
| return; | |
| } | |
| runtime_invisible_type_annotations_exists = true; | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } else if (_major_version >= JAVA_11_VERSION) { | |
| if (tag == vmSymbols::tag_nest_members()) { | |
| // Check for NestMembers tag | |
| if (parsed_nest_members_attribute) { | |
| classfile_parse_error("Multiple NestMembers attributes in class file %s", THREAD); | |
| return; | |
| } else { | |
| parsed_nest_members_attribute = true; | |
| } | |
| if (parsed_nest_host_attribute) { | |
| classfile_parse_error("Conflicting NestHost and NestMembers attributes in class file %s", THREAD); | |
| return; | |
| } | |
| nest_members_attribute_start = cfs->current(); | |
| nest_members_attribute_length = attribute_length; | |
| cfs->skip_u1(nest_members_attribute_length, CHECK); | |
| } else if (tag == vmSymbols::tag_nest_host()) { | |
| if (parsed_nest_host_attribute) { | |
| classfile_parse_error("Multiple NestHost attributes in class file %s", THREAD); | |
| return; | |
| } else { | |
| parsed_nest_host_attribute = true; | |
| } | |
| if (parsed_nest_members_attribute) { | |
| classfile_parse_error("Conflicting NestMembers and NestHost attributes in class file %s", THREAD); | |
| return; | |
| } | |
| if (_need_verify) { | |
| guarantee_property(attribute_length == 2, "Wrong NestHost attribute length in class file %s", CHECK); | |
| } | |
| cfs->guarantee_more(2, CHECK); | |
| u2 class_info_index = cfs->get_u2_fast(); | |
| guarantee_property( | |
| valid_klass_reference_at(class_info_index), | |
| "Nest-host class_info_index %u has bad constant type in class file %s", | |
| class_info_index, CHECK); | |
| _nest_host = class_info_index; | |
| } else if (_major_version >= JAVA_16_VERSION) { | |
| if (tag == vmSymbols::tag_record()) { | |
| if (parsed_record_attribute) { | |
| classfile_parse_error("Multiple Record attributes in class file %s", THREAD); | |
| return; | |
| } | |
| parsed_record_attribute = true; | |
| record_attribute_start = cfs->current(); | |
| record_attribute_length = attribute_length; | |
| } else if (_major_version >= JAVA_17_VERSION) { | |
| if (tag == vmSymbols::tag_permitted_subclasses()) { | |
| if (parsed_permitted_subclasses_attribute) { | |
| classfile_parse_error("Multiple PermittedSubclasses attributes in class file %s", CHECK); | |
| return; | |
| } | |
| // Classes marked ACC_FINAL cannot have a PermittedSubclasses attribute. | |
| if (_access_flags.is_final()) { | |
| classfile_parse_error("PermittedSubclasses attribute in final class file %s", CHECK); | |
| return; | |
| } | |
| parsed_permitted_subclasses_attribute = true; | |
| permitted_subclasses_attribute_start = cfs->current(); | |
| permitted_subclasses_attribute_length = attribute_length; | |
| } | |
| } | |
| // Skip attribute_length for any attribute where major_verson >= JAVA_17_VERSION | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } else { | |
| // Unknown attribute | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } | |
| } else { | |
| // Unknown attribute | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } | |
| } else { | |
| // Unknown attribute | |
| cfs->skip_u1(attribute_length, CHECK); | |
| } | |
| } | |
| _class_annotations = allocate_annotations(runtime_visible_annotations, | |
| runtime_visible_annotations_length, | |
| CHECK); | |
| _class_type_annotations = allocate_annotations(runtime_visible_type_annotations, | |
| runtime_visible_type_annotations_length, | |
| CHECK); | |
| if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { | |
| const u2 num_of_classes = parse_classfile_inner_classes_attribute( | |
| cfs, | |
| cp, | |
| inner_classes_attribute_start, | |
| parsed_innerclasses_attribute, | |
| enclosing_method_class_index, | |
| enclosing_method_method_index, | |
| CHECK); | |
| if (parsed_innerclasses_attribute && _need_verify && _major_version >= JAVA_1_5_VERSION) { | |
| guarantee_property( | |
| inner_classes_attribute_length == sizeof(num_of_classes) + 4 * sizeof(u2) * num_of_classes, | |
| "Wrong InnerClasses attribute length in class file %s", CHECK); | |
| } | |
| } | |
| if (parsed_nest_members_attribute) { | |
| const u2 num_of_classes = parse_classfile_nest_members_attribute( | |
| cfs, | |
| nest_members_attribute_start, | |
| CHECK); | |
| if (_need_verify) { | |
| guarantee_property( | |
| nest_members_attribute_length == sizeof(num_of_classes) + sizeof(u2) * num_of_classes, | |
| "Wrong NestMembers attribute length in class file %s", CHECK); | |
| } | |
| } | |
| if (parsed_record_attribute) { | |
| const unsigned int calculated_attr_length = parse_classfile_record_attribute( | |
| cfs, | |
| cp, | |
| record_attribute_start, | |
| CHECK); | |
| if (_need_verify) { | |
| guarantee_property(record_attribute_length == calculated_attr_length, | |
| "Record attribute has wrong length in class file %s", | |
| CHECK); | |
| } | |
| } | |
| if (parsed_permitted_subclasses_attribute) { | |
| const u2 num_subclasses = parse_classfile_permitted_subclasses_attribute( | |
| cfs, | |
| permitted_subclasses_attribute_start, | |
| CHECK); | |
| if (_need_verify) { | |
| guarantee_property( | |
| permitted_subclasses_attribute_length == sizeof(num_subclasses) + sizeof(u2) * num_subclasses, | |
| "Wrong PermittedSubclasses attribute length in class file %s", CHECK); | |
| } | |
| } | |
| if (_max_bootstrap_specifier_index >= 0) { | |
| guarantee_property(parsed_bootstrap_methods_attribute, | |
| "Missing BootstrapMethods attribute in class file %s", CHECK); | |
| } | |
| } | |
| void ClassFileParser::apply_parsed_class_attributes(InstanceKlass* k) { | |
| assert(k != nullptr, "invariant"); | |
| if (_synthetic_flag) | |
| k->set_is_synthetic(); | |
| if (_sourcefile_index != 0) { | |
| k->set_source_file_name_index(_sourcefile_index); | |
| } | |
| if (_generic_signature_index != 0) { | |
| k->set_generic_signature_index(_generic_signature_index); | |
| } | |
| if (_sde_buffer != nullptr) { | |
| k->set_source_debug_extension(_sde_buffer, _sde_length); | |
| } | |
| } | |
| // Create the Annotations object that will | |
| // hold the annotations array for the Klass. | |
| void ClassFileParser::create_combined_annotations(TRAPS) { | |
| if (_class_annotations == nullptr && | |
| _class_type_annotations == nullptr && | |
| _fields_annotations == nullptr && | |
| _fields_type_annotations == nullptr) { | |
| // Don't create the Annotations object unnecessarily. | |
| return; | |
| } | |
| Annotations* const annotations = Annotations::allocate(_loader_data, CHECK); | |
| annotations->set_class_annotations(_class_annotations); | |
| annotations->set_class_type_annotations(_class_type_annotations); | |
| annotations->set_fields_annotations(_fields_annotations); | |
| annotations->set_fields_type_annotations(_fields_type_annotations); | |
| // This is the Annotations object that will be | |
| // assigned to InstanceKlass being constructed. | |
| _combined_annotations = annotations; | |
| // The annotations arrays below has been transferred the | |
| // _combined_annotations so these fields can now be cleared. | |
| _class_annotations = nullptr; | |
| _class_type_annotations = nullptr; | |
| _fields_annotations = nullptr; | |
| _fields_type_annotations = nullptr; | |
| } | |
| // Transfer ownership of metadata allocated to the InstanceKlass. | |
| void ClassFileParser::apply_parsed_class_metadata( | |
| InstanceKlass* this_klass, | |
| int java_fields_count) { | |
| assert(this_klass != nullptr, "invariant"); | |
| _cp->set_pool_holder(this_klass); | |
| this_klass->set_constants(_cp); | |
| this_klass->set_fieldinfo_stream(_fieldinfo_stream); | |
| this_klass->set_fieldinfo_search_table(_fieldinfo_search_table); | |
| this_klass->set_fields_status(_fields_status); | |
| this_klass->set_methods(_methods); | |
| this_klass->set_inner_classes(_inner_classes); | |
| this_klass->set_nest_members(_nest_members); | |
| this_klass->set_nest_host_index(_nest_host); | |
| this_klass->set_annotations(_combined_annotations); | |
| this_klass->set_permitted_subclasses(_permitted_subclasses); | |
| this_klass->set_record_components(_record_components); | |
| DEBUG_ONLY(FieldInfoStream::validate_search_table(_cp, _fieldinfo_stream, _fieldinfo_search_table)); | |
| // Delay the setting of _local_interfaces and _transitive_interfaces until after | |
| // initialize_supers() in fill_instance_klass(). It is because the _local_interfaces could | |
| // be shared with _transitive_interfaces and _transitive_interfaces may be shared with | |
| // its _super. If an OOM occurs while loading the current klass, its _super field | |
| // may not have been set. When GC tries to free the klass, the _transitive_interfaces | |
| // may be deallocated mistakenly in InstanceKlass::deallocate_interfaces(). Subsequent | |
| // dereferences to the deallocated _transitive_interfaces will result in a crash. | |
| // Clear out these fields so they don't get deallocated by the destructor | |
| clear_class_metadata(); | |
| } | |
| AnnotationArray* ClassFileParser::allocate_annotations(const u1* const anno, | |
| int anno_length, | |
| TRAPS) { | |
| AnnotationArray* annotations = nullptr; | |
| if (anno != nullptr) { | |
| annotations = MetadataFactory::new_array<u1>(_loader_data, | |
| anno_length, | |
| CHECK_(annotations)); | |
| for (int i = 0; i < anno_length; i++) { | |
| annotations->at_put(i, anno[i]); | |
| } | |
| } | |
| return annotations; | |
| } | |
| const InstanceKlass* ClassFileParser::parse_super_class(ConstantPool* const cp, | |
| const int super_class_index, | |
| const bool need_verify, | |
| TRAPS) { | |
| assert(cp != nullptr, "invariant"); | |
| const InstanceKlass* super_klass = nullptr; | |
| if (super_class_index == 0) { | |
| guarantee_property(_class_name == vmSymbols::java_lang_Object(), | |
| "Invalid superclass index %u in class file %s", | |
| super_class_index, | |
| CHECK_NULL); | |
| } else { | |
| guarantee_property(valid_klass_reference_at(super_class_index), | |
| "Invalid superclass index %u in class file %s", | |
| super_class_index, | |
| CHECK_NULL); | |
| // The class name should be legal because it is checked when parsing constant pool. | |
| // However, make sure it is not an array type. | |
| bool is_array = false; | |
| if (cp->tag_at(super_class_index).is_klass()) { | |
| super_klass = InstanceKlass::cast(cp->resolved_klass_at(super_class_index)); | |
| if (need_verify) | |
| is_array = super_klass->is_array_klass(); | |
| } else if (need_verify) { | |
| is_array = (cp->klass_name_at(super_class_index)->char_at(0) == JVM_SIGNATURE_ARRAY); | |
| } | |
| if (need_verify) { | |
| guarantee_property(!is_array, | |
| "Bad superclass name in class file %s", CHECK_NULL); | |
| } | |
| } | |
| return super_klass; | |
| } | |
| OopMapBlocksBuilder::OopMapBlocksBuilder(unsigned int max_blocks) { | |
| _max_nonstatic_oop_maps = max_blocks; | |
| _nonstatic_oop_map_count = 0; | |
| if (max_blocks == 0) { | |
| _nonstatic_oop_maps = nullptr; | |
| } else { | |
| _nonstatic_oop_maps = | |
| NEW_RESOURCE_ARRAY(OopMapBlock, _max_nonstatic_oop_maps); | |
| memset(_nonstatic_oop_maps, 0, sizeof(OopMapBlock) * max_blocks); | |
| } | |
| } | |
| OopMapBlock* OopMapBlocksBuilder::last_oop_map() const { | |
| assert(_nonstatic_oop_map_count > 0, "Has no oop maps"); | |
| return _nonstatic_oop_maps + (_nonstatic_oop_map_count - 1); | |
| } | |
| // addition of super oop maps | |
| void OopMapBlocksBuilder::initialize_inherited_blocks(OopMapBlock* blocks, unsigned int nof_blocks) { | |
| assert(nof_blocks && _nonstatic_oop_map_count == 0 && | |
| nof_blocks <= _max_nonstatic_oop_maps, "invariant"); | |
| memcpy(_nonstatic_oop_maps, blocks, sizeof(OopMapBlock) * nof_blocks); | |
| _nonstatic_oop_map_count += nof_blocks; | |
| } | |
| // collection of oops | |
| void OopMapBlocksBuilder::add(int offset, int count) { | |
| if (_nonstatic_oop_map_count == 0) { | |
| _nonstatic_oop_map_count++; | |
| } | |
| OopMapBlock* nonstatic_oop_map = last_oop_map(); | |
| if (nonstatic_oop_map->count() == 0) { // Unused map, set it up | |
| nonstatic_oop_map->set_offset(offset); | |
| nonstatic_oop_map->set_count(count); | |
| } else if (nonstatic_oop_map->is_contiguous(offset)) { // contiguous, add | |
| nonstatic_oop_map->increment_count(count); | |
| } else { // Need a new one... | |
| _nonstatic_oop_map_count++; | |
| assert(_nonstatic_oop_map_count <= _max_nonstatic_oop_maps, "range check"); | |
| nonstatic_oop_map = last_oop_map(); | |
| nonstatic_oop_map->set_offset(offset); | |
| nonstatic_oop_map->set_count(count); | |
| } | |
| } | |
| // general purpose copy, e.g. into allocated instanceKlass | |
| void OopMapBlocksBuilder::copy(OopMapBlock* dst) { | |
| if (_nonstatic_oop_map_count != 0) { | |
| memcpy(dst, _nonstatic_oop_maps, sizeof(OopMapBlock) * _nonstatic_oop_map_count); | |
| } | |
| } | |
| // Sort and compact adjacent blocks | |
| void OopMapBlocksBuilder::compact() { | |
| if (_nonstatic_oop_map_count <= 1) { | |
| return; | |
| } | |
| /* | |
| * Since field layout sneaks in oops before values, we will be able to condense | |
| * blocks. There is potential to compact between super, own refs and values | |
| * containing refs. | |
| * | |
| * Currently compaction is slightly limited due to values being 8 byte aligned. | |
| * This may well change: FixMe if it doesn't, the code below is fairly general purpose | |
| * and maybe it doesn't need to be. | |
| */ | |
| qsort(_nonstatic_oop_maps, _nonstatic_oop_map_count, sizeof(OopMapBlock), | |
| (_sort_Fn)OopMapBlock::compare_offset); | |
| if (_nonstatic_oop_map_count < 2) { | |
| return; | |
| } | |
| // Make a temp copy, and iterate through and copy back into the original | |
| ResourceMark rm; | |
| OopMapBlock* oop_maps_copy = | |
| NEW_RESOURCE_ARRAY(OopMapBlock, _nonstatic_oop_map_count); | |
| OopMapBlock* oop_maps_copy_end = oop_maps_copy + _nonstatic_oop_map_count; | |
| copy(oop_maps_copy); | |
| OopMapBlock* nonstatic_oop_map = _nonstatic_oop_maps; | |
| unsigned int new_count = 1; | |
| oop_maps_copy++; | |
| while(oop_maps_copy < oop_maps_copy_end) { | |
| assert(nonstatic_oop_map->offset() < oop_maps_copy->offset(), "invariant"); | |
| if (nonstatic_oop_map->is_contiguous(oop_maps_copy->offset())) { | |
| nonstatic_oop_map->increment_count(oop_maps_copy->count()); | |
| } else { | |
| nonstatic_oop_map++; | |
| new_count++; | |
| nonstatic_oop_map->set_offset(oop_maps_copy->offset()); | |
| nonstatic_oop_map->set_count(oop_maps_copy->count()); | |
| } | |
| oop_maps_copy++; | |
| } | |
| assert(new_count <= _nonstatic_oop_map_count, "end up with more maps after compact() ?"); | |
| _nonstatic_oop_map_count = new_count; | |
| } | |
| void OopMapBlocksBuilder::print_on(outputStream* st) const { | |
| st->print_cr(" OopMapBlocks: %3d /%3d", _nonstatic_oop_map_count, _max_nonstatic_oop_maps); | |
| if (_nonstatic_oop_map_count > 0) { | |
| OopMapBlock* map = _nonstatic_oop_maps; | |
| OopMapBlock* last_map = last_oop_map(); | |
| assert(map <= last_map, "Last less than first"); | |
| while (map <= last_map) { | |
| st->print_cr(" Offset: %3d -%3d Count: %3d", map->offset(), | |
| map->offset() + map->offset_span() - heapOopSize, map->count()); | |
| map++; | |
| } | |
| } | |
| } | |
| void OopMapBlocksBuilder::print_value_on(outputStream* st) const { | |
| print_on(st); | |
| } | |
| void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) { | |
| assert(ik != nullptr, "invariant"); | |
| const InstanceKlass* const super = ik->java_super(); | |
| // Check if this klass has an empty finalize method (i.e. one with return bytecode only), | |
| // in which case we don't have to register objects as finalizable | |
| if (!_has_empty_finalizer) { | |
| if (_has_finalizer || | |
| (super != nullptr && super->has_finalizer())) { | |
| ik->set_has_finalizer(); | |
| } | |
| } | |
| #ifdef ASSERT | |
| bool f = false; | |
| const Method* const m = ik->lookup_method(vmSymbols::finalize_method_name(), | |
| vmSymbols::void_method_signature()); | |
| if (InstanceKlass::is_finalization_enabled() && | |
| (m != nullptr) && !m->is_empty_method()) { | |
| f = true; | |
| } | |
| // Spec doesn't prevent agent from redefinition of empty finalizer. | |
| // Despite the fact that it's generally bad idea and redefined finalizer | |
| // will not work as expected we shouldn't abort vm in this case | |
| if (!ik->has_redefined_this_or_super()) { | |
| assert(ik->has_finalizer() == f, "inconsistent has_finalizer"); | |
| } | |
| #endif | |
| // Check if this klass supports the java.lang.Cloneable interface | |
| if (vmClasses::Cloneable_klass_loaded()) { | |
| if (ik->is_subtype_of(vmClasses::Cloneable_klass())) { | |
| ik->set_is_cloneable(); | |
| } | |
| } | |
| // If it cannot be fast-path allocated, set a bit in the layout helper. | |
| // See documentation of InstanceKlass::can_be_fastpath_allocated(). | |
| assert(ik->size_helper() > 0, "layout_helper is initialized"); | |
| if (ik->is_abstract() || ik->is_interface() | |
| || (ik->name() == vmSymbols::java_lang_Class() && ik->class_loader() == nullptr) | |
| || ik->size_helper() >= FastAllocateSizeLimit) { | |
| // Forbid fast-path allocation. | |
| const jint lh = Klass::instance_layout_helper(ik->size_helper(), true); | |
| ik->set_layout_helper(lh); | |
| } | |
| } | |
| // utility methods for appending an array with check for duplicates | |
| static void append_interfaces(GrowableArray<InstanceKlass*>* result, | |
| const Array<InstanceKlass*>* const ifs) { | |
| // iterate over new interfaces | |
| for (int i = 0; i < ifs->length(); i++) { | |
| InstanceKlass* const e = ifs->at(i); | |
| assert(e->is_klass() && e->is_interface(), "just checking"); | |
| // add new interface | |
| result->append_if_missing(e); | |
| } | |
| } | |
| static Array<InstanceKlass*>* compute_transitive_interfaces(const InstanceKlass* super, | |
| Array<InstanceKlass*>* local_ifs, | |
| ClassLoaderData* loader_data, | |
| TRAPS) { | |
| assert(local_ifs != nullptr, "invariant"); | |
| assert(loader_data != nullptr, "invariant"); | |
| // Compute maximum size for transitive interfaces | |
| int max_transitive_size = 0; | |
| int super_size = 0; | |
| // Add superclass transitive interfaces size | |
| if (super != nullptr) { | |
| super_size = super->transitive_interfaces()->length(); | |
| max_transitive_size += super_size; | |
| } | |
| // Add local interfaces' super interfaces | |
| const int local_size = local_ifs->length(); | |
| for (int i = 0; i < local_size; i++) { | |
| InstanceKlass* const l = local_ifs->at(i); | |
| max_transitive_size += l->transitive_interfaces()->length(); | |
| } | |
| // Finally add local interfaces | |
| max_transitive_size += local_size; | |
| // Construct array | |
| if (max_transitive_size == 0) { | |
| // no interfaces, use canonicalized array | |
| return Universe::the_empty_instance_klass_array(); | |
| } else if (max_transitive_size == super_size) { | |
| // no new local interfaces added, share superklass' transitive interface array | |
| return super->transitive_interfaces(); | |
| } else if (max_transitive_size == local_size) { | |
| // only local interfaces added, share local interface array | |
| return local_ifs; | |
| } else { | |
| ResourceMark rm; | |
| GrowableArray<InstanceKlass*>* const result = new GrowableArray<InstanceKlass*>(max_transitive_size); | |
| // Copy down from superclass | |
| if (super != nullptr) { | |
| append_interfaces(result, super->transitive_interfaces()); | |
| } | |
| // Copy down from local interfaces' superinterfaces | |
| for (int i = 0; i < local_size; i++) { | |
| InstanceKlass* const l = local_ifs->at(i); | |
| append_interfaces(result, l->transitive_interfaces()); | |
| } | |
| // Finally add local interfaces | |
| append_interfaces(result, local_ifs); | |
| // length will be less than the max_transitive_size if duplicates were removed | |
| const int length = result->length(); | |
| assert(length <= max_transitive_size, "just checking"); | |
| Array<InstanceKlass*>* const new_result = | |
| MetadataFactory::new_array<InstanceKlass*>(loader_data, length, CHECK_NULL); | |
| for (int i = 0; i < length; i++) { | |
| InstanceKlass* const e = result->at(i); | |
| assert(e != nullptr, "just checking"); | |
| new_result->at_put(i, e); | |
| } | |
| return new_result; | |
| } | |
| } | |
| void ClassFileParser::check_super_class_access(const InstanceKlass* this_klass, TRAPS) { | |
| assert(this_klass != nullptr, "invariant"); | |
| const Klass* const super = this_klass->super(); | |
| if (super != nullptr) { | |
| const InstanceKlass* super_ik = InstanceKlass::cast(super); | |
| if (super->is_final()) { | |
| classfile_icce_error("class %s cannot inherit from final class %s", super_ik, THREAD); | |
| return; | |
| } | |
| if (super_ik->is_sealed()) { | |
| stringStream ss; | |
| ResourceMark rm(THREAD); | |
| if (!super_ik->has_as_permitted_subclass(this_klass, ss)) { | |
| classfile_icce_error(ss.as_string(), THREAD); | |
| return; | |
| } | |
| } | |
| Reflection::VerifyClassAccessResults vca_result = | |
| Reflection::verify_class_access(this_klass, InstanceKlass::cast(super), false); | |
| if (vca_result != Reflection::ACCESS_OK) { | |
| ResourceMark rm(THREAD); | |
| char* msg = Reflection::verify_class_access_msg(this_klass, | |
| InstanceKlass::cast(super), | |
| vca_result); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| if (msg == nullptr) { | |
| bool same_module = (this_klass->module() == super->module()); | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_IllegalAccessError(), | |
| "class %s cannot access its %ssuperclass %s (%s%s%s)", | |
| this_klass->external_name(), | |
| super->is_abstract() ? "abstract " : "", | |
| super->external_name(), | |
| (same_module) ? this_klass->joint_in_module_of_loader(super) : this_klass->class_in_module_of_loader(), | |
| (same_module) ? "" : "; ", | |
| (same_module) ? "" : super->class_in_module_of_loader()); | |
| } else { | |
| // Add additional message content. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_IllegalAccessError(), | |
| "superclass access check failed: %s", | |
| msg); | |
| } | |
| } | |
| } | |
| } | |
| void ClassFileParser::check_super_interface_access(const InstanceKlass* this_klass, TRAPS) { | |
| assert(this_klass != nullptr, "invariant"); | |
| const Array<InstanceKlass*>* const local_interfaces = this_klass->local_interfaces(); | |
| const int lng = local_interfaces->length(); | |
| for (int i = lng - 1; i >= 0; i--) { | |
| InstanceKlass* const k = local_interfaces->at(i); | |
| assert (k != nullptr && k->is_interface(), "invalid interface"); | |
| if (k->is_sealed()) { | |
| stringStream ss; | |
| ResourceMark rm(THREAD); | |
| if (!k->has_as_permitted_subclass(this_klass, ss)) { | |
| classfile_icce_error(ss.as_string(), THREAD); | |
| return; | |
| } | |
| } | |
| Reflection::VerifyClassAccessResults vca_result = | |
| Reflection::verify_class_access(this_klass, k, false); | |
| if (vca_result != Reflection::ACCESS_OK) { | |
| ResourceMark rm(THREAD); | |
| char* msg = Reflection::verify_class_access_msg(this_klass, | |
| k, | |
| vca_result); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| if (msg == nullptr) { | |
| bool same_module = (this_klass->module() == k->module()); | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_IllegalAccessError(), | |
| "class %s cannot access its superinterface %s (%s%s%s)", | |
| this_klass->external_name(), | |
| k->external_name(), | |
| (same_module) ? this_klass->joint_in_module_of_loader(k) : this_klass->class_in_module_of_loader(), | |
| (same_module) ? "" : "; ", | |
| (same_module) ? "" : k->class_in_module_of_loader()); | |
| return; | |
| } else { | |
| // Add additional message content. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_IllegalAccessError(), | |
| "superinterface check failed: %s", | |
| msg); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| static void check_final_method_override(const InstanceKlass* this_klass, TRAPS) { | |
| assert(this_klass != nullptr, "invariant"); | |
| const Array<Method*>* const methods = this_klass->methods(); | |
| const int num_methods = methods->length(); | |
| // go thru each method and check if it overrides a final method | |
| for (int index = 0; index < num_methods; index++) { | |
| const Method* const m = methods->at(index); | |
| // skip private, static, and <init> methods | |
| if ((!m->is_private() && !m->is_static()) && | |
| (m->name() != vmSymbols::object_initializer_name())) { | |
| const Symbol* const name = m->name(); | |
| const Symbol* const signature = m->signature(); | |
| const InstanceKlass* k = this_klass->java_super(); | |
| const Method* super_m = nullptr; | |
| while (k != nullptr) { | |
| // skip supers that don't have final methods. | |
| if (k->has_final_method()) { | |
| // lookup a matching method in the super class hierarchy | |
| super_m = InstanceKlass::cast(k)->lookup_method(name, signature); | |
| if (super_m == nullptr) { | |
| break; // didn't find any match; get out | |
| } | |
| if (super_m->is_final() && !super_m->is_static() && | |
| !super_m->access_flags().is_private()) { | |
| // matching method in super is final, and not static or private | |
| bool can_access = Reflection::verify_member_access(this_klass, | |
| super_m->method_holder(), | |
| super_m->method_holder(), | |
| super_m->access_flags(), | |
| false, false, CHECK); | |
| if (can_access) { | |
| // this class can access super final method and therefore override | |
| ResourceMark rm(THREAD); | |
| THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), | |
| err_msg("class %s overrides final method %s.%s%s", | |
| this_klass->external_name(), | |
| super_m->method_holder()->external_name(), | |
| name->as_C_string(), | |
| signature->as_C_string())); | |
| } | |
| } | |
| // continue to look from super_m's holder's super. | |
| k = super_m->method_holder()->java_super(); | |
| continue; | |
| } | |
| k = k->java_super(); | |
| } | |
| } | |
| } | |
| } | |
| // assumes that this_klass is an interface | |
| static void check_illegal_static_method(const InstanceKlass* this_klass, TRAPS) { | |
| assert(this_klass != nullptr, "invariant"); | |
| assert(this_klass->is_interface(), "not an interface"); | |
| const Array<Method*>* methods = this_klass->methods(); | |
| const int num_methods = methods->length(); | |
| for (int index = 0; index < num_methods; index++) { | |
| const Method* const m = methods->at(index); | |
| // if m is static and not the init method, throw a verify error | |
| if ((m->is_static()) && (m->name() != vmSymbols::class_initializer_name())) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_VerifyError(), | |
| "Illegal static method %s in interface %s", | |
| m->name()->as_C_string(), | |
| this_klass->external_name() | |
| ); | |
| return; | |
| } | |
| } | |
| } | |
| // utility methods for format checking | |
| void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) const { | |
| const bool is_module = (flags & JVM_ACC_MODULE) != 0; | |
| assert(_major_version >= JAVA_9_VERSION || !is_module, "JVM_ACC_MODULE should not be set"); | |
| if (is_module) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_NoClassDefFoundError(), | |
| "%s is not a class because access_flag ACC_MODULE is set", | |
| _class_name->as_C_string()); | |
| return; | |
| } | |
| if (!_need_verify) { return; } | |
| const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0; | |
| const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; | |
| const bool is_final = (flags & JVM_ACC_FINAL) != 0; | |
| const bool is_super = (flags & JVM_ACC_SUPER) != 0; | |
| const bool is_enum = (flags & JVM_ACC_ENUM) != 0; | |
| const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0; | |
| const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; | |
| if ((is_abstract && is_final) || | |
| (is_interface && !is_abstract) || | |
| (is_interface && major_gte_1_5 && (is_super || is_enum)) || | |
| (!is_interface && major_gte_1_5 && is_annotation)) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Illegal class modifiers in class %s: 0x%X", | |
| _class_name->as_C_string(), flags | |
| ); | |
| return; | |
| } | |
| } | |
| static bool has_illegal_visibility(jint flags) { | |
| const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; | |
| const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; | |
| const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; | |
| return ((is_public && is_protected) || | |
| (is_public && is_private) || | |
| (is_protected && is_private)); | |
| } | |
| // A legal major_version.minor_version must be one of the following: | |
| // | |
| // Major_version >= 45 and major_version < 56, any minor_version. | |
| // Major_version >= 56 and major_version <= JVM_CLASSFILE_MAJOR_VERSION and minor_version = 0. | |
| // Major_version = JVM_CLASSFILE_MAJOR_VERSION and minor_version = 65535 and --enable-preview is present. | |
| // | |
| void ClassFileParser::verify_class_version(u2 major, u2 minor, Symbol* class_name, TRAPS){ | |
| ResourceMark rm(THREAD); | |
| const u2 max_version = JVM_CLASSFILE_MAJOR_VERSION; | |
| if (major < JAVA_MIN_SUPPORTED_VERSION) { | |
| classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid major version", | |
| class_name, major, minor, THREAD); | |
| return; | |
| } | |
| if (major > max_version) { | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_UnsupportedClassVersionError(), | |
| "%s has been compiled by a more recent version of the Java Runtime (class file version %u.%u), " | |
| "this version of the Java Runtime only recognizes class file versions up to %u.0", | |
| class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION); | |
| return; | |
| } | |
| if (major < JAVA_12_VERSION || minor == 0) { | |
| return; | |
| } | |
| if (minor == JAVA_PREVIEW_MINOR_VERSION) { | |
| if (major != max_version) { | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_UnsupportedClassVersionError(), | |
| "%s (class file version %u.%u) was compiled with preview features that are unsupported. " | |
| "This version of the Java Runtime only recognizes preview features for class file version %u.%u", | |
| class_name->as_C_string(), major, minor, JVM_CLASSFILE_MAJOR_VERSION, JAVA_PREVIEW_MINOR_VERSION); | |
| return; | |
| } | |
| if (!Arguments::enable_preview()) { | |
| classfile_ucve_error("Preview features are not enabled for %s (class file version %u.%u). Try running with '--enable-preview'", | |
| class_name, major, minor, THREAD); | |
| return; | |
| } | |
| } else { // minor != JAVA_PREVIEW_MINOR_VERSION | |
| classfile_ucve_error("%s (class file version %u.%u) was compiled with an invalid non-zero minor version", | |
| class_name, major, minor, THREAD); | |
| } | |
| } | |
| void ClassFileParser::verify_legal_field_modifiers(jint flags, | |
| bool is_interface, | |
| TRAPS) const { | |
| if (!_need_verify) { return; } | |
| const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; | |
| const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; | |
| const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; | |
| const bool is_static = (flags & JVM_ACC_STATIC) != 0; | |
| const bool is_final = (flags & JVM_ACC_FINAL) != 0; | |
| const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0; | |
| const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0; | |
| const bool is_enum = (flags & JVM_ACC_ENUM) != 0; | |
| const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; | |
| bool is_illegal = false; | |
| if (is_interface) { | |
| if (!is_public || !is_static || !is_final || is_private || | |
| is_protected || is_volatile || is_transient || | |
| (major_gte_1_5 && is_enum)) { | |
| is_illegal = true; | |
| } | |
| } else { // not interface | |
| if (has_illegal_visibility(flags) || (is_final && is_volatile)) { | |
| is_illegal = true; | |
| } | |
| } | |
| if (is_illegal) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Illegal field modifiers in class %s: 0x%X", | |
| _class_name->as_C_string(), flags); | |
| return; | |
| } | |
| } | |
| void ClassFileParser::verify_legal_method_modifiers(jint flags, | |
| bool is_interface, | |
| const Symbol* name, | |
| TRAPS) const { | |
| if (!_need_verify) { return; } | |
| const bool is_public = (flags & JVM_ACC_PUBLIC) != 0; | |
| const bool is_private = (flags & JVM_ACC_PRIVATE) != 0; | |
| const bool is_static = (flags & JVM_ACC_STATIC) != 0; | |
| const bool is_final = (flags & JVM_ACC_FINAL) != 0; | |
| const bool is_native = (flags & JVM_ACC_NATIVE) != 0; | |
| const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0; | |
| const bool is_bridge = (flags & JVM_ACC_BRIDGE) != 0; | |
| const bool is_strict = (flags & JVM_ACC_STRICT) != 0; | |
| const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0; | |
| const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0; | |
| const bool major_gte_1_5 = _major_version >= JAVA_1_5_VERSION; | |
| const bool major_gte_8 = _major_version >= JAVA_8_VERSION; | |
| const bool major_gte_17 = _major_version >= JAVA_17_VERSION; | |
| const bool is_initializer = (name == vmSymbols::object_initializer_name()); | |
| bool is_illegal = false; | |
| if (is_interface) { | |
| if (major_gte_8) { | |
| // Class file version is JAVA_8_VERSION or later Methods of | |
| // interfaces may set any of the flags except ACC_PROTECTED, | |
| // ACC_FINAL, ACC_NATIVE, and ACC_SYNCHRONIZED; they must | |
| // have exactly one of the ACC_PUBLIC or ACC_PRIVATE flags set. | |
| if ((is_public == is_private) || /* Only one of private and public should be true - XNOR */ | |
| (is_native || is_protected || is_final || is_synchronized) || | |
| // If a specific method of a class or interface has its | |
| // ACC_ABSTRACT flag set, it must not have any of its | |
| // ACC_FINAL, ACC_NATIVE, ACC_PRIVATE, ACC_STATIC, | |
| // ACC_STRICT, or ACC_SYNCHRONIZED flags set. No need to | |
| // check for ACC_FINAL, ACC_NATIVE or ACC_SYNCHRONIZED as | |
| // those flags are illegal irrespective of ACC_ABSTRACT being set or not. | |
| (is_abstract && (is_private || is_static || (!major_gte_17 && is_strict)))) { | |
| is_illegal = true; | |
| } | |
| } else if (major_gte_1_5) { | |
| // Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION) | |
| if (!is_public || is_private || is_protected || is_static || is_final || | |
| is_synchronized || is_native || !is_abstract || is_strict) { | |
| is_illegal = true; | |
| } | |
| } else { | |
| // Class file version is pre-JAVA_1_5_VERSION | |
| if (!is_public || is_static || is_final || is_native || !is_abstract) { | |
| is_illegal = true; | |
| } | |
| } | |
| } else { // not interface | |
| if (has_illegal_visibility(flags)) { | |
| is_illegal = true; | |
| } else { | |
| if (is_initializer) { | |
| if (is_static || is_final || is_synchronized || is_native || | |
| is_abstract || (major_gte_1_5 && is_bridge)) { | |
| is_illegal = true; | |
| } | |
| } else { // not initializer | |
| if (is_abstract) { | |
| if ((is_final || is_native || is_private || is_static || | |
| (major_gte_1_5 && (is_synchronized || (!major_gte_17 && is_strict))))) { | |
| is_illegal = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (is_illegal) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Method %s in class %s has illegal modifiers: 0x%X", | |
| name->as_C_string(), _class_name->as_C_string(), flags); | |
| return; | |
| } | |
| } | |
| void ClassFileParser::verify_legal_utf8(const unsigned char* buffer, | |
| int length, | |
| TRAPS) const { | |
| assert(_need_verify, "only called when _need_verify is true"); | |
| // Note: 0 <= length < 64K, as it comes from a u2 entry in the CP. | |
| if (!UTF8::is_legal_utf8(buffer, static_cast<size_t>(length), _major_version <= 47)) { | |
| classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", THREAD); | |
| } | |
| } | |
| // Unqualified names may not contain the characters '.', ';', '[', or '/'. | |
| // In class names, '/' separates unqualified names. This is verified in this function also. | |
| // Method names also may not contain the characters '<' or '>', unless <init> | |
| // or <clinit>. Note that method names may not be <init> or <clinit> in this | |
| // method. Because these names have been checked as special cases before | |
| // calling this method in verify_legal_method_name. | |
| // | |
| // This method is also called from the modular system APIs in modules.cpp | |
| // to verify the validity of module and package names. | |
| bool ClassFileParser::verify_unqualified_name(const char* name, | |
| unsigned int length, | |
| int type) { | |
| if (length == 0) return false; // Must have at least one char. | |
| for (const char* p = name; p != name + length; p++) { | |
| switch(*p) { | |
| case JVM_SIGNATURE_DOT: | |
| case JVM_SIGNATURE_ENDCLASS: | |
| case JVM_SIGNATURE_ARRAY: | |
| // do not permit '.', ';', or '[' | |
| return false; | |
| case JVM_SIGNATURE_SLASH: | |
| // check for '//' or leading or trailing '/' which are not legal | |
| // unqualified name must not be empty | |
| if (type == ClassFileParser::LegalClass) { | |
| if (p == name || p+1 >= name+length || | |
| *(p+1) == JVM_SIGNATURE_SLASH) { | |
| return false; | |
| } | |
| } else { | |
| return false; // do not permit '/' unless it's class name | |
| } | |
| break; | |
| case JVM_SIGNATURE_SPECIAL: | |
| case JVM_SIGNATURE_ENDSPECIAL: | |
| // do not permit '<' or '>' in method names | |
| if (type == ClassFileParser::LegalMethod) { | |
| return false; | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| // Take pointer to a UTF8 byte string (not NUL-terminated). | |
| // Skip over the longest part of the string that could | |
| // be taken as a fieldname. Allow non-trailing '/'s if slash_ok is true. | |
| // Return a pointer to just past the fieldname. | |
| // Return null if no fieldname at all was found, or in the case of slash_ok | |
| // being true, we saw consecutive slashes (meaning we were looking for a | |
| // qualified path but found something that was badly-formed). | |
| static const char* skip_over_field_name(const char* const name, | |
| bool slash_ok, | |
| unsigned int length) { | |
| const char* p; | |
| jboolean last_is_slash = false; | |
| jboolean not_first_ch = false; | |
| for (p = name; p != name + length; not_first_ch = true) { | |
| const char* old_p = p; | |
| jchar ch = *p; | |
| if (ch < 128) { | |
| p++; | |
| // quick check for ascii | |
| if ((ch >= 'a' && ch <= 'z') || | |
| (ch >= 'A' && ch <= 'Z') || | |
| (ch == '_' || ch == '$') || | |
| (not_first_ch && ch >= '0' && ch <= '9')) { | |
| last_is_slash = false; | |
| continue; | |
| } | |
| if (slash_ok && ch == JVM_SIGNATURE_SLASH) { | |
| if (last_is_slash) { | |
| return nullptr; // Don't permit consecutive slashes | |
| } | |
| last_is_slash = true; | |
| continue; | |
| } | |
| } | |
| else { | |
| jint unicode_ch; | |
| char* tmp_p = UTF8::next_character(p, &unicode_ch); | |
| p = tmp_p; | |
| last_is_slash = false; | |
| // Check if ch is Java identifier start or is Java identifier part | |
| // 4672820: call java.lang.Character methods directly without generating separate tables. | |
| EXCEPTION_MARK; | |
| // return value | |
| JavaValue result(T_BOOLEAN); | |
| // Set up the arguments to isJavaIdentifierStart or isJavaIdentifierPart | |
| JavaCallArguments args; | |
| args.push_int(unicode_ch); | |
| if (not_first_ch) { | |
| // public static boolean isJavaIdentifierPart(char ch); | |
| JavaCalls::call_static(&result, | |
| vmClasses::Character_klass(), | |
| vmSymbols::isJavaIdentifierPart_name(), | |
| vmSymbols::int_bool_signature(), | |
| &args, | |
| THREAD); | |
| } else { | |
| // public static boolean isJavaIdentifierStart(char ch); | |
| JavaCalls::call_static(&result, | |
| vmClasses::Character_klass(), | |
| vmSymbols::isJavaIdentifierStart_name(), | |
| vmSymbols::int_bool_signature(), | |
| &args, | |
| THREAD); | |
| } | |
| if (HAS_PENDING_EXCEPTION) { | |
| CLEAR_PENDING_EXCEPTION; | |
| return nullptr; | |
| } | |
| if(result.get_jboolean()) { | |
| continue; | |
| } | |
| } | |
| return (not_first_ch) ? old_p : nullptr; | |
| } | |
| return (not_first_ch && !last_is_slash) ? p : nullptr; | |
| } | |
| // Take pointer to a UTF8 byte string (not NUL-terminated). | |
| // Skip over the longest part of the string that could | |
| // be taken as a field signature. Allow "void" if void_ok. | |
| // Return a pointer to just past the signature. | |
| // Return null if no legal signature is found. | |
| const char* ClassFileParser::skip_over_field_signature(const char* signature, | |
| bool void_ok, | |
| unsigned int length, | |
| TRAPS) const { | |
| unsigned int array_dim = 0; | |
| while (length > 0) { | |
| switch (signature[0]) { | |
| case JVM_SIGNATURE_VOID: if (!void_ok) { return nullptr; } | |
| case JVM_SIGNATURE_BOOLEAN: | |
| case JVM_SIGNATURE_BYTE: | |
| case JVM_SIGNATURE_CHAR: | |
| case JVM_SIGNATURE_SHORT: | |
| case JVM_SIGNATURE_INT: | |
| case JVM_SIGNATURE_FLOAT: | |
| case JVM_SIGNATURE_LONG: | |
| case JVM_SIGNATURE_DOUBLE: | |
| return signature + 1; | |
| case JVM_SIGNATURE_CLASS: { | |
| if (_major_version < JAVA_1_5_VERSION) { | |
| // Skip over the class name if one is there | |
| const char* const p = skip_over_field_name(signature + 1, true, --length); | |
| // The next character better be a semicolon | |
| if (p && (p - signature) > 1 && p[0] == JVM_SIGNATURE_ENDCLASS) { | |
| return p + 1; | |
| } | |
| } | |
| else { | |
| // Skip leading 'L' and ignore first appearance of ';' | |
| signature++; | |
| const char* c = (const char*) memchr(signature, JVM_SIGNATURE_ENDCLASS, length - 1); | |
| // Format check signature | |
| if (c != nullptr) { | |
| int newlen = pointer_delta_as_int(c, (char*) signature); | |
| bool legal = verify_unqualified_name(signature, newlen, LegalClass); | |
| if (!legal) { | |
| classfile_parse_error("Class name is empty or contains illegal character " | |
| "in descriptor in class file %s", | |
| THREAD); | |
| return nullptr; | |
| } | |
| return signature + newlen + 1; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| case JVM_SIGNATURE_ARRAY: | |
| array_dim++; | |
| if (array_dim > 255) { | |
| // 4277370: array descriptor is valid only if it represents 255 or fewer dimensions. | |
| classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", THREAD); | |
| return nullptr; | |
| } | |
| // The rest of what's there better be a legal signature | |
| signature++; | |
| length--; | |
| void_ok = false; | |
| break; | |
| default: | |
| return nullptr; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| // Checks if name is a legal class name. | |
| void ClassFileParser::verify_legal_class_name(const Symbol* name, TRAPS) const { | |
| if (!_need_verify) { return; } | |
| assert(name->refcount() > 0, "symbol must be kept alive"); | |
| char* bytes = (char*)name->bytes(); | |
| unsigned int length = name->utf8_length(); | |
| bool legal = false; | |
| if (length > 0) { | |
| const char* p; | |
| if (bytes[0] == JVM_SIGNATURE_ARRAY) { | |
| p = skip_over_field_signature(bytes, false, length, CHECK); | |
| legal = (p != nullptr) && ((p - bytes) == (int)length); | |
| } else if (_major_version < JAVA_1_5_VERSION) { | |
| if (bytes[0] != JVM_SIGNATURE_SPECIAL) { | |
| p = skip_over_field_name(bytes, true, length); | |
| legal = (p != nullptr) && ((p - bytes) == (int)length); | |
| } | |
| } else { | |
| // 4900761: relax the constraints based on JSR202 spec | |
| // Class names may be drawn from the entire Unicode character set. | |
| // Identifiers between '/' must be unqualified names. | |
| // The utf8 string has been verified when parsing cpool entries. | |
| legal = verify_unqualified_name(bytes, length, LegalClass); | |
| } | |
| } | |
| if (!legal) { | |
| ResourceMark rm(THREAD); | |
| assert(_class_name != nullptr, "invariant"); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Illegal class name \"%.*s\" in class file %s", length, bytes, | |
| _class_name->as_C_string() | |
| ); | |
| return; | |
| } | |
| } | |
| // Checks if name is a legal field name. | |
| void ClassFileParser::verify_legal_field_name(const Symbol* name, TRAPS) const { | |
| if (!_need_verify) { return; } | |
| char* bytes = (char*)name->bytes(); | |
| unsigned int length = name->utf8_length(); | |
| bool legal = false; | |
| if (length > 0) { | |
| if (_major_version < JAVA_1_5_VERSION) { | |
| if (bytes[0] != JVM_SIGNATURE_SPECIAL) { | |
| const char* p = skip_over_field_name(bytes, false, length); | |
| legal = (p != nullptr) && ((p - bytes) == (int)length); | |
| } | |
| } else { | |
| // 4881221: relax the constraints based on JSR202 spec | |
| legal = verify_unqualified_name(bytes, length, LegalField); | |
| } | |
| } | |
| if (!legal) { | |
| ResourceMark rm(THREAD); | |
| assert(_class_name != nullptr, "invariant"); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Illegal field name \"%.*s\" in class %s", length, bytes, | |
| _class_name->as_C_string() | |
| ); | |
| return; | |
| } | |
| } | |
| // Checks if name is a legal method name. | |
| void ClassFileParser::verify_legal_method_name(const Symbol* name, TRAPS) const { | |
| if (!_need_verify) { return; } | |
| assert(name != nullptr, "method name is null"); | |
| char* bytes = (char*)name->bytes(); | |
| unsigned int length = name->utf8_length(); | |
| bool legal = false; | |
| if (length > 0) { | |
| if (bytes[0] == JVM_SIGNATURE_SPECIAL) { | |
| if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) { | |
| legal = true; | |
| } | |
| } else if (_major_version < JAVA_1_5_VERSION) { | |
| const char* p; | |
| p = skip_over_field_name(bytes, false, length); | |
| legal = (p != nullptr) && ((p - bytes) == (int)length); | |
| } else { | |
| // 4881221: relax the constraints based on JSR202 spec | |
| legal = verify_unqualified_name(bytes, length, LegalMethod); | |
| } | |
| } | |
| if (!legal) { | |
| ResourceMark rm(THREAD); | |
| assert(_class_name != nullptr, "invariant"); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow( | |
| THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_ClassFormatError(), | |
| "Illegal method name \"%.*s\" in class %s", length, bytes, | |
| _class_name->as_C_string() | |
| ); | |
| return; | |
| } | |
| } | |
| // Checks if signature is a legal field signature. | |
| void ClassFileParser::verify_legal_field_signature(const Symbol* name, | |
| const Symbol* signature, | |
| TRAPS) const { | |
| if (!_need_verify) { return; } | |
| const char* const bytes = (const char*)signature->bytes(); | |
| const unsigned int length = signature->utf8_length(); | |
| const char* const p = skip_over_field_signature(bytes, false, length, CHECK); | |
| if (p == nullptr || (p - bytes) != (int)length) { | |
| throwIllegalSignature("Field", name, signature, CHECK); | |
| } | |
| } | |
| // Check that the signature is compatible with the method name. For example, | |
| // check that <init> has a void signature. | |
| void ClassFileParser::verify_legal_name_with_signature(const Symbol* name, | |
| const Symbol* signature, | |
| TRAPS) const { | |
| if (!_need_verify) { | |
| return; | |
| } | |
| // Class initializers cannot have args for class format version >= 51. | |
| if (name == vmSymbols::class_initializer_name() && | |
| signature != vmSymbols::void_method_signature() && | |
| _major_version >= JAVA_7_VERSION) { | |
| throwIllegalSignature("Method", name, signature, THREAD); | |
| return; | |
| } | |
| int sig_length = signature->utf8_length(); | |
| if (name->utf8_length() > 0 && | |
| name->char_at(0) == JVM_SIGNATURE_SPECIAL && | |
| sig_length > 0 && | |
| signature->char_at(sig_length - 1) != JVM_SIGNATURE_VOID) { | |
| throwIllegalSignature("Method", name, signature, THREAD); | |
| } | |
| } | |
| // Checks if signature is a legal method signature. | |
| // Returns number of parameters | |
| int ClassFileParser::verify_legal_method_signature(const Symbol* name, | |
| const Symbol* signature, | |
| TRAPS) const { | |
| if (!_need_verify) { | |
| // make sure caller's args_size will be less than 0 even for non-static | |
| // method so it will be recomputed in compute_size_of_parameters(). | |
| return -2; | |
| } | |
| unsigned int args_size = 0; | |
| const char* p = (const char*)signature->bytes(); | |
| unsigned int length = signature->utf8_length(); | |
| const char* nextp; | |
| // The first character must be a '(' | |
| if ((length > 0) && (*p++ == JVM_SIGNATURE_FUNC)) { | |
| length--; | |
| // Skip over legal field signatures | |
| nextp = skip_over_field_signature(p, false, length, CHECK_0); | |
| while ((length > 0) && (nextp != nullptr)) { | |
| args_size++; | |
| if (p[0] == 'J' || p[0] == 'D') { | |
| args_size++; | |
| } | |
| length -= pointer_delta_as_int(nextp, p); | |
| p = nextp; | |
| nextp = skip_over_field_signature(p, false, length, CHECK_0); | |
| } | |
| // The first non-signature thing better be a ')' | |
| if ((length > 0) && (*p++ == JVM_SIGNATURE_ENDFUNC)) { | |
| length--; | |
| // Now we better just have a return value | |
| nextp = skip_over_field_signature(p, true, length, CHECK_0); | |
| if (nextp && ((int)length == (nextp - p))) { | |
| return args_size; | |
| } | |
| } | |
| } | |
| // Report error | |
| throwIllegalSignature("Method", name, signature, THREAD); | |
| return 0; | |
| } | |
| int ClassFileParser::static_field_size() const { | |
| assert(_field_info != nullptr, "invariant"); | |
| return _field_info->_static_field_size; | |
| } | |
| int ClassFileParser::total_oop_map_count() const { | |
| assert(_field_info != nullptr, "invariant"); | |
| return _field_info->oop_map_blocks->_nonstatic_oop_map_count; | |
| } | |
| jint ClassFileParser::layout_size() const { | |
| assert(_field_info != nullptr, "invariant"); | |
| return _field_info->_instance_size; | |
| } | |
| static void check_methods_for_intrinsics(const InstanceKlass* ik, | |
| const Array<Method*>* methods) { | |
| assert(ik != nullptr, "invariant"); | |
| assert(methods != nullptr, "invariant"); | |
| // Set up Method*::intrinsic_id as soon as we know the names of methods. | |
| // (We used to do this lazily, but now we query it in Rewriter, | |
| // which is eagerly done for every method, so we might as well do it now, | |
| // when everything is fresh in memory.) | |
| const vmSymbolID klass_id = Method::klass_id_for_intrinsics(ik); | |
| if (klass_id != vmSymbolID::NO_SID) { | |
| for (int j = 0; j < methods->length(); ++j) { | |
| Method* method = methods->at(j); | |
| method->init_intrinsic_id(klass_id); | |
| if (CheckIntrinsics) { | |
| // Check if an intrinsic is defined for method 'method', | |
| // but the method is not annotated with @IntrinsicCandidate. | |
| if (method->intrinsic_id() != vmIntrinsics::_none && | |
| !method->intrinsic_candidate()) { | |
| tty->print("Compiler intrinsic is defined for method [%s], " | |
| "but the method is not annotated with @IntrinsicCandidate.%s", | |
| method->name_and_sig_as_C_string(), | |
| NOT_DEBUG(" Method will not be inlined.") DEBUG_ONLY(" Exiting.") | |
| ); | |
| tty->cr(); | |
| DEBUG_ONLY(vm_exit(1)); | |
| } | |
| // Check is the method 'method' is annotated with @IntrinsicCandidate, | |
| // but there is no intrinsic available for it. | |
| if (method->intrinsic_candidate() && | |
| method->intrinsic_id() == vmIntrinsics::_none) { | |
| tty->print("Method [%s] is annotated with @IntrinsicCandidate, " | |
| "but no compiler intrinsic is defined for the method.%s", | |
| method->name_and_sig_as_C_string(), | |
| NOT_DEBUG("") DEBUG_ONLY(" Exiting.") | |
| ); | |
| tty->cr(); | |
| DEBUG_ONLY(vm_exit(1)); | |
| } | |
| } | |
| } // end for | |
| #ifdef ASSERT | |
| if (CheckIntrinsics) { | |
| // Check for orphan methods in the current class. A method m | |
| // of a class C is orphan if an intrinsic is defined for method m, | |
| // but class C does not declare m. | |
| // The check is potentially expensive, therefore it is available | |
| // only in debug builds. | |
| for (auto id : EnumRange<vmIntrinsicID>{}) { | |
| if (vmIntrinsics::_compiledLambdaForm == id) { | |
| // The _compiledLamdbdaForm intrinsic is a special marker for bytecode | |
| // generated for the JVM from a LambdaForm and therefore no method | |
| // is defined for it. | |
| continue; | |
| } | |
| if (vmIntrinsics::_blackhole == id) { | |
| // The _blackhole intrinsic is a special marker. No explicit method | |
| // is defined for it. | |
| continue; | |
| } | |
| if (vmIntrinsics::class_for(id) == klass_id) { | |
| // Check if the current class contains a method with the same | |
| // name, flags, signature. | |
| bool match = false; | |
| for (int j = 0; j < methods->length(); ++j) { | |
| const Method* method = methods->at(j); | |
| if (method->intrinsic_id() == id) { | |
| match = true; | |
| break; | |
| } | |
| } | |
| if (!match) { | |
| char buf[1000]; | |
| tty->print("Compiler intrinsic is defined for method [%s], " | |
| "but the method is not available in class [%s].%s", | |
| vmIntrinsics::short_name_as_C_string(id, buf, sizeof(buf)), | |
| ik->name()->as_C_string(), | |
| NOT_DEBUG("") DEBUG_ONLY(" Exiting.") | |
| ); | |
| tty->cr(); | |
| DEBUG_ONLY(vm_exit(1)); | |
| } | |
| } | |
| } // end for | |
| } // CheckIntrinsics | |
| #endif // ASSERT | |
| } | |
| } | |
| InstanceKlass* ClassFileParser::create_instance_klass(bool changed_by_loadhook, | |
| const ClassInstanceInfo& cl_inst_info, | |
| TRAPS) { | |
| if (_klass != nullptr) { | |
| return _klass; | |
| } | |
| InstanceKlass* const ik = | |
| InstanceKlass::allocate_instance_klass(*this, CHECK_NULL); | |
| if (is_hidden()) { | |
| mangle_hidden_class_name(ik); | |
| } | |
| fill_instance_klass(ik, changed_by_loadhook, cl_inst_info, CHECK_NULL); | |
| assert(_klass == ik, "invariant"); | |
| return ik; | |
| } | |
| void ClassFileParser::fill_instance_klass(InstanceKlass* ik, | |
| bool changed_by_loadhook, | |
| const ClassInstanceInfo& cl_inst_info, | |
| TRAPS) { | |
| assert(ik != nullptr, "invariant"); | |
| // Set name and CLD before adding to CLD | |
| ik->set_class_loader_data(_loader_data); | |
| ik->set_class_loader_type(); | |
| ik->set_name(_class_name); | |
| // Add all classes to our internal class loader list here, | |
| // including classes in the bootstrap (null) class loader. | |
| const bool publicize = !is_internal(); | |
| _loader_data->add_class(ik, publicize); | |
| set_klass_to_deallocate(ik); | |
| assert(_field_info != nullptr, "invariant"); | |
| assert(ik->static_field_size() == _field_info->_static_field_size, "sanity"); | |
| assert(ik->nonstatic_oop_map_count() == _field_info->oop_map_blocks->_nonstatic_oop_map_count, | |
| "sanity"); | |
| assert(ik->is_instance_klass(), "sanity"); | |
| assert(ik->size_helper() == _field_info->_instance_size, "sanity"); | |
| // Fill in information already parsed | |
| ik->set_should_verify_class(_need_verify); | |
| // Not yet: supers are done below to support the new subtype-checking fields | |
| ik->set_nonstatic_field_size(_field_info->_nonstatic_field_size); | |
| ik->set_has_nonstatic_fields(_field_info->_has_nonstatic_fields); | |
| ik->set_static_oop_field_count(_static_oop_count); | |
| // this transfers ownership of a lot of arrays from | |
| // the parser onto the InstanceKlass* | |
| apply_parsed_class_metadata(ik, _java_fields_count); | |
| // can only set dynamic nest-host after static nest information is set | |
| if (cl_inst_info.dynamic_nest_host() != nullptr) { | |
| ik->set_nest_host(cl_inst_info.dynamic_nest_host()); | |
| } | |
| // note that is not safe to use the fields in the parser from this point on | |
| assert(nullptr == _cp, "invariant"); | |
| assert(nullptr == _fieldinfo_stream, "invariant"); | |
| assert(nullptr == _fieldinfo_search_table, "invariant"); | |
| assert(nullptr == _fields_status, "invariant"); | |
| assert(nullptr == _methods, "invariant"); | |
| assert(nullptr == _inner_classes, "invariant"); | |
| assert(nullptr == _nest_members, "invariant"); | |
| assert(nullptr == _combined_annotations, "invariant"); | |
| assert(nullptr == _record_components, "invariant"); | |
| assert(nullptr == _permitted_subclasses, "invariant"); | |
| if (_has_localvariable_table) { | |
| ik->set_has_localvariable_table(true); | |
| } | |
| if (_has_final_method) { | |
| ik->set_has_final_method(); | |
| } | |
| ik->copy_method_ordering(_method_ordering, CHECK); | |
| // The InstanceKlass::_methods_jmethod_ids cache | |
| // is managed on the assumption that the initial cache | |
| // size is equal to the number of methods in the class. If | |
| // that changes, then InstanceKlass::idnum_can_increment() | |
| // has to be changed accordingly. | |
| ik->set_initial_method_idnum(checked_cast<u2>(ik->methods()->length())); | |
| ik->set_this_class_index(_this_class_index); | |
| if (_is_hidden) { | |
| // _this_class_index is a CONSTANT_Class entry that refers to this | |
| // hidden class itself. If this class needs to refer to its own methods | |
| // or fields, it would use a CONSTANT_MethodRef, etc, which would reference | |
| // _this_class_index. However, because this class is hidden (it's | |
| // not stored in SystemDictionary), _this_class_index cannot be resolved | |
| // with ConstantPool::klass_at_impl, which does a SystemDictionary lookup. | |
| // Therefore, we must eagerly resolve _this_class_index now. | |
| ik->constants()->klass_at_put(_this_class_index, ik); | |
| } | |
| ik->set_minor_version(_minor_version); | |
| ik->set_major_version(_major_version); | |
| ik->set_has_nonstatic_concrete_methods(_has_nonstatic_concrete_methods); | |
| ik->set_declares_nonstatic_concrete_methods(_declares_nonstatic_concrete_methods); | |
| assert(!_is_hidden || ik->is_hidden(), "must be set already"); | |
| // Set PackageEntry for this_klass | |
| oop cl = ik->class_loader(); | |
| Handle clh = Handle(THREAD, cl); | |
| ClassLoaderData* cld = ClassLoaderData::class_loader_data_or_null(clh()); | |
| ik->set_package(cld, nullptr, CHECK); | |
| const Array<Method*>* const methods = ik->methods(); | |
| assert(methods != nullptr, "invariant"); | |
| const int methods_len = methods->length(); | |
| check_methods_for_intrinsics(ik, methods); | |
| // Fill in field values obtained by parse_classfile_attributes | |
| if (_parsed_annotations->has_any_annotations()) { | |
| _parsed_annotations->apply_to(ik); | |
| } | |
| apply_parsed_class_attributes(ik); | |
| // Miranda methods | |
| if ((_num_miranda_methods > 0) || | |
| // if this class introduced new miranda methods or | |
| (_super_klass != nullptr && _super_klass->has_miranda_methods()) | |
| // super class exists and this class inherited miranda methods | |
| ) { | |
| ik->set_has_miranda_methods(); // then set a flag | |
| } | |
| // Fill in information needed to compute superclasses. | |
| ik->initialize_supers(const_cast<InstanceKlass*>(_super_klass), _transitive_interfaces, CHECK); | |
| ik->set_transitive_interfaces(_transitive_interfaces); | |
| ik->set_local_interfaces(_local_interfaces); | |
| _transitive_interfaces = nullptr; | |
| _local_interfaces = nullptr; | |
| // Initialize itable offset tables | |
| klassItable::setup_itable_offset_table(ik); | |
| // Compute transitive closure of interfaces this class implements | |
| // Do final class setup | |
| OopMapBlocksBuilder* oop_map_blocks = _field_info->oop_map_blocks; | |
| if (oop_map_blocks->_nonstatic_oop_map_count > 0) { | |
| oop_map_blocks->copy(ik->start_of_nonstatic_oop_maps()); | |
| } | |
| if (_has_contended_fields || _parsed_annotations->is_contended() || | |
| ( _super_klass != nullptr && _super_klass->has_contended_annotations())) { | |
| ik->set_has_contended_annotations(true); | |
| } | |
| // Fill in has_finalizer and layout_helper | |
| set_precomputed_flags(ik); | |
| // check if this class can access its super class | |
| check_super_class_access(ik, CHECK); | |
| // check if this class can access its superinterfaces | |
| check_super_interface_access(ik, CHECK); | |
| // check if this class overrides any final method | |
| check_final_method_override(ik, CHECK); | |
| // reject static interface methods prior to Java 8 | |
| if (ik->is_interface() && _major_version < JAVA_8_VERSION) { | |
| check_illegal_static_method(ik, CHECK); | |
| } | |
| // Obtain this_klass' module entry | |
| ModuleEntry* module_entry = ik->module(); | |
| assert(module_entry != nullptr, "module_entry should always be set"); | |
| // Obtain java.lang.Module | |
| Handle module_handle(THREAD, module_entry->module()); | |
| // Allocate mirror and initialize static fields | |
| java_lang_Class::create_mirror(ik, | |
| Handle(THREAD, _loader_data->class_loader()), | |
| module_handle, | |
| _protection_domain, | |
| cl_inst_info.class_data(), | |
| CHECK); | |
| assert(_all_mirandas != nullptr, "invariant"); | |
| // Generate any default methods - default methods are public interface methods | |
| // that have a default implementation. This is new with Java 8. | |
| if (_has_nonstatic_concrete_methods) { | |
| DefaultMethods::generate_default_methods(ik, | |
| _all_mirandas, | |
| CHECK); | |
| } | |
| // Add read edges to the unnamed modules of the bootstrap and app class loaders. | |
| if (changed_by_loadhook && !module_handle.is_null() && module_entry->is_named() && | |
| !module_entry->has_default_read_edges()) { | |
| if (!module_entry->set_has_default_read_edges()) { | |
| // We won a potential race | |
| JvmtiExport::add_default_read_edges(module_handle, THREAD); | |
| } | |
| } | |
| ClassLoadingService::notify_class_loaded(ik, false /* not shared class */); | |
| if (!is_internal()) { | |
| ik->print_class_load_logging(_loader_data, module_entry, _stream); | |
| if (ik->minor_version() == JAVA_PREVIEW_MINOR_VERSION && | |
| ik->major_version() == JVM_CLASSFILE_MAJOR_VERSION && | |
| log_is_enabled(Info, class, preview)) { | |
| ResourceMark rm; | |
| log_info(class, preview)("Loading class %s that depends on preview features (class file version %d.65535)", | |
| ik->external_name(), JVM_CLASSFILE_MAJOR_VERSION); | |
| } | |
| if (log_is_enabled(Debug, class, resolve)) { | |
| ResourceMark rm; | |
| // print out the superclass. | |
| const char * from = ik->external_name(); | |
| if (ik->java_super() != nullptr) { | |
| log_debug(class, resolve)("%s %s (super)", | |
| from, | |
| ik->java_super()->external_name()); | |
| } | |
| // print out each of the interface classes referred to by this class. | |
| const Array<InstanceKlass*>* const local_interfaces = ik->local_interfaces(); | |
| if (local_interfaces != nullptr) { | |
| const int length = local_interfaces->length(); | |
| for (int i = 0; i < length; i++) { | |
| const InstanceKlass* const k = local_interfaces->at(i); | |
| const char * to = k->external_name(); | |
| log_debug(class, resolve)("%s %s (interface)", from, to); | |
| } | |
| } | |
| } | |
| } | |
| JFR_ONLY(INIT_ID(ik);) | |
| // If we reach here, all is well. | |
| // Now remove the InstanceKlass* from the _klass_to_deallocate field | |
| // in order for it to not be destroyed in the ClassFileParser destructor. | |
| set_klass_to_deallocate(nullptr); | |
| // it's official | |
| set_klass(ik); | |
| DEBUG_ONLY(ik->verify();) | |
| } | |
| void ClassFileParser::update_class_name(Symbol* new_class_name) { | |
| // Decrement the refcount in the old name, since we're clobbering it. | |
| _class_name->decrement_refcount(); | |
| _class_name = new_class_name; | |
| // Increment the refcount of the new name. | |
| // Now the ClassFileParser owns this name and will decrement in | |
| // the destructor. | |
| _class_name->increment_refcount(); | |
| } | |
| ClassFileParser::ClassFileParser(ClassFileStream* stream, | |
| Symbol* name, | |
| ClassLoaderData* loader_data, | |
| const ClassLoadInfo* cl_info, | |
| Publicity pub_level, | |
| TRAPS) : | |
| _stream(stream), | |
| _class_name(nullptr), | |
| _loader_data(loader_data), | |
| _is_hidden(cl_info->is_hidden()), | |
| _can_access_vm_annotations(cl_info->can_access_vm_annotations()), | |
| _orig_cp_size(0), | |
| _static_oop_count(0), | |
| _super_klass(), | |
| _cp(nullptr), | |
| _fieldinfo_stream(nullptr), | |
| _fieldinfo_search_table(nullptr), | |
| _fields_status(nullptr), | |
| _methods(nullptr), | |
| _inner_classes(nullptr), | |
| _nest_members(nullptr), | |
| _nest_host(0), | |
| _permitted_subclasses(nullptr), | |
| _record_components(nullptr), | |
| _local_interfaces(nullptr), | |
| _transitive_interfaces(nullptr), | |
| _combined_annotations(nullptr), | |
| _class_annotations(nullptr), | |
| _class_type_annotations(nullptr), | |
| _fields_annotations(nullptr), | |
| _fields_type_annotations(nullptr), | |
| _klass(nullptr), | |
| _klass_to_deallocate(nullptr), | |
| _parsed_annotations(nullptr), | |
| _field_info(nullptr), | |
| _temp_field_info(nullptr), | |
| _method_ordering(nullptr), | |
| _all_mirandas(nullptr), | |
| _vtable_size(0), | |
| _itable_size(0), | |
| _num_miranda_methods(0), | |
| _protection_domain(cl_info->protection_domain()), | |
| _access_flags(), | |
| _pub_level(pub_level), | |
| _bad_constant_seen(0), | |
| _synthetic_flag(false), | |
| _sde_length(false), | |
| _sde_buffer(nullptr), | |
| _sourcefile_index(0), | |
| _generic_signature_index(0), | |
| _major_version(0), | |
| _minor_version(0), | |
| _this_class_index(0), | |
| _super_class_index(0), | |
| _itfs_len(0), | |
| _java_fields_count(0), | |
| _need_verify(false), | |
| _has_nonstatic_concrete_methods(false), | |
| _declares_nonstatic_concrete_methods(false), | |
| _has_localvariable_table(false), | |
| _has_final_method(false), | |
| _has_contended_fields(false), | |
| _has_finalizer(false), | |
| _has_empty_finalizer(false), | |
| _max_bootstrap_specifier_index(-1) { | |
| _class_name = name != nullptr ? name : vmSymbols::unknown_class_name(); | |
| _class_name->increment_refcount(); | |
| assert(_loader_data != nullptr, "invariant"); | |
| assert(stream != nullptr, "invariant"); | |
| assert(_stream != nullptr, "invariant"); | |
| assert(_stream->buffer() == _stream->current(), "invariant"); | |
| assert(_class_name != nullptr, "invariant"); | |
| assert(0 == _access_flags.as_unsigned_short(), "invariant"); | |
| // Figure out whether we can skip format checking (matching classic VM behavior) | |
| // Always verify CFLH bytes from the user agents. | |
| _need_verify = stream->from_class_file_load_hook() ? true : Verifier::should_verify_for(_loader_data->class_loader()); | |
| // synch back verification state to stream to check for truncation. | |
| stream->set_need_verify(_need_verify); | |
| parse_stream(stream, CHECK); | |
| post_process_parsed_stream(stream, _cp, CHECK); | |
| } | |
| void ClassFileParser::clear_class_metadata() { | |
| // metadata created before the instance klass is created. Must be | |
| // deallocated if classfile parsing returns an error. | |
| _cp = nullptr; | |
| _fieldinfo_stream = nullptr; | |
| _fieldinfo_search_table = nullptr; | |
| _fields_status = nullptr; | |
| _methods = nullptr; | |
| _inner_classes = nullptr; | |
| _nest_members = nullptr; | |
| _permitted_subclasses = nullptr; | |
| _combined_annotations = nullptr; | |
| _class_annotations = _class_type_annotations = nullptr; | |
| _fields_annotations = _fields_type_annotations = nullptr; | |
| _record_components = nullptr; | |
| } | |
| // Destructor to clean up | |
| ClassFileParser::~ClassFileParser() { | |
| _class_name->decrement_refcount(); | |
| if (_cp != nullptr) { | |
| MetadataFactory::free_metadata(_loader_data, _cp); | |
| } | |
| if (_fieldinfo_stream != nullptr) { | |
| MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_stream); | |
| } | |
| MetadataFactory::free_array<u1>(_loader_data, _fieldinfo_search_table); | |
| if (_fields_status != nullptr) { | |
| MetadataFactory::free_array<FieldStatus>(_loader_data, _fields_status); | |
| } | |
| if (_methods != nullptr) { | |
| // Free methods | |
| InstanceKlass::deallocate_methods(_loader_data, _methods); | |
| } | |
| // beware of the Universe::empty_blah_array!! | |
| if (_inner_classes != nullptr && _inner_classes != Universe::the_empty_short_array()) { | |
| MetadataFactory::free_array<u2>(_loader_data, _inner_classes); | |
| } | |
| if (_nest_members != nullptr && _nest_members != Universe::the_empty_short_array()) { | |
| MetadataFactory::free_array<u2>(_loader_data, _nest_members); | |
| } | |
| if (_record_components != nullptr) { | |
| InstanceKlass::deallocate_record_components(_loader_data, _record_components); | |
| } | |
| if (_permitted_subclasses != nullptr && _permitted_subclasses != Universe::the_empty_short_array()) { | |
| MetadataFactory::free_array<u2>(_loader_data, _permitted_subclasses); | |
| } | |
| // Free interfaces | |
| InstanceKlass::deallocate_interfaces(_loader_data, _super_klass, | |
| _local_interfaces, _transitive_interfaces); | |
| if (_combined_annotations != nullptr) { | |
| // After all annotations arrays have been created, they are installed into the | |
| // Annotations object that will be assigned to the InstanceKlass being created. | |
| // Deallocate the Annotations object and the installed annotations arrays. | |
| _combined_annotations->deallocate_contents(_loader_data); | |
| // If the _combined_annotations pointer is non-null, | |
| // then the other annotations fields should have been cleared. | |
| assert(_class_annotations == nullptr, "Should have been cleared"); | |
| assert(_class_type_annotations == nullptr, "Should have been cleared"); | |
| assert(_fields_annotations == nullptr, "Should have been cleared"); | |
| assert(_fields_type_annotations == nullptr, "Should have been cleared"); | |
| } else { | |
| // If the annotations arrays were not installed into the Annotations object, | |
| // then they have to be deallocated explicitly. | |
| MetadataFactory::free_array<u1>(_loader_data, _class_annotations); | |
| MetadataFactory::free_array<u1>(_loader_data, _class_type_annotations); | |
| Annotations::free_contents(_loader_data, _fields_annotations); | |
| Annotations::free_contents(_loader_data, _fields_type_annotations); | |
| } | |
| clear_class_metadata(); | |
| _transitive_interfaces = nullptr; | |
| _local_interfaces = nullptr; | |
| // deallocate the klass if already created. Don't directly deallocate, but add | |
| // to the deallocate list so that the klass is removed from the CLD::_klasses list | |
| // at a safepoint. | |
| if (_klass_to_deallocate != nullptr) { | |
| _loader_data->add_to_deallocate_list(_klass_to_deallocate); | |
| } | |
| } | |
| void ClassFileParser::parse_stream(const ClassFileStream* const stream, | |
| TRAPS) { | |
| assert(stream != nullptr, "invariant"); | |
| assert(_class_name != nullptr, "invariant"); | |
| // BEGIN STREAM PARSING | |
| stream->guarantee_more(8, CHECK); // magic, major, minor | |
| // Magic value | |
| const u4 magic = stream->get_u4_fast(); | |
| guarantee_property(magic == JAVA_CLASSFILE_MAGIC, | |
| "Incompatible magic value %u in class file %s", | |
| magic, CHECK); | |
| // Version numbers | |
| _minor_version = stream->get_u2_fast(); | |
| _major_version = stream->get_u2_fast(); | |
| // Check version numbers - we check this even with verifier off | |
| verify_class_version(_major_version, _minor_version, _class_name, CHECK); | |
| stream->guarantee_more(3, CHECK); // length, first cp tag | |
| u2 cp_size = stream->get_u2_fast(); | |
| guarantee_property( | |
| cp_size >= 1, "Illegal constant pool size %u in class file %s", | |
| cp_size, CHECK); | |
| _orig_cp_size = cp_size; | |
| if (is_hidden()) { // Add a slot for hidden class name. | |
| cp_size++; | |
| } | |
| _cp = ConstantPool::allocate(_loader_data, | |
| cp_size, | |
| CHECK); | |
| ConstantPool* const cp = _cp; | |
| parse_constant_pool(stream, cp, _orig_cp_size, CHECK); | |
| assert(cp_size == (u2)cp->length(), "invariant"); | |
| // ACCESS FLAGS | |
| stream->guarantee_more(8, CHECK); // flags, this_class, super_class, infs_len | |
| // Access flags | |
| u2 flags; | |
| // JVM_ACC_MODULE is defined in JDK-9 and later. | |
| if (_major_version >= JAVA_9_VERSION) { | |
| flags = stream->get_u2_fast() & (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_MODULE); | |
| } else { | |
| flags = stream->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS; | |
| } | |
| if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) { | |
| // Set abstract bit for old class files for backward compatibility | |
| flags |= JVM_ACC_ABSTRACT; | |
| } | |
| verify_legal_class_modifiers(flags, CHECK); | |
| short bad_constant = class_bad_constant_seen(); | |
| if (bad_constant != 0) { | |
| // Do not throw CFE until after the access_flags are checked because if | |
| // ACC_MODULE is set in the access flags, then NCDFE must be thrown, not CFE. | |
| classfile_parse_error("Unknown constant tag %u in class file %s", bad_constant, THREAD); | |
| return; | |
| } | |
| _access_flags.set_flags(flags); | |
| // This class and superclass | |
| _this_class_index = stream->get_u2_fast(); | |
| guarantee_property( | |
| valid_cp_range(_this_class_index, cp_size) && | |
| cp->tag_at(_this_class_index).is_unresolved_klass(), | |
| "Invalid this class index %u in constant pool in class file %s", | |
| _this_class_index, CHECK); | |
| Symbol* const class_name_in_cp = cp->klass_name_at(_this_class_index); | |
| assert(class_name_in_cp != nullptr, "class_name can't be null"); | |
| // Don't need to check whether this class name is legal or not. | |
| // It has been checked when constant pool is parsed. | |
| // However, make sure it is not an array type. | |
| if (_need_verify) { | |
| guarantee_property(class_name_in_cp->char_at(0) != JVM_SIGNATURE_ARRAY, | |
| "Bad class name in class file %s", | |
| CHECK); | |
| } | |
| #ifdef ASSERT | |
| // Basic sanity checks | |
| if (_is_hidden) { | |
| assert(_class_name != vmSymbols::unknown_class_name(), "hidden classes should have a special name"); | |
| } | |
| #endif | |
| // Update the _class_name as needed depending on whether this is a named, un-named, or hidden class. | |
| if (_is_hidden) { | |
| assert(_class_name != nullptr, "Unexpected null _class_name"); | |
| #ifdef ASSERT | |
| if (_need_verify) { | |
| verify_legal_class_name(_class_name, CHECK); | |
| } | |
| #endif | |
| } else { | |
| // Check if name in class file matches given name | |
| if (_class_name != class_name_in_cp) { | |
| if (_class_name != vmSymbols::unknown_class_name()) { | |
| ResourceMark rm(THREAD); | |
| // Names are all known to be < 64k so we know this formatted message is not excessively large. | |
| Exceptions::fthrow(THREAD_AND_LOCATION, | |
| vmSymbols::java_lang_NoClassDefFoundError(), | |
| "%s (wrong name: %s)", | |
| _class_name->as_C_string(), | |
| class_name_in_cp->as_C_string() | |
| ); | |
| return; | |
| } else { | |
| // The class name was not known by the caller so we set it from | |
| // the value in the CP. | |
| update_class_name(class_name_in_cp); | |
| } | |
| // else nothing to do: the expected class name matches what is in the CP | |
| } | |
| } | |
| // Verification prevents us from creating names with dots in them, this | |
| // asserts that that's the case. | |
| assert(is_internal_format(_class_name), "external class name format used internally"); | |
| if (!is_internal()) { | |
| LogTarget(Debug, class, preorder) lt; | |
| if (lt.is_enabled()){ | |
| ResourceMark rm(THREAD); | |
| LogStream ls(lt); | |
| ls.print("%s", _class_name->as_klass_external_name()); | |
| if (stream->source() != nullptr) { | |
| ls.print(" source: %s", stream->source()); | |
| } | |
| ls.cr(); | |
| } | |
| } | |
| // SUPERKLASS | |
| _super_class_index = stream->get_u2_fast(); | |
| _super_klass = parse_super_class(cp, | |
| _super_class_index, | |
| _need_verify, | |
| CHECK); | |
| // Interfaces | |
| _itfs_len = stream->get_u2_fast(); | |
| parse_interfaces(stream, | |
| _itfs_len, | |
| cp, | |
| &_has_nonstatic_concrete_methods, | |
| CHECK); | |
| assert(_local_interfaces != nullptr, "invariant"); | |
| // Fields (offsets are filled in later) | |
| parse_fields(stream, | |
| _access_flags.is_interface(), | |
| cp, | |
| cp_size, | |
| &_java_fields_count, | |
| CHECK); | |
| assert(_temp_field_info != nullptr, "invariant"); | |
| // Methods | |
| parse_methods(stream, | |
| _access_flags.is_interface(), | |
| &_has_localvariable_table, | |
| &_has_final_method, | |
| &_declares_nonstatic_concrete_methods, | |
| CHECK); | |
| assert(_methods != nullptr, "invariant"); | |
| if (_declares_nonstatic_concrete_methods) { | |
| _has_nonstatic_concrete_methods = true; | |
| } | |
| // Additional attributes/annotations | |
| _parsed_annotations = new ClassAnnotationCollector(); | |
| parse_classfile_attributes(stream, cp, _parsed_annotations, CHECK); | |
| assert(_inner_classes != nullptr, "invariant"); | |
| // Finalize the Annotations metadata object, | |
| // now that all annotation arrays have been created. | |
| create_combined_annotations(CHECK); | |
| // Make sure this is the end of class file stream | |
| guarantee_property(stream->at_eos(), | |
| "Extra bytes at the end of class file %s", | |
| CHECK); | |
| // all bytes in stream read and parsed | |
| } | |
| void ClassFileParser::mangle_hidden_class_name(InstanceKlass* const ik) { | |
| ResourceMark rm; | |
| // Construct hidden name from _class_name, "+", and &ik. Note that we can't | |
| // use a '/' because that confuses finding the class's package. Also, can't | |
| // use an illegal char such as ';' because that causes serialization issues | |
| // and issues with hidden classes that create their own hidden classes. | |
| char addr_buf[20]; | |
| if (CDSConfig::is_dumping_static_archive()) { | |
| // We want stable names for the archived hidden classes (only for static | |
| // archive for now). Spaces under default_SharedBaseAddress() will be | |
| // occupied by the archive at run time, so we know that no dynamically | |
| // loaded InstanceKlass will be placed under there. | |
| static volatile size_t counter = 0; | |
| Atomic::cmpxchg(&counter, (size_t)0, Arguments::default_SharedBaseAddress()); // initialize it | |
| size_t new_id = Atomic::add(&counter, (size_t)1); | |
| jio_snprintf(addr_buf, 20, "0x%zx", new_id); | |
| } else { | |
| jio_snprintf(addr_buf, 20, INTPTR_FORMAT, p2i(ik)); | |
| } | |
| size_t new_name_len = _class_name->utf8_length() + 2 + strlen(addr_buf); | |
| char* new_name = NEW_RESOURCE_ARRAY(char, new_name_len); | |
| jio_snprintf(new_name, new_name_len, "%s+%s", | |
| _class_name->as_C_string(), addr_buf); | |
| update_class_name(SymbolTable::new_symbol(new_name)); | |
| // Add a Utf8 entry containing the hidden name. | |
| assert(_class_name != nullptr, "Unexpected null _class_name"); | |
| int hidden_index = _orig_cp_size; // this is an extra slot we added | |
| _cp->symbol_at_put(hidden_index, _class_name); | |
| // Update this_class_index's slot in the constant pool with the new Utf8 entry. | |
| // We have to update the resolved_klass_index and the name_index together | |
| // so extract the existing resolved_klass_index first. | |
| CPKlassSlot cp_klass_slot = _cp->klass_slot_at(_this_class_index); | |
| int resolved_klass_index = cp_klass_slot.resolved_klass_index(); | |
| _cp->unresolved_klass_at_put(_this_class_index, hidden_index, resolved_klass_index); | |
| assert(_cp->klass_slot_at(_this_class_index).name_index() == _orig_cp_size, | |
| "Bad name_index"); | |
| } | |
| void ClassFileParser::post_process_parsed_stream(const ClassFileStream* const stream, | |
| ConstantPool* cp, | |
| TRAPS) { | |
| assert(stream != nullptr, "invariant"); | |
| assert(stream->at_eos(), "invariant"); | |
| assert(cp != nullptr, "invariant"); | |
| assert(_loader_data != nullptr, "invariant"); | |
| if (_class_name == vmSymbols::java_lang_Object()) { | |
| guarantee_property(_local_interfaces == Universe::the_empty_instance_klass_array(), | |
| "java.lang.Object cannot implement an interface in class file %s", | |
| CHECK); | |
| } | |
| // We check super class after class file is parsed and format is checked | |
| if (_super_class_index > 0 && nullptr == _super_klass) { | |
| Symbol* const super_class_name = cp->klass_name_at(_super_class_index); | |
| if (_access_flags.is_interface()) { | |
| // Before attempting to resolve the superclass, check for class format | |
| // errors not checked yet. | |
| guarantee_property(super_class_name == vmSymbols::java_lang_Object(), | |
| "Interfaces must have java.lang.Object as superclass in class file %s", | |
| CHECK); | |
| } | |
| Handle loader(THREAD, _loader_data->class_loader()); | |
| if (loader.is_null() && super_class_name == vmSymbols::java_lang_Object()) { | |
| _super_klass = vmClasses::Object_klass(); | |
| } else { | |
| _super_klass = (const InstanceKlass*) | |
| SystemDictionary::resolve_super_or_fail(_class_name, | |
| super_class_name, | |
| loader, | |
| true, | |
| CHECK); | |
| } | |
| } | |
| if (_super_klass != nullptr) { | |
| if (_super_klass->has_nonstatic_concrete_methods()) { | |
| _has_nonstatic_concrete_methods = true; | |
| } | |
| if (_super_klass->is_interface()) { | |
| classfile_icce_error("class %s has interface %s as super class", _super_klass, THREAD); | |
| return; | |
| } | |
| } | |
| // Compute the transitive list of all unique interfaces implemented by this class | |
| _transitive_interfaces = | |
| compute_transitive_interfaces(_super_klass, | |
| _local_interfaces, | |
| _loader_data, | |
| CHECK); | |
| assert(_transitive_interfaces != nullptr, "invariant"); | |
| // sort methods | |
| _method_ordering = sort_methods(_methods); | |
| _all_mirandas = new GrowableArray<Method*>(20); | |
| Handle loader(THREAD, _loader_data->class_loader()); | |
| klassVtable::compute_vtable_size_and_num_mirandas(&_vtable_size, | |
| &_num_miranda_methods, | |
| _all_mirandas, | |
| _super_klass, | |
| _methods, | |
| _access_flags, | |
| _major_version, | |
| loader, | |
| _class_name, | |
| _local_interfaces); | |
| // Size of Java itable (in words) | |
| _itable_size = _access_flags.is_interface() ? 0 : | |
| klassItable::compute_itable_size(_transitive_interfaces); | |
| assert(_parsed_annotations != nullptr, "invariant"); | |
| _field_info = new FieldLayoutInfo(); | |
| FieldLayoutBuilder lb(class_name(), super_klass(), _cp, /*_fields*/ _temp_field_info, | |
| _parsed_annotations->is_contended(), _field_info); | |
| lb.build_layout(); | |
| int injected_fields_count = _temp_field_info->length() - _java_fields_count; | |
| _fieldinfo_stream = | |
| FieldInfoStream::create_FieldInfoStream(_temp_field_info, _java_fields_count, | |
| injected_fields_count, loader_data(), CHECK); | |
| _fieldinfo_search_table = FieldInfoStream::create_search_table(_cp, _fieldinfo_stream, _loader_data, CHECK); | |
| _fields_status = | |
| MetadataFactory::new_array<FieldStatus>(_loader_data, _temp_field_info->length(), | |
| FieldStatus(0), CHECK); | |
| } | |
| void ClassFileParser::set_klass(InstanceKlass* klass) { | |
| #ifdef ASSERT | |
| if (klass != nullptr) { | |
| assert(nullptr == _klass, "leaking?"); | |
| } | |
| #endif | |
| _klass = klass; | |
| } | |
| void ClassFileParser::set_klass_to_deallocate(InstanceKlass* klass) { | |
| #ifdef ASSERT | |
| if (klass != nullptr) { | |
| assert(nullptr == _klass_to_deallocate, "leaking?"); | |
| } | |
| #endif | |
| _klass_to_deallocate = klass; | |
| } | |
| // Caller responsible for ResourceMark | |
| // clone stream with rewound position | |
| const ClassFileStream* ClassFileParser::clone_stream() const { | |
| assert(_stream != nullptr, "invariant"); | |
| return _stream->clone(); | |
| } | |
| ReferenceType ClassFileParser::super_reference_type() const { | |
| return _super_klass == nullptr ? REF_NONE : _super_klass->reference_type(); | |
| } | |
| bool ClassFileParser::is_instance_ref_klass() const { | |
| // Only the subclasses of j.l.r.Reference are InstanceRefKlass. | |
| // j.l.r.Reference itself is InstanceKlass because InstanceRefKlass denotes a | |
| // klass requiring special treatment in ref-processing. The abstract | |
| // j.l.r.Reference cannot be instantiated so doesn't partake in | |
| // ref-processing. | |
| return is_java_lang_ref_Reference_subclass(); | |
| } | |
| bool ClassFileParser::is_java_lang_ref_Reference_subclass() const { | |
| if (_super_klass == nullptr) { | |
| return false; | |
| } | |
| if (_super_klass->name() == vmSymbols::java_lang_ref_Reference()) { | |
| // Direct subclass of j.l.r.Reference: Soft|Weak|Final|Phantom | |
| return true; | |
| } | |
| return _super_klass->reference_type() != REF_NONE; | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // debugging | |
| #ifdef ASSERT | |
| // return true if class_name contains no '.' (internal format is '/') | |
| bool ClassFileParser::is_internal_format(Symbol* class_name) { | |
| if (class_name != nullptr) { | |
| ResourceMark rm; | |
| char* name = class_name->as_C_string(); | |
| return strchr(name, JVM_SIGNATURE_DOT) == nullptr; | |
| } else { | |
| return true; | |
| } | |
| } | |
| #endif |