Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
6372 lines (5756 sloc)
255 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 1997, 2018, 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 "jvm.h" | |
#include "aot/aotLoader.hpp" | |
#include "classfile/classFileParser.hpp" | |
#include "classfile/classFileStream.hpp" | |
#include "classfile/classLoader.hpp" | |
#include "classfile/classLoaderData.inline.hpp" | |
#include "classfile/defaultMethods.hpp" | |
#include "classfile/dictionary.hpp" | |
#include "classfile/javaClasses.inline.hpp" | |
#include "classfile/moduleEntry.hpp" | |
#include "classfile/symbolTable.hpp" | |
#include "classfile/systemDictionary.hpp" | |
#include "classfile/verificationType.hpp" | |
#include "classfile/verifier.hpp" | |
#include "classfile/vmSymbols.hpp" | |
#include "logging/log.hpp" | |
#include "logging/logStream.hpp" | |
#include "memory/allocation.hpp" | |
#include "memory/metadataFactory.hpp" | |
#include "memory/oopFactory.hpp" | |
#include "memory/resourceArea.hpp" | |
#include "memory/universe.hpp" | |
#include "oops/annotations.hpp" | |
#include "oops/constantPool.inline.hpp" | |
#include "oops/fieldStreams.hpp" | |
#include "oops/instanceKlass.hpp" | |
#include "oops/instanceMirrorKlass.hpp" | |
#include "oops/klass.inline.hpp" | |
#include "oops/klassVtable.hpp" | |
#include "oops/metadata.hpp" | |
#include "oops/method.hpp" | |
#include "oops/oop.inline.hpp" | |
#include "oops/symbol.hpp" | |
#include "prims/jvmtiExport.hpp" | |
#include "prims/jvmtiThreadState.hpp" | |
#include "runtime/arguments.hpp" | |
#include "runtime/handles.inline.hpp" | |
#include "runtime/javaCalls.hpp" | |
#include "runtime/os.hpp" | |
#include "runtime/perfData.hpp" | |
#include "runtime/reflection.hpp" | |
#include "runtime/safepointVerifiers.hpp" | |
#include "runtime/signature.hpp" | |
#include "runtime/timer.hpp" | |
#include "services/classLoadingService.hpp" | |
#include "services/threadService.hpp" | |
#include "utilities/align.hpp" | |
#include "utilities/bitMap.inline.hpp" | |
#include "utilities/copy.hpp" | |
#include "utilities/exceptions.hpp" | |
#include "utilities/globalDefinitions.hpp" | |
#include "utilities/growableArray.hpp" | |
#include "utilities/macros.hpp" | |
#include "utilities/ostream.hpp" | |
#include "utilities/resourceHash.hpp" | |
#if INCLUDE_CDS | |
#include "classfile/systemDictionaryShared.hpp" | |
#endif | |
#if INCLUDE_JFR | |
#include "jfr/support/jfrTraceIdExtension.hpp" | |
#endif | |
// We generally try to create the oops directly when parsing, rather than | |
// allocating temporary data structures and copying the bytes twice. A | |
// temporary area is only needed when parsing utf8 entries in the constant | |
// pool and when parsing line number tables. | |
// We add assert in debug mode when class format is not checked. | |
#define JAVA_CLASSFILE_MAGIC 0xCAFEBABE | |
#define JAVA_MIN_SUPPORTED_VERSION 45 | |
#define JAVA_PREVIEW_MINOR_VERSION 65535 | |
// Used for two backward compatibility reasons: | |
// - to check for new additions to the class file format in JDK1.5 | |
// - to check for bug fixes in the format checker in JDK1.5 | |
#define JAVA_1_5_VERSION 49 | |
// Used for backward compatibility reasons: | |
// - to check for javac bug fixes that happened after 1.5 | |
// - also used as the max version when running in jdk6 | |
#define JAVA_6_VERSION 50 | |
// Used for backward compatibility reasons: | |
// - to disallow argument and require ACC_STATIC for <clinit> methods | |
#define JAVA_7_VERSION 51 | |
// Extension method support. | |
#define JAVA_8_VERSION 52 | |
#define JAVA_9_VERSION 53 | |
#define JAVA_10_VERSION 54 | |
#define JAVA_11_VERSION 55 | |
void ClassFileParser::set_class_bad_constant_seen(short bad_constant) { | |
assert((bad_constant == 19 || bad_constant == 20) && _major_version >= JAVA_9_VERSION, | |
"Unexpected bad constant pool entry"); | |
if (_bad_constant_seen == 0) _bad_constant_seen = bad_constant; | |
} | |
void ClassFileParser::parse_constant_pool_entries(const ClassFileStream* const stream, | |
ConstantPool* cp, | |
const int length, | |
TRAPS) { | |
assert(stream != NULL, "invariant"); | |
assert(cp != NULL, "invariant"); | |
// Use a local copy of ClassFileStream. It helps the C++ compiler to optimize | |
// this function (_current can be allocated in a register, with scalar | |
// replacement of aggregates). The _current pointer is copied back to | |
// stream() when this function returns. DON'T call another method within | |
// this method that uses stream(). | |
const ClassFileStream cfs1 = *stream; | |
const ClassFileStream* const cfs = &cfs1; | |
assert(cfs->allocated_on_stack(), "should be local"); | |
debug_only(const u1* const old_current = stream->current();) | |
// Used for batching symbol allocations. | |
const char* names[SymbolTable::symbol_alloc_batch_size]; | |
int lengths[SymbolTable::symbol_alloc_batch_size]; | |
int indices[SymbolTable::symbol_alloc_batch_size]; | |
unsigned int hashValues[SymbolTable::symbol_alloc_batch_size]; | |
int names_count = 0; | |
// parsing Index 0 is unused | |
for (int index = 1; index < length; index++) { | |
// Each of the following case guarantees one more byte in the stream | |
// for the following tag or the access_flags following constant pool, | |
// so we don't need bounds-check for reading tag. | |
const u1 tag = cfs->get_u1_fast(); | |
switch (tag) { | |
case JVM_CONSTANT_Class : { | |
cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags | |
const u2 name_index = cfs->get_u2_fast(); | |
cp->klass_index_at_put(index, name_index); | |
break; | |
} | |
case JVM_CONSTANT_Fieldref: { | |
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
const u2 class_index = cfs->get_u2_fast(); | |
const u2 name_and_type_index = cfs->get_u2_fast(); | |
cp->field_at_put(index, class_index, name_and_type_index); | |
break; | |
} | |
case JVM_CONSTANT_Methodref: { | |
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
const u2 class_index = cfs->get_u2_fast(); | |
const u2 name_and_type_index = cfs->get_u2_fast(); | |
cp->method_at_put(index, class_index, name_and_type_index); | |
break; | |
} | |
case JVM_CONSTANT_InterfaceMethodref: { | |
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags | |
const u2 class_index = cfs->get_u2_fast(); | |
const u2 name_and_type_index = cfs->get_u2_fast(); | |
cp->interface_method_at_put(index, class_index, name_and_type_index); | |
break; | |
} | |
case JVM_CONSTANT_String : { | |
cfs->guarantee_more(3, CHECK); // string_index, tag/access_flags | |
const u2 string_index = cfs->get_u2_fast(); | |
cp->string_index_at_put(index, string_index); | |
break; | |
} | |
case JVM_CONSTANT_MethodHandle : | |
case JVM_CONSTANT_MethodType: { | |
if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { | |
classfile_parse_error( | |
"Class file version does not support constant tag %u in class file %s", | |
tag, CHECK); | |
} | |
if (tag == JVM_CONSTANT_MethodHandle) { | |
cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags | |
const u1 ref_kind = cfs->get_u1_fast(); | |
const u2 method_index = cfs->get_u2_fast(); | |
cp->method_handle_index_at_put(index, ref_kind, method_index); | |
} | |
else if (tag == JVM_CONSTANT_MethodType) { | |
cfs->guarantee_more(3, CHECK); // signature_index, tag/access_flags | |
const u2 signature_index = cfs->get_u2_fast(); | |
cp->method_type_index_at_put(index, signature_index); | |
} | |
else { | |
ShouldNotReachHere(); | |
} | |
break; | |
} | |
case JVM_CONSTANT_Dynamic : { | |
if (_major_version < Verifier::DYNAMICCONSTANT_MAJOR_VERSION) { | |
classfile_parse_error( | |
"Class file version does not support constant tag %u in class file %s", | |
tag, CHECK); | |
} | |
cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags | |
const u2 bootstrap_specifier_index = cfs->get_u2_fast(); | |
const u2 name_and_type_index = cfs->get_u2_fast(); | |
if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) { | |
_max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later | |
} | |
cp->dynamic_constant_at_put(index, bootstrap_specifier_index, name_and_type_index); | |
break; | |
} | |
case JVM_CONSTANT_InvokeDynamic : { | |
if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) { | |
classfile_parse_error( | |
"Class file version does not support constant tag %u in class file %s", | |
tag, CHECK); | |
} | |
cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags | |
const u2 bootstrap_specifier_index = cfs->get_u2_fast(); | |
const u2 name_and_type_index = cfs->get_u2_fast(); | |
if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index) { | |
_max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later | |
} | |
cp->invoke_dynamic_at_put(index, bootstrap_specifier_index, name_and_type_index); | |
break; | |
} | |
case JVM_CONSTANT_Integer: { | |
cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags | |
const u4 bytes = cfs->get_u4_fast(); | |
cp->int_at_put(index, (jint)bytes); | |
break; | |
} | |
case JVM_CONSTANT_Float: { | |
cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags | |
const u4 bytes = cfs->get_u4_fast(); | |
cp->float_at_put(index, *(jfloat*)&bytes); | |
break; | |
} | |
case JVM_CONSTANT_Long: { | |
// A mangled type might cause you to overrun allocated memory | |
guarantee_property(index + 1 < length, | |
"Invalid constant pool entry %u in class file %s", | |
index, | |
CHECK); | |
cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags | |
const u8 bytes = cfs->get_u8_fast(); | |
cp->long_at_put(index, bytes); | |
index++; // Skip entry following eigth-byte constant, see JVM book p. 98 | |
break; | |
} | |
case JVM_CONSTANT_Double: { | |
// A mangled type might cause you to overrun allocated memory | |
guarantee_property(index+1 < length, | |
"Invalid constant pool entry %u in class file %s", | |
index, | |
CHECK); | |
cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags | |
const u8 bytes = cfs->get_u8_fast(); | |
cp->double_at_put(index, *(jdouble*)&bytes); | |
index++; // Skip entry following eigth-byte constant, see JVM book p. 98 | |
break; | |
} | |
case JVM_CONSTANT_NameAndType: { | |
cfs->guarantee_more(5, CHECK); // name_index, signature_index, tag/access_flags | |
const u2 name_index = cfs->get_u2_fast(); | |
const u2 signature_index = cfs->get_u2_fast(); | |
cp->name_and_type_at_put(index, name_index, signature_index); | |
break; | |
} | |
case JVM_CONSTANT_Utf8 : { | |
cfs->guarantee_more(2, CHECK); // utf8_length | |
u2 utf8_length = cfs->get_u2_fast(); | |
const u1* utf8_buffer = cfs->current(); | |
assert(utf8_buffer != 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(utf8_buffer, utf8_length, CHECK); | |
} | |
if (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); | |
const char* const str = java_lang_String::as_utf8_string(patch()); | |
// (could use java_lang_String::as_symbol instead, but might as well batch them) | |
utf8_buffer = (const u1*) str; | |
utf8_length = (u2) strlen(str); | |
} | |
unsigned int hash; | |
Symbol* const result = SymbolTable::lookup_only((const char*)utf8_buffer, | |
utf8_length, | |
hash); | |
if (result == NULL) { | |
names[names_count] = (const char*)utf8_buffer; | |
lengths[names_count] = utf8_length; | |
indices[names_count] = index; | |
hashValues[names_count++] = hash; | |
if (names_count == SymbolTable::symbol_alloc_batch_size) { | |
SymbolTable::new_symbols(_loader_data, | |
cp, | |
names_count, | |
names, | |
lengths, | |
indices, | |
hashValues, | |
CHECK); | |
names_count = 0; | |
} | |
} else { | |
cp->symbol_at_put(index, result); | |
} | |
break; | |
} | |
case 19: | |
case 20: { | |
// Record that an error occurred in these two cases but keep parsing so | |
// that ACC_Module can be checked for in the access_flags. Need to | |
// throw NoClassDefFoundError in that case. | |
if (_major_version >= JAVA_9_VERSION) { | |
cfs->guarantee_more(3, CHECK); | |
cfs->get_u2_fast(); | |
set_class_bad_constant_seen(tag); | |
break; | |
} | |
} | |
default: { | |
classfile_parse_error("Unknown constant tag %u in class file %s", | |
tag, | |
CHECK); | |
break; | |
} | |
} // end of switch(tag) | |
} // end of for | |
// 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. | |
assert(stream->current() == old_current, "non-exclusive use of stream"); | |
stream->set_current(cfs1.current()); | |
} | |
static inline bool valid_cp_range(int index, int length) { | |
return (index > 0 && index < length); | |
} | |
static inline Symbol* check_symbol_at(const ConstantPool* cp, int index) { | |
assert(cp != NULL, "invariant"); | |
if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8()) { | |
return cp->symbol_at(index); | |
} | |
return NULL; | |
} | |
#ifdef ASSERT | |
PRAGMA_DIAG_PUSH | |
PRAGMA_FORMAT_NONLITERAL_IGNORED | |
void ClassFileParser::report_assert_property_failure(const char* msg, TRAPS) const { | |
ResourceMark rm(THREAD); | |
fatal(msg, _class_name->as_C_string()); | |
} | |
void ClassFileParser::report_assert_property_failure(const char* msg, | |
int index, | |
TRAPS) const { | |
ResourceMark rm(THREAD); | |
fatal(msg, index, _class_name->as_C_string()); | |
} | |
PRAGMA_DIAG_POP | |
#endif | |
void ClassFileParser::parse_constant_pool(const ClassFileStream* const stream, | |
ConstantPool* const cp, | |
const int length, | |
TRAPS) { | |
assert(cp != NULL, "invariant"); | |
assert(stream != NULL, "invariant"); | |
// parsing constant pool entries | |
parse_constant_pool_entries(stream, cp, length, CHECK); | |
if (class_bad_constant_seen() != 0) { | |
// a bad CP entry has been detected previously so stop parsing and just return. | |
return; | |
} | |
int index = 1; // declared outside of loops for portability | |
int num_klasses = 0; | |
// first verification pass - validate cross references | |
// and fixup class and string constants | |
for (index = 1; index < length; index++) { // Index 0 is unused | |
const jbyte tag = cp->tag_at(index).value(); | |
switch (tag) { | |
case JVM_CONSTANT_Class: { | |
ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present | |
break; | |
} | |
case JVM_CONSTANT_Fieldref: | |
// fall through | |
case JVM_CONSTANT_Methodref: | |
// fall through | |
case JVM_CONSTANT_InterfaceMethodref: { | |
if (!_need_verify) break; | |
const int klass_ref_index = cp->klass_ref_index_at(index); | |
const 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); | |
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); | |
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); | |
break; | |
} | |
case JVM_CONSTANT_NameAndType: { | |
if (!_need_verify) break; | |
const int name_ref_index = cp->name_ref_index_at(index); | |
const int signature_ref_index = cp->signature_ref_index_at(index); | |
check_property(valid_symbol_at(name_ref_index), | |
"Invalid constant pool index %u in class file %s", | |
name_ref_index, CHECK); | |
check_property(valid_symbol_at(signature_ref_index), | |
"Invalid constant pool index %u in class file %s", | |
signature_ref_index, CHECK); | |
break; | |
} | |
case JVM_CONSTANT_Utf8: | |
break; | |
case JVM_CONSTANT_UnresolvedClass: // fall-through | |
case JVM_CONSTANT_UnresolvedClassInError: { | |
ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present | |
break; | |
} | |
case JVM_CONSTANT_ClassIndex: { | |
const int class_index = cp->klass_index_at(index); | |
check_property(valid_symbol_at(class_index), | |
"Invalid constant pool index %u in class file %s", | |
class_index, CHECK); | |
cp->unresolved_klass_at_put(index, class_index, num_klasses++); | |
break; | |
} | |
case JVM_CONSTANT_StringIndex: { | |
const int string_index = cp->string_index_at(index); | |
check_property(valid_symbol_at(string_index), | |
"Invalid constant pool index %u in class file %s", | |
string_index, CHECK); | |
Symbol* const sym = cp->symbol_at(string_index); | |
cp->unresolved_string_at_put(index, sym); | |
break; | |
} | |
case JVM_CONSTANT_MethodHandle: { | |
const int ref_index = cp->method_handle_index_at(index); | |
check_property(valid_cp_range(ref_index, length), | |
"Invalid constant pool index %u in class file %s", | |
ref_index, CHECK); | |
const constantTag tag = cp->tag_at(ref_index); | |
const int ref_kind = cp->method_handle_ref_kind_at(index); | |
switch (ref_kind) { | |
case JVM_REF_getField: | |
case JVM_REF_getStatic: | |
case JVM_REF_putField: | |
case JVM_REF_putStatic: { | |
check_property( | |
tag.is_field(), | |
"Invalid constant pool index %u in class file %s (not a field)", | |
ref_index, CHECK); | |
break; | |
} | |
case JVM_REF_invokeVirtual: | |
case JVM_REF_newInvokeSpecial: { | |
check_property( | |
tag.is_method(), | |
"Invalid constant pool index %u in class file %s (not a method)", | |
ref_index, CHECK); | |
break; | |
} | |
case JVM_REF_invokeStatic: | |
case JVM_REF_invokeSpecial: { | |
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); | |
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); | |
break; | |
} | |
default: { | |
classfile_parse_error( | |
"Bad method handle kind at constant pool index %u in class file %s", | |
index, CHECK); | |
} | |
} // switch(refkind) | |
// Keep the ref_index unchanged. It will be indirected at link-time. | |
break; | |
} // case MethodHandle | |
case JVM_CONSTANT_MethodType: { | |
const int ref_index = cp->method_type_index_at(index); | |
check_property(valid_symbol_at(ref_index), | |
"Invalid constant pool index %u in class file %s", | |
ref_index, CHECK); | |
break; | |
} | |
case JVM_CONSTANT_Dynamic: { | |
const int name_and_type_ref_index = | |
cp->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); | |
// bootstrap specifier index must be checked later, | |
// when BootstrapMethods attr is available | |
// Mark the constant pool as having a CONSTANT_Dynamic_info structure | |
cp->set_has_dynamic_constant(); | |
break; | |
} | |
case JVM_CONSTANT_InvokeDynamic: { | |
const int name_and_type_ref_index = | |
cp->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); | |
// bootstrap specifier index must be checked later, | |
// when BootstrapMethods attr is available | |
break; | |
} | |
default: { | |
fatal("bad constant pool tag value %u", cp->tag_at(index).value()); | |
ShouldNotReachHere(); | |
break; | |
} | |
} // switch(tag) | |
} // end of for | |
_first_patched_klass_resolved_index = num_klasses; | |
cp->allocate_resolved_klasses(_loader_data, num_klasses + _max_num_patched_klasses, CHECK); | |
if (_cp_patches != NULL) { | |
// need to treat this_class specially... | |
// Add dummy utf8 entries in the space reserved for names of patched classes. We'll use "*" | |
// for now. These will be replaced with actual names of the patched classes in patch_class(). | |
Symbol* s = vmSymbols::star_name(); | |
for (int n=_orig_cp_size; n<cp->length(); n++) { | |
cp->symbol_at_put(n, s); | |
} | |
int this_class_index; | |
{ | |
stream->guarantee_more(8, CHECK); // flags, this_class, super_class, infs_len | |
const u1* const mark = stream->current(); | |
stream->skip_u2_fast(1); // skip flags | |
this_class_index = stream->get_u2_fast(); | |
stream->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); | |
patch_constant_pool(cp, index, cp_patch_at(index), CHECK); | |
} | |
} | |
} | |
if (!_need_verify) { | |
return; | |
} | |
// second verification pass - checks the strings are of the right format. | |
// but not yet to the other entries | |
for (index = 1; index < length; index++) { | |
const jbyte tag = cp->tag_at(index).value(); | |
switch (tag) { | |
case JVM_CONSTANT_UnresolvedClass: { | |
const Symbol* const class_name = cp->klass_name_at(index); | |
// check the name, even if _cp_patches will overwrite it | |
verify_legal_class_name(class_name, CHECK); | |
break; | |
} | |
case JVM_CONSTANT_NameAndType: { | |
if (_need_verify) { | |
const int sig_index = cp->signature_ref_index_at(index); | |
const int name_index = cp->name_ref_index_at(index); | |
const Symbol* const name = cp->symbol_at(name_index); | |
const Symbol* const sig = cp->symbol_at(sig_index); | |
guarantee_property(sig->utf8_length() != 0, | |
"Illegal zero length constant pool entry at %d in class %s", | |
sig_index, CHECK); | |
guarantee_property(name->utf8_length() != 0, | |
"Illegal zero length constant pool entry at %d in class %s", | |
name_index, CHECK); | |
if (sig->byte_at(0) == JVM_SIGNATURE_FUNC) { | |
// Format check method name and signature | |
verify_legal_method_name(name, CHECK); | |
verify_legal_method_signature(name, sig, CHECK); | |
} else { | |
// Format check field name and signature | |
verify_legal_field_name(name, CHECK); | |
verify_legal_field_signature(name, sig, CHECK); | |
} | |
} | |
break; | |
} | |
case JVM_CONSTANT_Dynamic: { | |
const int name_and_type_ref_index = | |
cp->name_and_type_ref_index_at(index); | |
// already verified to be utf8 | |
const int name_ref_index = | |
cp->name_ref_index_at(name_and_type_ref_index); | |
// already verified to be utf8 | |
const int signature_ref_index = | |
cp->signature_ref_index_at(name_and_type_ref_index); | |
const Symbol* const name = cp->symbol_at(name_ref_index); | |
const Symbol* const signature = cp->symbol_at(signature_ref_index); | |
if (_need_verify) { | |
// CONSTANT_Dynamic's name and signature are verified above, when iterating NameAndType_info. | |
// Need only to be sure signature is non-zero length and the right type. | |
if (signature->utf8_length() == 0 || | |
signature->byte_at(0) == JVM_SIGNATURE_FUNC) { | |
throwIllegalSignature("CONSTANT_Dynamic", name, signature, CHECK); | |
} | |
} | |
break; | |
} | |
case JVM_CONSTANT_InvokeDynamic: | |
case JVM_CONSTANT_Fieldref: | |
case JVM_CONSTANT_Methodref: | |
case JVM_CONSTANT_InterfaceMethodref: { | |
const int name_and_type_ref_index = | |
cp->name_and_type_ref_index_at(index); | |
// already verified to be utf8 | |
const int name_ref_index = | |
cp->name_ref_index_at(name_and_type_ref_index); | |
// already verified to be utf8 | |
const int signature_ref_index = | |
cp->signature_ref_index_at(name_and_type_ref_index); | |
const Symbol* const name = cp->symbol_at(name_ref_index); | |
const Symbol* const signature = cp->symbol_at(signature_ref_index); | |
if (tag == JVM_CONSTANT_Fieldref) { | |
if (_need_verify) { | |
// Field name and signature are verified above, when iterating NameAndType_info. | |
// Need only to be sure signature is non-zero length and the right type. | |
if (signature->utf8_length() == 0 || | |
signature->byte_at(0) == JVM_SIGNATURE_FUNC) { | |
throwIllegalSignature("Field", name, signature, CHECK); | |
} | |
} | |
} else { | |
if (_need_verify) { | |
// Method name and signature are verified above, when iterating NameAndType_info. | |
// Need only to be sure signature is non-zero length and the right type. | |
if (signature->utf8_length() == 0 || | |
signature->byte_at(0) != JVM_SIGNATURE_FUNC) { | |
throwIllegalSignature("Method", name, signature, CHECK); | |
} | |
} | |
// 4509014: If a class method name begins with '<', it must be "<init>" | |
const unsigned int name_len = name->utf8_length(); | |
if (tag == JVM_CONSTANT_Methodref && | |
name_len != 0 && | |
name->byte_at(0) == '<' && | |
name != vmSymbols::object_initializer_name()) { | |
classfile_parse_error( | |
"Bad method name at constant pool index %u in class file %s", | |
name_ref_index, CHECK); | |
} | |
} | |
break; | |
} | |
case JVM_CONSTANT_MethodHandle: { | |
const int ref_index = cp->method_handle_index_at(index); | |
const int ref_kind = cp->method_handle_ref_kind_at(index); | |
switch (ref_kind) { | |
case JVM_REF_invokeVirtual: | |
case JVM_REF_invokeStatic: | |
case JVM_REF_invokeSpecial: | |
case JVM_REF_newInvokeSpecial: { | |
const int name_and_type_ref_index = | |
cp->name_and_type_ref_index_at(ref_index); | |
const int name_ref_index = | |
cp->name_ref_index_at(name_and_type_ref_index); | |
const Symbol* const name = cp->symbol_at(name_ref_index); | |
if (ref_kind == JVM_REF_newInvokeSpecial) { | |
if (name != vmSymbols::object_initializer_name()) { | |
classfile_parse_error( | |
"Bad constructor name at constant pool index %u in class file %s", | |
name_ref_index, CHECK); | |
} | |
} 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); | |
} | |
} | |
break; | |
} | |
// Other ref_kinds are already fully checked in previous pass. | |
} // switch(ref_kind) | |
break; | |
} | |
case JVM_CONSTANT_MethodType: { | |
const Symbol* const no_name = vmSymbols::type_name(); // place holder | |
const Symbol* const signature = cp->method_type_signature_at(index); | |
verify_legal_method_signature(no_name, signature, CHECK); | |
break; | |
} | |
case JVM_CONSTANT_Utf8: { | |
assert(cp->symbol_at(index)->refcount() != 0, "count corrupted"); | |
} | |
} // switch(tag) | |
} // end of for | |
} | |
Handle ClassFileParser::clear_cp_patch_at(int index) { | |
Handle patch = cp_patch_at(index); | |
_cp_patches->at_put(index, Handle()); | |
assert(!has_cp_patch_at(index), ""); | |
return patch; | |
} | |
void ClassFileParser::patch_class(ConstantPool* cp, int class_index, Klass* k, Symbol* name) { | |
int name_index = _orig_cp_size + _num_patched_klasses; | |
int resolved_klass_index = _first_patched_klass_resolved_index + _num_patched_klasses; | |
cp->klass_at_put(class_index, name_index, resolved_klass_index, k, name); | |
_num_patched_klasses ++; | |
} | |
void ClassFileParser::patch_constant_pool(ConstantPool* cp, | |
int index, | |
Handle patch, | |
TRAPS) { | |
assert(cp != NULL, "invariant"); | |
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); | |
Klass* k = java_lang_Class::as_Klass(patch()); | |
patch_class(cp, index, k, k->name()); | |
} else { | |
guarantee_property(java_lang_String::is_instance(patch()), | |
"Illegal class patch at %d in class file %s", | |
index, CHECK); | |
Symbol* const name = java_lang_String::as_symbol(patch(), CHECK); | |
patch_class(cp, index, NULL, 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, ""); | |
} | |
} // end patch_prim label | |
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; | |
} | |
} // end of switch(tag) | |
// On fall-through, mark the patch as used. | |
clear_cp_patch_at(index); | |
} | |
class NameSigHash: public ResourceObj { | |
public: | |
const Symbol* _name; // name | |
const Symbol* _sig; // signature | |
NameSigHash* _next; // Next entry in hash table | |
}; | |
static const int HASH_ROW_SIZE = 256; | |
static unsigned int hash(const Symbol* name, const 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; | |
} | |
static 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. | |
static bool put_after_lookup(const Symbol* name, const 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; | |
} | |
// Side-effects: populates the _local_interfaces field | |
void ClassFileParser::parse_interfaces(const ClassFileStream* const stream, | |
const int itfs_len, | |
ConstantPool* const cp, | |
bool* const has_nonstatic_concrete_methods, | |
TRAPS) { | |
assert(stream != NULL, "invariant"); | |
assert(cp != NULL, "invariant"); | |
assert(has_nonstatic_concrete_methods != NULL, "invariant"); | |
if (itfs_len == 0) { | |
_local_interfaces = Universe::the_empty_klass_array(); | |
} else { | |
assert(itfs_len > 0, "only called for len>0"); | |
_local_interfaces = MetadataFactory::new_array<Klass*>(_loader_data, itfs_len, NULL, CHECK); | |
int index; | |
for (index = 0; index < itfs_len; index++) { | |
const u2 interface_index = stream->get_u2(CHECK); | |
Klass* interf; | |
check_property( | |
valid_klass_reference_at(interface_index), | |
"Interface name has bad constant pool index %u in class file %s", | |
interface_index, CHECK); | |
if (cp->tag_at(interface_index).is_klass()) { | |
interf = cp->resolved_klass_at(interface_index); | |
} else { | |
Symbol* const unresolved_klass = cp->klass_name_at(interface_index); | |
// Don't need to check legal name because it's checked when parsing constant pool. | |
// But need to make sure it's not an array type. | |
guarantee_property(unresolved_klass->byte_at(0) != JVM_SIGNATURE_ARRAY, | |
"Bad interface name in class file %s", CHECK); | |
// Call resolve_super so classcircularity is checked | |
interf = SystemDictionary::resolve_super_or_fail( | |
_class_name, | |
unresolved_klass, | |
Handle(THREAD, _loader_data->class_loader()), | |
_protection_domain, | |
false, | |
CHECK); | |
} | |
if (!interf->is_interface()) { | |
THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(), | |
err_msg("class %s can not implement %s, because it is not an interface (%s)", | |
_class_name->as_klass_external_name(), | |
interf->external_name(), | |
interf->class_in_module_of_loader())); | |
} | |
if (InstanceKlass::cast(interf)->has_nonstatic_concrete_methods()) { | |
*has_nonstatic_concrete_methods = true; | |
} | |
_local_interfaces->at_put(index, interf); | |
} | |
if (!_need_verify || itfs_len <= 1) { | |
return; | |
} | |
// 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; | |
const Symbol* name = NULL; | |
{ | |
debug_only(NoSafepointVerifier nsv;) | |
for (index = 0; index < itfs_len; index++) { | |
const Klass* const k = _local_interfaces->at(index); | |
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 \"%s\" in class file %s", | |
name->as_C_string(), CHECK); | |
} | |
} | |
} | |
void ClassFileParser::verify_constantvalue(const ConstantPool* const cp, | |
int constantvalue_index, | |
int signature_index, | |
TRAPS) const { | |
// Make sure the constant pool entry is of a type appropriate to this field | |
guarantee_property( | |
(constantvalue_index > 0 && | |
constantvalue_index < cp->length()), | |
"Bad initial value index %u in ConstantValue attribute in class file %s", | |
constantvalue_index, CHECK); | |
const constantTag value_type = cp->tag_at(constantvalue_index); | |
switch(cp->basic_type_for_signature_at(signature_index)) { | |
case T_LONG: { | |
guarantee_property(value_type.is_long(), | |
"Inconsistent constant value type in class file %s", | |
CHECK); | |
break; | |
} | |
case T_FLOAT: { | |
guarantee_property(value_type.is_float(), | |
"Inconsistent constant value type in class file %s", | |
CHECK); | |
break; | |
} | |
case T_DOUBLE: { | |
guarantee_property(value_type.is_double(), | |
"Inconsistent constant value type in class file %s", | |
CHECK); | |
break; | |
} | |
case T_BYTE: | |
case T_CHAR: | |
case T_SHORT: | |
case T_BOOLEAN: | |
case T_INT: { | |
guarantee_property(value_type.is_int(), | |
"Inconsistent constant value type in class file %s", | |
CHECK); | |
break; | |
} | |
case T_OBJECT: { | |
guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;") | |
&& value_type.is_string()), | |
"Bad string initial value in class file %s", | |
CHECK); | |
break; | |
} | |
default: { | |
classfile_parse_error("Unable to set initial value %u in class file %s", | |
constantvalue_index, | |
CHECK); | |
} | |
} | |
} | |
class AnnotationCollector : public ResourceObj{ | |
public: | |
enum Location { _in_field, _in_method, _in_class }; | |
enum ID { | |
_unknown = 0, | |
_method_CallerSensitive, | |
_method_ForceInline, | |
_method_DontInline, | |
_method_InjectedProfile, | |
_method_LambdaForm_Compiled, | |
_method_LambdaForm_Hidden, | |
_method_HotSpotIntrinsicCandidate, | |
_jdk_internal_vm_annotation_Contended, | |
_field_Stable, | |
_jdk_internal_vm_annotation_ReservedStackAccess, | |
_annotation_LIMIT | |
}; | |
const Location _location; | |
int _annotations_present; | |
u2 _contended_group; | |
AnnotationCollector(Location location) | |
: _location(location), _annotations_present(0) | |
{ | |
assert((int)_annotation_LIMIT <= (int)sizeof(_annotations_present) * BitsPerByte, ""); | |
} | |
// If this annotation name has an ID, report it (or _none). | |
ID annotation_index(const ClassLoaderData* loader_data, const Symbol* name); | |
// Set the annotation name: | |
void set_annotation(ID id) { | |
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); | |
_annotations_present |= nth_bit((int)id); | |
} | |
void remove_annotation(ID id) { | |
assert((int)id >= 0 && (int)id < (int)_annotation_LIMIT, "oob"); | |
_annotations_present &= ~nth_bit((int)id); | |
} | |
// Report if the annotation is present. | |
bool has_any_annotations() const { return _annotations_present != 0; } | |
bool has_annotation(ID id) const { return (nth_bit((int)id) & _annotations_present) != 0; } | |
void set_contended_group(u2 group) { _contended_group = group; } | |
u2 contended_group() const { return _contended_group; } | |
bool is_contended() const { return has_annotation(_jdk_internal_vm_annotation_Contended); } | |
void set_stable(bool stable) { set_annotation(_field_Stable); } | |
bool is_stable() const { return has_annotation(_field_Stable); } | |
}; | |
// This class also doubles as a holder for metadata cleanup. | |
class ClassFileParser::FieldAnnotationCollector : public AnnotationCollector { | |
private: | |
ClassLoaderData* _loader_data; | |
AnnotationArray* _field_annotations; | |
AnnotationArray* _field_type_annotations; | |
public: | |
FieldAnnotationCollector(ClassLoaderData* loader_data) : | |
AnnotationCollector(_in_field), | |
_loader_data(loader_data), | |
_field_annotations(NULL), | |
_field_type_annotations(NULL) {} | |
~FieldAnnotationCollector(); | |
void apply_to(FieldInfo* f); | |
AnnotationArray* field_annotations() { return _field_annotations; } | |
AnnotationArray* field_type_annotations() { return _field_type_annotations; } | |
void set_field_annotations(AnnotationArray* a) { _field_annotations = a; } | |
void set_field_type_annotations(AnnotationArray* a) { _field_type_annotations = a; } | |
}; | |
class MethodAnnotationCollector : public AnnotationCollector{ | |
public: | |
MethodAnnotationCollector() : AnnotationCollector(_in_method) { } | |
void apply_to(const methodHandle& m); | |
}; | |
class ClassFileParser::ClassAnnotationCollector : public AnnotationCollector{ | |
public: | |
ClassAnnotationCollector() : AnnotationCollector(_in_class) { } | |
void apply_to(InstanceKlass* ik); | |
}; | |
static int skip_annotation_value(const u1*, int, int); // fwd decl | |
// Safely increment index by val if does not pass limit | |
#define SAFE_ADD(index, limit, val) \ | |
if (index >= limit - val) return limit; \ | |
index += val; | |
// Skip an annotation. Return >=limit if there is any problem. | |
static int skip_annotation(const u1* buffer, int limit, int index) { | |
assert(buffer != NULL, "invariant"); | |
// annotation := atype:u2 do(nmem:u2) {member:u2 value} | |
// value := switch (tag:u1) { ... } | |
SAFE_ADD(index, limit, 4); // skip atype and read nmem | |
int nmem = Bytes::get_Java_u2((address)buffer + index - 2); | |
while (--nmem >= 0 && index < limit) { | |
SAFE_ADD(index, limit, 2); // skip member | |
index = skip_annotation_value(buffer, limit, index); | |
} | |
return index; | |
} | |
// Skip an annotation value. Return >=limit if there is any problem. | |
static int skip_annotation_value(const u1* buffer, int limit, int index) { | |
assert(buffer != NULL, "invariant"); | |
// value := switch (tag:u1) { | |
// case B, C, I, S, Z, D, F, J, c: con:u2; | |
// case e: e_class:u2 e_name:u2; | |
// case s: s_con:u2; | |
// case [: do(nval:u2) {value}; | |
// case @: annotation; | |
// case s: s_con:u2; | |
// } | |
SAFE_ADD(index, limit, 1); // read tag | |
const u1 tag = buffer[index - 1]; | |
switch (tag) { | |
case 'B': | |
case 'C': | |
case 'I': | |
case 'S': | |
case 'Z': | |
case 'D': | |
case 'F': | |
case 'J': | |
case 'c': | |
case 's': | |
SAFE_ADD(index, limit, 2); // skip con or s_con | |
break; | |
case 'e': | |
SAFE_ADD(index, limit, 4); // skip e_class, e_name | |
break; | |
case '[': | |
{ | |
SAFE_ADD(index, limit, 2); // read nval | |
int nval = Bytes::get_Java_u2((address)buffer + index - 2); | |
while (--nval >= 0 && index < limit) { | |
index = skip_annotation_value(buffer, limit, index); | |
} | |
} | |
break; | |
case '@': | |
index = skip_annotation(buffer, limit, index); | |
break; | |
default: | |
return limit; // bad tag byte | |
} | |
return index; | |
} | |
// Sift through annotations, looking for those significant to the VM: | |
static void parse_annotations(const ConstantPool* const cp, | |
const u1* buffer, int limit, | |
AnnotationCollector* coll, | |
ClassLoaderData* loader_data, | |
TRAPS) { | |
assert(cp != NULL, "invariant"); | |
assert(buffer != NULL, "invariant"); | |
assert(coll != NULL, "invariant"); | |
assert(loader_data != NULL, "invariant"); | |
// annotations := do(nann:u2) {annotation} | |
int index = 2; // read nann | |
if (index >= limit) return; | |
int nann = Bytes::get_Java_u2((address)buffer + index - 2); | |
enum { // initial annotation layout | |
atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;' | |
count_off = 2, // u2 such as 1 (one value) | |
member_off = 4, // utf8 such as 'value' | |
tag_off = 6, // u1 such as 'c' (type) or 'e' (enum) | |
e_tag_val = 'e', | |
e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;' | |
e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME' | |
e_size = 11, // end of 'e' annotation | |
c_tag_val = 'c', // payload is type | |
c_con_off = 7, // utf8 payload, such as 'I' | |
c_size = 9, // end of 'c' annotation | |
s_tag_val = 's', // payload is String | |
s_con_off = 7, // utf8 payload, such as 'Ljava/lang/String;' | |
s_size = 9, | |
min_size = 6 // smallest possible size (zero members) | |
}; | |
// Cannot add min_size to index in case of overflow MAX_INT | |
while ((--nann) >= 0 && (index - 2 <= limit - min_size)) { | |
int index0 = index; | |
index = skip_annotation(buffer, limit, index); | |
const u1* const abase = buffer + index0; | |
const int atype = Bytes::get_Java_u2((address)abase + atype_off); | |
const int count = Bytes::get_Java_u2((address)abase + count_off); | |
const Symbol* const aname = check_symbol_at(cp, atype); | |
if (aname == NULL) break; // invalid annotation name | |
const Symbol* member = NULL; | |
if (count >= 1) { | |
const int member_index = Bytes::get_Java_u2((address)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 (AnnotationCollector::_unknown == id) continue; | |
coll->set_annotation(id); | |
if (AnnotationCollector::_jdk_internal_vm_annotation_Contended == id) { | |
// @Contended can optionally specify the contention group. | |
// | |
// Contended group defines the equivalence class over the fields: | |
// the fields within the same contended group are not treated distinct. | |
// The only exception is default group, which does not incur the | |
// equivalence. Naturally, contention group for classes is meaningless. | |
// | |
// While the contention group is specified as String, annotation | |
// values are already interned, and we might as well use the constant | |
// pool index as the group tag. | |
// | |
u2 group_index = 0; // default contended group | |
if (count == 1 | |
&& s_size == (index - index0) // match size | |
&& s_tag_val == *(abase + tag_off) | |
&& member == vmSymbols::value_name()) { | |
group_index = Bytes::get_Java_u2((address)abase + s_con_off); | |
if (cp->symbol_at(group_index)->utf8_length() == 0) { | |
group_index = 0; // default contended group | |
} | |
} | |
coll->set_contended_group(group_index); | |
} | |
} | |
} | |
// Parse attributes for a field. | |
void ClassFileParser::parse_field_attributes(const ClassFileStream* const cfs, | |
u2 attributes_count, | |
bool is_static, u2 signature_index, | |
u2* const constantvalue_index_addr, | |
bool* const is_synthetic_addr, | |
u2* const generic_signature_index_addr, | |
ClassFileParser::FieldAnnotationCollector* parsed_annotations, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
assert(constantvalue_index_addr != NULL, "invariant"); | |
assert(is_synthetic_addr != NULL, "invariant"); | |
assert(generic_signature_index_addr != NULL, "invariant"); | |
assert(parsed_annotations != NULL, "invariant"); | |
assert(attributes_count > 0, "attributes_count should be greater than 0"); | |
u2 constantvalue_index = 0; | |
u2 generic_signature_index = 0; | |
bool is_synthetic = false; | |
const u1* runtime_visible_annotations = NULL; | |
int runtime_visible_annotations_length = 0; | |
const u1* runtime_invisible_annotations = NULL; | |
int runtime_invisible_annotations_length = 0; | |
const u1* runtime_visible_type_annotations = NULL; | |
int runtime_visible_type_annotations_length = 0; | |
const u1* runtime_invisible_type_annotations = NULL; | |
int runtime_invisible_type_annotations_length = 0; | |
bool runtime_invisible_annotations_exists = false; | |
bool runtime_invisible_type_annotations_exists = false; | |
const ConstantPool* const cp = _cp; | |
while (attributes_count--) { | |
cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length | |
const u2 attribute_name_index = cfs->get_u2_fast(); | |
const u4 attribute_length = cfs->get_u4_fast(); | |
check_property(valid_symbol_at(attribute_name_index), | |
"Invalid field attribute index %u in class file %s", | |
attribute_name_index, | |
CHECK); | |
const Symbol* const attribute_name = cp->symbol_at(attribute_name_index); | |
if (is_static && attribute_name == vmSymbols::tag_constant_value()) { | |
// ignore if non-static | |
if (constantvalue_index != 0) { | |
classfile_parse_error("Duplicate ConstantValue attribute in class file %s", 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(cp, constantvalue_index, signature_index, CHECK); | |
} | |
} else if (attribute_name == vmSymbols::tag_synthetic()) { | |
if (attribute_length != 0) { | |
classfile_parse_error( | |
"Invalid Synthetic field attribute length %u in class file %s", | |
attribute_length, 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 (generic_signature_index != 0) { | |
classfile_parse_error( | |
"Multiple Signature attributes for field in class file %s", CHECK); | |
} | |
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 = parse_generic_signature_attribute(cfs, CHECK); | |
} else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) { | |
if (runtime_visible_annotations != NULL) { | |
classfile_parse_error( | |
"Multiple RuntimeVisibleAnnotations attributes for field in class file %s", CHECK); | |
} | |
runtime_visible_annotations_length = attribute_length; | |
runtime_visible_annotations = cfs->current(); | |
assert(runtime_visible_annotations != NULL, "null visible annotations"); | |
cfs->guarantee_more(runtime_visible_annotations_length, CHECK); | |
parse_annotations(cp, | |
runtime_visible_annotations, | |
runtime_visible_annotations_length, | |
parsed_annotations, | |
_loader_data, | |
CHECK); | |
cfs->skip_u1_fast(runtime_visible_annotations_length); | |
} else if (attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { | |
if (runtime_invisible_annotations_exists) { | |
classfile_parse_error( | |
"Multiple RuntimeInvisibleAnnotations attributes for field in class file %s", CHECK); | |
} | |
runtime_invisible_annotations_exists = true; | |
if (PreserveAllAnnotations) { | |
runtime_invisible_annotations_length = attribute_length; | |
runtime_invisible_annotations = cfs->current(); | |
assert(runtime_invisible_annotations != NULL, "null invisible annotations"); | |
} | |
cfs->skip_u1(attribute_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->current(); | |
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->current(); | |
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 ClassFileParser::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); | |
if (atype != BAD_ALLOCATION_TYPE) { | |
// Make sure there is no overflow with injected fields. | |
assert(count[atype] < 0xFFFF, "More than 65535 fields"); | |
count[atype]++; | |
} | |
return atype; | |
} | |
}; | |
// Side-effects: populates the _fields, _fields_annotations, | |
// _fields_type_annotations fields | |
void ClassFileParser::parse_fields(const ClassFileStream* const cfs, | |
bool is_interface, | |
FieldAllocationCount* const fac, | |
ConstantPool* cp, | |
const int cp_size, | |
u2* const java_fields_count_ptr, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
assert(fac != NULL, "invariant"); | |
assert(cp != NULL, "invariant"); | |
assert(java_fields_count_ptr != NULL, "invariant"); | |
assert(NULL == _fields, "invariant"); | |
assert(NULL == _fields_annotations, "invariant"); | |
assert(NULL == _fields_type_annotations, "invariant"); | |
cfs->guarantee_more(2, CHECK); // length | |
const u2 length = cfs->get_u2_fast(); | |
*java_fields_count_ptr = length; | |
int num_injected = 0; | |
const InjectedField* const injected = JavaClasses::get_injected(_class_name, | |
&num_injected); | |
const int total_fields = length + num_injected; | |
// 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* const 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++) { | |
// access_flags, name_index, descriptor_index, attributes_count | |
cfs->guarantee_more(8, CHECK); | |
AccessFlags access_flags; | |
const jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS; | |
verify_legal_field_modifiers(flags, is_interface, CHECK); | |
access_flags.set_flags(flags); | |
const u2 name_index = cfs->get_u2_fast(); | |
check_property(valid_symbol_at(name_index), | |
"Invalid constant pool index %u for field name in class file %s", | |
name_index, CHECK); | |
const Symbol* const name = cp->symbol_at(name_index); | |
verify_legal_field_name(name, CHECK); | |
const u2 signature_index = cfs->get_u2_fast(); | |
check_property(valid_symbol_at(signature_index), | |
"Invalid constant pool index %u for field signature in class file %s", | |
signature_index, CHECK); | |
const Symbol* const sig = cp->symbol_at(signature_index); | |
verify_legal_field_signature(name, sig, CHECK); | |
u2 constantvalue_index = 0; | |
bool is_synthetic = false; | |
u2 generic_signature_index = 0; | |
const bool is_static = access_flags.is_static(); | |
FieldAnnotationCollector parsed_annotations(_loader_data); | |
const u2 attributes_count = cfs->get_u2_fast(); | |
if (attributes_count > 0) { | |
parse_field_attributes(cfs, | |
attributes_count, | |
is_static, | |
signature_index, | |
&constantvalue_index, | |
&is_synthetic, | |
&generic_signature_index, | |
&parsed_annotations, | |
CHECK); | |
if (parsed_annotations.field_annotations() != NULL) { | |
if (_fields_annotations == NULL) { | |
_fields_annotations = MetadataFactory::new_array<AnnotationArray*>( | |
_loader_data, length, NULL, | |
CHECK); | |
} | |
_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); | |
} | |
_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* const field = FieldInfo::from_field_array(fa, n); | |
field->initialize(access_flags.as_short(), | |
name_index, | |
signature_index, | |
constantvalue_index); | |
const BasicType type = cp->basic_type_for_signature_at(signature_index); | |
// Remember how many oops we encountered and compute allocation type | |
const 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) { | |
const Symbol* const name = injected[n].name(); | |
const Symbol* const signature = injected[n].signature(); | |
bool duplicate = false; | |
for (int i = 0; i < length; i++) { | |
const FieldInfo* const f = 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* const field = FieldInfo::from_field_array(fa, index); | |
field->initialize(JVM_ACC_FIELD_INTERNAL, | |
injected[n].name_index, | |
injected[n].signature_index, | |
0); | |
const BasicType type = FieldType::basic_type(injected[n].signature()); | |
// Remember how many oops we encountered and compute allocation type | |
const FieldAllocationType atype = fac->update(false, type); | |
field->set_allocation_type(atype); | |
index++; | |
} | |
} | |
assert(NULL == _fields, "invariant"); | |
_fields = | |
MetadataFactory::new_array<u2>(_loader_data, | |
index * FieldInfo::field_slots + num_generic_signature, | |
CHECK); | |
// 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. | |
{ | |
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(_fields->length() == i, ""); | |
} | |
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; | |
const Symbol* name = NULL; | |
const Symbol* sig = NULL; | |
{ | |
debug_only(NoSafepointVerifier nsv;) | |
for (AllFieldStream fs(_fields, cp); !fs.done(); fs.next()) { | |
name = fs.name(); | |
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 \"%s\" with signature \"%s\" in class file %s", | |
name->as_C_string(), sig->as_klass_external_name(), CHECK); | |
} | |
} | |
} | |
const ClassFileParser::unsafe_u2* ClassFileParser::parse_exception_table(const ClassFileStream* const cfs, | |
u4 code_length, | |
u4 exception_table_length, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
const unsafe_u2* const exception_table_start = cfs->current(); | |
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++) { | |
const u2 start_pc = cfs->get_u2_fast(); | |
const u2 end_pc = cfs->get_u2_fast(); | |
const u2 handler_pc = cfs->get_u2_fast(); | |
const u2 catch_type_index = cfs->get_u2_fast(); | |
guarantee_property((start_pc < end_pc) && (end_pc <= code_length), | |
"Illegal exception table range in class file %s", | |
CHECK_NULL); | |
guarantee_property(handler_pc < code_length, | |
"Illegal exception table handler in class file %s", | |
CHECK_NULL); | |
if (catch_type_index != 0) { | |
guarantee_property(valid_klass_reference_at(catch_type_index), | |
"Catch type in exception table has bad constant type in class file %s", CHECK_NULL); | |
} | |
} | |
} else { | |
cfs->skip_u2_fast(exception_table_length * 4); | |
} | |
return exception_table_start; | |
} | |
void ClassFileParser::parse_linenumber_table(u4 code_attribute_length, | |
u4 code_length, | |
CompressedLineNumberWriteStream**const write_stream, | |
TRAPS) { | |
const ClassFileStream* const cfs = _stream; | |
unsigned int num_entries = cfs->get_u2(CHECK); | |
// Each entry is a u2 start_pc, and a u2 line_number | |
const unsigned int length_in_bytes = num_entries * (sizeof(u2) * 2); | |
// Verify line number attribute and table length | |
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) { | |
const u2 bci = cfs->get_u2_fast(); // start_pc | |
const u2 line = cfs->get_u2_fast(); // line_number | |
guarantee_property(bci < code_length, | |
"Invalid pc in LineNumberTable in class file %s", CHECK); | |
(*write_stream)->write_pair(bci, line); | |
} | |
} | |
class LVT_Hash : public AllStatic { | |
public: | |
static bool equals(LocalVariableTableElement const& e0, LocalVariableTableElement const& e1) { | |
/* | |
* 3-tuple start_bci/length/slot has to be unique key, | |
* so the following comparison seems to be redundant: | |
* && elem->name_cp_index == entry->_elem->name_cp_index | |
*/ | |
return (e0.start_bci == e1.start_bci && | |
e0.length == e1.length && | |
e0.name_cp_index == e1.name_cp_index && | |
e0.slot == e1.slot); | |
} | |
static unsigned int hash(LocalVariableTableElement const& e0) { | |
unsigned int raw_hash = e0.start_bci; | |
raw_hash = e0.length + raw_hash * 37; | |
raw_hash = e0.name_cp_index + raw_hash * 37; | |
raw_hash = e0.slot + raw_hash * 37; | |
return raw_hash; | |
} | |
}; | |
// Class file LocalVariableTable elements. | |
class Classfile_LVT_Element { | |
public: | |
u2 start_bci; | |
u2 length; | |
u2 name_cp_index; | |
u2 descriptor_cp_index; | |
u2 slot; | |
}; | |
static void copy_lvt_element(const Classfile_LVT_Element* const src, | |
LocalVariableTableElement* const lvt) { | |
lvt->start_bci = Bytes::get_Java_u2((u1*) &src->start_bci); | |
lvt->length = Bytes::get_Java_u2((u1*) &src->length); | |
lvt->name_cp_index = Bytes::get_Java_u2((u1*) &src->name_cp_index); | |
lvt->descriptor_cp_index = Bytes::get_Java_u2((u1*) &src->descriptor_cp_index); | |
lvt->signature_cp_index = 0; | |
lvt->slot = Bytes::get_Java_u2((u1*) &src->slot); | |
} | |
// Function is used to parse both attributes: | |
// LocalVariableTable (LVT) and LocalVariableTypeTable (LVTT) | |
const ClassFileParser::unsafe_u2* ClassFileParser::parse_localvariable_table(const ClassFileStream* cfs, | |
u4 code_length, | |
u2 max_locals, | |
u4 code_attribute_length, | |
u2* const localvariable_table_length, | |
bool isLVTT, | |
TRAPS) { | |
const char* const tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable"; | |
*localvariable_table_length = cfs->get_u2(CHECK_NULL); | |
const unsigned int size = | |
(*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2); | |
const ConstantPool* const cp = _cp; | |
// Verify local variable table attribute has right length | |
if (_need_verify) { | |
guarantee_property(code_attribute_length == (sizeof(*localvariable_table_length) + size * sizeof(u2)), | |
"%s has wrong length in class file %s", tbl_name, CHECK_NULL); | |
} | |
const unsafe_u2* const localvariable_table_start = cfs->current(); | |
assert(localvariable_table_start != 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++) { | |
const u2 start_pc = cfs->get_u2_fast(); | |
const u2 length = cfs->get_u2_fast(); | |
const u2 name_index = cfs->get_u2_fast(); | |
const u2 descriptor_index = cfs->get_u2_fast(); | |
const u2 index = cfs->get_u2_fast(); | |
// Assign to a u4 to avoid overflow | |
const u4 end_pc = (u4)start_pc + (u4)length; | |
if (start_pc >= code_length) { | |
classfile_parse_error( | |
"Invalid start_pc %u in %s in class file %s", | |
start_pc, tbl_name, CHECK_NULL); | |
} | |
if (end_pc > code_length) { | |
classfile_parse_error( | |
"Invalid length %u in %s in class file %s", | |
length, tbl_name, CHECK_NULL); | |
} | |
const int cp_size = cp->length(); | |
guarantee_property(valid_symbol_at(name_index), | |
"Name index %u in %s has bad constant type in class file %s", | |
name_index, tbl_name, CHECK_NULL); | |
guarantee_property(valid_symbol_at(descriptor_index), | |
"Signature index %u in %s has bad constant type in class file %s", | |
descriptor_index, tbl_name, CHECK_NULL); | |
const Symbol* const name = cp->symbol_at(name_index); | |
const Symbol* const sig = cp->symbol_at(descriptor_index); | |
verify_legal_field_name(name, CHECK_NULL); | |
u2 extra_slot = 0; | |
if (!isLVTT) { | |
verify_legal_field_signature(name, sig, CHECK_NULL); | |
// 4894874: check special cases for double and long local variables | |
if (sig == vmSymbols::type_signature(T_DOUBLE) || | |
sig == vmSymbols::type_signature(T_LONG)) { | |
extra_slot = 1; | |
} | |
} | |
guarantee_property((index + extra_slot) < max_locals, | |
"Invalid index %u in %s in class file %s", | |
index, tbl_name, CHECK_NULL); | |
} | |
} | |
return localvariable_table_start; | |
} | |
void ClassFileParser::parse_type_array(u2 array_length, | |
u4 code_length, | |
u4* const u1_index, | |
u4* const u2_index, | |
u1* const u1_array, | |
u2* const u2_array, | |
TRAPS) { | |
const ClassFileStream* const 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++) { | |
const u1 tag = u1_array[i1++] = cfs->get_u1(CHECK); | |
index++; | |
if (tag == ITEM_Long || tag == ITEM_Double) { | |
index++; | |
} else if (tag == ITEM_Object) { | |
const 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) { | |
const 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; | |
} | |
static const u1* parse_stackmap_table(const ClassFileStream* const cfs, | |
u4 code_attribute_length, | |
bool need_verify, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
if (0 == code_attribute_length) { | |
return NULL; | |
} | |
const u1* const stackmap_table_start = cfs->current(); | |
assert(stackmap_table_start != NULL, "null stackmap table"); | |
// check code_attribute_length first | |
cfs->skip_u1(code_attribute_length, CHECK_NULL); | |
if (!need_verify && !DumpSharedSpaces) { | |
return NULL; | |
} | |
return stackmap_table_start; | |
} | |
const ClassFileParser::unsafe_u2* ClassFileParser::parse_checked_exceptions(const ClassFileStream* const cfs, | |
u2* const checked_exceptions_length, | |
u4 method_attribute_length, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
assert(checked_exceptions_length != NULL, "invariant"); | |
cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length | |
*checked_exceptions_length = cfs->get_u2_fast(); | |
const unsigned int size = | |
(*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2); | |
const unsafe_u2* const checked_exceptions_start = cfs->current(); | |
assert(checked_exceptions_start != NULL, "null checked exceptions"); | |
if (!_need_verify) { | |
cfs->skip_u2_fast(size); | |
} else { | |
// Verify each value in the checked exception table | |
u2 checked_exception; | |
const u2 len = *checked_exceptions_length; | |
cfs->guarantee_more(2 * len, CHECK_NULL); | |
for (int i = 0; i < len; i++) { | |
checked_exception = cfs->get_u2_fast(); | |
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, | |
const Symbol* name, | |
const Symbol* sig, | |
TRAPS) const { | |
assert(name != NULL, "invariant"); | |
assert(sig != NULL, "invariant"); | |
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()); | |
} | |
AnnotationCollector::ID | |
AnnotationCollector::annotation_index(const ClassLoaderData* loader_data, | |
const Symbol* name) { | |
const 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_platform_class_loader_data() || | |
loader_data->is_anonymous(); | |
switch (sid) { | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(reflect_CallerSensitive_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (!privileged) break; // only allow in privileged code | |
return _method_CallerSensitive; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ForceInline_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (!privileged) break; // only allow in privileged code | |
return _method_ForceInline; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_DontInline_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (!privileged) break; // only allow in privileged code | |
return _method_DontInline; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_InjectedProfile_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (!privileged) break; // only allow in privileged code | |
return _method_InjectedProfile; | |
} | |
case 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(jdk_internal_HotSpotIntrinsicCandidate_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (!privileged) break; // only allow in privileged code | |
return _method_HotSpotIntrinsicCandidate; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Stable_signature): { | |
if (_location != _in_field) break; // only allow for fields | |
if (!privileged) break; // only allow in privileged code | |
return _field_Stable; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_Contended_signature): { | |
if (_location != _in_field && _location != _in_class) { | |
break; // only allow for fields and classes | |
} | |
if (!EnableContended || (RestrictContended && !privileged)) { | |
break; // honor privileges | |
} | |
return _jdk_internal_vm_annotation_Contended; | |
} | |
case vmSymbols::VM_SYMBOL_ENUM_NAME(jdk_internal_vm_annotation_ReservedStackAccess_signature): { | |
if (_location != _in_method) break; // only allow for methods | |
if (RestrictReservedStack && !privileged) break; // honor privileges | |
return _jdk_internal_vm_annotation_ReservedStackAccess; | |
} | |
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 MethodAnnotationCollector::apply_to(const 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_InjectedProfile)) | |
m->set_has_injected_profile(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); | |
if (has_annotation(_method_HotSpotIntrinsicCandidate) && !m->is_synthetic()) | |
m->set_intrinsic_candidate(true); | |
if (has_annotation(_jdk_internal_vm_annotation_ReservedStackAccess)) | |
m->set_has_reserved_stack_access(true); | |
} | |
void ClassFileParser::ClassAnnotationCollector::apply_to(InstanceKlass* ik) { | |
assert(ik != NULL, "invariant"); | |
ik->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(const ConstMethod* cm, | |
int lvt_cnt, | |
u2* const localvariable_table_length, | |
const unsafe_u2** const localvariable_table_start, | |
int lvtt_cnt, | |
u2* const localvariable_type_table_length, | |
const unsafe_u2** const localvariable_type_table_start, | |
TRAPS) { | |
ResourceMark rm(THREAD); | |
typedef ResourceHashtable<LocalVariableTableElement, LocalVariableTableElement*, | |
&LVT_Hash::hash, &LVT_Hash::equals> LVT_HashTable; | |
LVT_HashTable* const table = new LVT_HashTable(); | |
// To fill LocalVariableTable in | |
const Classfile_LVT_Element* cf_lvt; | |
LocalVariableTableElement* lvt = cm->localvariable_table_start(); | |
for (int tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { | |
cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; | |
for (int idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { | |
copy_lvt_element(&cf_lvt[idx], lvt); | |
// If no duplicates, add LVT elem in hashtable. | |
if (table->put(*lvt, lvt) == false | |
&& _need_verify | |
&& _major_version >= JAVA_1_5_VERSION) { | |
classfile_parse_error("Duplicated LocalVariableTable attribute " | |
"entry for '%s' in class file %s", | |
_cp->symbol_at(lvt->name_cp_index)->as_utf8(), | |
CHECK); | |
} | |
} | |
} | |
// To merge LocalVariableTable and LocalVariableTypeTable | |
const Classfile_LVT_Element* cf_lvtt; | |
LocalVariableTableElement lvtt_elem; | |
for (int tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { | |
cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; | |
for (int idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { | |
copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); | |
LocalVariableTableElement** entry = table->get(lvtt_elem); | |
if (entry == NULL) { | |
if (_need_verify) { | |
classfile_parse_error("LVTT entry for '%s' in class file %s " | |
"does not match any LVT entry", | |
_cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), | |
CHECK); | |
} | |
} else if ((*entry)->signature_cp_index != 0 && _need_verify) { | |
classfile_parse_error("Duplicated LocalVariableTypeTable attribute " | |
"entry for '%s' in class file %s", | |
_cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), | |
CHECK); | |
} else { | |
// to add generic signatures into LocalVariableTable | |
(*entry)->signature_cp_index = lvtt_elem.descriptor_cp_index; | |
} | |
} | |
} | |
} | |
void ClassFileParser::copy_method_annotations(ConstMethod* cm, | |
const u1* runtime_visible_annotations, | |
int runtime_visible_annotations_length, | |
const u1* runtime_invisible_annotations, | |
int runtime_invisible_annotations_length, | |
const u1* runtime_visible_parameter_annotations, | |
int runtime_visible_parameter_annotations_length, | |
const u1* runtime_invisible_parameter_annotations, | |
int runtime_invisible_parameter_annotations_length, | |
const u1* runtime_visible_type_annotations, | |
int runtime_visible_type_annotations_length, | |
const u1* runtime_invisible_type_annotations, | |
int runtime_invisible_type_annotations_length, | |
const 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. | |
Method* ClassFileParser::parse_method(const ClassFileStream* const cfs, | |
bool is_interface, | |
const ConstantPool* cp, | |
AccessFlags* const promoted_flags, | |
TRAPS) { | |
assert(cfs != NULL, "invariant"); | |
assert(cp != NULL, "invariant"); | |
assert(promoted_flags != NULL, "invariant"); | |
ResourceMark rm(THREAD); | |
// Parse fixed parts: | |
// access_flags, name_index, descriptor_index, attributes_count | |
cfs->guarantee_more(8, CHECK_NULL); | |
int flags = cfs->get_u2_fast(); | |
const u2 name_index = cfs->get_u2_fast(); | |
const 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_NULL); | |
const Symbol* const name = cp->symbol_at(name_index); | |
verify_legal_method_name(name, CHECK_NULL); | |
const u2 signature_index = cfs->get_u2_fast(); | |
guarantee_property( | |
valid_symbol_at(signature_index), | |
"Illegal constant pool index %u for method signature in class file %s", | |
signature_index, CHECK_NULL); | |
const Symbol* const signature = cp->symbol_at(signature_index); | |
if (name == vmSymbols::class_initializer_name()) { | |
// We ignore the other access flags for a valid class initializer. | |
// (JVM Spec 2nd ed., chapter 4.6) | |
if (_major_version < 51) { // backward compatibility | |
flags = JVM_ACC_STATIC; | |
} else if ((flags & JVM_ACC_STATIC) == JVM_ACC_STATIC) { | |
flags &= JVM_ACC_STATIC | JVM_ACC_STRICT; | |
} else { | |
classfile_parse_error("Method <clinit> is not static in class file %s", CHECK_NULL); | |
} | |
} else { | |
verify_legal_method_modifiers(flags, is_interface, name, CHECK_NULL); | |
} | |
if (name == vmSymbols::object_initializer_name() && is_interface) { | |
classfile_parse_error("Interface cannot have a method named <init>, class file %s", CHECK_NULL); | |
} | |
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_NULL); | |
if (args_size > MAX_ARGS_SIZE) { | |
classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_NULL); | |
} | |
} | |
AccessFlags access_flags(flags & JVM_RECOGNIZED_METHOD_MODIFIERS); | |
// Default values for code and exceptions attribute elements | |
u2 max_stack = 0; | |
u2 max_locals = 0; | |
u4 code_length = 0; | |
const u1* code_start = 0; | |
u2 exception_table_length = 0; | |
const unsafe_u2* exception_table_start = NULL; // (potentially unaligned) pointer to array of u2 elements | |
Array<int>* exception_handlers = Universe::the_empty_int_array(); | |
u2 checked_exceptions_length = 0; | |
const unsafe_u2* checked_exceptions_start = NULL; // (potentially unaligned) pointer to array of u2 elements | |
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 = NULL; | |
const unsafe_u2** localvariable_table_start = NULL; // (potentially unaligned) pointer to array of LVT attributes | |
u2* localvariable_type_table_length = NULL; | |
const unsafe_u2** localvariable_type_table_start = NULL; // (potentially unaligned) pointer to LVTT attributes | |
int method_parameters_length = -1; | |
const 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 | |
const u1* stackmap_data = NULL; | |
int stackmap_data_length = 0; | |
u2 generic_signature_index = 0; | |
MethodAnnotationCollector parsed_annotations; | |
const u1* runtime_visible_annotations = NULL; | |
int runtime_visible_annotations_length = 0; | |
const u1* runtime_invisible_annotations = NULL; | |
int runtime_invisible_annotations_length = 0; | |
const u1* runtime_visible_parameter_annotations = NULL; | |
int runtime_visible_parameter_annotations_length = 0; | |
const u1* runtime_invisible_parameter_annotations = NULL; | |
int runtime_invisible_parameter_annotations_length = 0; | |
const u1* runtime_visible_type_annotations = NULL; | |
int runtime_visible_type_annotations_length = 0; | |
const u1* runtime_invisible_type_annotations = NULL; | |
int runtime_invisible_type_annotations_length = 0; | |
bool runtime_invisible_annotations_exists = false; | |
bool runtime_invisible_type_annotations_exists = false; | |
bool runtime_invisible_parameter_annotations_exists = false; | |
const u1* annotation_default = 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_NULL); // method_attribute_name_index, method_attribute_length | |
const u2 method_attribute_name_index = cfs->get_u2_fast(); | |
const u4 method_attribute_length = cfs->get_u4_fast(); | |
check_property( | |
valid_symbol_at(method_attribute_name_index), | |
"Invalid method attribute name index %u in class file %s", | |
method_attribute_name_index, CHECK_NULL); | |
const Symbol* const method_attribute_name = cp->symbol_at(method_attribute_name_index); | |
if (method_attribute_name == vmSymbols::tag_code()) { | |
// Parse Code attribute | |
if (_need_verify) { | |
guarantee_property( | |
!access_flags.is_native() && !access_flags.is_abstract(), | |
"Code attribute in native or abstract methods in class file %s", | |
CHECK_NULL); | |
} | |
if (parsed_code_attribute) { | |
classfile_parse_error("Multiple Code attributes in class file %s", | |
CHECK_NULL); | |
} | |
parsed_code_attribute = true; | |
// Stack size, locals size, and code size | |
if (_major_version == 45 && _minor_version <= 2) { | |
cfs->guarantee_more(4, CHECK_NULL); | |
max_stack = cfs->get_u1_fast(); | |
max_locals = cfs->get_u1_fast(); | |
code_length = cfs->get_u2_fast(); | |
} else { | |
cfs->guarantee_more(8, CHECK_NULL); | |
max_stack = cfs->get_u2_fast(); | |
max_locals = cfs->get_u2_fast(); | |
code_length = cfs->get_u4_fast(); | |
} | |
if (_need_verify) { | |
guarantee_property(args_size <= max_locals, | |
"Arguments can't fit into locals in class file %s", | |
CHECK_NULL); | |
guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE, | |
"Invalid method Code length %u in class file %s", | |
code_length, CHECK_NULL); | |
} | |
// Code pointer | |
code_start = cfs->current(); | |
assert(code_start != NULL, "null code start"); | |
cfs->guarantee_more(code_length, CHECK_NULL); | |
cfs->skip_u1_fast(code_length); | |
// Exception handler table | |
cfs->guarantee_more(2, CHECK_NULL); // exception_table_length | |
exception_table_length = cfs->get_u2_fast(); | |
if (exception_table_length > 0) { | |
exception_table_start = parse_exception_table(cfs, | |
code_length, | |
exception_table_length, | |
CHECK_NULL); | |
} | |
// Parse additional attributes in code attribute | |
cfs->guarantee_more(2, CHECK_NULL); // code_attributes_count | |
u2 code_attributes_count = cfs->get_u2_fast(); | |
unsigned int calculated_attribute_length = 0; | |
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_NULL); // code_attribute_name_index, code_attribute_length | |
const u2 code_attribute_name_index = cfs->get_u2_fast(); | |
const u4 code_attribute_length = cfs->get_u4_fast(); | |
calculated_attribute_length += code_attribute_length + | |
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_NULL); | |
if (LoadLineNumberTables && | |
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) { | |
// Parse and compress line number table | |
parse_linenumber_table(code_attribute_length, | |
code_length, | |
&linenumber_table, | |
CHECK_NULL); | |
} else if (LoadLocalVariableTables && | |
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) { | |
// Parse local variable table | |
if (!lvt_allocated) { | |
localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
lvt_allocated = true; | |
} | |
if (lvt_cnt == max_lvt_cnt) { | |
max_lvt_cnt <<= 1; | |
localvariable_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt); | |
localvariable_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt); | |
} | |
localvariable_table_start[lvt_cnt] = | |
parse_localvariable_table(cfs, | |
code_length, | |
max_locals, | |
code_attribute_length, | |
&localvariable_table_length[lvt_cnt], | |
false, // is not LVTT | |
CHECK_NULL); | |
total_lvt_length += localvariable_table_length[lvt_cnt]; | |
lvt_cnt++; | |
} else if (LoadLocalVariableTypeTables && | |
_major_version >= JAVA_1_5_VERSION && | |
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_type_table()) { | |
if (!lvt_allocated) { | |
localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, u2, INITIAL_MAX_LVT_NUMBER); | |
localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD( | |
THREAD, const unsafe_u2*, INITIAL_MAX_LVT_NUMBER); | |
lvt_allocated = true; | |
} | |
// Parse local variable type table | |
if (lvtt_cnt == max_lvtt_cnt) { | |
max_lvtt_cnt <<= 1; | |
localvariable_type_table_length = REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt); | |
localvariable_type_table_start = REALLOC_RESOURCE_ARRAY(const unsafe_u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt); | |
} | |
localvariable_type_table_start[lvtt_cnt] = | |
parse_localvariable_table(cfs, | |
code_length, | |
max_locals, | |
code_attribute_length, | |
&localvariable_type_table_length[lvtt_cnt], | |
true, // is LVTT | |
CHECK_NULL); | |
lvtt_cnt++; | |
} else if (_major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION && | |
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) { | |
// Stack map is only needed by the new verifier in JDK1.5. | |
if (parsed_stackmap_attribute) { | |
classfile_parse_error("Multiple StackMapTable attributes in class file %s", CHECK_NULL); | |
} | |
stackmap_data = parse_stackmap_table(cfs, code_attribute_length, _need_verify, CHECK_NULL); | |
stackmap_data_length = code_attribute_length; | |
parsed_stackmap_attribute = true; | |
} else { | |
// Skip unknown attributes | |
cfs->skip_u1(code_attribute_length, CHECK_NULL); | |
} | |
} | |
// check method attribute length | |
if (_need_verify) { | |
guarantee_property(method_attribute_length == calculated_attribute_length, | |
"Code segment has wrong length in class file %s", | |
CHECK_NULL); | |
} | |
} else if (method_attribute_name == vmSymbols::tag_exceptions()) { | |
// Parse Exceptions attribute | |
if (parsed_checked_exceptions_attribute) { | |
classfile_parse_error("Multiple Exceptions attributes in class file %s", | |
CHECK_NULL); | |
} | |
parsed_checked_exceptions_attribute = true; | |
checked_exceptions_start = | |
parse_checked_exceptions(cfs, | |
&checked_exceptions_length, | |
method_attribute_length, | |
CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_method_parameters()) { | |
// reject multiple method parameters | |
if (method_parameters_seen) { | |
classfile_parse_error("Multiple MethodParameters attributes in class file %s", | |
CHECK_NULL); | |
} | |
method_parameters_seen = true; | |
method_parameters_length = cfs->get_u1_fast(); | |
const u2 real_length = (method_parameters_length * 4u) + 1u; | |
if (method_attribute_length != real_length) { | |
classfile_parse_error( | |
"Invalid MethodParameters method attribute length %u in class file", | |
method_attribute_length, CHECK_NULL); | |
} | |
method_parameters_data = cfs->current(); | |
cfs->skip_u2_fast(method_parameters_length); | |
cfs->skip_u2_fast(method_parameters_length); | |
// ignore this attribute if it cannot be reflected | |
if (!SystemDictionary::Parameter_klass_loaded()) | |
method_parameters_length = -1; | |
} else if (method_attribute_name == vmSymbols::tag_synthetic()) { | |
if (method_attribute_length != 0) { | |
classfile_parse_error( | |
"Invalid Synthetic method attribute length %u in class file %s", | |
method_attribute_length, CHECK_NULL); | |
} | |
// 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_NULL); | |
} | |
} else if (_major_version >= JAVA_1_5_VERSION) { | |
if (method_attribute_name == vmSymbols::tag_signature()) { | |
if (generic_signature_index != 0) { | |
classfile_parse_error( | |
"Multiple Signature attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
if (method_attribute_length != 2) { | |
classfile_parse_error( | |
"Invalid Signature attribute length %u in class file %s", | |
method_attribute_length, CHECK_NULL); | |
} | |
generic_signature_index = parse_generic_signature_attribute(cfs, CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) { | |
if (runtime_visible_annotations != NULL) { | |
classfile_parse_error( | |
"Multiple RuntimeVisibleAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
runtime_visible_annotations_length = method_attribute_length; | |
runtime_visible_annotations = cfs->current(); | |
assert(runtime_visible_annotations != NULL, "null visible annotations"); | |
cfs->guarantee_more(runtime_visible_annotations_length, CHECK_NULL); | |
parse_annotations(cp, | |
runtime_visible_annotations, | |
runtime_visible_annotations_length, | |
&parsed_annotations, | |
_loader_data, | |
CHECK_NULL); | |
cfs->skip_u1_fast(runtime_visible_annotations_length); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) { | |
if (runtime_invisible_annotations_exists) { | |
classfile_parse_error( | |
"Multiple RuntimeInvisibleAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
runtime_invisible_annotations_exists = true; | |
if (PreserveAllAnnotations) { | |
runtime_invisible_annotations_length = method_attribute_length; | |
runtime_invisible_annotations = cfs->current(); | |
assert(runtime_invisible_annotations != NULL, "null invisible annotations"); | |
} | |
cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) { | |
if (runtime_visible_parameter_annotations != NULL) { | |
classfile_parse_error( | |
"Multiple RuntimeVisibleParameterAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
runtime_visible_parameter_annotations_length = method_attribute_length; | |
runtime_visible_parameter_annotations = cfs->current(); | |
assert(runtime_visible_parameter_annotations != NULL, "null visible parameter annotations"); | |
cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) { | |
if (runtime_invisible_parameter_annotations_exists) { | |
classfile_parse_error( | |
"Multiple RuntimeInvisibleParameterAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
runtime_invisible_parameter_annotations_exists = true; | |
if (PreserveAllAnnotations) { | |
runtime_invisible_parameter_annotations_length = method_attribute_length; | |
runtime_invisible_parameter_annotations = cfs->current(); | |
assert(runtime_invisible_parameter_annotations != NULL, | |
"null invisible parameter annotations"); | |
} | |
cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_annotation_default()) { | |
if (annotation_default != NULL) { | |
classfile_parse_error( | |
"Multiple AnnotationDefault attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
annotation_default_length = method_attribute_length; | |
annotation_default = cfs->current(); | |
assert(annotation_default != NULL, "null annotation default"); | |
cfs->skip_u1(annotation_default_length, CHECK_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) { | |
if (runtime_visible_type_annotations != NULL) { | |
classfile_parse_error( | |
"Multiple RuntimeVisibleTypeAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} | |
runtime_visible_type_annotations_length = method_attribute_length; | |
runtime_visible_type_annotations = cfs->current(); | |
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_NULL); | |
} else if (method_attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) { | |
if (runtime_invisible_type_annotations_exists) { | |
classfile_parse_error( | |
"Multiple RuntimeInvisibleTypeAnnotations attributes for method in class file %s", | |
CHECK_NULL); | |
} else { | |
runtime_invisible_type_annotations_exists = true; | |
} | |
if (PreserveAllAnnotations) { | |
runtime_invisible_type_annotations_length = method_attribute_length; | |
runtime_invisible_type_annotations = cfs->current(); | |
assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations"); | |
} | |
cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
} else { | |
// Skip unknown attributes | |
cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
} | |
} else { | |
// Skip unknown attributes | |
cfs->skip_u1(method_attribute_length, CHECK_NULL); | |
} | |
} | |
if (linenumber_table != 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_NULL); | |
} | |
// All sizing information for a Method* is finally available, now create it | |
InlineTableSizes sizes( | |
total_lvt_length, | |
linenumber_table_length, | |
exception_table_length, | |
checked_exceptions_length, | |
method_parameters_length, | |
generic_signature_index, | |
runtime_visible_annotations_length + | |
runtime_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* const m = Method::allocate(_loader_data, | |
code_length, | |
access_flags, | |
&sizes, | |
ConstMethod::NORMAL, | |
CHECK_NULL); | |
ClassLoadingService::add_class_method_size(m->size()*wordSize); | |
// Fill in information from fixed part (access_flags already set) | |
m->set_constants(_cp); | |
m->set_name_index(name_index); | |
m->set_signature_index(signature_index); | |
ResultTypeFinder rtf(cp->symbol_at(signature_index)); | |
m->constMethod()->set_result_type(rtf.type()); | |
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, | |
(u1*)stackmap_data, | |
stackmap_data_length, | |
CHECK_NULL); | |
} | |
// Copy byte codes | |
m->set_code((u1*)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) { | |
Copy::conjoint_swap_if_needed<Endian::JAVA>(exception_table_start, | |
m->exception_table_start(), | |
exception_table_length * sizeof(ExceptionTableElement), | |
sizeof(u2)); | |
} | |
// Copy method parameters | |
if (method_parameters_length > 0) { | |
MethodParametersElement* elem = m->constMethod()->method_parameters_start(); | |
for (int i = 0; i < method_parameters_length; i++) { | |
elem[i].name_cp_index = Bytes::get_Java_u2((address)method_parameters_data); | |
method_parameters_data += 2; | |
elem[i].flags = Bytes::get_Java_u2((address)method_parameters_data); | |
method_parameters_data += 2; | |
} | |
} | |
// Copy checked exceptions | |
if (checked_exceptions_length > 0) { | |
Copy::conjoint_swap_if_needed<Endian::JAVA>(checked_exceptions_start, | |
m->checked_exceptions_start(), | |
checked_exceptions_length * sizeof(CheckedExceptionElement), | |
sizeof(u2)); | |
} | |
// Copy class file LVT's/LVTT's into the HotSpot internal LVT. | |
if (total_lvt_length > 0) { | |
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, |