| /* |
| * Copyright (c) 1997, 2014, 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 "precompiled.hpp" |
| #include "classfile/classFileParser.hpp" |
| #include "classfile/classLoader.hpp" |
| #include "classfile/classLoaderData.hpp" |
| #include "classfile/classLoaderData.inline.hpp" |
| #include "classfile/defaultMethods.hpp" |
| #include "classfile/javaClasses.hpp" |
| #include "classfile/symbolTable.hpp" |
| #include "classfile/systemDictionary.hpp" |
| #include "classfile/verificationType.hpp" |
| #include "classfile/verifier.hpp" |
| #include "classfile/vmSymbols.hpp" |
| #include "memory/allocation.hpp" |
| #include "memory/gcLocker.hpp" |
| #include "memory/metadataFactory.hpp" |
| #include "memory/oopFactory.hpp" |
| #include "memory/referenceType.hpp" |
| #include "memory/universe.inline.hpp" |
| #include "oops/constantPool.hpp" |
| #include "oops/fieldStreams.hpp" |
| #include "oops/instanceKlass.hpp" |
| #include "oops/instanceMirrorKlass.hpp" |
| #include "oops/klass.inline.hpp" |
| #include "oops/klassVtable.hpp" |
| #include "oops/method.hpp" |
| #include "oops/symbol.hpp" |
| #include "prims/jvm.h" |
| #include "prims/jvmtiExport.hpp" |
| #include "prims/jvmtiThreadState.hpp" |
| #include "runtime/javaCalls.hpp" |
| #include "runtime/perfData.hpp" |
| #include "runtime/reflection.hpp" |
| #include "runtime/signature.hpp" |
| #include "runtime/timer.hpp" |
| #include "services/classLoadingService.hpp" |
| #include "services/threadService.hpp" |
| #include "utilities/array.hpp" |
| #include "utilities/globalDefinitions.hpp" |
| |
| // 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_MAX_SUPPORTED_VERSION 52 |
| #define JAVA_MAX_SUPPORTED_MINOR_VERSION 0 |
| |
| // 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 check NameAndType_info signatures more aggressively |
| #define JAVA_7_VERSION 51 |
| |
| // Extension method support. |
| #define JAVA_8_VERSION 52 |
| |
| void ClassFileParser::parse_constant_pool_entries(int length, TRAPS) { |
| // 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(). |
| ClassFileStream* cfs0 = stream(); |
| ClassFileStream cfs1 = *cfs0; |
| ClassFileStream* cfs = &cfs1; |
| #ifdef ASSERT |
| assert(cfs->allocated_on_stack(),"should be local"); |
| u1* old_current = cfs0->current(); |
| #endif |
| Handle class_loader(THREAD, _loader_data->class_loader()); |
| |
| // 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. |
| u1 tag = cfs->get_u1_fast(); |
| switch (tag) { |
| case JVM_CONSTANT_Class : |
| { |
| cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags |
| 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 |
| u2 class_index = cfs->get_u2_fast(); |
| 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 |
| u2 class_index = cfs->get_u2_fast(); |
| 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 |
| u2 class_index = cfs->get_u2_fast(); |
| 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 |
| 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, CHECK); |
| } |
| if (!EnableInvokeDynamic) { |
| classfile_parse_error( |
| "This JVM does not support constant tag %u in class file %s", |
| tag, CHECK); |
| } |
| if (tag == JVM_CONSTANT_MethodHandle) { |
| cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags |
| u1 ref_kind = cfs->get_u1_fast(); |
| 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 |
| u2 signature_index = cfs->get_u2_fast(); |
| _cp->method_type_index_at_put(index, signature_index); |
| } else { |
| ShouldNotReachHere(); |
| } |
| 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, CHECK); |
| } |
| if (!EnableInvokeDynamic) { |
| classfile_parse_error( |
| "This JVM does not support constant tag %u in class file %s", |
| tag, CHECK); |
| } |
| cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags |
| u2 bootstrap_specifier_index = cfs->get_u2_fast(); |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| u2 name_index = cfs->get_u2_fast(); |
| 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(); |
| u1* utf8_buffer = cfs->get_u1_buffer(); |
| assert(utf8_buffer != NULL, "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((unsigned char*)utf8_buffer, utf8_length, CHECK); |
| } |
| |
| if (EnableInvokeDynamic && has_cp_patch_at(index)) { |
| Handle patch = clear_cp_patch_at(index); |
| guarantee_property(java_lang_String::is_instance(patch()), |
| "Illegal utf8 patch at %d in class file %s", |
| index, CHECK); |
| char* str = java_lang_String::as_utf8_string(patch()); |
| // (could use java_lang_String::as_symbol instead, but might as well batch them) |
| utf8_buffer = (u1*) str; |
| utf8_length = (int) strlen(str); |
| } |
| |
| unsigned int hash; |
| Symbol* result = SymbolTable::lookup_only((char*)utf8_buffer, utf8_length, hash); |
| if (result == NULL) { |
| names[names_count] = (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, _cp, names_count, names, lengths, indices, hashValues, CHECK); |
| names_count = 0; |
| } |
| } else { |
| _cp->symbol_at_put(index, result); |
| } |
| } |
| break; |
| default: |
| classfile_parse_error( |
| "Unknown constant tag %u in class file %s", tag, CHECK); |
| break; |
| } |
| } |
| |
| // Allocate the remaining symbols |
| if (names_count > 0) { |
| SymbolTable::new_symbols(_loader_data, _cp, names_count, names, lengths, indices, hashValues, CHECK); |
| } |
| |
| // Copy _current pointer of local copy back to stream(). |
| #ifdef ASSERT |
| assert(cfs0->current() == old_current, "non-exclusive use of stream()"); |
| #endif |
| cfs0->set_current(cfs1.current()); |
| } |
| |
| bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); } |
| |
| inline Symbol* check_symbol_at(constantPoolHandle cp, int index) { |
| if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8()) |
| return cp->symbol_at(index); |
| else |
| return NULL; |
| } |
| |
| constantPoolHandle ClassFileParser::parse_constant_pool(TRAPS) { |
| ClassFileStream* cfs = stream(); |
| constantPoolHandle nullHandle; |
| |
| cfs->guarantee_more(3, CHECK_(nullHandle)); // length, first cp tag |
| u2 length = cfs->get_u2_fast(); |
| guarantee_property( |
| length >= 1, "Illegal constant pool size %u in class file %s", |
| length, CHECK_(nullHandle)); |
| ConstantPool* constant_pool = ConstantPool::allocate(_loader_data, length, |
| CHECK_(nullHandle)); |
| _cp = constant_pool; // save in case of errors |
| constantPoolHandle cp (THREAD, constant_pool); |
| |
| // parsing constant pool entries |
| parse_constant_pool_entries(length, CHECK_(nullHandle)); |
| |
| int index = 1; // declared outside of loops for portability |
| |
| // first verification pass - validate cross references and fixup class and string constants |
| for (index = 1; index < length; index++) { // Index 0 is unused |
| 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; |
| int klass_ref_index = cp->klass_ref_index_at(index); |
| int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); |
| check_property(valid_klass_reference_at(klass_ref_index), |
| "Invalid constant pool index %u in class file %s", |
| klass_ref_index, |
| CHECK_(nullHandle)); |
| check_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_(nullHandle)); |
| 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++; |
| check_property( |
| (index < length && cp->tag_at(index).is_invalid()), |
| "Improper constant pool long/double index %u in class file %s", |
| index, CHECK_(nullHandle)); |
| break; |
| case JVM_CONSTANT_NameAndType : { |
| if (!_need_verify) break; |
| int name_ref_index = cp->name_ref_index_at(index); |
| int signature_ref_index = cp->signature_ref_index_at(index); |
| check_property(valid_symbol_at(name_ref_index), |
| "Invalid constant pool index %u in class file %s", |
| name_ref_index, CHECK_(nullHandle)); |
| check_property(valid_symbol_at(signature_ref_index), |
| "Invalid constant pool index %u in class file %s", |
| signature_ref_index, CHECK_(nullHandle)); |
| 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 : |
| { |
| int class_index = cp->klass_index_at(index); |
| check_property(valid_symbol_at(class_index), |
| "Invalid constant pool index %u in class file %s", |
| class_index, CHECK_(nullHandle)); |
| cp->unresolved_klass_at_put(index, cp->symbol_at(class_index)); |
| } |
| break; |
| case JVM_CONSTANT_StringIndex : |
| { |
| int string_index = cp->string_index_at(index); |
| check_property(valid_symbol_at(string_index), |
| "Invalid constant pool index %u in class file %s", |
| string_index, CHECK_(nullHandle)); |
| Symbol* sym = cp->symbol_at(string_index); |
| cp->unresolved_string_at_put(index, sym); |
| } |
| break; |
| case JVM_CONSTANT_MethodHandle : |
| { |
| int ref_index = cp->method_handle_index_at(index); |
| check_property( |
| valid_cp_range(ref_index, length) && |
| EnableInvokeDynamic, |
| "Invalid constant pool index %u in class file %s", |
| ref_index, CHECK_(nullHandle)); |
| constantTag tag = cp->tag_at(ref_index); |
| 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: |
| check_property( |
| tag.is_field(), |
| "Invalid constant pool index %u in class file %s (not a field)", |
| ref_index, CHECK_(nullHandle)); |
| break; |
| case JVM_REF_invokeVirtual: |
| case JVM_REF_newInvokeSpecial: |
| check_property( |
| tag.is_method(), |
| "Invalid constant pool index %u in class file %s (not a method)", |
| ref_index, CHECK_(nullHandle)); |
| break; |
| case JVM_REF_invokeStatic: |
| case JVM_REF_invokeSpecial: |
| check_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_(nullHandle)); |
| break; |
| case JVM_REF_invokeInterface: |
| check_property( |
| tag.is_interface_method(), |
| "Invalid constant pool index %u in class file %s (not an interface method)", |
| ref_index, CHECK_(nullHandle)); |
| break; |
| default: |
| classfile_parse_error( |
| "Bad method handle kind at constant pool index %u in class file %s", |
| index, CHECK_(nullHandle)); |
| } |
| // Keep the ref_index unchanged. It will be indirected at link-time. |
| } |
| break; |
| case JVM_CONSTANT_MethodType : |
| { |
| int ref_index = cp->method_type_index_at(index); |
| check_property(valid_symbol_at(ref_index) && EnableInvokeDynamic, |
| "Invalid constant pool index %u in class file %s", |
| ref_index, CHECK_(nullHandle)); |
| } |
| break; |
| case JVM_CONSTANT_InvokeDynamic : |
| { |
| int name_and_type_ref_index = cp->invoke_dynamic_name_and_type_ref_index_at(index); |
| check_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_(nullHandle)); |
| // bootstrap specifier index must be checked later, when BootstrapMethods attr is available |
| break; |
| } |
| default: |
| fatal(err_msg("bad constant pool tag value %u", |
| cp->tag_at(index).value())); |
| ShouldNotReachHere(); |
| break; |
| } // end of switch |
| } // end of for |
| |
| if (_cp_patches != NULL) { |
| // need to treat this_class specially... |
| assert(EnableInvokeDynamic, ""); |
| int this_class_index; |
| { |
| cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len |
| u1* mark = cfs->current(); |
| u2 flags = cfs->get_u2_fast(); |
| this_class_index = cfs->get_u2_fast(); |
| cfs->set_current(mark); // revert to mark |
| } |
| |
| for (index = 1; index < length; index++) { // Index 0 is unused |
| if (has_cp_patch_at(index)) { |
| guarantee_property(index != this_class_index, |
| "Illegal constant pool patch to self at %d in class file %s", |
| index, CHECK_(nullHandle)); |
| patch_constant_pool(cp, index, cp_patch_at(index), CHECK_(nullHandle)); |
| } |
| } |
| } |
| |
| if (!_need_verify) { |
| return cp; |
| } |
| |
| // second verification pass - checks the strings are of the right format. |
| // but not yet to the other entries |
| for (index = 1; index < length; index++) { |
| jbyte tag = cp->tag_at(index).value(); |
| switch (tag) { |
| case JVM_CONSTANT_UnresolvedClass: { |
| Symbol* class_name = cp->unresolved_klass_at(index); |
| // check the name, even if _cp_patches will overwrite it |
| verify_legal_class_name(class_name, CHECK_(nullHandle)); |
| break; |
| } |
| case JVM_CONSTANT_NameAndType: { |
| if (_need_verify && _major_version >= JAVA_7_VERSION) { |
| int sig_index = cp->signature_ref_index_at(index); |
| int name_index = cp->name_ref_index_at(index); |
| Symbol* name = cp->symbol_at(name_index); |
| Symbol* sig = cp->symbol_at(sig_index); |
| if (sig->byte_at(0) == JVM_SIGNATURE_FUNC) { |
| verify_legal_method_signature(name, sig, CHECK_(nullHandle)); |
| } else { |
| verify_legal_field_signature(name, sig, CHECK_(nullHandle)); |
| } |
| } |
| break; |
| } |
| case JVM_CONSTANT_InvokeDynamic: |
| case JVM_CONSTANT_Fieldref: |
| case JVM_CONSTANT_Methodref: |
| case JVM_CONSTANT_InterfaceMethodref: { |
| int name_and_type_ref_index = cp->name_and_type_ref_index_at(index); |
| // already verified to be utf8 |
| int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index); |
| // already verified to be utf8 |
| int signature_ref_index = cp->signature_ref_index_at(name_and_type_ref_index); |
| Symbol* name = cp->symbol_at(name_ref_index); |
| Symbol* signature = cp->symbol_at(signature_ref_index); |
| if (tag == JVM_CONSTANT_Fieldref) { |
| verify_legal_field_name(name, CHECK_(nullHandle)); |
| if (_need_verify && _major_version >= JAVA_7_VERSION) { |
| // Signature is verified above, when iterating NameAndType_info. |
| // Need only to be sure it's the right type. |
| if (signature->byte_at(0) == JVM_SIGNATURE_FUNC) { |
| throwIllegalSignature( |
| "Field", name, signature, CHECK_(nullHandle)); |
| } |
| } else { |
| verify_legal_field_signature(name, signature, CHECK_(nullHandle)); |
| } |
| } else { |
| verify_legal_method_name(name, CHECK_(nullHandle)); |
| if (_need_verify && _major_version >= JAVA_7_VERSION) { |
| // Signature is verified above, when iterating NameAndType_info. |
| // Need only to be sure it's the right type. |
| if (signature->byte_at(0) != JVM_SIGNATURE_FUNC) { |
| throwIllegalSignature( |
| "Method", name, signature, CHECK_(nullHandle)); |
| } |
| } else { |
| verify_legal_method_signature(name, signature, CHECK_(nullHandle)); |
| } |
| if (tag == JVM_CONSTANT_Methodref) { |
| // 4509014: If a class method name begins with '<', it must be "<init>". |
| assert(name != NULL, "method name in constant pool is null"); |
| unsigned int name_len = name->utf8_length(); |
| assert(name_len > 0, "bad method name"); // already verified as legal name |
| if (name->byte_at(0) == '<') { |
| if (name != vmSymbols::object_initializer_name()) { |
| classfile_parse_error( |
| "Bad method name at constant pool index %u in class file %s", |
| name_ref_index, CHECK_(nullHandle)); |
| } |
| } |
| } |
| } |
| break; |
| } |
| case JVM_CONSTANT_MethodHandle: { |
| int ref_index = cp->method_handle_index_at(index); |
| 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: |
| { |
| int name_and_type_ref_index = cp->name_and_type_ref_index_at(ref_index); |
| int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index); |
| Symbol* 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, CHECK_(nullHandle)); |
| } |
| } 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, CHECK_(nullHandle)); |
| } |
| } |
| } |
| break; |
| // Other ref_kinds are already fully checked in previous pass. |
| } |
| break; |
| } |
| case JVM_CONSTANT_MethodType: { |
| Symbol* no_name = vmSymbols::type_name(); // place holder |
| Symbol* signature = cp->method_type_signature_at(index); |
| verify_legal_method_signature(no_name, signature, CHECK_(nullHandle)); |
| break; |
| } |
| case JVM_CONSTANT_Utf8: { |
| assert(cp->symbol_at(index)->refcount() != 0, "count corrupted"); |
| } |
| } // end of switch |
| } // end of for |
| |
| return cp; |
| } |
| |
| |
| void ClassFileParser::patch_constant_pool(constantPoolHandle cp, int index, Handle patch, TRAPS) { |
| assert(EnableInvokeDynamic, ""); |
| BasicType patch_type = T_VOID; |
| |
| switch (cp->tag_at(index).value()) { |
| |
| case JVM_CONSTANT_UnresolvedClass : |
| // Patching a class means pre-resolving it. |
| // The name in the constant pool is ignored. |
| if (java_lang_Class::is_instance(patch())) { |
| guarantee_property(!java_lang_Class::is_primitive(patch()), |
| "Illegal class patch at %d in class file %s", |
| index, CHECK); |
| cp->klass_at_put(index, java_lang_Class::as_Klass(patch())); |
| } else { |
| guarantee_property(java_lang_String::is_instance(patch()), |
| "Illegal class patch at %d in class file %s", |
| index, CHECK); |
| Symbol* name = java_lang_String::as_symbol(patch(), CHECK); |
| cp->unresolved_klass_at_put(index, name); |
| } |
| break; |
| |
| case JVM_CONSTANT_String : |
| // skip this patch and don't clear it. Needs the oop array for resolved |
| // references to be created first. |
| return; |
| |
| case JVM_CONSTANT_Integer : patch_type = T_INT; goto patch_prim; |
| case JVM_CONSTANT_Float : patch_type = T_FLOAT; goto patch_prim; |
| case JVM_CONSTANT_Long : patch_type = T_LONG; goto patch_prim; |
| case JVM_CONSTANT_Double : patch_type = T_DOUBLE; goto patch_prim; |
| patch_prim: |
| { |
| jvalue value; |
| BasicType value_type = java_lang_boxing_object::get_value(patch(), &value); |
| guarantee_property(value_type == patch_type, |
| "Illegal primitive patch at %d in class file %s", |
| index, CHECK); |
| switch (value_type) { |
| case T_INT: cp->int_at_put(index, value.i); break; |
| case T_FLOAT: cp->float_at_put(index, value.f); break; |
| case T_LONG: cp->long_at_put(index, value.j); break; |
| case T_DOUBLE: cp->double_at_put(index, value.d); break; |
| default: assert(false, ""); |
| } |
| } |
| break; |
| |
| default: |
| // %%% TODO: put method handles into CONSTANT_InterfaceMethodref, etc. |
| guarantee_property(!has_cp_patch_at(index), |
| "Illegal unexpected patch at %d in class file %s", |
| index, CHECK); |
| return; |
| } |
| |
| // On fall-through, mark the patch as used. |
| clear_cp_patch_at(index); |
| } |
| |
| |
| |
| class NameSigHash: public ResourceObj { |
| public: |
| Symbol* _name; // name |
| Symbol* _sig; // signature |
| NameSigHash* _next; // Next entry in hash table |
| }; |
| |
| |
| #define HASH_ROW_SIZE 256 |
| |
| unsigned int hash(Symbol* name, Symbol* sig) { |
| unsigned int raw_hash = 0; |
| raw_hash += ((unsigned int)(uintptr_t)name) >> (LogHeapWordSize + 2); |
| raw_hash += ((unsigned int)(uintptr_t)sig) >> LogHeapWordSize; |
| |
| return (raw_hash + (unsigned int)(uintptr_t)name) % HASH_ROW_SIZE; |
| } |
| |
| |
| void initialize_hashtable(NameSigHash** table) { |
| memset((void*)table, 0, sizeof(NameSigHash*) * HASH_ROW_SIZE); |
| } |
| |
| // Return false if the name/sig combination is found in table. |
| // Return true if no duplicate is found. And name/sig is added as a new entry in table. |
| // The old format checker uses heap sort to find duplicates. |
| // NOTE: caller should guarantee that GC doesn't happen during the life cycle |
| // of table since we don't expect Symbol*'s to move. |
| bool put_after_lookup(Symbol* name, Symbol* sig, NameSigHash** table) { |
| assert(name != NULL, "name in constant pool is NULL"); |
| |
| // First lookup for duplicates |
| int index = hash(name, sig); |
| NameSigHash* entry = table[index]; |
| while (entry != NULL) { |
| if (entry->_name == name && entry->_sig == sig) { |
| return false; |
| } |
| entry = entry->_next; |
| } |
| |
| // No duplicate is found, allocate a new entry and fill it. |
| entry = new NameSigHash(); |
| entry->_name = name; |
| entry->_sig = sig; |
| |
| // Insert into hash table |
| entry->_next = table[index]; |
| table[index] = entry; |
| |
| return true; |
| } |
| |
| |
| Array<Klass*>* ClassFileParser::parse_interfaces(int length, |
| Handle protection_domain, |
| Symbol* class_name, |
| bool* has_default_methods, |
| TRAPS) { |
| if (length == 0) { |
| _local_interfaces = Universe::the_empty_klass_array(); |
| } else { |
| ClassFileStream* cfs = stream(); |
| assert(length > 0, "only called for length>0"); |
| _local_interfaces = MetadataFactory::new_array<Klass*>(_loader_data, length, NULL, CHECK_NULL); |
| |
| int index; |
| for (index = 0; index < length; index++) { |
| u2 interface_index = cfs->get_u2(CHECK_NULL); |
| KlassHandle interf; |
| check_property( |
| valid_klass_reference_at(interface_index), |
| "Interface name has bad constant pool index %u in class file %s", |
| interface_index, CHECK_NULL); |
| if (_cp->tag_at(interface_index).is_klass()) { |
| interf = KlassHandle(THREAD, _cp->resolved_klass_at(interface_index)); |
| } else { |
| Symbol* 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->byte_at(0) != JVM_SIGNATURE_ARRAY, |
| "Bad interface name in class file %s", CHECK_NULL); |
| Handle class_loader(THREAD, _loader_data->class_loader()); |
| |
| // Call resolve_super so classcircularity is checked |
| Klass* k = SystemDictionary::resolve_super_or_fail(class_name, |
| unresolved_klass, class_loader, protection_domain, |
| false, CHECK_NULL); |
| interf = KlassHandle(THREAD, k); |
| } |
| |
| if (!interf()->is_interface()) { |
| THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(), "Implementing class", NULL); |
| } |
| if (InstanceKlass::cast(interf())->has_default_methods()) { |
| *has_default_methods = true; |
| } |
| _local_interfaces->at_put(index, interf()); |
| } |
| |
| if (!_need_verify || length <= 1) { |
| return _local_interfaces; |
| } |
| |
| // Check if there's any duplicates in interfaces |
| ResourceMark rm(THREAD); |
| NameSigHash** interface_names = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, NameSigHash*, HASH_ROW_SIZE); |
| initialize_hashtable(interface_names); |
| bool dup = false; |
| { |
| debug_only(No_Safepoint_Verifier nsv;) |
| for (index = 0; index < length; index++) { |
| Klass* k = _local_interfaces->at(index); |
| Symbol* name = InstanceKlass::cast(k)->name(); |
| // If no duplicates, add (name, NULL) in hashtable interface_names. |
| if (!put_after_lookup(name, NULL, interface_names)) { |
| dup = true; |
| break; |
| } |
| } |
| } |
| if (dup) { |
| classfile_parse_error("Duplicate interface name in class file %s", CHECK_NULL); |
| } |
| } |
| return _local_interfaces; |
| } |
| |
| |
| void ClassFileParser::verify_constantvalue(int constantvalue_index, int signature_index, TRAPS) { |
| // 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); |
| 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, CHECK); |
| } |
| } |
| |
| |
| // Parse attributes for a field. |
| void ClassFileParser::parse_field_attributes(u2 attributes_count, |
| bool is_static, u2 signature_index, |
| u2* constantvalue_index_addr, |
| bool* is_synthetic_addr, |
| u2* generic_signature_index_addr, |
| ClassFileParser::FieldAnnotationCollector* parsed_annotations, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| assert(attributes_count > 0, "length should be greater than 0"); |
| u2 constantvalue_index = 0; |
| u2 generic_signature_index = 0; |
| bool is_synthetic = false; |
| u1* runtime_visible_annotations = NULL; |
| int runtime_visible_annotations_length = 0; |
| u1* runtime_invisible_annotations = NULL; |
| int runtime_invisible_annotations_length = 0; |
| u1* runtime_visible_type_annotations = NULL; |
| int runtime_visible_type_annotations_length = 0; |
| u1* runtime_invisible_type_annotations = NULL; |
| int runtime_invisible_type_annotations_length = 0; |
| bool runtime_invisible_type_annotations_exists = false; |
| while (attributes_count--) { |
| cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length |
| u2 attribute_name_index = cfs->get_u2_fast(); |
| u4 attribute_length = cfs->get_u4_fast(); |
| check_property(valid_symbol_at(attribute_name_index), |
| "Invalid field attribute index %u in class file %s", |
| attribute_name_index, |
| CHECK); |
| Symbol* 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", CHECK); |
| } |
| check_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(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, CHECK); |
| } |
| 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, CHECK); |
| } |
| } else if (_major_version >= JAVA_1_5_VERSION) { |
| if (attribute_name == vmSymbols::tag_signature()) { |
| if (attribute_length != 2) { |
| classfile_parse_error( |
| "Wrong size %u for field's Signature attribute in class file %s", |
| attribute_length, CHECK); |
| } |
| generic_signature_index = cfs->get_u2(CHECK); |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { |
| runtime_visible_annotations_length = attribute_length; |
| runtime_visible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_annotations != NULL, "null visible annotations"); |
| parse_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| parsed_annotations, |
| CHECK); |
| cfs->skip_u1(runtime_visible_annotations_length, CHECK); |
| } else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { |
| runtime_invisible_annotations_length = attribute_length; |
| runtime_invisible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_annotations != NULL, "null invisible annotations"); |
| cfs->skip_u1(runtime_invisible_annotations_length, CHECK); |
| } else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { |
| if (runtime_visible_type_annotations != NULL) { |
| classfile_parse_error( |
| "Multiple RuntimeVisibleTypeAnnotations attributes for field in class file %s", CHECK); |
| } |
| runtime_visible_type_annotations_length = attribute_length; |
| runtime_visible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_type_annotations != NULL, "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", CHECK); |
| } else { |
| runtime_invisible_type_annotations_exists = true; |
| } |
| if (PreserveAllAnnotations) { |
| runtime_invisible_type_annotations_length = attribute_length; |
| runtime_invisible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); |
| } |
| 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 = assemble_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| runtime_invisible_annotations, |
| runtime_invisible_annotations_length, |
| CHECK); |
| parsed_annotations->set_field_annotations(a); |
| a = assemble_annotations(runtime_visible_type_annotations, |
| runtime_visible_type_annotations_length, |
| runtime_invisible_type_annotations, |
| runtime_invisible_type_annotations_length, |
| CHECK); |
| parsed_annotations->set_field_type_annotations(a); |
| return; |
| } |
| |
| |
| // Field allocation types. Used for computing field offsets. |
| |
| enum FieldAllocationType { |
| STATIC_OOP, // Oops |
| STATIC_BYTE, // Boolean, Byte, char |
| STATIC_SHORT, // shorts |
| STATIC_WORD, // ints |
| STATIC_DOUBLE, // aligned long or double |
| NONSTATIC_OOP, |
| NONSTATIC_BYTE, |
| NONSTATIC_SHORT, |
| NONSTATIC_WORD, |
| NONSTATIC_DOUBLE, |
| MAX_FIELD_ALLOCATION_TYPE, |
| BAD_ALLOCATION_TYPE = -1 |
| }; |
| |
| static FieldAllocationType _basic_type_to_atype[2 * (T_CONFLICT + 1)] = { |
| BAD_ALLOCATION_TYPE, // 0 |
| BAD_ALLOCATION_TYPE, // 1 |
| BAD_ALLOCATION_TYPE, // 2 |
| BAD_ALLOCATION_TYPE, // 3 |
| NONSTATIC_BYTE , // T_BOOLEAN = 4, |
| NONSTATIC_SHORT, // T_CHAR = 5, |
| NONSTATIC_WORD, // T_FLOAT = 6, |
| NONSTATIC_DOUBLE, // T_DOUBLE = 7, |
| NONSTATIC_BYTE, // T_BYTE = 8, |
| NONSTATIC_SHORT, // T_SHORT = 9, |
| NONSTATIC_WORD, // T_INT = 10, |
| NONSTATIC_DOUBLE, // T_LONG = 11, |
| NONSTATIC_OOP, // T_OBJECT = 12, |
| NONSTATIC_OOP, // T_ARRAY = 13, |
| BAD_ALLOCATION_TYPE, // T_VOID = 14, |
| BAD_ALLOCATION_TYPE, // T_ADDRESS = 15, |
| BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16, |
| BAD_ALLOCATION_TYPE, // T_METADATA = 17, |
| BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18, |
| BAD_ALLOCATION_TYPE, // T_CONFLICT = 19, |
| BAD_ALLOCATION_TYPE, // 0 |
| BAD_ALLOCATION_TYPE, // 1 |
| BAD_ALLOCATION_TYPE, // 2 |
| BAD_ALLOCATION_TYPE, // 3 |
| STATIC_BYTE , // T_BOOLEAN = 4, |
| STATIC_SHORT, // T_CHAR = 5, |
| STATIC_WORD, // T_FLOAT = 6, |
| STATIC_DOUBLE, // T_DOUBLE = 7, |
| STATIC_BYTE, // T_BYTE = 8, |
| STATIC_SHORT, // T_SHORT = 9, |
| STATIC_WORD, // T_INT = 10, |
| STATIC_DOUBLE, // T_LONG = 11, |
| STATIC_OOP, // T_OBJECT = 12, |
| STATIC_OOP, // T_ARRAY = 13, |
| BAD_ALLOCATION_TYPE, // T_VOID = 14, |
| BAD_ALLOCATION_TYPE, // T_ADDRESS = 15, |
| BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16, |
| BAD_ALLOCATION_TYPE, // T_METADATA = 17, |
| BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18, |
| BAD_ALLOCATION_TYPE, // T_CONFLICT = 19, |
| }; |
| |
| static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type) { |
| assert(type >= T_BOOLEAN && type < T_VOID, "only allowable values"); |
| FieldAllocationType result = _basic_type_to_atype[type + (is_static ? (T_CONFLICT + 1) : 0)]; |
| assert(result != BAD_ALLOCATION_TYPE, "bad type"); |
| return result; |
| } |
| |
| class FieldAllocationCount: public ResourceObj { |
| public: |
| u2 count[MAX_FIELD_ALLOCATION_TYPE]; |
| |
| FieldAllocationCount() { |
| for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) { |
| count[i] = 0; |
| } |
| } |
| |
| FieldAllocationType update(bool is_static, BasicType type) { |
| FieldAllocationType atype = basic_type_to_atype(is_static, type); |
| // Make sure there is no overflow with injected fields. |
| assert(count[atype] < 0xFFFF, "More than 65535 fields"); |
| count[atype]++; |
| return atype; |
| } |
| }; |
| |
| Array<u2>* ClassFileParser::parse_fields(Symbol* class_name, |
| bool is_interface, |
| FieldAllocationCount *fac, |
| u2* java_fields_count_ptr, TRAPS) { |
| ClassFileStream* cfs = stream(); |
| cfs->guarantee_more(2, CHECK_NULL); // length |
| u2 length = cfs->get_u2_fast(); |
| *java_fields_count_ptr = length; |
| |
| int num_injected = 0; |
| InjectedField* injected = JavaClasses::get_injected(class_name, &num_injected); |
| int total_fields = length + num_injected; |
| |
| // The field array starts with tuples of shorts |
| // [access, name index, sig index, initial value index, byte offset]. |
| // A generic signature slot only exists for field with generic |
| // signature attribute. And the access flag is set with |
| // JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE for that field. The generic |
| // signature slots are at the end of the field array and after all |
| // other fields data. |
| // |
| // f1: [access, name index, sig index, initial value index, low_offset, high_offset] |
| // f2: [access, name index, sig index, initial value index, low_offset, high_offset] |
| // ... |
| // fn: [access, name index, sig index, initial value index, low_offset, high_offset] |
| // [generic signature index] |
| // [generic signature index] |
| // ... |
| // |
| // Allocate a temporary resource array for field data. For each field, |
| // a slot is reserved in the temporary array for the generic signature |
| // index. After parsing all fields, the data are copied to a permanent |
| // array and any unused slots will be discarded. |
| ResourceMark rm(THREAD); |
| u2* fa = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, u2, total_fields * (FieldInfo::field_slots + 1)); |
| |
| // The generic signature slots start after all other fields' data. |
| int generic_signature_slot = total_fields * FieldInfo::field_slots; |
| int num_generic_signature = 0; |
| for (int n = 0; n < length; n++) { |
| cfs->guarantee_more(8, CHECK_NULL); // access_flags, name_index, descriptor_index, attributes_count |
| |
| AccessFlags access_flags; |
| jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; |
| verify_legal_field_modifiers(flags, is_interface, CHECK_NULL); |
| access_flags.set_flags(flags); |
| |
| u2 name_index = cfs->get_u2_fast(); |
| int cp_size = _cp->length(); |
| check_property(valid_symbol_at(name_index), |
| "Invalid constant pool index %u for field name in class file %s", |
| name_index, |
| CHECK_NULL); |
| Symbol* name = _cp->symbol_at(name_index); |
| verify_legal_field_name(name, CHECK_NULL); |
| |
| u2 signature_index = cfs->get_u2_fast(); |
| check_property(valid_symbol_at(signature_index), |
| "Invalid constant pool index %u for field signature in class file %s", |
| signature_index, CHECK_NULL); |
| Symbol* sig = _cp->symbol_at(signature_index); |
| verify_legal_field_signature(name, sig, CHECK_NULL); |
| |
| u2 constantvalue_index = 0; |
| bool is_synthetic = false; |
| u2 generic_signature_index = 0; |
| bool is_static = access_flags.is_static(); |
| FieldAnnotationCollector parsed_annotations(_loader_data); |
| |
| u2 attributes_count = cfs->get_u2_fast(); |
| if (attributes_count > 0) { |
| parse_field_attributes(attributes_count, is_static, signature_index, |
| &constantvalue_index, &is_synthetic, |
| &generic_signature_index, &parsed_annotations, |
| CHECK_NULL); |
| if (parsed_annotations.field_annotations() != NULL) { |
| if (_fields_annotations == NULL) { |
| _fields_annotations = MetadataFactory::new_array<AnnotationArray*>( |
| _loader_data, length, NULL, |
| CHECK_NULL); |
| } |
| _fields_annotations->at_put(n, parsed_annotations.field_annotations()); |
| parsed_annotations.set_field_annotations(NULL); |
| } |
| if (parsed_annotations.field_type_annotations() != NULL) { |
| if (_fields_type_annotations == NULL) { |
| _fields_type_annotations = MetadataFactory::new_array<AnnotationArray*>( |
| _loader_data, length, NULL, |
| CHECK_NULL); |
| } |
| _fields_type_annotations->at_put(n, parsed_annotations.field_type_annotations()); |
| parsed_annotations.set_field_type_annotations(NULL); |
| } |
| |
| if (is_synthetic) { |
| access_flags.set_is_synthetic(); |
| } |
| if (generic_signature_index != 0) { |
| access_flags.set_field_has_generic_signature(); |
| fa[generic_signature_slot] = generic_signature_index; |
| generic_signature_slot ++; |
| num_generic_signature ++; |
| } |
| } |
| |
| FieldInfo* field = FieldInfo::from_field_array(fa, n); |
| field->initialize(access_flags.as_short(), |
| name_index, |
| signature_index, |
| constantvalue_index); |
| BasicType type = _cp->basic_type_for_signature_at(signature_index); |
| |
| // Remember how many oops we encountered and compute allocation type |
| FieldAllocationType atype = fac->update(is_static, type); |
| field->set_allocation_type(atype); |
| |
| // After field is initialized with type, we can augment it with aux info |
| if (parsed_annotations.has_any_annotations()) |
| parsed_annotations.apply_to(field); |
| } |
| |
| int index = length; |
| if (num_injected != 0) { |
| for (int n = 0; n < num_injected; n++) { |
| // Check for duplicates |
| if (injected[n].may_be_java) { |
| Symbol* name = injected[n].name(); |
| Symbol* signature = injected[n].signature(); |
| bool duplicate = false; |
| for (int i = 0; i < length; i++) { |
| FieldInfo* f = FieldInfo::from_field_array(fa, 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* field = FieldInfo::from_field_array(fa, index); |
| field->initialize(JVM_ACC_FIELD_INTERNAL, |
| injected[n].name_index, |
| injected[n].signature_index, |
| 0); |
| |
| BasicType type = FieldType::basic_type(injected[n].signature()); |
| |
| // Remember how many oops we encountered and compute allocation type |
| FieldAllocationType atype = fac->update(false, type); |
| field->set_allocation_type(atype); |
| index++; |
| } |
| } |
| |
| // Now copy the fields' data from the temporary resource array. |
| // Sometimes injected fields already exist in the Java source so |
| // the fields array could be too long. In that case the |
| // fields array is trimed. Also unused slots that were reserved |
| // for generic signature indexes are discarded. |
| Array<u2>* fields = MetadataFactory::new_array<u2>( |
| _loader_data, index * FieldInfo::field_slots + num_generic_signature, |
| CHECK_NULL); |
| _fields = fields; // save in case of error |
| { |
| int i = 0; |
| for (; i < index * FieldInfo::field_slots; i++) { |
| fields->at_put(i, fa[i]); |
| } |
| for (int j = total_fields * FieldInfo::field_slots; |
| j < generic_signature_slot; j++) { |
| fields->at_put(i++, fa[j]); |
| } |
| assert(i == fields->length(), ""); |
| } |
| |
| if (_need_verify && length > 1) { |
| // Check duplicated fields |
| ResourceMark rm(THREAD); |
| NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, NameSigHash*, HASH_ROW_SIZE); |
| initialize_hashtable(names_and_sigs); |
| bool dup = false; |
| { |
| debug_only(No_Safepoint_Verifier nsv;) |
| for (AllFieldStream fs(fields, _cp); !fs.done(); fs.next()) { |
| Symbol* name = fs.name(); |
| Symbol* sig = fs.signature(); |
| // If no duplicates, add name/signature in hashtable names_and_sigs. |
| if (!put_after_lookup(name, sig, names_and_sigs)) { |
| dup = true; |
| break; |
| } |
| } |
| } |
| if (dup) { |
| classfile_parse_error("Duplicate field name&signature in class file %s", |
| CHECK_NULL); |
| } |
| } |
| |
| return fields; |
| } |
| |
| |
| static void copy_u2_with_conversion(u2* dest, u2* src, int length) { |
| while (length-- > 0) { |
| *dest++ = Bytes::get_Java_u2((u1*) (src++)); |
| } |
| } |
| |
| |
| u2* ClassFileParser::parse_exception_table(u4 code_length, |
| u4 exception_table_length, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| |
| u2* exception_table_start = cfs->get_u2_buffer(); |
| assert(exception_table_start != NULL, "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++) { |
| u2 start_pc = cfs->get_u2_fast(); |
| u2 end_pc = cfs->get_u2_fast(); |
| u2 handler_pc = cfs->get_u2_fast(); |
| 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** write_stream, TRAPS) { |
| ClassFileStream* cfs = stream(); |
| unsigned int num_entries = cfs->get_u2(CHECK); |
| |
| // Each entry is a u2 start_pc, and a u2 line_number |
| unsigned int length_in_bytes = num_entries * (sizeof(u2) + sizeof(u2)); |
| |
| // Verify line number attribute and table length |
| check_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) == NULL) { |
| 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) { |
| u2 bci = cfs->get_u2_fast(); // start_pc |
| 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 file LocalVariableTable elements. |
| class Classfile_LVT_Element VALUE_OBJ_CLASS_SPEC { |
| public: |
| u2 start_bci; |
| u2 length; |
| u2 name_cp_index; |
| u2 descriptor_cp_index; |
| u2 slot; |
| }; |
| |
| |
| class LVT_Hash: public CHeapObj<mtClass> { |
| public: |
| LocalVariableTableElement *_elem; // element |
| LVT_Hash* _next; // Next entry in hash table |
| }; |
| |
| unsigned int hash(LocalVariableTableElement *elem) { |
| unsigned int raw_hash = elem->start_bci; |
| |
| raw_hash = elem->length + raw_hash * 37; |
| raw_hash = elem->name_cp_index + raw_hash * 37; |
| raw_hash = elem->slot + raw_hash * 37; |
| |
| return raw_hash % HASH_ROW_SIZE; |
| } |
| |
| void initialize_hashtable(LVT_Hash** table) { |
| for (int i = 0; i < HASH_ROW_SIZE; i++) { |
| table[i] = NULL; |
| } |
| } |
| |
| void clear_hashtable(LVT_Hash** table) { |
| for (int i = 0; i < HASH_ROW_SIZE; i++) { |
| LVT_Hash* current = table[i]; |
| LVT_Hash* next; |
| while (current != NULL) { |
| next = current->_next; |
| current->_next = NULL; |
| delete(current); |
| current = next; |
| } |
| table[i] = NULL; |
| } |
| } |
| |
| LVT_Hash* LVT_lookup(LocalVariableTableElement *elem, int index, LVT_Hash** table) { |
| LVT_Hash* entry = table[index]; |
| |
| /* |
| * 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 |
| */ |
| while (entry != NULL) { |
| if (elem->start_bci == entry->_elem->start_bci |
| && elem->length == entry->_elem->length |
| && elem->name_cp_index == entry->_elem->name_cp_index |
| && elem->slot == entry->_elem->slot |
| ) { |
| return entry; |
| } |
| entry = entry->_next; |
| } |
| return NULL; |
| } |
| |
| // Return false if the local variable is found in table. |
| // Return true if no duplicate is found. |
| // And local variable is added as a new entry in table. |
| bool LVT_put_after_lookup(LocalVariableTableElement *elem, LVT_Hash** table) { |
| // First lookup for duplicates |
| int index = hash(elem); |
| LVT_Hash* entry = LVT_lookup(elem, index, table); |
| |
| if (entry != NULL) { |
| return false; |
| } |
| // No duplicate is found, allocate a new entry and fill it. |
| if ((entry = new LVT_Hash()) == NULL) { |
| return false; |
| } |
| entry->_elem = elem; |
| |
| // Insert into hash table |
| entry->_next = table[index]; |
| table[index] = entry; |
| |
| return true; |
| } |
| |
| void copy_lvt_element(Classfile_LVT_Element *src, LocalVariableTableElement *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) |
| u2* ClassFileParser::parse_localvariable_table(u4 code_length, |
| u2 max_locals, |
| u4 code_attribute_length, |
| u2* localvariable_table_length, |
| bool isLVTT, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| const char * tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable"; |
| *localvariable_table_length = cfs->get_u2(CHECK_NULL); |
| unsigned int size = (*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2); |
| // 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); |
| } |
| u2* localvariable_table_start = cfs->get_u2_buffer(); |
| assert(localvariable_table_start != NULL, "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++) { |
| u2 start_pc = cfs->get_u2_fast(); |
| u2 length = cfs->get_u2_fast(); |
| u2 name_index = cfs->get_u2_fast(); |
| u2 descriptor_index = cfs->get_u2_fast(); |
| u2 index = cfs->get_u2_fast(); |
| // Assign to a u4 to avoid overflow |
| 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, CHECK_NULL); |
| } |
| if (end_pc > code_length) { |
| classfile_parse_error( |
| "Invalid length %u in %s in class file %s", |
| length, tbl_name, CHECK_NULL); |
| } |
| 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); |
| |
| Symbol* name = _cp->symbol_at(name_index); |
| Symbol* 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; |
| } |
| |
| |
| void ClassFileParser::parse_type_array(u2 array_length, u4 code_length, u4* u1_index, u4* u2_index, |
| u1* u1_array, u2* u2_array, TRAPS) { |
| ClassFileStream* cfs = stream(); |
| u2 index = 0; // index in the array with long/double occupying two slots |
| u4 i1 = *u1_index; |
| u4 i2 = *u2_index + 1; |
| for(int i = 0; i < array_length; i++) { |
| u1 tag = u1_array[i1++] = cfs->get_u1(CHECK); |
| index++; |
| if (tag == ITEM_Long || tag == ITEM_Double) { |
| index++; |
| } else if (tag == ITEM_Object) { |
| u2 class_index = u2_array[i2++] = cfs->get_u2(CHECK); |
| guarantee_property(valid_klass_reference_at(class_index), |
| "Bad class index %u in StackMap in class file %s", |
| class_index, CHECK); |
| } else if (tag == ITEM_Uninitialized) { |
| u2 offset = u2_array[i2++] = cfs->get_u2(CHECK); |
| guarantee_property( |
| offset < code_length, |
| "Bad uninitialized type offset %u in StackMap in class file %s", |
| offset, CHECK); |
| } else { |
| guarantee_property( |
| tag <= (u1)ITEM_Uninitialized, |
| "Unknown variable type %u in StackMap in class file %s", |
| tag, CHECK); |
| } |
| } |
| u2_array[*u2_index] = index; |
| *u1_index = i1; |
| *u2_index = i2; |
| } |
| |
| u1* ClassFileParser::parse_stackmap_table( |
| u4 code_attribute_length, TRAPS) { |
| if (code_attribute_length == 0) |
| return NULL; |
| |
| ClassFileStream* cfs = stream(); |
| u1* stackmap_table_start = cfs->get_u1_buffer(); |
| assert(stackmap_table_start != NULL, "null stackmap table"); |
| |
| // check code_attribute_length first |
| stream()->skip_u1(code_attribute_length, CHECK_NULL); |
| |
| if (!_need_verify && !DumpSharedSpaces) { |
| return NULL; |
| } |
| return stackmap_table_start; |
| } |
| |
| u2* ClassFileParser::parse_checked_exceptions(u2* checked_exceptions_length, |
| u4 method_attribute_length, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length |
| *checked_exceptions_length = cfs->get_u2_fast(); |
| unsigned int size = (*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2); |
| u2* checked_exceptions_start = cfs->get_u2_buffer(); |
| assert(checked_exceptions_start != NULL, "null checked exceptions"); |
| if (!_need_verify) { |
| cfs->skip_u2_fast(size); |
| } else { |
| // Verify each value in the checked exception table |
| u2 checked_exception; |
| 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(); |
| check_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, Symbol* name, Symbol* sig, TRAPS) { |
| ResourceMark rm(THREAD); |
| 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()); |
| } |
| |
| // Skip an annotation. Return >=limit if there is any problem. |
| int ClassFileParser::skip_annotation(u1* buffer, int limit, int index) { |
| // annotation := atype:u2 do(nmem:u2) {member:u2 value} |
| // value := switch (tag:u1) { ... } |
| index += 2; // skip atype |
| if ((index += 2) >= limit) return limit; // read nmem |
| int nmem = Bytes::get_Java_u2(buffer+index-2); |
| while (--nmem >= 0 && index < limit) { |
| index += 2; // skip member |
| index = skip_annotation_value(buffer, limit, index); |
| } |
| return index; |
| } |
| |
| // Skip an annotation value. Return >=limit if there is any problem. |
| int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) { |
| // 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; |
| // } |
| if ((index += 1) >= limit) return limit; // read tag |
| 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': |
| index += 2; // skip con or s_con |
| break; |
| case 'e': |
| index += 4; // skip e_class, e_name |
| break; |
| case '[': |
| { |
| if ((index += 2) >= limit) return limit; // read nval |
| int nval = Bytes::get_Java_u2(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: |
| assert(false, "annotation tag"); |
| return limit; // bad tag byte |
| } |
| return index; |
| } |
| |
| // Sift through annotations, looking for those significant to the VM: |
| void ClassFileParser::parse_annotations(u1* buffer, int limit, |
| ClassFileParser::AnnotationCollector* coll, |
| TRAPS) { |
| // annotations := do(nann:u2) {annotation} |
| int index = 0; |
| if ((index += 2) >= limit) return; // read nann |
| int nann = Bytes::get_Java_u2(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, |
| min_size = 6 // smallest possible size (zero members) |
| }; |
| while ((--nann) >= 0 && (index-2 + min_size <= limit)) { |
| int index0 = index; |
| index = skip_annotation(buffer, limit, index); |
| u1* abase = buffer + index0; |
| int atype = Bytes::get_Java_u2(abase + atype_off); |
| int count = Bytes::get_Java_u2(abase + count_off); |
| Symbol* aname = check_symbol_at(_cp, atype); |
| if (aname == NULL) break; // invalid annotation name |
| Symbol* member = NULL; |
| if (count >= 1) { |
| int member_index = Bytes::get_Java_u2(abase + member_off); |
| member = check_symbol_at(_cp, member_index); |
| if (member == NULL) break; // invalid member name |
| } |
| |
| // Here is where parsing particular annotations will take place. |
| AnnotationCollector::ID id = coll->annotation_index(_loader_data, aname); |
| if (id == AnnotationCollector::_unknown) continue; |
| coll->set_annotation(id); |
| |
| if (id == AnnotationCollector::_sun_misc_Contended) { |
| // @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(abase + s_con_off); |
| if (_cp->symbol_at(group_index)->utf8_length() == 0) { |
| group_index = 0; // default contended group |
| } |
| } |
| coll->set_contended_group(group_index); |
| } |
| } |
| } |
| |
| ClassFileParser::AnnotationCollector::ID |
| ClassFileParser::AnnotationCollector::annotation_index(ClassLoaderData* loader_data, |
| Symbol* name) { |
| vmSymbols::SID sid = vmSymbols::find_sid(name); |
| // Privileged code can use all annotations. Other code silently drops some. |
| const bool privileged = loader_data->is_the_null_class_loader_data() || |
| loader_data->is_ext_class_loader_data() || |
| loader_data->is_anonymous(); |
| switch (sid) { |
| case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_reflect_CallerSensitive_signature): |
| if (_location != _in_method) break; // only allow for methods |
| if (!privileged) break; // only allow in privileged code |
| return _method_CallerSensitive; |
| case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature): |
| if (_location != _in_method) break; // only allow for methods |
| if (!privileged) break; // only allow in privileged code |
| return _method_ForceInline; |
| case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_DontInline_signature): |
| if (_location != _in_method) break; // only allow for methods |
| if (!privileged) break; // only allow in privileged code |
| return _method_DontInline; |
| case vmSymbols::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 vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Hidden_signature): |
| if (_location != _in_method) break; // only allow for methods |
| if (!privileged) break; // only allow in privileged code |
| return _method_LambdaForm_Hidden; |
| case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_Stable_signature): |
| if (_location != _in_field) break; // only allow for fields |
| if (!privileged) break; // only allow in privileged code |
| return _field_Stable; |
| case vmSymbols::VM_SYMBOL_ENUM_NAME(sun_misc_Contended_signature): |
| if (_location != _in_field && _location != _in_class) break; // only allow for fields and classes |
| if (!EnableContended || (RestrictContended && !privileged)) break; // honor privileges |
| return _sun_misc_Contended; |
| default: break; |
| } |
| return AnnotationCollector::_unknown; |
| } |
| |
| void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) { |
| if (is_contended()) |
| f->set_contended_group(contended_group()); |
| if (is_stable()) |
| f->set_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 ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) { |
| if (has_annotation(_method_CallerSensitive)) |
| m->set_caller_sensitive(true); |
| if (has_annotation(_method_ForceInline)) |
| m->set_force_inline(true); |
| if (has_annotation(_method_DontInline)) |
| m->set_dont_inline(true); |
| if (has_annotation(_method_LambdaForm_Compiled) && m->intrinsic_id() == vmIntrinsics::_none) |
| m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm); |
| if (has_annotation(_method_LambdaForm_Hidden)) |
| m->set_hidden(true); |
| } |
| |
| void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) { |
| k->set_is_contended(is_contended()); |
| } |
| |
| |
| #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(ConstMethod* cm, |
| int lvt_cnt, |
| u2* localvariable_table_length, |
| u2** localvariable_table_start, |
| int lvtt_cnt, |
| u2* localvariable_type_table_length, |
| u2** localvariable_type_table_start, |
| TRAPS) { |
| |
| LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); |
| initialize_hashtable(lvt_Hash); |
| |
| // To fill LocalVariableTable in |
| 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 lvt_Hash. |
| if (LVT_put_after_lookup(lvt, lvt_Hash) == false |
| && _need_verify |
| && _major_version >= JAVA_1_5_VERSION) { |
| clear_hashtable(lvt_Hash); |
| classfile_parse_error("Duplicated LocalVariableTable attribute " |
| "entry for '%s' in class file %s", |
| _cp->symbol_at(lvt->name_cp_index)->as_utf8(), |
| CHECK); |
| } |
| } |
| } |
| |
| // To merge LocalVariableTable and LocalVariableTypeTable |
| 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); |
| int index = hash(&lvtt_elem); |
| LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); |
| if (entry == NULL) { |
| if (_need_verify) { |
| clear_hashtable(lvt_Hash); |
| 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(), |
| CHECK); |
| } |
| } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { |
| clear_hashtable(lvt_Hash); |
| classfile_parse_error("Duplicated LocalVariableTypeTable attribute " |
| "entry for '%s' in class file %s", |
| _cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), |
| CHECK); |
| } else { |
| // to add generic signatures into LocalVariableTable |
| entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; |
| } |
| } |
| } |
| clear_hashtable(lvt_Hash); |
| } |
| |
| |
| void ClassFileParser::copy_method_annotations(ConstMethod* cm, |
| u1* runtime_visible_annotations, |
| int runtime_visible_annotations_length, |
| u1* runtime_invisible_annotations, |
| int runtime_invisible_annotations_length, |
| u1* runtime_visible_parameter_annotations, |
| int runtime_visible_parameter_annotations_length, |
| u1* runtime_invisible_parameter_annotations, |
| int runtime_invisible_parameter_annotations_length, |
| u1* runtime_visible_type_annotations, |
| int runtime_visible_type_annotations_length, |
| u1* runtime_invisible_type_annotations, |
| int runtime_invisible_type_annotations_length, |
| u1* annotation_default, |
| int annotation_default_length, |
| TRAPS) { |
| |
| AnnotationArray* a; |
| |
| if (runtime_visible_annotations_length + |
| runtime_invisible_annotations_length > 0) { |
| a = assemble_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| runtime_invisible_annotations, |
| runtime_invisible_annotations_length, |
| CHECK); |
| cm->set_method_annotations(a); |
| } |
| |
| if (runtime_visible_parameter_annotations_length + |
| runtime_invisible_parameter_annotations_length > 0) { |
| a = assemble_annotations(runtime_visible_parameter_annotations, |
| runtime_visible_parameter_annotations_length, |
| runtime_invisible_parameter_annotations, |
| runtime_invisible_parameter_annotations_length, |
| CHECK); |
| cm->set_parameter_annotations(a); |
| } |
| |
| if (annotation_default_length > 0) { |
| a = assemble_annotations(annotation_default, |
| annotation_default_length, |
| NULL, |
| 0, |
| CHECK); |
| cm->set_default_annotations(a); |
| } |
| |
| if (runtime_visible_type_annotations_length + |
| runtime_invisible_type_annotations_length > 0) { |
| a = assemble_annotations(runtime_visible_type_annotations, |
| runtime_visible_type_annotations_length, |
| runtime_invisible_type_annotations, |
| runtime_invisible_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 promoted_flags parameter is used to pass relevant access_flags |
| // from the method back up to the containing klass. These flag values |
| // are added to klass's access_flags. |
| |
| methodHandle ClassFileParser::parse_method(bool is_interface, |
| AccessFlags *promoted_flags, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| methodHandle nullHandle; |
| ResourceMark rm(THREAD); |
| // Parse fixed parts |
| cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count |
| |
| int flags = cfs->get_u2_fast(); |
| u2 name_index = cfs->get_u2_fast(); |
| int cp_size = _cp->length(); |
| check_property( |
| valid_symbol_at(name_index), |
| "Illegal constant pool index %u for method name in class file %s", |
| name_index, CHECK_(nullHandle)); |
| Symbol* name = _cp->symbol_at(name_index); |
| verify_legal_method_name(name, CHECK_(nullHandle)); |
| |
| 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_(nullHandle)); |
| Symbol* signature = _cp->symbol_at(signature_index); |
| |
| AccessFlags access_flags; |
| 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 | JVM_ACC_STRICT; |
| } |
| } else { |
| verify_legal_method_modifiers(flags, is_interface, name, CHECK_(nullHandle)); |
| } |
| |
| int args_size = -1; // only used when _need_verify is true |
| if (_need_verify) { |
| args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) + |
| verify_legal_method_signature(name, signature, CHECK_(nullHandle)); |
| if (args_size > MAX_ARGS_SIZE) { |
| classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle)); |
| } |
| } |
| |
| access_flags.set_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; |
| u1* code_start = 0; |
| u2 exception_table_length = 0; |
| u2* exception_table_start = NULL; |
| Array<int>* exception_handlers = Universe::the_empty_int_array(); |
| u2 checked_exceptions_length = 0; |
| u2* checked_exceptions_start = NULL; |
| CompressedLineNumberWriteStream* linenumber_table = NULL; |
| 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; |
| u2** localvariable_table_start; |
| u2* localvariable_type_table_length; |
| u2** localvariable_type_table_start; |
| u2 method_parameters_length = 0; |
| u1* method_parameters_data = NULL; |
| 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 |
| u1* stackmap_data = NULL; |
| int stackmap_data_length = 0; |
| u2 generic_signature_index = 0; |
| MethodAnnotationCollector parsed_annotations; |
| u1* runtime_visible_annotations = NULL; |
| int runtime_visible_annotations_length = 0; |
| u1* runtime_invisible_annotations = NULL; |
| int runtime_invisible_annotations_length = 0; |
| u1* runtime_visible_parameter_annotations = NULL; |
| int runtime_visible_parameter_annotations_length = 0; |
| u1* runtime_invisible_parameter_annotations = NULL; |
| int runtime_invisible_parameter_annotations_length = 0; |
| u1* runtime_visible_type_annotations = NULL; |
| int runtime_visible_type_annotations_length = 0; |
| u1* runtime_invisible_type_annotations = NULL; |
| int runtime_invisible_type_annotations_length = 0; |
| bool runtime_invisible_type_annotations_exists = false; |
| u1* annotation_default = NULL; |
| 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_(nullHandle)); // method_attribute_name_index, method_attribute_length |
| u2 method_attribute_name_index = cfs->get_u2_fast(); |
| u4 method_attribute_length = cfs->get_u4_fast(); |
| check_property( |
| valid_symbol_at(method_attribute_name_index), |
| "Invalid method attribute name index %u in class file %s", |
| method_attribute_name_index, CHECK_(nullHandle)); |
| |
| Symbol* 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_(nullHandle)); |
| } |
| if (parsed_code_attribute) { |
| classfile_parse_error("Multiple Code attributes in class file %s", CHECK_(nullHandle)); |
| } |
| parsed_code_attribute = true; |
| |
| // Stack size, locals size, and code size |
| if (_major_version == 45 && _minor_version <= 2) { |
| cfs->guarantee_more(4, CHECK_(nullHandle)); |
| max_stack = cfs->get_u1_fast(); |
| max_locals = cfs->get_u1_fast(); |
| code_length = cfs->get_u2_fast(); |
| } else { |
| cfs->guarantee_more(8, CHECK_(nullHandle)); |
| 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_(nullHandle)); |
| guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE, |
| "Invalid method Code length %u in class file %s", |
| code_length, CHECK_(nullHandle)); |
| } |
| // Code pointer |
| code_start = cfs->get_u1_buffer(); |
| assert(code_start != NULL, "null code start"); |
| cfs->guarantee_more(code_length, CHECK_(nullHandle)); |
| cfs->skip_u1_fast(code_length); |
| |
| // Exception handler table |
| cfs->guarantee_more(2, CHECK_(nullHandle)); // exception_table_length |
| exception_table_length = cfs->get_u2_fast(); |
| if (exception_table_length > 0) { |
| exception_table_start = |
| parse_exception_table(code_length, exception_table_length, CHECK_(nullHandle)); |
| } |
| |
| // Parse additional attributes in code attribute |
| cfs->guarantee_more(2, CHECK_(nullHandle)); // code_attributes_count |
| u2 code_attributes_count = cfs->get_u2_fast(); |
| |
| unsigned int calculated_attribute_length = 0; |
| |
| if (_major_version > 45 || (_major_version == 45 && _minor_version > 2)) { |
| calculated_attribute_length = |
| sizeof(max_stack) + sizeof(max_locals) + sizeof(code_length); |
| } else { |
| // max_stack, locals and length are smaller in pre-version 45.2 classes |
| calculated_attribute_length = sizeof(u1) + sizeof(u1) + sizeof(u2); |
| } |
| calculated_attribute_length += |
| 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_(nullHandle)); // code_attribute_name_index, code_attribute_length |
| u2 code_attribute_name_index = cfs->get_u2_fast(); |
| u4 code_attribute_length = cfs->get_u4_fast(); |
| calculated_attribute_length += code_attribute_length + |
| sizeof(code_attribute_name_index) + |
| sizeof(code_attribute_length); |
| check_property(valid_symbol_at(code_attribute_name_index), |
| "Invalid code attribute name index %u in class file %s", |
| code_attribute_name_index, |
| CHECK_(nullHandle)); |
| 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_(nullHandle)); |
| |
| } 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, 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, 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(u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); |
| } |
| localvariable_table_start[lvt_cnt] = |
| parse_localvariable_table(code_length, |
| max_locals, |
| code_attribute_length, |
| &localvariable_table_length[lvt_cnt], |
| false, // is not LVTT |
| CHECK_(nullHandle)); |
| 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, 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, 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(u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); |
| } |
| localvariable_type_table_start[lvtt_cnt] = |
| parse_localvariable_table(code_length, |
| max_locals, |
| code_attribute_length, |
| &localvariable_type_table_length[lvtt_cnt], |
| true, // is LVTT |
| CHECK_(nullHandle)); |
| 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", CHECK_(nullHandle)); |
| } |
| stackmap_data = parse_stackmap_table(code_attribute_length, CHECK_(nullHandle)); |
| stackmap_data_length = code_attribute_length; |
| parsed_stackmap_attribute = true; |
| } else { |
| // Skip unknown attributes |
| cfs->skip_u1(code_attribute_length, CHECK_(nullHandle)); |
| } |
| } |
| // 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_(nullHandle)); |
| } |
| } 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", CHECK_(nullHandle)); |
| } |
| parsed_checked_exceptions_attribute = true; |
| checked_exceptions_start = |
| parse_checked_exceptions(&checked_exceptions_length, |
| method_attribute_length, |
| CHECK_(nullHandle)); |
| } 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", CHECK_(nullHandle)); |
| } |
| method_parameters_seen = true; |
| method_parameters_length = cfs->get_u1_fast(); |
| if (method_attribute_length != (method_parameters_length * 4u) + 1u) { |
| classfile_parse_error( |
| "Invalid MethodParameters method attribute length %u in class file", |
| method_attribute_length, CHECK_(nullHandle)); |
| } |
| method_parameters_data = cfs->get_u1_buffer(); |
| cfs->skip_u2_fast(method_parameters_length); |
| cfs->skip_u2_fast(method_parameters_length); |
| // ignore this attribute if it cannot be reflected |
| if (!SystemDictionary::Parameter_klass_loaded()) |
| method_parameters_length = 0; |
| } 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, CHECK_(nullHandle)); |
| } |
| // 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, CHECK_(nullHandle)); |
| } |
| } else if (_major_version >= JAVA_1_5_VERSION) { |
| if (method_attribute_name == vmSymbols::tag_signature()) { |
| if (method_attribute_length != 2) { |
| classfile_parse_error( |
| "Invalid Signature attribute length %u in class file %s", |
| method_attribute_length, CHECK_(nullHandle)); |
| } |
| cfs->guarantee_more(2, CHECK_(nullHandle)); // generic_signature_index |
| generic_signature_index = cfs->get_u2_fast(); |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { |
| runtime_visible_annotations_length = method_attribute_length; |
| runtime_visible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_annotations != NULL, "null visible annotations"); |
| parse_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, &parsed_annotations, |
| CHECK_(nullHandle)); |
| cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle)); |
| } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { |
| runtime_invisible_annotations_length = method_attribute_length; |
| runtime_invisible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_annotations != NULL, "null invisible annotations"); |
| cfs->skip_u1(runtime_invisible_annotations_length, CHECK_(nullHandle)); |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { |
| runtime_visible_parameter_annotations_length = method_attribute_length; |
| runtime_visible_parameter_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_parameter_annotations != NULL, "null visible parameter annotations"); |
| cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_(nullHandle)); |
| } else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) { |
| runtime_invisible_parameter_annotations_length = method_attribute_length; |
| runtime_invisible_parameter_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_parameter_annotations != NULL, "null invisible parameter annotations"); |
| cfs->skip_u1(runtime_invisible_parameter_annotations_length, CHECK_(nullHandle)); |
| } else if (method_attribute_name == vmSymbols::tag_annotation_default()) { |
| annotation_default_length = method_attribute_length; |
| annotation_default = cfs->get_u1_buffer(); |
| assert(annotation_default != NULL, "null annotation default"); |
| cfs->skip_u1(annotation_default_length, CHECK_(nullHandle)); |
| } else if (method_attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { |
| if (runtime_visible_type_annotations != NULL) { |
| classfile_parse_error( |
| "Multiple RuntimeVisibleTypeAnnotations attributes for method in class file %s", |
| CHECK_(nullHandle)); |
| } |
| runtime_visible_type_annotations_length = method_attribute_length; |
| runtime_visible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_type_annotations != NULL, "null visible type annotations"); |
| // No need for the VM to parse Type annotations |
| cfs->skip_u1(runtime_visible_type_annotations_length, CHECK_(nullHandle)); |
| } 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", |
| CHECK_(nullHandle)); |
| } else { |
| runtime_invisible_type_annotations_exists = true; |
| } |
| if (PreserveAllAnnotations) { |
| runtime_invisible_type_annotations_length = method_attribute_length; |
| runtime_invisible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); |
| } |
| cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); |
| } else { |
| // Skip unknown attributes |
| cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); |
| } |
| } else { |
| // Skip unknown attributes |
| cfs->skip_u1(method_attribute_length, CHECK_(nullHandle)); |
| } |
| } |
| |
| if (linenumber_table != NULL) { |
| 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_(nullHandle)); |
| } |
| |
| // 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_invisible_annotations_length, |
| runtime_visible_parameter_annotations_length + |
| runtime_invisible_parameter_annotations_length, |
| runtime_visible_type_annotations_length + |
| runtime_invisible_type_annotations_length, |
| annotation_default_length, |
| 0); |
| |
| Method* m = Method::allocate( |
| _loader_data, code_length, access_flags, &sizes, |
| ConstMethod::NORMAL, CHECK_(nullHandle)); |
| |
| ClassLoadingService::add_class_method_size(m->size()*HeapWordSize); |
| |
| // 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); |
| #ifdef CC_INTERP |
| // hmm is there a gc issue here?? |
| ResultTypeFinder rtf(_cp->symbol_at(signature_index)); |
| m->set_result_index(rtf.type()); |
| #endif |
| |
| if (args_size >= 0) { |
| m->set_size_of_parameters(args_size); |
| } else { |
| m->compute_size_of_parameters(THREAD); |
| } |
| #ifdef ASSERT |
| if (args_size >= 0) { |
| m->compute_size_of_parameters(THREAD); |
| assert(args_size == m->size_of_parameters(), ""); |
| } |
| #endif |
| |
| // Fill in code attribute information |
| m->set_max_stack(max_stack); |
| m->set_max_locals(max_locals); |
| if (stackmap_data != NULL) { |
| m->constMethod()->copy_stackmap_data(_loader_data, stackmap_data, |
| stackmap_data_length, CHECK_NULL); |
| } |
| |
| // Copy byte codes |
| m->set_code(code_start); |
| |
| // Copy line number table |
| if (linenumber_table != NULL) { |
| memcpy(m->compressed_linenumber_table(), |
| linenumber_table->buffer(), linenumber_table_length); |
| } |
| |
| // Copy exception table |
| if (exception_table_length > 0) { |
| int size = |
| exception_table_length * sizeof(ExceptionTableElement) / sizeof(u2); |
| copy_u2_with_conversion((u2*) m->exception_table_start(), |
| exception_table_start, size); |
| } |
| |
| // 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(method_parameters_data); |
| method_parameters_data += 2; |
| elem[i].flags = Bytes::get_Java_u2(method_parameters_data); |
| method_parameters_data += 2; |
| } |
| } |
| |
| // Copy checked exceptions |
| if (checked_exceptions_length > 0) { |
| int size = checked_exceptions_length * sizeof(CheckedExceptionElement) / sizeof(u2); |
| copy_u2_with_conversion((u2*) m->checked_exceptions_start(), checked_exceptions_start, size); |
| } |
| |
| // Copy class file LVT's/LVTT's into the HotSpot internal LVT. |
| if (total_lvt_length > 0) { |
| promoted_flags->set_has_localvariable_table(); |
| 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(m); |
| |
| // Copy annotations |
| copy_method_annotations(m->constMethod(), |
| runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| runtime_invisible_annotations, |
| runtime_invisible_annotations_length, |
| runtime_visible_parameter_annotations, |
| runtime_visible_parameter_annotations_length, |
| runtime_invisible_parameter_annotations, |
| runtime_invisible_parameter_annotations_length, |
| runtime_visible_type_annotations, |
| runtime_visible_type_annotations_length, |
| runtime_invisible_type_annotations, |
| runtime_invisible_type_annotations_length, |
| annotation_default, |
| annotation_default_length, |
| CHECK_NULL); |
| |
| if (name == vmSymbols::finalize_method_name() && |
| signature == vmSymbols::void_method_signature()) { |
| if (m->is_empty_method()) { |
| _has_empty_finalizer = true; |
| } else { |
| _has_finalizer = true; |
| } |
| } |
| if (name == vmSymbols::object_initializer_name() && |
| signature == vmSymbols::void_method_signature() && |
| m->is_vanilla_constructor()) { |
| _has_vanilla_constructor = true; |
| } |
| |
| NOT_PRODUCT(m->verify()); |
| return m; |
| } |
| |
| |
| // The promoted_flags parameter is used to pass relevant access_flags |
| // from the methods back up to the containing klass. These flag values |
| // are added to klass's access_flags. |
| |
| Array<Method*>* ClassFileParser::parse_methods(bool is_interface, |
| AccessFlags* promoted_flags, |
| bool* has_final_method, |
| bool* has_default_methods, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| cfs->guarantee_more(2, CHECK_NULL); // length |
| u2 length = cfs->get_u2_fast(); |
| if (length == 0) { |
| _methods = Universe::the_empty_method_array(); |
| } else { |
| _methods = MetadataFactory::new_array<Method*>(_loader_data, length, NULL, CHECK_NULL); |
| |
| HandleMark hm(THREAD); |
| for (int index = 0; index < length; index++) { |
| methodHandle method = parse_method(is_interface, |
| promoted_flags, |
| CHECK_NULL); |
| |
| if (method->is_final()) { |
| *has_final_method = true; |
| } |
| if (is_interface && !(*has_default_methods) |
| && !method->is_abstract() && !method->is_static() |
| && !method->is_private()) { |
| // default method |
| *has_default_methods = true; |
| } |
| _methods->at_put(index, method()); |
| } |
| |
| if (_need_verify && length > 1) { |
| // Check duplicated methods |
| ResourceMark rm(THREAD); |
| NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, NameSigHash*, HASH_ROW_SIZE); |
| initialize_hashtable(names_and_sigs); |
| bool dup = false; |
| { |
| debug_only(No_Safepoint_Verifier nsv;) |
| for (int i = 0; i < length; i++) { |
| Method* m = _methods->at(i); |
| // If no duplicates, add name/signature in hashtable names_and_sigs. |
| if (!put_after_lookup(m->name(), m->signature(), names_and_sigs)) { |
| dup = true; |
| break; |
| } |
| } |
| } |
| if (dup) { |
| classfile_parse_error("Duplicate method name&signature in class file %s", |
| CHECK_NULL); |
| } |
| } |
| } |
| return _methods; |
| } |
| |
| |
| intArray* ClassFileParser::sort_methods(Array<Method*>* methods) { |
| 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() || DumpSharedSpaces) { |
| for (int index = 0; index < length; index++) { |
| Method* 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 = NULL; |
| // If JVMTI original method ordering or sharing is enabled construct int |
| // array remembering the original ordering |
| if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) { |
| method_ordering = new intArray(length); |
| for (int index = 0; index < length; index++) { |
| Method* m = methods->at(index); |
| 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; |
| } |
| |
| |
| void ClassFileParser::parse_classfile_sourcefile_attribute(TRAPS) { |
| ClassFileStream* cfs = stream(); |
| cfs->guarantee_more(2, CHECK); // sourcefile_index |
| u2 sourcefile_index = cfs->get_u2_fast(); |
| check_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(int length, TRAPS) { |
| ClassFileStream* cfs = stream(); |
| u1* sde_buffer = cfs->get_u1_buffer(); |
| assert(sde_buffer != NULL, "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* 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((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) |
| |
| // Return number of classes in the inner classes attribute table |
| u2 ClassFileParser::parse_classfile_inner_classes_attribute(u1* inner_classes_attribute_start, |
| bool parsed_enclosingmethod_attribute, |
| u2 enclosing_method_class_index, |
| u2 enclosing_method_method_index, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| u1* current_mark = cfs->current(); |
| u2 length = 0; |
| if (inner_classes_attribute_start != NULL) { |
| 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] |
| 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; |
| int cp_size = _cp->length(); |
| cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2 |
| for (int n = 0; n < length; n++) { |
| // Inner class index |
| u2 inner_class_info_index = cfs->get_u2_fast(); |
| check_property( |
| inner_class_info_index == 0 || |
| 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 |
| u2 outer_class_info_index = cfs->get_u2_fast(); |
| check_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); |
| // Inner class name |
| u2 inner_name_index = cfs->get_u2_fast(); |
| check_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 |
| AccessFlags inner_access_flags; |
| jint 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); |
| inner_access_flags.set_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_short()); |
| } |
| |
| // 4347400: make sure there's no duplicate entry in the classes array |
| if (_need_verify && _major_version >= JAVA_1_5_VERSION) { |
| for(int i = 0; i < length * 4; i += 4) { |
| for(int j = i + 4; j < length * 4; j += 4) { |
| guarantee_property((inner_classes->at(i) != inner_classes->at(j) || |
| inner_classes->at(i+1) != inner_classes->at(j+1) || |
| inner_classes->at(i+2) != inner_classes->at(j+2) || |
| inner_classes->at(i+3) != inner_classes->at(j+3)), |
| "Duplicate entry in InnerClasses in class file %s", |
| CHECK_0); |
| } |
| } |
| } |
| |
| // 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, "wrong size"); |
| |
| // Restore buffer's current position. |
| cfs->set_current(current_mark); |
| |
| return length; |
| } |
| |
| void ClassFileParser::parse_classfile_synthetic_attribute(TRAPS) { |
| set_class_synthetic_flag(true); |
| } |
| |
| void ClassFileParser::parse_classfile_signature_attribute(TRAPS) { |
| ClassFileStream* cfs = stream(); |
| u2 signature_index = cfs->get_u2(CHECK); |
| check_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(u4 attribute_byte_length, TRAPS) { |
| ClassFileStream* cfs = stream(); |
| u1* current_start = cfs->current(); |
| |
| cfs->guarantee_more(2, CHECK); // length |
| 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}} |
| int operand_count = (attribute_byte_length - sizeof(u2)) / 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. |
| int index_size = (attribute_array_length * 2); |
| |
| Array<u2>* 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; |
| 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 |
| u2 bootstrap_method_index = cfs->get_u2_fast(); |
| u2 argument_count = cfs->get_u2_fast(); |
| check_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); |
| 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++) { |
| u2 argument_index = cfs->get_u2_fast(); |
| check_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); |
| } |
| } |
| |
| assert(operand_fill_index == operands->length(), "exact fill"); |
| assert(ConstantPool::operand_array_length(operands) == attribute_array_length, "correct decode"); |
| |
| u1* current_end = cfs->current(); |
| guarantee_property(current_end == current_start + attribute_byte_length, |
| "Bad length on BootstrapMethods in class file %s", |
| CHECK); |
| } |
| |
| void ClassFileParser::parse_classfile_attributes(ClassFileParser::ClassAnnotationCollector* parsed_annotations, |
| TRAPS) { |
| ClassFileStream* cfs = stream(); |
| // Set inner classes attribute to default sentinel |
| _inner_classes = 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_enclosingmethod_attribute = false; |
| bool parsed_bootstrap_methods_attribute = false; |
| u1* runtime_visible_annotations = NULL; |
| int runtime_visible_annotations_length = 0; |
| u1* runtime_invisible_annotations = NULL; |
| int runtime_invisible_annotations_length = 0; |
| u1* runtime_visible_type_annotations = NULL; |
| int runtime_visible_type_annotations_length = 0; |
| u1* runtime_invisible_type_annotations = NULL; |
| int runtime_invisible_type_annotations_length = 0; |
| bool runtime_invisible_type_annotations_exists = false; |
| u1* inner_classes_attribute_start = NULL; |
| u4 inner_classes_attribute_length = 0; |
| u2 enclosing_method_class_index = 0; |
| u2 enclosing_method_method_index = 0; |
| // Iterate over attributes |
| while (attributes_count--) { |
| cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length |
| u2 attribute_name_index = cfs->get_u2_fast(); |
| u4 attribute_length = cfs->get_u4_fast(); |
| check_property( |
| valid_symbol_at(attribute_name_index), |
| "Attribute name has bad constant pool index %u in class file %s", |
| attribute_name_index, CHECK); |
| Symbol* 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", CHECK); |
| } else { |
| parsed_sourcefile_attribute = true; |
| } |
| parse_classfile_sourcefile_attribute(CHECK); |
| } else if (tag == vmSymbols::tag_source_debug_extension()) { |
| // Check for SourceDebugExtension tag |
| parse_classfile_source_debug_extension_attribute((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", CHECK); |
| } else { |
| parsed_innerclasses_attribute = true; |
| } |
| inner_classes_attribute_start = cfs->get_u1_buffer(); |
| 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, CHECK); |
| } |
| parse_classfile_synthetic_attribute(CHECK); |
| } else if (tag == vmSymbols::tag_deprecated()) { |
| // Check for Deprecatd tag - 4276120 |
| if (attribute_length != 0) { |
| classfile_parse_error( |
| "Invalid Deprecated classfile attribute length %u in class file %s", |
| attribute_length, CHECK); |
| } |
| } else if (_major_version >= JAVA_1_5_VERSION) { |
| if (tag == vmSymbols::tag_signature()) { |
| if (attribute_length != 2) { |
| classfile_parse_error( |
| "Wrong Signature attribute length %u in class file %s", |
| attribute_length, CHECK); |
| } |
| parse_classfile_signature_attribute(CHECK); |
| } else if (tag == vmSymbols::tag_runtime_visible_annotations()) { |
| runtime_visible_annotations_length = attribute_length; |
| runtime_visible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_annotations != NULL, "null visible annotations"); |
| parse_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| parsed_annotations, |
| CHECK); |
| cfs->skip_u1(runtime_visible_annotations_length, CHECK); |
| } else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_annotations()) { |
| runtime_invisible_annotations_length = attribute_length; |
| runtime_invisible_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_annotations != NULL, "null invisible annotations"); |
| cfs->skip_u1(runtime_invisible_annotations_length, CHECK); |
| } else if (tag == vmSymbols::tag_enclosing_method()) { |
| if (parsed_enclosingmethod_attribute) { |
| classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", CHECK); |
| } else { |
| parsed_enclosingmethod_attribute = true; |
| } |
| 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", CHECK); |
| } |
| // Validate the constant pool indices and types |
| check_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", CHECK); |
| } |
| } 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", CHECK); |
| parsed_bootstrap_methods_attribute = true; |
| parse_classfile_bootstrap_methods_attribute(attribute_length, CHECK); |
| } else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) { |
| if (runtime_visible_type_annotations != NULL) { |
| classfile_parse_error( |
| "Multiple RuntimeVisibleTypeAnnotations attributes in class file %s", CHECK); |
| } |
| runtime_visible_type_annotations_length = attribute_length; |
| runtime_visible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_visible_type_annotations != NULL, "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", CHECK); |
| } else { |
| runtime_invisible_type_annotations_exists = true; |
| } |
| if (PreserveAllAnnotations) { |
| runtime_invisible_type_annotations_length = attribute_length; |
| runtime_invisible_type_annotations = cfs->get_u1_buffer(); |
| assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); |
| } |
| cfs->skip_u1(attribute_length, CHECK); |
| } else { |
| // Unknown attribute |
| cfs->skip_u1(attribute_length, CHECK); |
| } |
| } else { |
| // Unknown attribute |
| cfs->skip_u1(attribute_length, CHECK); |
| } |
| } |
| _annotations = assemble_annotations(runtime_visible_annotations, |
| runtime_visible_annotations_length, |
| runtime_invisible_annotations, |
| runtime_invisible_annotations_length, |
| CHECK); |
| _type_annotations = assemble_annotations(runtime_visible_type_annotations, |
| runtime_visible_type_annotations_length, |
| runtime_invisible_type_annotations, |
| runtime_invisible_type_annotations_length, |
| CHECK); |
| |
| if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) { |
| u2 num_of_classes = parse_classfile_inner_classes_attribute( |
| 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 (_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(instanceKlassHandle k) { |
| 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 != NULL) { |
| k->set_source_debug_extension(_sde_buffer, _sde_length); |
| } |
| } |
| |
| // Transfer ownership of metadata allocated to the InstanceKlass. |
| void ClassFileParser::apply_parsed_class_metadata( |
| instanceKlassHandle this_klass, |
| int java_fields_count, TRAPS) { |
| // Assign annotations if needed |
| if (_annotations != NULL || _type_annotations != NULL || |
| _fields_annotations != NULL || _fields_type_annotations != NULL) { |
| Annotations* annotations = Annotations::allocate(_loader_data, CHECK); |
| annotations->set_class_annotations(_annotations); |
| annotations->set_class_type_annotations(_type_annotations); |
| annotations->set_fields_annotations(_fields_annotations); |
| annotations->set_fields_type_annotations(_fields_type_annotations); |
| this_klass->set_annotations(annotations); |
| } |
| |
| _cp->set_pool_holder(this_klass()); |
| this_klass->set_constants(_cp); |
| this_klass->set_fields(_fields, java_fields_count); |
| this_klass->set_methods(_methods); |
| this_klass->set_inner_classes(_inner_classes); |
| this_klass->set_local_interfaces(_local_interfaces); |
| this_klass->set_transitive_interfaces(_transitive_interfaces); |
| |
| // Clear out these fields so they don't get deallocated by the destructor |
| clear_class_metadata(); |
| } |
| |
| AnnotationArray* ClassFileParser::assemble_annotations(u1* runtime_visible_annotations, |
| int runtime_visible_annotations_length, |
| u1* runtime_invisible_annotations, |
| int runtime_invisible_annotations_length, TRAPS) { |
| AnnotationArray* annotations = NULL; |
| if (runtime_visible_annotations != NULL || |
| runtime_invisible_annotations != NULL) { |
| annotations = MetadataFactory::new_array<u1>(_loader_data, |
| runtime_visible_annotations_length + |
| runtime_invisible_annotations_length, |
| CHECK_(annotations)); |
| if (runtime_visible_annotations != NULL) { |
| for (int i = 0; i < runtime_visible_annotations_length; i++) { |
| annotations->at_put(i, runtime_visible_annotations[i]); |
| } |
| } |
| if (runtime_invisible_annotations != NULL) { |
| for (int i = 0; i < runtime_invisible_annotations_length; i++) { |
| int append = runtime_visible_annotations_length+i; |
| annotations->at_put(append, runtime_invisible_annotations[i]); |
| } |
| } |
| } |
| return annotations; |
| } |
| |
| instanceKlassHandle ClassFileParser::parse_super_class(int super_class_index, |
| TRAPS) { |
| instanceKlassHandle super_klass; |
| if (super_class_index == 0) { |
| check_property(_class_name == vmSymbols::java_lang_Object(), |
| "Invalid superclass index %u in class file %s", |
| super_class_index, |
| CHECK_NULL); |
| } else { |
| check_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 = instanceKlassHandle(THREAD, _cp->resolved_klass_at(super_class_index)); |
| if (_need_verify) |
| is_array = super_klass->oop_is_array(); |
| } else if (_need_verify) { |
| is_array = (_cp->unresolved_klass_at(super_class_index)->byte_at(0) == JVM_SIGNATURE_ARRAY); |
| } |
| if (_need_verify) { |
| guarantee_property(!is_array, |
| "Bad superclass name in class file %s", CHECK_NULL); |
| } |
| } |
| return super_klass; |
| } |
| |
| |
| // Values needed for oopmap and InstanceKlass creation |
| class FieldLayoutInfo : public StackObj { |
| public: |
| int* nonstatic_oop_offsets; |
| unsigned int* nonstatic_oop_counts; |
| unsigned int nonstatic_oop_map_count; |
| unsigned int total_oop_map_count; |
| int instance_size; |
| int nonstatic_field_size; |
| int static_field_size; |
| bool has_nonstatic_fields; |
| }; |
| |
| // Layout fields and fill in FieldLayoutInfo. Could use more refactoring! |
| void ClassFileParser::layout_fields(Handle class_loader, |
| FieldAllocationCount* fac, |
| ClassAnnotationCollector* parsed_annotations, |
| FieldLayoutInfo* info, |
| TRAPS) { |
| |
| // Field size and offset computation |
| int nonstatic_field_size = _super_klass() == NULL ? 0 : _super_klass()->nonstatic_field_size(); |
| int next_static_oop_offset; |
| int next_static_double_offset; |
| int next_static_word_offset; |
| int next_static_short_offset; |
| int next_static_byte_offset; |
| int next_nonstatic_oop_offset; |
| int next_nonstatic_double_offset; |
| int next_nonstatic_word_offset; |
| int next_nonstatic_short_offset; |
| int next_nonstatic_byte_offset; |
| int first_nonstatic_oop_offset; |
| int next_nonstatic_field_offset; |
| int next_nonstatic_padded_offset; |
| |
| // Count the contended fields by type. |
| // |
| // We ignore static fields, because @Contended is not supported for them. |
| // The layout code below will also ignore the static fields. |
| int nonstatic_contended_count = 0; |
| FieldAllocationCount fac_contended; |
| for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { |
| FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); |
| if (fs.is_contended()) { |
| fac_contended.count[atype]++; |
| if (!fs.access_flags().is_static()) { |
| nonstatic_contended_count++; |
| } |
| } |
| } |
| |
| |
| // Calculate the starting byte offsets |
| next_static_oop_offset = InstanceMirrorKlass::offset_of_static_fields(); |
| next_static_double_offset = next_static_oop_offset + |
| ((fac->count[STATIC_OOP]) * heapOopSize); |
| if ( fac->count[STATIC_DOUBLE] && |
| (Universe::field_type_should_be_aligned(T_DOUBLE) || |
| Universe::field_type_should_be_aligned(T_LONG)) ) { |
| next_static_double_offset = align_size_up(next_static_double_offset, BytesPerLong); |
| } |
| |
| next_static_word_offset = next_static_double_offset + |
| ((fac->count[STATIC_DOUBLE]) * BytesPerLong); |
| next_static_short_offset = next_static_word_offset + |
| ((fac->count[STATIC_WORD]) * BytesPerInt); |
| next_static_byte_offset = next_static_short_offset + |
| ((fac->count[STATIC_SHORT]) * BytesPerShort); |
| |
| int nonstatic_fields_start = instanceOopDesc::base_offset_in_bytes() + |
| nonstatic_field_size * heapOopSize; |
| |
| next_nonstatic_field_offset = nonstatic_fields_start; |
| |
| bool is_contended_class = parsed_annotations->is_contended(); |
| |
| // Class is contended, pad before all the fields |
| if (is_contended_class) { |
| next_nonstatic_field_offset += ContendedPaddingWidth; |
| } |
| |
| // Compute the non-contended fields count. |
| // The packing code below relies on these counts to determine if some field |
| // can be squeezed into the alignment gap. Contended fields are obviously |
| // exempt from that. |
| unsigned int nonstatic_double_count = fac->count[NONSTATIC_DOUBLE] - fac_contended.count[NONSTATIC_DOUBLE]; |
| unsigned int nonstatic_word_count = fac->count[NONSTATIC_WORD] - fac_contended.count[NONSTATIC_WORD]; |
| unsigned int nonstatic_short_count = fac->count[NONSTATIC_SHORT] - fac_contended.count[NONSTATIC_SHORT]; |
| unsigned int nonstatic_byte_count = fac->count[NONSTATIC_BYTE] - fac_contended.count[NONSTATIC_BYTE]; |
| unsigned int nonstatic_oop_count = fac->count[NONSTATIC_OOP] - fac_contended.count[NONSTATIC_OOP]; |
| |
| // Total non-static fields count, including every contended field |
| unsigned int nonstatic_fields_count = fac->count[NONSTATIC_DOUBLE] + fac->count[NONSTATIC_WORD] + |
| fac->count[NONSTATIC_SHORT] + fac->count[NONSTATIC_BYTE] + |
| fac->count[NONSTATIC_OOP]; |
| |
| bool super_has_nonstatic_fields = |
| (_super_klass() != NULL && _super_klass->has_nonstatic_fields()); |
| bool has_nonstatic_fields = super_has_nonstatic_fields || (nonstatic_fields_count != 0); |
| |
| |
| // Prepare list of oops for oop map generation. |
| // |
| // "offset" and "count" lists are describing the set of contiguous oop |
| // regions. offset[i] is the start of the i-th region, which then has |
| // count[i] oops following. Before we know how many regions are required, |
| // we pessimistically allocate the maps to fit all the oops into the |
| // distinct regions. |
| // |
| // TODO: We add +1 to always allocate non-zero resource arrays; we need |
| // to figure out if we still need to do this. |
| int* nonstatic_oop_offsets; |
| unsigned int* nonstatic_oop_counts; |
| unsigned int nonstatic_oop_map_count = 0; |
| unsigned int max_nonstatic_oop_maps = fac->count[NONSTATIC_OOP] + 1; |
| |
| nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, int, max_nonstatic_oop_maps); |
| nonstatic_oop_counts = NEW_RESOURCE_ARRAY_IN_THREAD( |
| THREAD, unsigned int, max_nonstatic_oop_maps); |
| |
| first_nonstatic_oop_offset = 0; // will be set for first oop field |
| |
| bool compact_fields = CompactFields; |
| int allocation_style = FieldsAllocationStyle; |
| if( allocation_style < 0 || allocation_style > 2 ) { // Out of range? |
| assert(false, "0 <= FieldsAllocationStyle <= 2"); |
| allocation_style = 1; // Optimistic |
| } |
| |
| // The next classes have predefined hard-coded fields offsets |
| // (see in JavaClasses::compute_hard_coded_offsets()). |
| // Use default fields allocation order for them. |
| if( (allocation_style != 0 || compact_fields ) && class_loader.is_null() && |
| (_class_name == vmSymbols::java_lang_AssertionStatusDirectives() || |
| _class_name == vmSymbols::java_lang_Class() || |
| _class_name == vmSymbols::java_lang_ClassLoader() || |
| _class_name == vmSymbols::java_lang_ref_Reference() || |
| _class_name == vmSymbols::java_lang_ref_SoftReference() || |
| _class_name == vmSymbols::java_lang_StackTraceElement() || |
| _class_name == vmSymbols::java_lang_String() || |
| _class_name == vmSymbols::java_lang_Throwable() || |
| _class_name == vmSymbols::java_lang_Boolean() || |
| _class_name == vmSymbols::java_lang_Character() || |
| _class_name == vmSymbols::java_lang_Float() || |
| _class_name == vmSymbols::java_lang_Double() || |
| _class_name == vmSymbols::java_lang_Byte() || |
| _class_name == vmSymbols::java_lang_Short() || |
| _class_name == vmSymbols::java_lang_Integer() || |
| _class_name == vmSymbols::java_lang_Long())) { |
| allocation_style = 0; // Allocate oops first |
| compact_fields = false; // Don't compact fields |
| } |
| |
| // Rearrange fields for a given allocation style |
| if( allocation_style == 0 ) { |
| // Fields order: oops, longs/doubles, ints, shorts/chars, bytes, padded fields |
| next_nonstatic_oop_offset = next_nonstatic_field_offset; |
| next_nonstatic_double_offset = next_nonstatic_oop_offset + |
| (nonstatic_oop_count * heapOopSize); |
| } else if( allocation_style == 1 ) { |
| // Fields order: longs/doubles, ints, shorts/chars, bytes, oops, padded fields |
| next_nonstatic_double_offset = next_nonstatic_field_offset; |
| } else if( allocation_style == 2 ) { |
| // Fields allocation: oops fields in super and sub classes are together. |
| if( nonstatic_field_size > 0 && _super_klass() != NULL && |
| _super_klass->nonstatic_oop_map_size() > 0 ) { |
| unsigned int map_count = _super_klass->nonstatic_oop_map_count(); |
| OopMapBlock* first_map = _super_klass->start_of_nonstatic_oop_maps(); |
| OopMapBlock* last_map = first_map + map_count - 1; |
| int next_offset = last_map->offset() + (last_map->count() * heapOopSize); |
| if (next_offset == next_nonstatic_field_offset) { |
| allocation_style = 0; // allocate oops first |
| next_nonstatic_oop_offset = next_nonstatic_field_offset; |
| next_nonstatic_double_offset = next_nonstatic_oop_offset + |
| (nonstatic_oop_count * heapOopSize); |
| } |
| } |
| if( allocation_style == 2 ) { |
| allocation_style = 1; // allocate oops last |
| next_nonstatic_double_offset = next_nonstatic_field_offset; |
| } |
| } else { |
| ShouldNotReachHere(); |
| } |
| |
| int nonstatic_oop_space_count = 0; |
| int nonstatic_word_space_count = 0; |
| int nonstatic_short_space_count = 0; |
| int nonstatic_byte_space_count = 0; |
| int nonstatic_oop_space_offset; |
| int nonstatic_word_space_offset; |
| int nonstatic_short_space_offset; |
| int nonstatic_byte_space_offset; |
| |
| // Try to squeeze some of the fields into the gaps due to |
| // long/double alignment. |
| if( nonstatic_double_count > 0 ) { |
| int offset = next_nonstatic_double_offset; |
| next_nonstatic_double_offset = align_size_up(offset, BytesPerLong); |
| if( compact_fields && offset != next_nonstatic_double_offset ) { |
| // Allocate available fields into the gap before double field. |
| int length = next_nonstatic_double_offset - offset; |
| assert(length == BytesPerInt, ""); |
| nonstatic_word_space_offset = offset; |
| if( nonstatic_word_count > 0 ) { |
| nonstatic_word_count -= 1; |
| nonstatic_word_space_count = 1; // Only one will fit |
| length -= BytesPerInt; |
| offset += BytesPerInt; |
| } |
| nonstatic_short_space_offset = offset; |
| while( length >= BytesPerShort && nonstatic_short_count > 0 ) { |
| nonstatic_short_count -= 1; |
| nonstatic_short_space_count += 1; |
| length -= BytesPerShort; |
| offset += BytesPerShort; |
| } |
| nonstatic_byte_space_offset = offset; |
| while( length > 0 && nonstatic_byte_count > 0 ) { |
| nonstatic_byte_count -= 1; |
| nonstatic_byte_space_count += 1; |
| length -= 1; |
| } |
| // Allocate oop field in the gap if there are no other fields for that. |
| nonstatic_oop_space_offset = offset; |
| if( length >= heapOopSize && nonstatic_oop_count > 0 && |
| allocation_style != 0 ) { // when oop fields not first |
| nonstatic_oop_count -= 1; |
| nonstatic_oop_space_count = 1; // Only one will fit |
| length -= heapOopSize; |
| offset += heapOopSize; |
| } |
| } |
| } |
| |
| next_nonstatic_word_offset = next_nonstatic_double_offset + |
| (nonstatic_double_count * BytesPerLong); |
| next_nonstatic_short_offset = next_nonstatic_word_offset + |
| (nonstatic_word_count * BytesPerInt); |
| next_nonstatic_byte_offset = next_nonstatic_short_offset + |
| (nonstatic_short_count * BytesPerShort); |
| next_nonstatic_padded_offset = next_nonstatic_byte_offset + |
| nonstatic_byte_count; |
| |
| // let oops jump before padding with this allocation style |
| if( allocation_style == 1 ) { |
| next_nonstatic_oop_offset = next_nonstatic_padded_offset; |
| if( nonstatic_oop_count > 0 ) { |
| next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, heapOopSize); |
| } |
| next_nonstatic_padded_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize); |
| } |
| |
| // Iterate over fields again and compute correct offsets. |
| // The field allocation type was temporarily stored in the offset slot. |
| // oop fields are located before non-oop fields (static and non-static). |
| for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { |
| |
| // skip already laid out fields |
| if (fs.is_offset_set()) continue; |
| |
| // contended instance fields are handled below |
| if (fs.is_contended() && !fs.access_flags().is_static()) continue; |
| |
| int real_offset; |
| FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); |
| |
| // pack the rest of the fields |
| switch (atype) { |
| case STATIC_OOP: |
| real_offset = next_static_oop_offset; |
| next_static_oop_offset += heapOopSize; |
| break; |
| case STATIC_BYTE: |
| real_offset = next_static_byte_offset; |
| next_static_byte_offset += 1; |
| break; |
| case STATIC_SHORT: |
| real_offset = next_static_short_offset; |
| next_static_short_offset += BytesPerShort; |
| break; |
| case STATIC_WORD: |
| real_offset = next_static_word_offset; |
| next_static_word_offset += BytesPerInt; |
| break; |
| case STATIC_DOUBLE: |
| real_offset = next_static_double_offset; |
| next_static_double_offset += BytesPerLong; |
| break; |
| case NONSTATIC_OOP: |
| if( nonstatic_oop_space_count > 0 ) { |
| real_offset = nonstatic_oop_space_offset; |
| nonstatic_oop_space_offset += heapOopSize; |
| nonstatic_oop_space_count -= 1; |
| } else { |
| real_offset = next_nonstatic_oop_offset; |
| next_nonstatic_oop_offset += heapOopSize; |
| } |
| // Update oop maps |
| if( nonstatic_oop_map_count > 0 && |
| nonstatic_oop_offsets[nonstatic_oop_map_count - 1] == |
| real_offset - |
| int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) * |
| heapOopSize ) { |
| // Extend current oop map |
| assert(nonstatic_oop_map_count - 1 < max_nonstatic_oop_maps, "range check"); |
| nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1; |
| } else { |
| // Create new oop map |
| assert(nonstatic_oop_map_count < max_nonstatic_oop_maps, "range check"); |
| nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; |
| nonstatic_oop_counts [nonstatic_oop_map_count] = 1; |
| nonstatic_oop_map_count += 1; |
| if( first_nonstatic_oop_offset == 0 ) { // Undefined |
| first_nonstatic_oop_offset = real_offset; |
| } |
| } |
| break; |
| case NONSTATIC_BYTE: |
| if( nonstatic_byte_space_count > 0 ) { |
| real_offset = nonstatic_byte_space_offset; |
| nonstatic_byte_space_offset += 1; |
| nonstatic_byte_space_count -= 1; |
| } else { |
| real_offset = next_nonstatic_byte_offset; |
| next_nonstatic_byte_offset += 1; |
| } |
| break; |
| case NONSTATIC_SHORT: |
| if( nonstatic_short_space_count > 0 ) { |
| real_offset = nonstatic_short_space_offset; |
| nonstatic_short_space_offset += BytesPerShort; |
| nonstatic_short_space_count -= 1; |
| } else { |
| real_offset = next_nonstatic_short_offset; |
| next_nonstatic_short_offset += BytesPerShort; |
| } |
| break; |
| case NONSTATIC_WORD: |
| if( nonstatic_word_space_count > 0 ) { |
| real_offset = nonstatic_word_space_offset; |
| nonstatic_word_space_offset += BytesPerInt; |
| nonstatic_word_space_count -= 1; |
| } else { |
| real_offset = next_nonstatic_word_offset; |
| next_nonstatic_word_offset += BytesPerInt; |
| } |
| break; |
| case NONSTATIC_DOUBLE: |
| real_offset = next_nonstatic_double_offset; |
| next_nonstatic_double_offset += BytesPerLong; |
| break; |
| default: |
| ShouldNotReachHere(); |
| } |
| fs.set_offset(real_offset); |
| } |
| |
| |
| // Handle the contended cases. |
| // |
| // Each contended field should not intersect the cache line with another contended field. |
| // In the absence of alignment information, we end up with pessimistically separating |
| // the fields with full-width padding. |
| // |
| // Additionally, this should not break alignment for the fields, so we round the alignment up |
| // for each field. |
| if (nonstatic_contended_count > 0) { |
| |
| // if there is at least one contended field, we need to have pre-padding for them |
| next_nonstatic_padded_offset += ContendedPaddingWidth; |
| |
| // collect all contended groups |
| BitMap bm(_cp->size()); |
| for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { |
| // skip already laid out fields |
| if (fs.is_offset_set()) continue; |
| |
| if (fs.is_contended()) { |
| bm.set_bit(fs.contended_group()); |
| } |
| } |
| |
| int current_group = -1; |
| while ((current_group = (int)bm.get_next_one_offset(current_group + 1)) != (int)bm.size()) { |
| |
| for (AllFieldStream fs(_fields, _cp); !fs.done(); fs.next()) { |
| |
| // skip already laid out fields |
| if (fs.is_offset_set()) continue; |
| |
| // skip non-contended fields and fields from different group |
| if (!fs.is_contended() || (fs.contended_group() != current_group)) continue; |
| |
| // handle statics below |
| if (fs.access_flags().is_static()) continue; |
| |
| int real_offset; |
| FieldAllocationType atype = (FieldAllocationType) fs.allocation_type(); |
| |
| switch (atype) { |
| case NONSTATIC_BYTE: |
| next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, 1); |
| real_offset = next_nonstatic_padded_offset; |
| next_nonstatic_padded_offset += 1; |
| break; |
| |
| case NONSTATIC_SHORT: |
| next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerShort); |
| real_offset = next_nonstatic_padded_offset; |
| next_nonstatic_padded_offset += BytesPerShort; |
| break; |
| |
| case NONSTATIC_WORD: |
| next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerInt); |
| real_offset = next_nonstatic_padded_offset; |
| next_nonstatic_padded_offset += BytesPerInt; |
| break; |
| |
| case NONSTATIC_DOUBLE: |
| next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, BytesPerLong); |
| real_offset = next_nonstatic_padded_offset; |
| next_nonstatic_padded_offset += BytesPerLong; |
| break; |
| |
| case NONSTATIC_OOP: |
| next_nonstatic_padded_offset = align_size_up(next_nonstatic_padded_offset, heapOopSize); |
| real_offset = next_nonstatic_padded_offset; |
| next_nonstatic_padded_offset += heapOopSize; |
| |
| // Create new oop map |
| assert(nonstatic_oop_map_count < max_nonstatic_oop_maps, "range check"); |
| nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset; |
| nonstatic_oop_counts [nonstatic_oop_map_count] = 1; |
| nonstatic_oop_map_count += 1; |
| if( first_nonstatic_oop_offset == 0 ) { // Undefined |
| first_nonstatic_oop_offset = real_offset; |
| } |
| break; |
| |
| default: |
| ShouldNotReachHere(); |
| } |
| |
| if (fs.contended_group() == 0) { |
| // Contended group defines the equivalence class over the fields: |
| // the fields within the same contended group are not inter-padded. |
| // The only exception is default group, which does not incur the |
| // equivalence, and so requires intra-padding. |
| next_nonstatic_padded_offset += ContendedPaddingWidth; |
| } |
| |
| fs.set_offset(real_offset); |
| } // for |
| |
| // Start laying out the next group. |
| // Note that this will effectively pad the last group in the back; |
| // this is expected to alleviate memory contention effects for |
| // subclass fields and/or adjacent object. |
| // If this was the default group, the padding is already in place. |
| if (current_group != 0) { |
| next_nonstatic_padded_offset += ContendedPaddingWidth; |
| } |
| } |
| |
| // handle static fields |
| } |
| |
| // Entire class is contended, pad in the back. |
| // This helps to alleviate memory contention effects for subclass fields |
| // and/or adjacent object. |
| if (is_contended_class) { |
| next_nonstatic_padded_offset += ContendedPaddingWidth; |
| } |
| |
| int notaligned_nonstatic_fields_end = next_nonstatic_padded_offset; |
| |
| int nonstatic_fields_end = align_size_up(notaligned_nonstatic_fields_end, heapOopSize); |
| int instance_end = align_size_up(notaligned_nonstatic_fields_end, wordSize); |
| int static_fields_end = align_size_up(next_static_byte_offset, wordSize); |
| |
| int static_field_size = (static_fields_end - |
| InstanceMirrorKlass::offset_of_static_fields()) / wordSize; |
| nonstatic_field_size = nonstatic_field_size + |
| (nonstatic_fields_end - nonstatic_fields_start) / heapOopSize; |
| |
| int instance_size = align_object_size(instance_end / wordSize); |
| |
| assert(instance_size == align_object_size(align_size_up( |
| (instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize), |
| wordSize) / wordSize), "consistent layout helper value"); |
| |
| // Invariant: nonstatic_field end/start should only change if there are |
| // nonstatic fields in the class, or if the class is contended. We compare |
| // against the non-aligned value, so that end alignment will not fail the |
| // assert without actually having the fields. |
| assert((notaligned_nonstatic_fields_end == nonstatic_fields_start) || |
| is_contended_class || |
| (nonstatic_fields_count > 0), "double-check nonstatic start/end"); |
| |
| // Number of non-static oop map blocks allocated at end of klass. |
| const unsigned int total_oop_map_count = |
| compute_oop_map_count(_super_klass, nonstatic_oop_map_count, |
| first_nonstatic_oop_offset); |
| |
| #ifndef PRODUCT |
| if (PrintFieldLayout) { |
| print_field_layout(_class_name, |
| _fields, |
| _cp, |
| instance_size, |
| nonstatic_fields_start, |
| nonstatic_fields_end, |
| static_fields_end); |
| } |
| |
| #endif |
| // Pass back information needed for InstanceKlass creation |
| info->nonstatic_oop_offsets = nonstatic_oop_offsets; |
| info->nonstatic_oop_counts = nonstatic_oop_counts; |
| info->nonstatic_oop_map_count = nonstatic_oop_map_count; |
| info->total_oop_map_count = total_oop_map_count; |
| info->instance_size = instance_size; |
| info->static_field_size = static_field_size; |
| info->nonstatic_field_size = nonstatic_field_size; |
| info->has_nonstatic_fields = has_nonstatic_fields; |
| } |
| |
| |
| instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, |
| ClassLoaderData* loader_data, |
| Handle protection_domain, |
| KlassHandle host_klass, |
| GrowableArray<Handle>* cp_patches, |
| TempNewSymbol& parsed_name, |
| bool verify, |
| TRAPS) { |
| |
| // When a retransformable agent is attached, JVMTI caches the |
| // class bytes that existed before the first retransformation. |
| // If RedefineClasses() was used before the retransformable |
| // agent attached, then the cached class bytes may not be the |
| // original class bytes. |
| JvmtiCachedClassFileData *cached_class_file = NULL; |
| Handle class_loader(THREAD, loader_data->class_loader()); |
| bool has_default_methods = false; |
| ResourceMark rm(THREAD); |
| |
| ClassFileStream* cfs = stream(); |
| // Timing |
| assert(THREAD->is_Java_thread(), "must be a JavaThread"); |
| JavaThread* jt = (JavaThread*) THREAD; |
| |
| PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(), |
| ClassLoader::perf_class_parse_selftime(), |
| NULL, |
| jt->get_thread_stat()->perf_recursion_counts_addr(), |
| jt->get_thread_stat()->perf_timers_addr(), |
| PerfClassTraceTime::PARSE_CLASS); |
| |
| init_parsed_class_attributes(loader_data); |
| |
| if (JvmtiExport::should_post_class_file_load_hook()) { |
| // Get the cached class file bytes (if any) from the class that |
| // is being redefined or retransformed. We use jvmti_thread_state() |
| // instead of JvmtiThreadState::state_for(jt) so we don't allocate |
| // a JvmtiThreadState any earlier than necessary. This will help |
| // avoid the bug described by 7126851. |
| JvmtiThreadState *state = jt->jvmti_thread_state(); |
| if (state != NULL) { |
| KlassHandle *h_class_being_redefined = |
| state->get_class_being_redefined(); |
| if (h_class_being_redefined != NULL) { |
| instanceKlassHandle ikh_class_being_redefined = |
| instanceKlassHandle(THREAD, (*h_class_being_redefined)()); |
| cached_class_file = ikh_class_being_redefined->get_cached_class_file(); |
| } |
| } |
| |
| unsigned char* ptr = cfs->buffer(); |
| unsigned char* end_ptr = cfs->buffer() + cfs->length(); |
| |
| JvmtiExport::post_class_file_load_hook(name, class_loader(), protection_domain, |
| &ptr, &end_ptr, &cached_class_file); |
| |
| if (ptr != cfs->buffer()) { |
| // JVMTI agent has modified class file data. |
| // Set new class file stream using JVMTI agent modified |
| // class file data. |
| cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source()); |
| set_stream(cfs); |
| } |
| } |
| |
| _host_klass = host_klass; |
| _cp_patches = cp_patches; |
| |
| instanceKlassHandle nullHandle; |
| |
| // Figure out whether we can skip format checking (matching classic VM behavior) |
| _need_verify = Verifier::should_verify_for(class_loader(), verify); |
| |
| // Set the verify flag in stream |
| cfs->set_verify(_need_verify); |
| |
| // Save the class file name for easier error message printing. |
| _class_name = (name != NULL) ? name : vmSymbols::unknown_class_name(); |
| |
| cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor |
| // Magic value |
| u4 magic = cfs->get_u4_fast(); |
| guarantee_property(magic == JAVA_CLASSFILE_MAGIC, |
| "Incompatible magic value %u in class file %s", |
| magic, CHECK_(nullHandle)); |
| |
| // Version numbers |
| u2 minor_version = cfs->get_u2_fast(); |
| u2 major_version = cfs->get_u2_fast(); |
| |
| // Check version numbers - we check this even with verifier off |
| if (!is_supported_version(major_version, minor_version)) { |
| if (name == NULL) { |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_UnsupportedClassVersionError(), |
| "Unsupported major.minor version %u.%u", |
| major_version, |
| minor_version); |
| } else { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_UnsupportedClassVersionError(), |
| "%s : Unsupported major.minor version %u.%u", |
| name->as_C_string(), |
| major_version, |
| minor_version); |
| } |
| return nullHandle; |
| } |
| |
| _major_version = major_version; |
| _minor_version = minor_version; |
| |
| |
| // Check if verification needs to be relaxed for this class file |
| // Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376) |
| _relax_verify = Verifier::relax_verify_for(class_loader()); |
| |
| // Constant pool |
| constantPoolHandle cp = parse_constant_pool(CHECK_(nullHandle)); |
| |
| int cp_size = cp->length(); |
| |
| cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len |
| |
| // Access flags |
| AccessFlags access_flags; |
| jint flags = cfs->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_(nullHandle)); |
| access_flags.set_flags(flags); |
| |
| // This class and superclass |
| u2 this_class_index = cfs->get_u2_fast(); |
| check_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_(nullHandle)); |
| |
| Symbol* class_name = cp->unresolved_klass_at(this_class_index); |
| assert(class_name != NULL, "class_name can't be null"); |
| |
| // It's important to set parsed_name *before* resolving the super class. |
| // (it's used for cleanup by the caller if parsing fails) |
| parsed_name = class_name; |
| // parsed_name is returned and can be used if there's an error, so add to |
| // its reference count. Caller will decrement the refcount. |
| parsed_name->increment_refcount(); |
| |
| // Update _class_name which could be null previously to be class_name |
| _class_name = class_name; |
| |
| // 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->byte_at(0) != JVM_SIGNATURE_ARRAY, |
| "Bad class name in class file %s", |
| CHECK_(nullHandle)); |
| } |
| |
| Klass* preserve_this_klass; // for storing result across HandleMark |
| |
| // release all handles when parsing is done |
| { HandleMark hm(THREAD); |
| |
| // Checks if name in class file matches requested name |
| if (name != NULL && class_name != name) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_NoClassDefFoundError(), |
| "%s (wrong name: %s)", |
| name->as_C_string(), |
| class_name->as_C_string() |
| ); |
| return nullHandle; |
| } |
| |
| if (TraceClassLoadingPreorder) { |
| tty->print("[Loading %s", (name != NULL) ? name->as_klass_external_name() : "NoName"); |
| if (cfs->source() != NULL) tty->print(" from %s", cfs->source()); |
| tty->print_cr("]"); |
| } |
| |
| u2 super_class_index = cfs->get_u2_fast(); |
| instanceKlassHandle super_klass = parse_super_class(super_class_index, |
| CHECK_NULL); |
| |
| // Interfaces |
| u2 itfs_len = cfs->get_u2_fast(); |
| Array<Klass*>* local_interfaces = |
| parse_interfaces(itfs_len, protection_domain, _class_name, |
| &has_default_methods, CHECK_(nullHandle)); |
| |
| u2 java_fields_count = 0; |
| // Fields (offsets are filled in later) |
| FieldAllocationCount fac; |
| Array<u2>* fields = parse_fields(class_name, |
| access_flags.is_interface(), |
| &fac, &java_fields_count, |
| CHECK_(nullHandle)); |
| // Methods |
| bool has_final_method = false; |
| AccessFlags promoted_flags; |
| promoted_flags.set_flags(0); |
| Array<Method*>* methods = parse_methods(access_flags.is_interface(), |
| &promoted_flags, |
| &has_final_method, |
| &has_default_methods, |
| CHECK_(nullHandle)); |
| |
| // Additional attributes |
| ClassAnnotationCollector parsed_annotations; |
| parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle)); |
| |
| // Make sure this is the end of class file stream |
| guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle)); |
| |
| // We check super class after class file is parsed and format is checked |
| if (super_class_index > 0 && super_klass.is_null()) { |
| Symbol* sk = 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(sk == vmSymbols::java_lang_Object(), |
| "Interfaces must have java.lang.Object as superclass in class file %s", |
| CHECK_(nullHandle)); |
| } |
| Klass* k = SystemDictionary::resolve_super_or_fail(class_name, sk, |
| class_loader, |
| protection_domain, |
| true, |
| CHECK_(nullHandle)); |
| |
| KlassHandle kh (THREAD, k); |
| super_klass = instanceKlassHandle(THREAD, kh()); |
| } |
| if (super_klass.not_null()) { |
| |
| if (super_klass->has_default_methods()) { |
| has_default_methods = true; |
| } |
| |
| if (super_klass->is_interface()) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_IncompatibleClassChangeError(), |
| "class %s has interface %s as super class", |
| class_name->as_klass_external_name(), |
| super_klass->external_name() |
| ); |
| return nullHandle; |
| } |
| // Make sure super class is not final |
| if (super_klass->is_final()) { |
| THROW_MSG_(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class", nullHandle); |
| } |
| } |
| |
| // save super klass for error handling. |
| _super_klass = super_klass; |
| |
| // Compute the transitive list of all unique interfaces implemented by this class |
| _transitive_interfaces = |
| compute_transitive_interfaces(super_klass, local_interfaces, CHECK_(nullHandle)); |
| |
| // sort methods |
| intArray* method_ordering = sort_methods(methods); |
| |
| // promote flags from parse_methods() to the klass' flags |
| access_flags.add_promoted_flags(promoted_flags.as_int()); |
| |
| // Size of Java vtable (in words) |
| int vtable_size = 0; |
| int itable_size = 0; |
| int num_miranda_methods = 0; |
| |
| GrowableArray<Method*> all_mirandas(20); |
| |
| klassVtable::compute_vtable_size_and_num_mirandas( |
| &vtable_size, &num_miranda_methods, &all_mirandas, super_klass(), methods, |
| access_flags, class_loader, class_name, local_interfaces, |
| CHECK_(nullHandle)); |
| |
| // Size of Java itable (in words) |
| itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(_transitive_interfaces); |
| |
| FieldLayoutInfo info; |
| layout_fields(class_loader, &fac, &parsed_annotations, &info, CHECK_NULL); |
| |
| int total_oop_map_size2 = |
| InstanceKlass::nonstatic_oop_map_size(info.total_oop_map_count); |
| |
| // Compute reference type |
| ReferenceType rt; |
| if (super_klass() == NULL) { |
| rt = REF_NONE; |
| } else { |
| rt = super_klass->reference_type(); |
| } |
| |
| // We can now create the basic Klass* for this klass |
| _klass = InstanceKlass::allocate_instance_klass(loader_data, |
| vtable_size, |
| itable_size, |
| info.static_field_size, |
| total_oop_map_size2, |
| rt, |
| access_flags, |
| name, |
| super_klass(), |
| !host_klass.is_null(), |
| CHECK_(nullHandle)); |
| instanceKlassHandle this_klass (THREAD, _klass); |
| |
| assert(this_klass->static_field_size() == info.static_field_size, "sanity"); |
| assert(this_klass->nonstatic_oop_map_count() == info.total_oop_map_count, |
| "sanity"); |
| |
| // Fill in information already parsed |
| this_klass->set_should_verify_class(verify); |
| jint lh = Klass::instance_layout_helper(info.instance_size, false); |
| this_klass->set_layout_helper(lh); |
| assert(this_klass->oop_is_instance(), "layout is correct"); |
| assert(this_klass->size_helper() == info.instance_size, "correct size_helper"); |
| // Not yet: supers are done below to support the new subtype-checking fields |
| //this_klass->set_super(super_klass()); |
| this_klass->set_class_loader_data(loader_data); |
| this_klass->set_nonstatic_field_size(info.nonstatic_field_size); |
| this_klass->set_has_nonstatic_fields(info.has_nonstatic_fields); |
| this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]); |
| |
| apply_parsed_class_metadata(this_klass, java_fields_count, CHECK_NULL); |
| |
| if (has_final_method) { |
| this_klass->set_has_final_method(); |
| } |
| this_klass->copy_method_ordering(method_ordering, CHECK_NULL); |
| // 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. |
| this_klass->set_initial_method_idnum(methods->length()); |
| this_klass->set_name(cp->klass_name_at(this_class_index)); |
| if (is_anonymous()) // I am well known to myself |
| cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve |
| |
| this_klass->set_minor_version(minor_version); |
| this_klass->set_major_version(major_version); |
| this_klass->set_has_default_methods(has_default_methods); |
| |
| // 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.) |
| if (Method::klass_id_for_intrinsics(this_klass()) != vmSymbols::NO_SID) { |
| for (int j = 0; j < methods->length(); j++) { |
| methods->at(j)->init_intrinsic_id(); |
| } |
| } |
| |
| if (cached_class_file != NULL) { |
| // JVMTI: we have an InstanceKlass now, tell it about the cached bytes |
| this_klass->set_cached_class_file(cached_class_file); |
| } |
| |
| // Fill in field values obtained by parse_classfile_attributes |
| if (parsed_annotations.has_any_annotations()) |
| parsed_annotations.apply_to(this_klass); |
| apply_parsed_class_attributes(this_klass); |
| |
| // Miranda methods |
| if ((num_miranda_methods > 0) || |
| // if this class introduced new miranda methods or |
| (super_klass.not_null() && (super_klass->has_miranda_methods())) |
| // super class exists and this class inherited miranda methods |
| ) { |
| this_klass->set_has_miranda_methods(); // then set a flag |
| } |
| |
| // Fill in information needed to compute superclasses. |
| this_klass->initialize_supers(super_klass(), CHECK_(nullHandle)); |
| |
| // Initialize itable offset tables |
| klassItable::setup_itable_offset_table(this_klass); |
| |
| // Compute transitive closure of interfaces this class implements |
| // Do final class setup |
| fill_oop_maps(this_klass, info.nonstatic_oop_map_count, info.nonstatic_oop_offsets, info.nonstatic_oop_counts); |
| |
| // Fill in has_finalizer, has_vanilla_constructor, and layout_helper |
| set_precomputed_flags(this_klass); |
| |
| // reinitialize modifiers, using the InnerClasses attribute |
| int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle)); |
| this_klass->set_modifier_flags(computed_modifiers); |
| |
| // check if this class can access its super class |
| check_super_class_access(this_klass, CHECK_(nullHandle)); |
| |
| // check if this class can access its superinterfaces |
| check_super_interface_access(this_klass, CHECK_(nullHandle)); |
| |
| // check if this class overrides any final method |
| check_final_method_override(this_klass, CHECK_(nullHandle)); |
| |
| // check that if this class is an interface then it doesn't have static methods |
| if (this_klass->is_interface()) { |
| /* An interface in a JAVA 8 classfile can be static */ |
| if (_major_version < JAVA_8_VERSION) { |
| check_illegal_static_method(this_klass, CHECK_(nullHandle)); |
| } |
| } |
| |
| // Allocate mirror and initialize static fields |
| java_lang_Class::create_mirror(this_klass, protection_domain, CHECK_(nullHandle)); |
| |
| |
| // Generate any default methods - default methods are interface methods |
| // that have a default implementation. This is new with Lambda project. |
| if (has_default_methods ) { |
| DefaultMethods::generate_default_methods( |
| this_klass(), &all_mirandas, CHECK_(nullHandle)); |
| } |
| |
| // Update the loader_data graph. |
| record_defined_class_dependencies(this_klass, CHECK_NULL); |
| |
| ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()), |
| false /* not shared class */); |
| |
| if (TraceClassLoading) { |
| ResourceMark rm; |
| // print in a single call to reduce interleaving of output |
| if (cfs->source() != NULL) { |
| tty->print("[Loaded %s from %s]\n", this_klass->external_name(), |
| cfs->source()); |
| } else if (class_loader.is_null()) { |
| Klass* caller = |
| THREAD->is_Java_thread() |
| ? ((JavaThread*)THREAD)->security_get_caller_class(1) |
| : NULL; |
| // caller can be NULL, for example, during a JVMTI VM_Init hook |
| if (caller != NULL) { |
| tty->print("[Loaded %s by instance of %s]\n", |
| this_klass->external_name(), |
| InstanceKlass::cast(caller)->external_name()); |
| } else { |
| tty->print("[Loaded %s]\n", this_klass->external_name()); |
| } |
| } else { |
| tty->print("[Loaded %s from %s]\n", this_klass->external_name(), |
| InstanceKlass::cast(class_loader->klass())->external_name()); |
| } |
| } |
| |
| if (TraceClassResolution) { |
| ResourceMark rm; |
| // print out the superclass. |
| const char * from = this_klass()->external_name(); |
| if (this_klass->java_super() != NULL) { |
| tty->print("RESOLVE %s %s (super)\n", from, InstanceKlass::cast(this_klass->java_super())->external_name()); |
| } |
| // print out each of the interface classes referred to by this class. |
| Array<Klass*>* local_interfaces = this_klass->local_interfaces(); |
| if (local_interfaces != NULL) { |
| int length = local_interfaces->length(); |
| for (int i = 0; i < length; i++) { |
| Klass* k = local_interfaces->at(i); |
| InstanceKlass* to_class = InstanceKlass::cast(k); |
| const char * to = to_class->external_name(); |
| tty->print("RESOLVE %s %s (interface)\n", from, to); |
| } |
| } |
| } |
| |
| // preserve result across HandleMark |
| preserve_this_klass = this_klass(); |
| } |
| |
| // Create new handle outside HandleMark (might be needed for |
| // Extended Class Redefinition) |
| instanceKlassHandle this_klass (THREAD, preserve_this_klass); |
| debug_only(this_klass->verify();) |
| |
| // Clear class if no error has occurred so destructor doesn't deallocate it |
| _klass = NULL; |
| return this_klass; |
| } |
| |
| // Destructor to clean up if there's an error |
| ClassFileParser::~ClassFileParser() { |
| MetadataFactory::free_metadata(_loader_data, _cp); |
| MetadataFactory::free_array<u2>(_loader_data, _fields); |
| |
| // Free methods |
| InstanceKlass::deallocate_methods(_loader_data, _methods); |
| |
| // beware of the Universe::empty_blah_array!! |
| if (_inner_classes != Universe::the_empty_short_array()) { |
| MetadataFactory::free_array<u2>(_loader_data, _inner_classes); |
| } |
| |
| // Free interfaces |
| InstanceKlass::deallocate_interfaces(_loader_data, _super_klass(), |
| _local_interfaces, _transitive_interfaces); |
| |
| MetadataFactory::free_array<u1>(_loader_data, _annotations); |
| MetadataFactory::free_array<u1>(_loader_data, _type_annotations); |
| Annotations::free_contents(_loader_data, _fields_annotations); |
| Annotations::free_contents(_loader_data, _fields_type_annotations); |
| |
| clear_class_metadata(); |
| |
| // deallocate the klass if already created. |
| MetadataFactory::free_metadata(_loader_data, _klass); |
| _klass = NULL; |
| } |
| |
| void ClassFileParser::print_field_layout(Symbol* name, |
| Array<u2>* fields, |
| constantPoolHandle cp, |
| int instance_size, |
| int instance_fields_start, |
| int instance_fields_end, |
| int static_fields_end) { |
| tty->print("%s: field layout\n", name->as_klass_external_name()); |
| tty->print(" @%3d %s\n", instance_fields_start, "--- instance fields start ---"); |
| for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { |
| if (!fs.access_flags().is_static()) { |
| tty->print(" @%3d \"%s\" %s\n", |
| fs.offset(), |
| fs.name()->as_klass_external_name(), |
| fs.signature()->as_klass_external_name()); |
| } |
| } |
| tty->print(" @%3d %s\n", instance_fields_end, "--- instance fields end ---"); |
| tty->print(" @%3d %s\n", instance_size * wordSize, "--- instance ends ---"); |
| tty->print(" @%3d %s\n", InstanceMirrorKlass::offset_of_static_fields(), "--- static fields start ---"); |
| for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) { |
| if (fs.access_flags().is_static()) { |
| tty->print(" @%3d \"%s\" %s\n", |
| fs.offset(), |
| fs.name()->as_klass_external_name(), |
| fs.signature()->as_klass_external_name()); |
| } |
| } |
| tty->print(" @%3d %s\n", static_fields_end, "--- static fields end ---"); |
| tty->print("\n"); |
| } |
| |
| unsigned int |
| ClassFileParser::compute_oop_map_count(instanceKlassHandle super, |
| unsigned int nonstatic_oop_map_count, |
| int first_nonstatic_oop_offset) { |
| unsigned int map_count = |
| super.is_null() ? 0 : super->nonstatic_oop_map_count(); |
| if (nonstatic_oop_map_count > 0) { |
| // We have oops to add to map |
| if (map_count == 0) { |
| map_count = nonstatic_oop_map_count; |
| } else { |
| // Check whether we should add a new map block or whether the last one can |
| // be extended |
| OopMapBlock* const first_map = super->start_of_nonstatic_oop_maps(); |
| OopMapBlock* const last_map = first_map + map_count - 1; |
| |
| int next_offset = last_map->offset() + last_map->count() * heapOopSize; |
| if (next_offset == first_nonstatic_oop_offset) { |
| // There is no gap bettwen superklass's last oop field and first |
| // local oop field, merge maps. |
| nonstatic_oop_map_count -= 1; |
| } else { |
| // Superklass didn't end with a oop field, add extra maps |
| assert(next_offset < first_nonstatic_oop_offset, "just checking"); |
| } |
| map_count += nonstatic_oop_map_count; |
| } |
| } |
| return map_count; |
| } |
| |
| |
| void ClassFileParser::fill_oop_maps(instanceKlassHandle k, |
| unsigned int nonstatic_oop_map_count, |
| int* nonstatic_oop_offsets, |
| unsigned int* nonstatic_oop_counts) { |
| OopMapBlock* this_oop_map = k->start_of_nonstatic_oop_maps(); |
| const InstanceKlass* const super = k->superklass(); |
| const unsigned int super_count = super ? super->nonstatic_oop_map_count() : 0; |
| if (super_count > 0) { |
| // Copy maps from superklass |
| OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps(); |
| for (unsigned int i = 0; i < super_count; ++i) { |
| *this_oop_map++ = *super_oop_map++; |
| } |
| } |
| |
| if (nonstatic_oop_map_count > 0) { |
| if (super_count + nonstatic_oop_map_count > k->nonstatic_oop_map_count()) { |
| // The counts differ because there is no gap between superklass's last oop |
| // field and the first local oop field. Extend the last oop map copied |
| // from the superklass instead of creating new one. |
| nonstatic_oop_map_count--; |
| nonstatic_oop_offsets++; |
| this_oop_map--; |
| this_oop_map->set_count(this_oop_map->count() + *nonstatic_oop_counts++); |
| this_oop_map++; |
| } |
| |
| // Add new map blocks, fill them |
| while (nonstatic_oop_map_count-- > 0) { |
| this_oop_map->set_offset(*nonstatic_oop_offsets++); |
| this_oop_map->set_count(*nonstatic_oop_counts++); |
| this_oop_map++; |
| } |
| assert(k->start_of_nonstatic_oop_maps() + k->nonstatic_oop_map_count() == |
| this_oop_map, "sanity"); |
| } |
| } |
| |
| |
| void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) { |
| Klass* super = k->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 != NULL && super->has_finalizer())) { |
| k->set_has_finalizer(); |
| } |
| } |
| |
| #ifdef ASSERT |
| bool f = false; |
| Method* m = k->lookup_method(vmSymbols::finalize_method_name(), |
| vmSymbols::void_method_signature()); |
| if (m != NULL && !m->is_empty_method()) { |
| f = true; |
| } |
| assert(f == k->has_finalizer(), "inconsistent has_finalizer"); |
| #endif |
| |
| // Check if this klass supports the java.lang.Cloneable interface |
| if (SystemDictionary::Cloneable_klass_loaded()) { |
| if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) { |
| k->set_is_cloneable(); |
| } |
| } |
| |
| // Check if this klass has a vanilla default constructor |
| if (super == NULL) { |
| // java.lang.Object has empty default constructor |
| k->set_has_vanilla_constructor(); |
| } else { |
| if (super->has_vanilla_constructor() && |
| _has_vanilla_constructor) { |
| k->set_has_vanilla_constructor(); |
| } |
| #ifdef ASSERT |
| bool v = false; |
| if (super->has_vanilla_constructor()) { |
| Method* constructor = k->find_method(vmSymbols::object_initializer_name( |
| ), vmSymbols::void_method_signature()); |
| if (constructor != NULL && constructor->is_vanilla_constructor()) { |
| v = true; |
| } |
| } |
| assert(v == k->has_vanilla_constructor(), "inconsistent has_vanilla_constructor"); |
| #endif |
| } |
| |
| // If it cannot be fast-path allocated, set a bit in the layout helper. |
| // See documentation of InstanceKlass::can_be_fastpath_allocated(). |
| assert(k->size_helper() > 0, "layout_helper is initialized"); |
| if ((!RegisterFinalizersAtInit && k->has_finalizer()) |
| || k->is_abstract() || k->is_interface() |
| || (k->name() == vmSymbols::java_lang_Class() && k->class_loader() == NULL) |
| || k->size_helper() >= FastAllocateSizeLimit) { |
| // Forbid fast-path allocation. |
| jint lh = Klass::instance_layout_helper(k->size_helper(), true); |
| k->set_layout_helper(lh); |
| } |
| } |
| |
| // Attach super classes and interface classes to class loader data |
| void ClassFileParser::record_defined_class_dependencies(instanceKlassHandle defined_klass, TRAPS) { |
| ClassLoaderData * defining_loader_data = defined_klass->class_loader_data(); |
| if (defining_loader_data->is_the_null_class_loader_data()) { |
| // Dependencies to null class loader data are implicit. |
| return; |
| } else { |
| // add super class dependency |
| Klass* super = defined_klass->super(); |
| if (super != NULL) { |
| defining_loader_data->record_dependency(super, CHECK); |
| } |
| |
| // add super interface dependencies |
| Array<Klass*>* local_interfaces = defined_klass->local_interfaces(); |
| if (local_interfaces != NULL) { |
| int length = local_interfaces->length(); |
| for (int i = 0; i < length; i++) { |
| defining_loader_data->record_dependency(local_interfaces->at(i), CHECK); |
| } |
| } |
| } |
| } |
| |
| // utility methods for appending an array with check for duplicates |
| |
| void append_interfaces(GrowableArray<Klass*>* result, Array<Klass*>* ifs) { |
| // iterate over new interfaces |
| for (int i = 0; i < ifs->length(); i++) { |
| Klass* e = ifs->at(i); |
| assert(e->is_klass() && InstanceKlass::cast(e)->is_interface(), "just checking"); |
| // add new interface |
| result->append_if_missing(e); |
| } |
| } |
| |
| Array<Klass*>* ClassFileParser::compute_transitive_interfaces( |
| instanceKlassHandle super, |
| Array<Klass*>* local_ifs, TRAPS) { |
| // Compute maximum size for transitive interfaces |
| int max_transitive_size = 0; |
| int super_size = 0; |
| // Add superclass transitive interfaces size |
| if (super.not_null()) { |
| super_size = super->transitive_interfaces()->length(); |
| max_transitive_size += super_size; |
| } |
| // Add local interfaces' super interfaces |
| int local_size = local_ifs->length(); |
| for (int i = 0; i < local_size; i++) { |
| Klass* l = local_ifs->at(i); |
| max_transitive_size += InstanceKlass::cast(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_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<Klass*>* result = new GrowableArray<Klass*>(max_transitive_size); |
| |
| // Copy down from superclass |
| if (super.not_null()) { |
| append_interfaces(result, super->transitive_interfaces()); |
| } |
| |
| // Copy down from local interfaces' superinterfaces |
| for (int i = 0; i < local_ifs->length(); i++) { |
| Klass* l = local_ifs->at(i); |
| append_interfaces(result, InstanceKlass::cast(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 |
| int length = result->length(); |
| assert(length <= max_transitive_size, "just checking"); |
| Array<Klass*>* new_result = MetadataFactory::new_array<Klass*>(_loader_data, length, CHECK_NULL); |
| for (int i = 0; i < length; i++) { |
| Klass* e = result->at(i); |
| assert(e != NULL, "just checking"); |
| new_result->at_put(i, e); |
| } |
| return new_result; |
| } |
| } |
| |
| void ClassFileParser::check_super_class_access(instanceKlassHandle this_klass, TRAPS) { |
| Klass* super = this_klass->super(); |
| if ((super != NULL) && |
| (!Reflection::verify_class_access(this_klass(), super, false))) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_IllegalAccessError(), |
| "class %s cannot access its superclass %s", |
| this_klass->external_name(), |
| InstanceKlass::cast(super)->external_name() |
| ); |
| return; |
| } |
| } |
| |
| |
| void ClassFileParser::check_super_interface_access(instanceKlassHandle this_klass, TRAPS) { |
| Array<Klass*>* local_interfaces = this_klass->local_interfaces(); |
| int lng = local_interfaces->length(); |
| for (int i = lng - 1; i >= 0; i--) { |
| Klass* k = local_interfaces->at(i); |
| assert (k != NULL && k->is_interface(), "invalid interface"); |
| if (!Reflection::verify_class_access(this_klass(), k, false)) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_IllegalAccessError(), |
| "class %s cannot access its superinterface %s", |
| this_klass->external_name(), |
| InstanceKlass::cast(k)->external_name() |
| ); |
| return; |
| } |
| } |
| } |
| |
| |
| void ClassFileParser::check_final_method_override(instanceKlassHandle this_klass, TRAPS) { |
| Array<Method*>* methods = this_klass->methods(); |
| 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++) { |
| Method* m = methods->at(index); |
| |
| // skip private, static, and <init> methods |
| if ((!m->is_private() && !m->is_static()) && |
| (m->name() != vmSymbols::object_initializer_name())) { |
| |
| Symbol* name = m->name(); |
| Symbol* signature = m->signature(); |
| Klass* k = this_klass->super(); |
| Method* super_m = NULL; |
| while (k != NULL) { |
| // 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 == NULL) { |
| break; // didn't find any match; get out |
| } |
| |
| if (super_m->is_final() && !super_m->is_static() && |
| // matching method in super is final, and not static |
| (Reflection::verify_field_access(this_klass(), |
| super_m->method_holder(), |
| super_m->method_holder(), |
| super_m->access_flags(), false)) |
| // this class can access super final method and therefore override |
| ) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_VerifyError(), |
| "class %s overrides final method %s.%s", |
| this_klass->external_name(), |
| name->as_C_string(), |
| signature->as_C_string() |
| ); |
| return; |
| } |
| |
| // continue to look from super_m's holder's super. |
| k = super_m->method_holder()->super(); |
| continue; |
| } |
| |
| k = k->super(); |
| } |
| } |
| } |
| } |
| |
| |
| // assumes that this_klass is an interface |
| void ClassFileParser::check_illegal_static_method(instanceKlassHandle this_klass, TRAPS) { |
| assert(this_klass->is_interface(), "not an interface"); |
| Array<Method*>* methods = this_klass->methods(); |
| int num_methods = methods->length(); |
| |
| for (int index = 0; index < num_methods; index++) { |
| Method* 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); |
| 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) { |
| 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_15 = _major_version >= JAVA_1_5_VERSION; |
| |
| if ((is_abstract && is_final) || |
| (is_interface && !is_abstract) || |
| (is_interface && major_gte_15 && (is_super || is_enum)) || |
| (!is_interface && major_gte_15 && is_annotation)) { |
| ResourceMark rm(THREAD); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_ClassFormatError(), |
| "Illegal class modifiers in class %s: 0x%X", |
| _class_name->as_C_string(), flags |
| ); |
| return; |
| } |
| } |
| |
| bool ClassFileParser::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)); |
| } |
| |
| bool ClassFileParser::is_supported_version(u2 major, u2 minor) { |
| u2 max_version = |
| JDK_Version::is_gte_jdk17x_version() ? JAVA_MAX_SUPPORTED_VERSION : |
| (JDK_Version::is_gte_jdk16x_version() ? JAVA_6_VERSION : JAVA_1_5_VERSION); |
| return (major >= JAVA_MIN_SUPPORTED_VERSION) && |
| (major <= max_version) && |
| ((major != max_version) || |
| (minor <= JAVA_MAX_SUPPORTED_MINOR_VERSION)); |
| } |
| |
| void ClassFileParser::verify_legal_field_modifiers( |
| jint flags, bool is_interface, TRAPS) { |
| 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_15 = _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_15 && 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); |
| 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, Symbol* name, TRAPS) { |
| 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_15 = _major_version >= JAVA_1_5_VERSION; |
| const bool major_gte_8 = _major_version >= JAVA_8_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 || is_strict))) { |
| is_illegal = true; |
| } |
| } else if (major_gte_15) { |
| // Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION) |
| if (!is_public || 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 (is_initializer) { |
| if (is_static || is_final || is_synchronized || is_native || |
| is_abstract || (major_gte_15 && is_bridge)) { |
| is_illegal = true; |
| } |
| } else { // not initializer |
| if (is_abstract) { |
| if ((is_final || is_native || is_private || is_static || |
| (major_gte_15 && (is_synchronized || is_strict)))) { |
| is_illegal = true; |
| } |
| } |
| if (has_illegal_visibility(flags)) { |
| is_illegal = true; |
| } |
| } |
| } |
| |
| if (is_illegal) { |
| ResourceMark rm(THREAD); |
| 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) { |
| assert(_need_verify, "only called when _need_verify is true"); |
| int i = 0; |
| int count = length >> 2; |
| for (int k=0; k<count; k++) { |
| unsigned char b0 = buffer[i]; |
| unsigned char b1 = buffer[i+1]; |
| unsigned char b2 = buffer[i+2]; |
| unsigned char b3 = buffer[i+3]; |
| // For an unsigned char v, |
| // (v | v - 1) is < 128 (highest bit 0) for 0 < v < 128; |
| // (v | v - 1) is >= 128 (highest bit 1) for v == 0 or v >= 128. |
| unsigned char res = b0 | b0 - 1 | |
| b1 | b1 - 1 | |
| b2 | b2 - 1 | |
| b3 | b3 - 1; |
| if (res >= 128) break; |
| i += 4; |
| } |
| for(; i < length; i++) { |
| unsigned short c; |
| // no embedded zeros |
| guarantee_property((buffer[i] != 0), "Illegal UTF8 string in constant pool in class file %s", CHECK); |
| if(buffer[i] < 128) { |
| continue; |
| } |
| if ((i + 5) < length) { // see if it's legal supplementary character |
| if (UTF8::is_supplementary_character(&buffer[i])) { |
| c = UTF8::get_supplementary_character(&buffer[i]); |
| i += 5; |
| continue; |
| } |
| } |
| switch (buffer[i] >> 4) { |
| default: break; |
| case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: |
| classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); |
| case 0xC: case 0xD: // 110xxxxx 10xxxxxx |
| c = (buffer[i] & 0x1F) << 6; |
| i++; |
| if ((i < length) && ((buffer[i] & 0xC0) == 0x80)) { |
| c += buffer[i] & 0x3F; |
| if (_major_version <= 47 || c == 0 || c >= 0x80) { |
| // for classes with major > 47, c must a null or a character in its shortest form |
| break; |
| } |
| } |
| classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); |
| case 0xE: // 1110xxxx 10xxxxxx 10xxxxxx |
| c = (buffer[i] & 0xF) << 12; |
| i += 2; |
| if ((i < length) && ((buffer[i-1] & 0xC0) == 0x80) && ((buffer[i] & 0xC0) == 0x80)) { |
| c += ((buffer[i-1] & 0x3F) << 6) + (buffer[i] & 0x3F); |
| if (_major_version <= 47 || c >= 0x800) { |
| // for classes with major > 47, c must be in its shortest form |
| break; |
| } |
| } |
| classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK); |
| } // end of switch |
| } // end of for |
| } |
| |
| // Checks if name is a legal class name. |
| void ClassFileParser::verify_legal_class_name(Symbol* name, TRAPS) { |
| if (!_need_verify || _relax_verify) { return; } |
| |
| char buf[fixed_buffer_size]; |
| char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); |
| unsigned int length = name->utf8_length(); |
| bool legal = false; |
| |
| if (length > 0) { |
| char* p; |
| if (bytes[0] == JVM_SIGNATURE_ARRAY) { |
| p = skip_over_field_signature(bytes, false, length, CHECK); |
| legal = (p != NULL) && ((p - bytes) == (int)length); |
| } else if (_major_version < JAVA_1_5_VERSION) { |
| if (bytes[0] != '<') { |
| p = skip_over_field_name(bytes, true, length); |
| legal = (p != NULL) && ((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); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_ClassFormatError(), |
| "Illegal class name \"%s\" in class file %s", bytes, |
| _class_name->as_C_string() |
| ); |
| return; |
| } |
| } |
| |
| // Checks if name is a legal field name. |
| void ClassFileParser::verify_legal_field_name(Symbol* name, TRAPS) { |
| if (!_need_verify || _relax_verify) { return; } |
| |
| char buf[fixed_buffer_size]; |
| char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); |
| unsigned int length = name->utf8_length(); |
| bool legal = false; |
| |
| if (length > 0) { |
| if (_major_version < JAVA_1_5_VERSION) { |
| if (bytes[0] != '<') { |
| char* p = skip_over_field_name(bytes, false, length); |
| legal = (p != NULL) && ((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); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_ClassFormatError(), |
| "Illegal field name \"%s\" in class %s", bytes, |
| _class_name->as_C_string() |
| ); |
| return; |
| } |
| } |
| |
| // Checks if name is a legal method name. |
| void ClassFileParser::verify_legal_method_name(Symbol* name, TRAPS) { |
| if (!_need_verify || _relax_verify) { return; } |
| |
| assert(name != NULL, "method name is null"); |
| char buf[fixed_buffer_size]; |
| char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); |
| unsigned int length = name->utf8_length(); |
| bool legal = false; |
| |
| if (length > 0) { |
| if (bytes[0] == '<') { |
| if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) { |
| legal = true; |
| } |
| } else if (_major_version < JAVA_1_5_VERSION) { |
| char* p; |
| p = skip_over_field_name(bytes, false, length); |
| legal = (p != NULL) && ((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); |
| Exceptions::fthrow( |
| THREAD_AND_LOCATION, |
| vmSymbols::java_lang_ClassFormatError(), |
| "Illegal method name \"%s\" in class %s", bytes, |
| _class_name->as_C_string() |
| ); |
| return; |
| } |
| } |
| |
| |
| // Checks if signature is a legal field signature. |
| void ClassFileParser::verify_legal_field_signature(Symbol* name, Symbol* signature, TRAPS) { |
| if (!_need_verify) { return; } |
| |
| char buf[fixed_buffer_size]; |
| char* bytes = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); |
| unsigned int length = signature->utf8_length(); |
| char* p = skip_over_field_signature(bytes, false, length, CHECK); |
| |
| if (p == NULL || (p - bytes) != (int)length) { |
| throwIllegalSignature("Field", name, signature, CHECK); |
| } |
| } |
| |
| // Checks if signature is a legal method signature. |
| // Returns number of parameters |
| int ClassFileParser::verify_legal_method_signature(Symbol* name, Symbol* signature, TRAPS) { |
| 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; |
| char buf[fixed_buffer_size]; |
| char* p = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size); |
| unsigned int length = signature->utf8_length(); |
| 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 != NULL)) { |
| args_size++; |
| if (p[0] == 'J' || p[0] == 'D') { |
| args_size++; |
| } |
| length -= 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--; |
| if (name->utf8_length() > 0 && name->byte_at(0) == '<') { |
| // All internal methods must return void |
| if ((length == 1) && (p[0] == JVM_SIGNATURE_VOID)) { |
| return args_size; |
| } |
| } else { |
| // 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, CHECK_0); |
| return 0; |
| } |
| |
| |
| // Unqualified names may not contain the characters '.', ';', '[', or '/'. |
| // 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. |
| bool ClassFileParser::verify_unqualified_name( |
| char* name, unsigned int length, int type) { |
| jchar ch; |
| |
| for (char* p = name; p != name + length; ) { |
| ch = *p; |
| if (ch < 128) { |
| p++; |
| if (ch == '.' || ch == ';' || ch == '[' ) { |
| return false; // do not permit '.', ';', or '[' |
| } |
| if (type != LegalClass && ch == '/') { |
| return false; // do not permit '/' unless it's class name |
| } |
| if (type == LegalMethod && (ch == '<' || ch == '>')) { |
| return false; // do not permit '<' or '>' in method names |
| } |
| } else { |
| char* tmp_p = UTF8::next(p, &ch); |
| p = tmp_p; |
| } |
| } |
| return true; |
| } |
| |
| |
| // Take pointer to a string. Skip over the longest part of the string that could |
| // be taken as a fieldname. Allow '/' 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). |
| char* ClassFileParser::skip_over_field_name(char* name, bool slash_ok, unsigned int length) { |
| char* p; |
| jchar ch; |
| jboolean last_is_slash = false; |
| jboolean not_first_ch = false; |
| |
| for (p = name; p != name + length; not_first_ch = true) { |
| char* old_p = p; |
| 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 == '/') { |
| if (last_is_slash) { |
| return NULL; // 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; |
| instanceKlassHandle klass (THREAD, SystemDictionary::Character_klass()); |
| |
| // return value |
| JavaValue result(T_BOOLEAN); |
| // Set up the arguments to isJavaIdentifierStart and isJavaIdentifierPart |
| JavaCallArguments args; |
| args.push_int(unicode_ch); |
| |
| // public static boolean isJavaIdentifierStart(char ch); |
| JavaCalls::call_static(&result, |
| klass, |
| vmSymbols::isJavaIdentifierStart_name(), |
| vmSymbols::int_bool_signature(), |
| &args, |
| THREAD); |
| |
| if (HAS_PENDING_EXCEPTION) { |
| CLEAR_PENDING_EXCEPTION; |
| return 0; |
| } |
| if (result.get_jboolean()) { |
| continue; |
| } |
| |
| if (not_first_ch) { |
| // public static boolean isJavaIdentifierPart(char ch); |
| JavaCalls::call_static(&result, |
| klass, |
| vmSymbols::isJavaIdentifierPart_name(), |
| vmSymbols::int_bool_signature(), |
| &args, |
| THREAD); |
| |
| if (HAS_PENDING_EXCEPTION) { |
| CLEAR_PENDING_EXCEPTION; |
| return 0; |
| } |
| |
| if (result.get_jboolean()) { |
| continue; |
| } |
| } |
| } |
| return (not_first_ch) ? old_p : NULL; |
| } |
| return (not_first_ch) ? p : NULL; |
| } |
| |
| |
| // Take pointer to a string. 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. |
| char* ClassFileParser::skip_over_field_signature(char* signature, |
| bool void_ok, |
| unsigned int length, |
| TRAPS) { |
| unsigned int array_dim = 0; |
| while (length > 0) { |
| switch (signature[0]) { |
| case JVM_SIGNATURE_VOID: if (!void_ok) { return NULL; } |
| 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 |
| char* p = skip_over_field_name(signature + 1, true, --length); |
| |
| // The next character better be a semicolon |
| if (p && (p - signature) > 1 && p[0] == ';') { |
| return p + 1; |
| } |
| } else { |
| // 4900761: For class version > 48, any unicode is allowed in class name. |
| length--; |
| signature++; |
| while (length > 0 && signature[0] != ';') { |
| if (signature[0] == '.') { |
| classfile_parse_error("Class name contains illegal character '.' in descriptor in class file %s", CHECK_0); |
| } |
| length--; |
| signature++; |
| } |
| if (signature[0] == ';') { return signature + 1; } |
| } |
| |
| return NULL; |
| } |
| 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", CHECK_0); |
| } |
| // The rest of what's there better be a legal signature |
| signature++; |
| length--; |
| void_ok = false; |
| break; |
| |
| default: |
| return NULL; |
| } |
| } |
| return NULL; |
| } |