be0c8e9f08
This patch addresses an alignment trap due to the storage format of method parameters data in constMethod. It also adds code to validate constant pool indexes for method parameters data. Reviewed-by: jrose, dholmes
4977 lines
202 KiB
C++
4977 lines
202 KiB
C++
/*
|
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*
|
|
*/
|
|
|
|
#include "precompiled.hpp"
|
|
#include "classfile/classFileParser.hpp"
|
|
#include "classfile/classLoader.hpp"
|
|
#include "classfile/classLoaderData.hpp"
|
|
#include "classfile/classLoaderData.inline.hpp"
|
|
#include "classfile/defaultMethods.hpp"
|
|
#include "classfile/genericSignatures.hpp"
|
|
#include "classfile/javaClasses.hpp"
|
|
#include "classfile/symbolTable.hpp"
|
|
#include "classfile/systemDictionary.hpp"
|
|
#include "classfile/verificationType.hpp"
|
|
#include "classfile/verifier.hpp"
|
|
#include "classfile/vmSymbols.hpp"
|
|
#include "memory/allocation.hpp"
|
|
#include "memory/gcLocker.hpp"
|
|
#include "memory/metadataFactory.hpp"
|
|
#include "memory/oopFactory.hpp"
|
|
#include "memory/universe.inline.hpp"
|
|
#include "oops/constantPool.hpp"
|
|
#include "oops/fieldStreams.hpp"
|
|
#include "oops/instanceKlass.hpp"
|
|
#include "oops/instanceMirrorKlass.hpp"
|
|
#include "oops/klass.inline.hpp"
|
|
#include "oops/klassVtable.hpp"
|
|
#include "oops/method.hpp"
|
|
#include "oops/symbol.hpp"
|
|
#include "prims/jvm.h"
|
|
#include "prims/jvmtiExport.hpp"
|
|
#include "prims/jvmtiThreadState.hpp"
|
|
#include "runtime/javaCalls.hpp"
|
|
#include "runtime/perfData.hpp"
|
|
#include "runtime/reflection.hpp"
|
|
#include "runtime/signature.hpp"
|
|
#include "runtime/timer.hpp"
|
|
#include "services/classLoadingService.hpp"
|
|
#include "services/threadService.hpp"
|
|
#include "utilities/array.hpp"
|
|
#include "utilities/globalDefinitions.hpp"
|
|
|
|
// We generally try to create the oops directly when parsing, rather than
|
|
// allocating temporary data structures and copying the bytes twice. A
|
|
// temporary area is only needed when parsing utf8 entries in the constant
|
|
// pool and when parsing line number tables.
|
|
|
|
// We add assert in debug mode when class format is not checked.
|
|
|
|
#define JAVA_CLASSFILE_MAGIC 0xCAFEBABE
|
|
#define JAVA_MIN_SUPPORTED_VERSION 45
|
|
#define JAVA_MAX_SUPPORTED_VERSION 52
|
|
#define JAVA_MAX_SUPPORTED_MINOR_VERSION 0
|
|
|
|
// Used for two backward compatibility reasons:
|
|
// - to check for new additions to the class file format in JDK1.5
|
|
// - to check for bug fixes in the format checker in JDK1.5
|
|
#define JAVA_1_5_VERSION 49
|
|
|
|
// Used for backward compatibility reasons:
|
|
// - to check for javac bug fixes that happened after 1.5
|
|
// - also used as the max version when running in jdk6
|
|
#define JAVA_6_VERSION 50
|
|
|
|
// Used for backward compatibility reasons:
|
|
// - to check NameAndType_info signatures more aggressively
|
|
#define JAVA_7_VERSION 51
|
|
|
|
// Extension method support.
|
|
#define JAVA_8_VERSION 52
|
|
|
|
|
|
void ClassFileParser::parse_constant_pool_entries(ClassLoaderData* loader_data, constantPoolHandle cp, int length, TRAPS) {
|
|
// Use a local copy of ClassFileStream. It helps the C++ compiler to optimize
|
|
// this function (_current can be allocated in a register, with scalar
|
|
// replacement of aggregates). The _current pointer is copied back to
|
|
// stream() when this function returns. DON'T call another method within
|
|
// this method that uses stream().
|
|
ClassFileStream* cfs0 = stream();
|
|
ClassFileStream cfs1 = *cfs0;
|
|
ClassFileStream* cfs = &cfs1;
|
|
#ifdef ASSERT
|
|
assert(cfs->allocated_on_stack(),"should be local");
|
|
u1* old_current = cfs0->current();
|
|
#endif
|
|
Handle class_loader(THREAD, loader_data->class_loader());
|
|
|
|
// Used for batching symbol allocations.
|
|
const char* names[SymbolTable::symbol_alloc_batch_size];
|
|
int lengths[SymbolTable::symbol_alloc_batch_size];
|
|
int indices[SymbolTable::symbol_alloc_batch_size];
|
|
unsigned int hashValues[SymbolTable::symbol_alloc_batch_size];
|
|
int names_count = 0;
|
|
|
|
// parsing Index 0 is unused
|
|
for (int index = 1; index < length; index++) {
|
|
// Each of the following case guarantees one more byte in the stream
|
|
// for the following tag or the access_flags following constant pool,
|
|
// so we don't need bounds-check for reading tag.
|
|
u1 tag = cfs->get_u1_fast();
|
|
switch (tag) {
|
|
case JVM_CONSTANT_Class :
|
|
{
|
|
cfs->guarantee_more(3, CHECK); // name_index, tag/access_flags
|
|
u2 name_index = cfs->get_u2_fast();
|
|
cp->klass_index_at_put(index, name_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Fieldref :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags
|
|
u2 class_index = cfs->get_u2_fast();
|
|
u2 name_and_type_index = cfs->get_u2_fast();
|
|
cp->field_at_put(index, class_index, name_and_type_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Methodref :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags
|
|
u2 class_index = cfs->get_u2_fast();
|
|
u2 name_and_type_index = cfs->get_u2_fast();
|
|
cp->method_at_put(index, class_index, name_and_type_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_InterfaceMethodref :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // class_index, name_and_type_index, tag/access_flags
|
|
u2 class_index = cfs->get_u2_fast();
|
|
u2 name_and_type_index = cfs->get_u2_fast();
|
|
cp->interface_method_at_put(index, class_index, name_and_type_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_String :
|
|
{
|
|
cfs->guarantee_more(3, CHECK); // string_index, tag/access_flags
|
|
u2 string_index = cfs->get_u2_fast();
|
|
cp->string_index_at_put(index, string_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_MethodHandle :
|
|
case JVM_CONSTANT_MethodType :
|
|
if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
|
|
classfile_parse_error(
|
|
"Class file version does not support constant tag %u in class file %s",
|
|
tag, CHECK);
|
|
}
|
|
if (!EnableInvokeDynamic) {
|
|
classfile_parse_error(
|
|
"This JVM does not support constant tag %u in class file %s",
|
|
tag, CHECK);
|
|
}
|
|
if (tag == JVM_CONSTANT_MethodHandle) {
|
|
cfs->guarantee_more(4, CHECK); // ref_kind, method_index, tag/access_flags
|
|
u1 ref_kind = cfs->get_u1_fast();
|
|
u2 method_index = cfs->get_u2_fast();
|
|
cp->method_handle_index_at_put(index, ref_kind, method_index);
|
|
} else if (tag == JVM_CONSTANT_MethodType) {
|
|
cfs->guarantee_more(3, CHECK); // signature_index, tag/access_flags
|
|
u2 signature_index = cfs->get_u2_fast();
|
|
cp->method_type_index_at_put(index, signature_index);
|
|
} else {
|
|
ShouldNotReachHere();
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_InvokeDynamic :
|
|
{
|
|
if (_major_version < Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
|
|
classfile_parse_error(
|
|
"Class file version does not support constant tag %u in class file %s",
|
|
tag, CHECK);
|
|
}
|
|
if (!EnableInvokeDynamic) {
|
|
classfile_parse_error(
|
|
"This JVM does not support constant tag %u in class file %s",
|
|
tag, CHECK);
|
|
}
|
|
cfs->guarantee_more(5, CHECK); // bsm_index, nt, tag/access_flags
|
|
u2 bootstrap_specifier_index = cfs->get_u2_fast();
|
|
u2 name_and_type_index = cfs->get_u2_fast();
|
|
if (_max_bootstrap_specifier_index < (int) bootstrap_specifier_index)
|
|
_max_bootstrap_specifier_index = (int) bootstrap_specifier_index; // collect for later
|
|
cp->invoke_dynamic_at_put(index, bootstrap_specifier_index, name_and_type_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Integer :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags
|
|
u4 bytes = cfs->get_u4_fast();
|
|
cp->int_at_put(index, (jint) bytes);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Float :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // bytes, tag/access_flags
|
|
u4 bytes = cfs->get_u4_fast();
|
|
cp->float_at_put(index, *(jfloat*)&bytes);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Long :
|
|
// A mangled type might cause you to overrun allocated memory
|
|
guarantee_property(index+1 < length,
|
|
"Invalid constant pool entry %u in class file %s",
|
|
index, CHECK);
|
|
{
|
|
cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags
|
|
u8 bytes = cfs->get_u8_fast();
|
|
cp->long_at_put(index, bytes);
|
|
}
|
|
index++; // Skip entry following eigth-byte constant, see JVM book p. 98
|
|
break;
|
|
case JVM_CONSTANT_Double :
|
|
// A mangled type might cause you to overrun allocated memory
|
|
guarantee_property(index+1 < length,
|
|
"Invalid constant pool entry %u in class file %s",
|
|
index, CHECK);
|
|
{
|
|
cfs->guarantee_more(9, CHECK); // bytes, tag/access_flags
|
|
u8 bytes = cfs->get_u8_fast();
|
|
cp->double_at_put(index, *(jdouble*)&bytes);
|
|
}
|
|
index++; // Skip entry following eigth-byte constant, see JVM book p. 98
|
|
break;
|
|
case JVM_CONSTANT_NameAndType :
|
|
{
|
|
cfs->guarantee_more(5, CHECK); // name_index, signature_index, tag/access_flags
|
|
u2 name_index = cfs->get_u2_fast();
|
|
u2 signature_index = cfs->get_u2_fast();
|
|
cp->name_and_type_at_put(index, name_index, signature_index);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_Utf8 :
|
|
{
|
|
cfs->guarantee_more(2, CHECK); // utf8_length
|
|
u2 utf8_length = cfs->get_u2_fast();
|
|
u1* utf8_buffer = cfs->get_u1_buffer();
|
|
assert(utf8_buffer != NULL, "null utf8 buffer");
|
|
// Got utf8 string, guarantee utf8_length+1 bytes, set stream position forward.
|
|
cfs->guarantee_more(utf8_length+1, CHECK); // utf8 string, tag/access_flags
|
|
cfs->skip_u1_fast(utf8_length);
|
|
|
|
// Before storing the symbol, make sure it's legal
|
|
if (_need_verify) {
|
|
verify_legal_utf8((unsigned char*)utf8_buffer, utf8_length, CHECK);
|
|
}
|
|
|
|
if (EnableInvokeDynamic && has_cp_patch_at(index)) {
|
|
Handle patch = clear_cp_patch_at(index);
|
|
guarantee_property(java_lang_String::is_instance(patch()),
|
|
"Illegal utf8 patch at %d in class file %s",
|
|
index, CHECK);
|
|
char* str = java_lang_String::as_utf8_string(patch());
|
|
// (could use java_lang_String::as_symbol instead, but might as well batch them)
|
|
utf8_buffer = (u1*) str;
|
|
utf8_length = (int) strlen(str);
|
|
}
|
|
|
|
unsigned int hash;
|
|
Symbol* result = SymbolTable::lookup_only((char*)utf8_buffer, utf8_length, hash);
|
|
if (result == NULL) {
|
|
names[names_count] = (char*)utf8_buffer;
|
|
lengths[names_count] = utf8_length;
|
|
indices[names_count] = index;
|
|
hashValues[names_count++] = hash;
|
|
if (names_count == SymbolTable::symbol_alloc_batch_size) {
|
|
SymbolTable::new_symbols(loader_data, cp, names_count, names, lengths, indices, hashValues, CHECK);
|
|
names_count = 0;
|
|
}
|
|
} else {
|
|
cp->symbol_at_put(index, result);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
classfile_parse_error(
|
|
"Unknown constant tag %u in class file %s", tag, CHECK);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Allocate the remaining symbols
|
|
if (names_count > 0) {
|
|
SymbolTable::new_symbols(loader_data, cp, names_count, names, lengths, indices, hashValues, CHECK);
|
|
}
|
|
|
|
// Copy _current pointer of local copy back to stream().
|
|
#ifdef ASSERT
|
|
assert(cfs0->current() == old_current, "non-exclusive use of stream()");
|
|
#endif
|
|
cfs0->set_current(cfs1.current());
|
|
}
|
|
|
|
// This class unreferences constant pool symbols if an error has occurred
|
|
// while parsing the class before it is assigned into the class.
|
|
// If it gets an error after that it is unloaded and the constant pool will
|
|
// be cleaned up then.
|
|
class ConstantPoolCleaner : public StackObj {
|
|
constantPoolHandle _cphandle;
|
|
bool _in_error;
|
|
public:
|
|
ConstantPoolCleaner(constantPoolHandle cp) : _cphandle(cp), _in_error(true) {}
|
|
~ConstantPoolCleaner() {
|
|
if (_in_error && _cphandle.not_null()) {
|
|
_cphandle->unreference_symbols();
|
|
}
|
|
}
|
|
void set_in_error(bool clean) { _in_error = clean; }
|
|
};
|
|
|
|
bool inline valid_cp_range(int index, int length) { return (index > 0 && index < length); }
|
|
|
|
inline Symbol* check_symbol_at(constantPoolHandle cp, int index) {
|
|
if (valid_cp_range(index, cp->length()) && cp->tag_at(index).is_utf8())
|
|
return cp->symbol_at(index);
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
constantPoolHandle ClassFileParser::parse_constant_pool(ClassLoaderData* loader_data, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
constantPoolHandle nullHandle;
|
|
|
|
cfs->guarantee_more(3, CHECK_(nullHandle)); // length, first cp tag
|
|
u2 length = cfs->get_u2_fast();
|
|
guarantee_property(
|
|
length >= 1, "Illegal constant pool size %u in class file %s",
|
|
length, CHECK_(nullHandle));
|
|
ConstantPool* constant_pool =
|
|
ConstantPool::allocate(loader_data,
|
|
length,
|
|
CHECK_(nullHandle));
|
|
constantPoolHandle cp (THREAD, constant_pool);
|
|
|
|
ConstantPoolCleaner cp_in_error(cp); // set constant pool to be cleaned up.
|
|
|
|
// parsing constant pool entries
|
|
parse_constant_pool_entries(loader_data, cp, length, CHECK_(nullHandle));
|
|
|
|
int index = 1; // declared outside of loops for portability
|
|
|
|
// first verification pass - validate cross references and fixup class and string constants
|
|
for (index = 1; index < length; index++) { // Index 0 is unused
|
|
jbyte tag = cp->tag_at(index).value();
|
|
switch (tag) {
|
|
case JVM_CONSTANT_Class :
|
|
ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present
|
|
break;
|
|
case JVM_CONSTANT_Fieldref :
|
|
// fall through
|
|
case JVM_CONSTANT_Methodref :
|
|
// fall through
|
|
case JVM_CONSTANT_InterfaceMethodref : {
|
|
if (!_need_verify) break;
|
|
int klass_ref_index = cp->klass_ref_index_at(index);
|
|
int name_and_type_ref_index = cp->name_and_type_ref_index_at(index);
|
|
check_property(valid_cp_range(klass_ref_index, length) &&
|
|
is_klass_reference(cp, klass_ref_index),
|
|
"Invalid constant pool index %u in class file %s",
|
|
klass_ref_index,
|
|
CHECK_(nullHandle));
|
|
check_property(valid_cp_range(name_and_type_ref_index, length) &&
|
|
cp->tag_at(name_and_type_ref_index).is_name_and_type(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
name_and_type_ref_index,
|
|
CHECK_(nullHandle));
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_String :
|
|
ShouldNotReachHere(); // Only JVM_CONSTANT_StringIndex should be present
|
|
break;
|
|
case JVM_CONSTANT_Integer :
|
|
break;
|
|
case JVM_CONSTANT_Float :
|
|
break;
|
|
case JVM_CONSTANT_Long :
|
|
case JVM_CONSTANT_Double :
|
|
index++;
|
|
check_property(
|
|
(index < length && cp->tag_at(index).is_invalid()),
|
|
"Improper constant pool long/double index %u in class file %s",
|
|
index, CHECK_(nullHandle));
|
|
break;
|
|
case JVM_CONSTANT_NameAndType : {
|
|
if (!_need_verify) break;
|
|
int name_ref_index = cp->name_ref_index_at(index);
|
|
int signature_ref_index = cp->signature_ref_index_at(index);
|
|
check_property(
|
|
valid_cp_range(name_ref_index, length) &&
|
|
cp->tag_at(name_ref_index).is_utf8(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
name_ref_index, CHECK_(nullHandle));
|
|
check_property(
|
|
valid_cp_range(signature_ref_index, length) &&
|
|
cp->tag_at(signature_ref_index).is_utf8(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
signature_ref_index, CHECK_(nullHandle));
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_Utf8 :
|
|
break;
|
|
case JVM_CONSTANT_UnresolvedClass : // fall-through
|
|
case JVM_CONSTANT_UnresolvedClassInError:
|
|
ShouldNotReachHere(); // Only JVM_CONSTANT_ClassIndex should be present
|
|
break;
|
|
case JVM_CONSTANT_ClassIndex :
|
|
{
|
|
int class_index = cp->klass_index_at(index);
|
|
check_property(
|
|
valid_cp_range(class_index, length) &&
|
|
cp->tag_at(class_index).is_utf8(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
class_index, CHECK_(nullHandle));
|
|
cp->unresolved_klass_at_put(index, cp->symbol_at(class_index));
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_StringIndex :
|
|
{
|
|
int string_index = cp->string_index_at(index);
|
|
check_property(
|
|
valid_cp_range(string_index, length) &&
|
|
cp->tag_at(string_index).is_utf8(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
string_index, CHECK_(nullHandle));
|
|
Symbol* sym = cp->symbol_at(string_index);
|
|
cp->unresolved_string_at_put(index, sym);
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_MethodHandle :
|
|
{
|
|
int ref_index = cp->method_handle_index_at(index);
|
|
check_property(
|
|
valid_cp_range(ref_index, length) &&
|
|
EnableInvokeDynamic,
|
|
"Invalid constant pool index %u in class file %s",
|
|
ref_index, CHECK_(nullHandle));
|
|
constantTag tag = cp->tag_at(ref_index);
|
|
int ref_kind = cp->method_handle_ref_kind_at(index);
|
|
switch (ref_kind) {
|
|
case JVM_REF_getField:
|
|
case JVM_REF_getStatic:
|
|
case JVM_REF_putField:
|
|
case JVM_REF_putStatic:
|
|
check_property(
|
|
tag.is_field(),
|
|
"Invalid constant pool index %u in class file %s (not a field)",
|
|
ref_index, CHECK_(nullHandle));
|
|
break;
|
|
case JVM_REF_invokeVirtual:
|
|
case JVM_REF_invokeStatic:
|
|
case JVM_REF_invokeSpecial:
|
|
case JVM_REF_newInvokeSpecial:
|
|
check_property(
|
|
tag.is_method(),
|
|
"Invalid constant pool index %u in class file %s (not a method)",
|
|
ref_index, CHECK_(nullHandle));
|
|
break;
|
|
case JVM_REF_invokeInterface:
|
|
check_property(
|
|
tag.is_interface_method(),
|
|
"Invalid constant pool index %u in class file %s (not an interface method)",
|
|
ref_index, CHECK_(nullHandle));
|
|
break;
|
|
default:
|
|
classfile_parse_error(
|
|
"Bad method handle kind at constant pool index %u in class file %s",
|
|
index, CHECK_(nullHandle));
|
|
}
|
|
// Keep the ref_index unchanged. It will be indirected at link-time.
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_MethodType :
|
|
{
|
|
int ref_index = cp->method_type_index_at(index);
|
|
check_property(
|
|
valid_cp_range(ref_index, length) &&
|
|
cp->tag_at(ref_index).is_utf8() &&
|
|
EnableInvokeDynamic,
|
|
"Invalid constant pool index %u in class file %s",
|
|
ref_index, CHECK_(nullHandle));
|
|
}
|
|
break;
|
|
case JVM_CONSTANT_InvokeDynamic :
|
|
{
|
|
int name_and_type_ref_index = cp->invoke_dynamic_name_and_type_ref_index_at(index);
|
|
check_property(valid_cp_range(name_and_type_ref_index, length) &&
|
|
cp->tag_at(name_and_type_ref_index).is_name_and_type(),
|
|
"Invalid constant pool index %u in class file %s",
|
|
name_and_type_ref_index,
|
|
CHECK_(nullHandle));
|
|
// bootstrap specifier index must be checked later, when BootstrapMethods attr is available
|
|
break;
|
|
}
|
|
default:
|
|
fatal(err_msg("bad constant pool tag value %u",
|
|
cp->tag_at(index).value()));
|
|
ShouldNotReachHere();
|
|
break;
|
|
} // end of switch
|
|
} // end of for
|
|
|
|
if (_cp_patches != NULL) {
|
|
// need to treat this_class specially...
|
|
assert(EnableInvokeDynamic, "");
|
|
int this_class_index;
|
|
{
|
|
cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len
|
|
u1* mark = cfs->current();
|
|
u2 flags = cfs->get_u2_fast();
|
|
this_class_index = cfs->get_u2_fast();
|
|
cfs->set_current(mark); // revert to mark
|
|
}
|
|
|
|
for (index = 1; index < length; index++) { // Index 0 is unused
|
|
if (has_cp_patch_at(index)) {
|
|
guarantee_property(index != this_class_index,
|
|
"Illegal constant pool patch to self at %d in class file %s",
|
|
index, CHECK_(nullHandle));
|
|
patch_constant_pool(cp, index, cp_patch_at(index), CHECK_(nullHandle));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!_need_verify) {
|
|
cp_in_error.set_in_error(false);
|
|
return cp;
|
|
}
|
|
|
|
// second verification pass - checks the strings are of the right format.
|
|
// but not yet to the other entries
|
|
for (index = 1; index < length; index++) {
|
|
jbyte tag = cp->tag_at(index).value();
|
|
switch (tag) {
|
|
case JVM_CONSTANT_UnresolvedClass: {
|
|
Symbol* class_name = cp->unresolved_klass_at(index);
|
|
// check the name, even if _cp_patches will overwrite it
|
|
verify_legal_class_name(class_name, CHECK_(nullHandle));
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_NameAndType: {
|
|
if (_need_verify && _major_version >= JAVA_7_VERSION) {
|
|
int sig_index = cp->signature_ref_index_at(index);
|
|
int name_index = cp->name_ref_index_at(index);
|
|
Symbol* name = cp->symbol_at(name_index);
|
|
Symbol* sig = cp->symbol_at(sig_index);
|
|
if (sig->byte_at(0) == JVM_SIGNATURE_FUNC) {
|
|
verify_legal_method_signature(name, sig, CHECK_(nullHandle));
|
|
} else {
|
|
verify_legal_field_signature(name, sig, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_InvokeDynamic:
|
|
case JVM_CONSTANT_Fieldref:
|
|
case JVM_CONSTANT_Methodref:
|
|
case JVM_CONSTANT_InterfaceMethodref: {
|
|
int name_and_type_ref_index = cp->name_and_type_ref_index_at(index);
|
|
// already verified to be utf8
|
|
int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index);
|
|
// already verified to be utf8
|
|
int signature_ref_index = cp->signature_ref_index_at(name_and_type_ref_index);
|
|
Symbol* name = cp->symbol_at(name_ref_index);
|
|
Symbol* signature = cp->symbol_at(signature_ref_index);
|
|
if (tag == JVM_CONSTANT_Fieldref) {
|
|
verify_legal_field_name(name, CHECK_(nullHandle));
|
|
if (_need_verify && _major_version >= JAVA_7_VERSION) {
|
|
// Signature is verified above, when iterating NameAndType_info.
|
|
// Need only to be sure it's the right type.
|
|
if (signature->byte_at(0) == JVM_SIGNATURE_FUNC) {
|
|
throwIllegalSignature(
|
|
"Field", name, signature, CHECK_(nullHandle));
|
|
}
|
|
} else {
|
|
verify_legal_field_signature(name, signature, CHECK_(nullHandle));
|
|
}
|
|
} else {
|
|
verify_legal_method_name(name, CHECK_(nullHandle));
|
|
if (_need_verify && _major_version >= JAVA_7_VERSION) {
|
|
// Signature is verified above, when iterating NameAndType_info.
|
|
// Need only to be sure it's the right type.
|
|
if (signature->byte_at(0) != JVM_SIGNATURE_FUNC) {
|
|
throwIllegalSignature(
|
|
"Method", name, signature, CHECK_(nullHandle));
|
|
}
|
|
} else {
|
|
verify_legal_method_signature(name, signature, CHECK_(nullHandle));
|
|
}
|
|
if (tag == JVM_CONSTANT_Methodref) {
|
|
// 4509014: If a class method name begins with '<', it must be "<init>".
|
|
assert(name != NULL, "method name in constant pool is null");
|
|
unsigned int name_len = name->utf8_length();
|
|
assert(name_len > 0, "bad method name"); // already verified as legal name
|
|
if (name->byte_at(0) == '<') {
|
|
if (name != vmSymbols::object_initializer_name()) {
|
|
classfile_parse_error(
|
|
"Bad method name at constant pool index %u in class file %s",
|
|
name_ref_index, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_MethodHandle: {
|
|
int ref_index = cp->method_handle_index_at(index);
|
|
int ref_kind = cp->method_handle_ref_kind_at(index);
|
|
switch (ref_kind) {
|
|
case JVM_REF_invokeVirtual:
|
|
case JVM_REF_invokeStatic:
|
|
case JVM_REF_invokeSpecial:
|
|
case JVM_REF_newInvokeSpecial:
|
|
{
|
|
int name_and_type_ref_index = cp->name_and_type_ref_index_at(ref_index);
|
|
int name_ref_index = cp->name_ref_index_at(name_and_type_ref_index);
|
|
Symbol* name = cp->symbol_at(name_ref_index);
|
|
if (ref_kind == JVM_REF_newInvokeSpecial) {
|
|
if (name != vmSymbols::object_initializer_name()) {
|
|
classfile_parse_error(
|
|
"Bad constructor name at constant pool index %u in class file %s",
|
|
name_ref_index, CHECK_(nullHandle));
|
|
}
|
|
} else {
|
|
if (name == vmSymbols::object_initializer_name()) {
|
|
classfile_parse_error(
|
|
"Bad method name at constant pool index %u in class file %s",
|
|
name_ref_index, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
// Other ref_kinds are already fully checked in previous pass.
|
|
}
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_MethodType: {
|
|
Symbol* no_name = vmSymbols::type_name(); // place holder
|
|
Symbol* signature = cp->method_type_signature_at(index);
|
|
verify_legal_method_signature(no_name, signature, CHECK_(nullHandle));
|
|
break;
|
|
}
|
|
case JVM_CONSTANT_Utf8: {
|
|
assert(cp->symbol_at(index)->refcount() != 0, "count corrupted");
|
|
}
|
|
} // end of switch
|
|
} // end of for
|
|
|
|
cp_in_error.set_in_error(false);
|
|
return cp;
|
|
}
|
|
|
|
|
|
void ClassFileParser::patch_constant_pool(constantPoolHandle cp, int index, Handle patch, TRAPS) {
|
|
assert(EnableInvokeDynamic, "");
|
|
BasicType patch_type = T_VOID;
|
|
|
|
switch (cp->tag_at(index).value()) {
|
|
|
|
case JVM_CONSTANT_UnresolvedClass :
|
|
// Patching a class means pre-resolving it.
|
|
// The name in the constant pool is ignored.
|
|
if (java_lang_Class::is_instance(patch())) {
|
|
guarantee_property(!java_lang_Class::is_primitive(patch()),
|
|
"Illegal class patch at %d in class file %s",
|
|
index, CHECK);
|
|
cp->klass_at_put(index, java_lang_Class::as_Klass(patch()));
|
|
} else {
|
|
guarantee_property(java_lang_String::is_instance(patch()),
|
|
"Illegal class patch at %d in class file %s",
|
|
index, CHECK);
|
|
Symbol* name = java_lang_String::as_symbol(patch(), CHECK);
|
|
cp->unresolved_klass_at_put(index, name);
|
|
}
|
|
break;
|
|
|
|
case JVM_CONSTANT_String :
|
|
// skip this patch and don't clear it. Needs the oop array for resolved
|
|
// references to be created first.
|
|
return;
|
|
|
|
case JVM_CONSTANT_Integer : patch_type = T_INT; goto patch_prim;
|
|
case JVM_CONSTANT_Float : patch_type = T_FLOAT; goto patch_prim;
|
|
case JVM_CONSTANT_Long : patch_type = T_LONG; goto patch_prim;
|
|
case JVM_CONSTANT_Double : patch_type = T_DOUBLE; goto patch_prim;
|
|
patch_prim:
|
|
{
|
|
jvalue value;
|
|
BasicType value_type = java_lang_boxing_object::get_value(patch(), &value);
|
|
guarantee_property(value_type == patch_type,
|
|
"Illegal primitive patch at %d in class file %s",
|
|
index, CHECK);
|
|
switch (value_type) {
|
|
case T_INT: cp->int_at_put(index, value.i); break;
|
|
case T_FLOAT: cp->float_at_put(index, value.f); break;
|
|
case T_LONG: cp->long_at_put(index, value.j); break;
|
|
case T_DOUBLE: cp->double_at_put(index, value.d); break;
|
|
default: assert(false, "");
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// %%% TODO: put method handles into CONSTANT_InterfaceMethodref, etc.
|
|
guarantee_property(!has_cp_patch_at(index),
|
|
"Illegal unexpected patch at %d in class file %s",
|
|
index, CHECK);
|
|
return;
|
|
}
|
|
|
|
// On fall-through, mark the patch as used.
|
|
clear_cp_patch_at(index);
|
|
}
|
|
|
|
|
|
|
|
class NameSigHash: public ResourceObj {
|
|
public:
|
|
Symbol* _name; // name
|
|
Symbol* _sig; // signature
|
|
NameSigHash* _next; // Next entry in hash table
|
|
};
|
|
|
|
|
|
#define HASH_ROW_SIZE 256
|
|
|
|
unsigned int hash(Symbol* name, Symbol* sig) {
|
|
unsigned int raw_hash = 0;
|
|
raw_hash += ((unsigned int)(uintptr_t)name) >> (LogHeapWordSize + 2);
|
|
raw_hash += ((unsigned int)(uintptr_t)sig) >> LogHeapWordSize;
|
|
|
|
return (raw_hash + (unsigned int)(uintptr_t)name) % HASH_ROW_SIZE;
|
|
}
|
|
|
|
|
|
void initialize_hashtable(NameSigHash** table) {
|
|
memset((void*)table, 0, sizeof(NameSigHash*) * HASH_ROW_SIZE);
|
|
}
|
|
|
|
// Return false if the name/sig combination is found in table.
|
|
// Return true if no duplicate is found. And name/sig is added as a new entry in table.
|
|
// The old format checker uses heap sort to find duplicates.
|
|
// NOTE: caller should guarantee that GC doesn't happen during the life cycle
|
|
// of table since we don't expect Symbol*'s to move.
|
|
bool put_after_lookup(Symbol* name, Symbol* sig, NameSigHash** table) {
|
|
assert(name != NULL, "name in constant pool is NULL");
|
|
|
|
// First lookup for duplicates
|
|
int index = hash(name, sig);
|
|
NameSigHash* entry = table[index];
|
|
while (entry != NULL) {
|
|
if (entry->_name == name && entry->_sig == sig) {
|
|
return false;
|
|
}
|
|
entry = entry->_next;
|
|
}
|
|
|
|
// No duplicate is found, allocate a new entry and fill it.
|
|
entry = new NameSigHash();
|
|
entry->_name = name;
|
|
entry->_sig = sig;
|
|
|
|
// Insert into hash table
|
|
entry->_next = table[index];
|
|
table[index] = entry;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
Array<Klass*>* ClassFileParser::parse_interfaces(constantPoolHandle cp,
|
|
int length,
|
|
ClassLoaderData* loader_data,
|
|
Handle protection_domain,
|
|
Symbol* class_name,
|
|
bool* has_default_methods,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
assert(length > 0, "only called for length>0");
|
|
// FIXME: Leak at later OOM.
|
|
Array<Klass*>* interfaces = MetadataFactory::new_array<Klass*>(loader_data, length, NULL, CHECK_NULL);
|
|
|
|
int index;
|
|
for (index = 0; index < length; index++) {
|
|
u2 interface_index = cfs->get_u2(CHECK_NULL);
|
|
KlassHandle interf;
|
|
check_property(
|
|
valid_cp_range(interface_index, cp->length()) &&
|
|
is_klass_reference(cp, interface_index),
|
|
"Interface name has bad constant pool index %u in class file %s",
|
|
interface_index, CHECK_NULL);
|
|
if (cp->tag_at(interface_index).is_klass()) {
|
|
interf = KlassHandle(THREAD, cp->resolved_klass_at(interface_index));
|
|
} else {
|
|
Symbol* unresolved_klass = cp->klass_name_at(interface_index);
|
|
|
|
// Don't need to check legal name because it's checked when parsing constant pool.
|
|
// But need to make sure it's not an array type.
|
|
guarantee_property(unresolved_klass->byte_at(0) != JVM_SIGNATURE_ARRAY,
|
|
"Bad interface name in class file %s", CHECK_NULL);
|
|
Handle class_loader(THREAD, loader_data->class_loader());
|
|
|
|
// Call resolve_super so classcircularity is checked
|
|
Klass* k = SystemDictionary::resolve_super_or_fail(class_name,
|
|
unresolved_klass, class_loader, protection_domain,
|
|
false, CHECK_NULL);
|
|
interf = KlassHandle(THREAD, k);
|
|
}
|
|
|
|
if (!interf()->is_interface()) {
|
|
THROW_MSG_(vmSymbols::java_lang_IncompatibleClassChangeError(), "Implementing class", NULL);
|
|
}
|
|
if (InstanceKlass::cast(interf())->has_default_methods()) {
|
|
*has_default_methods = true;
|
|
}
|
|
interfaces->at_put(index, interf());
|
|
}
|
|
|
|
if (!_need_verify || length <= 1) {
|
|
return interfaces;
|
|
}
|
|
|
|
// Check if there's any duplicates in interfaces
|
|
ResourceMark rm(THREAD);
|
|
NameSigHash** interface_names = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, NameSigHash*, HASH_ROW_SIZE);
|
|
initialize_hashtable(interface_names);
|
|
bool dup = false;
|
|
{
|
|
debug_only(No_Safepoint_Verifier nsv;)
|
|
for (index = 0; index < length; index++) {
|
|
Klass* k = interfaces->at(index);
|
|
Symbol* name = InstanceKlass::cast(k)->name();
|
|
// If no duplicates, add (name, NULL) in hashtable interface_names.
|
|
if (!put_after_lookup(name, NULL, interface_names)) {
|
|
dup = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dup) {
|
|
classfile_parse_error("Duplicate interface name in class file %s", CHECK_NULL);
|
|
}
|
|
|
|
return interfaces;
|
|
}
|
|
|
|
|
|
void ClassFileParser::verify_constantvalue(int constantvalue_index, int signature_index, constantPoolHandle cp, TRAPS) {
|
|
// Make sure the constant pool entry is of a type appropriate to this field
|
|
guarantee_property(
|
|
(constantvalue_index > 0 &&
|
|
constantvalue_index < cp->length()),
|
|
"Bad initial value index %u in ConstantValue attribute in class file %s",
|
|
constantvalue_index, CHECK);
|
|
constantTag value_type = cp->tag_at(constantvalue_index);
|
|
switch ( cp->basic_type_for_signature_at(signature_index) ) {
|
|
case T_LONG:
|
|
guarantee_property(value_type.is_long(), "Inconsistent constant value type in class file %s", CHECK);
|
|
break;
|
|
case T_FLOAT:
|
|
guarantee_property(value_type.is_float(), "Inconsistent constant value type in class file %s", CHECK);
|
|
break;
|
|
case T_DOUBLE:
|
|
guarantee_property(value_type.is_double(), "Inconsistent constant value type in class file %s", CHECK);
|
|
break;
|
|
case T_BYTE: case T_CHAR: case T_SHORT: case T_BOOLEAN: case T_INT:
|
|
guarantee_property(value_type.is_int(), "Inconsistent constant value type in class file %s", CHECK);
|
|
break;
|
|
case T_OBJECT:
|
|
guarantee_property((cp->symbol_at(signature_index)->equals("Ljava/lang/String;")
|
|
&& value_type.is_string()),
|
|
"Bad string initial value in class file %s", CHECK);
|
|
break;
|
|
default:
|
|
classfile_parse_error(
|
|
"Unable to set initial value %u in class file %s",
|
|
constantvalue_index, CHECK);
|
|
}
|
|
}
|
|
|
|
|
|
// Parse attributes for a field.
|
|
void ClassFileParser::parse_field_attributes(ClassLoaderData* loader_data,
|
|
constantPoolHandle cp,
|
|
u2 attributes_count,
|
|
bool is_static, u2 signature_index,
|
|
u2* constantvalue_index_addr,
|
|
bool* is_synthetic_addr,
|
|
u2* generic_signature_index_addr,
|
|
AnnotationArray** field_annotations,
|
|
AnnotationArray** field_type_annotations,
|
|
ClassFileParser::FieldAnnotationCollector* parsed_annotations,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
assert(attributes_count > 0, "length should be greater than 0");
|
|
u2 constantvalue_index = 0;
|
|
u2 generic_signature_index = 0;
|
|
bool is_synthetic = false;
|
|
u1* runtime_visible_annotations = NULL;
|
|
int runtime_visible_annotations_length = 0;
|
|
u1* runtime_invisible_annotations = NULL;
|
|
int runtime_invisible_annotations_length = 0;
|
|
u1* runtime_visible_type_annotations = NULL;
|
|
int runtime_visible_type_annotations_length = 0;
|
|
u1* runtime_invisible_type_annotations = NULL;
|
|
int runtime_invisible_type_annotations_length = 0;
|
|
while (attributes_count--) {
|
|
cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length
|
|
u2 attribute_name_index = cfs->get_u2_fast();
|
|
u4 attribute_length = cfs->get_u4_fast();
|
|
check_property(valid_cp_range(attribute_name_index, cp->length()) &&
|
|
cp->tag_at(attribute_name_index).is_utf8(),
|
|
"Invalid field attribute index %u in class file %s",
|
|
attribute_name_index,
|
|
CHECK);
|
|
Symbol* attribute_name = cp->symbol_at(attribute_name_index);
|
|
if (is_static && attribute_name == vmSymbols::tag_constant_value()) {
|
|
// ignore if non-static
|
|
if (constantvalue_index != 0) {
|
|
classfile_parse_error("Duplicate ConstantValue attribute in class file %s", CHECK);
|
|
}
|
|
check_property(
|
|
attribute_length == 2,
|
|
"Invalid ConstantValue field attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
constantvalue_index = cfs->get_u2(CHECK);
|
|
if (_need_verify) {
|
|
verify_constantvalue(constantvalue_index, signature_index, cp, CHECK);
|
|
}
|
|
} else if (attribute_name == vmSymbols::tag_synthetic()) {
|
|
if (attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Synthetic field attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
is_synthetic = true;
|
|
} else if (attribute_name == vmSymbols::tag_deprecated()) { // 4276120
|
|
if (attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Deprecated field attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
} else if (_major_version >= JAVA_1_5_VERSION) {
|
|
if (attribute_name == vmSymbols::tag_signature()) {
|
|
if (attribute_length != 2) {
|
|
classfile_parse_error(
|
|
"Wrong size %u for field's Signature attribute in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
generic_signature_index = cfs->get_u2(CHECK);
|
|
} else if (attribute_name == vmSymbols::tag_runtime_visible_annotations()) {
|
|
runtime_visible_annotations_length = attribute_length;
|
|
runtime_visible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_annotations != NULL, "null visible annotations");
|
|
cfs->skip_u1(runtime_visible_annotations_length, CHECK);
|
|
} else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
|
|
runtime_invisible_annotations_length = attribute_length;
|
|
runtime_invisible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_annotations != NULL, "null invisible annotations");
|
|
cfs->skip_u1(runtime_invisible_annotations_length, CHECK);
|
|
} else if (attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) {
|
|
runtime_visible_type_annotations_length = attribute_length;
|
|
runtime_visible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_type_annotations != NULL, "null visible type annotations");
|
|
cfs->skip_u1(runtime_visible_type_annotations_length, CHECK);
|
|
} else if (PreserveAllAnnotations && attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) {
|
|
runtime_invisible_type_annotations_length = attribute_length;
|
|
runtime_invisible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations");
|
|
cfs->skip_u1(runtime_invisible_type_annotations_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;
|
|
*field_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_annotations,
|
|
runtime_visible_annotations_length,
|
|
runtime_invisible_annotations,
|
|
runtime_invisible_annotations_length,
|
|
CHECK);
|
|
*field_type_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_type_annotations,
|
|
runtime_visible_type_annotations_length,
|
|
runtime_invisible_type_annotations,
|
|
runtime_invisible_type_annotations_length,
|
|
CHECK);
|
|
return;
|
|
}
|
|
|
|
|
|
// Field allocation types. Used for computing field offsets.
|
|
|
|
enum FieldAllocationType {
|
|
STATIC_OOP, // Oops
|
|
STATIC_BYTE, // Boolean, Byte, char
|
|
STATIC_SHORT, // shorts
|
|
STATIC_WORD, // ints
|
|
STATIC_DOUBLE, // aligned long or double
|
|
NONSTATIC_OOP,
|
|
NONSTATIC_BYTE,
|
|
NONSTATIC_SHORT,
|
|
NONSTATIC_WORD,
|
|
NONSTATIC_DOUBLE,
|
|
MAX_FIELD_ALLOCATION_TYPE,
|
|
BAD_ALLOCATION_TYPE = -1
|
|
};
|
|
|
|
static FieldAllocationType _basic_type_to_atype[2 * (T_CONFLICT + 1)] = {
|
|
BAD_ALLOCATION_TYPE, // 0
|
|
BAD_ALLOCATION_TYPE, // 1
|
|
BAD_ALLOCATION_TYPE, // 2
|
|
BAD_ALLOCATION_TYPE, // 3
|
|
NONSTATIC_BYTE , // T_BOOLEAN = 4,
|
|
NONSTATIC_SHORT, // T_CHAR = 5,
|
|
NONSTATIC_WORD, // T_FLOAT = 6,
|
|
NONSTATIC_DOUBLE, // T_DOUBLE = 7,
|
|
NONSTATIC_BYTE, // T_BYTE = 8,
|
|
NONSTATIC_SHORT, // T_SHORT = 9,
|
|
NONSTATIC_WORD, // T_INT = 10,
|
|
NONSTATIC_DOUBLE, // T_LONG = 11,
|
|
NONSTATIC_OOP, // T_OBJECT = 12,
|
|
NONSTATIC_OOP, // T_ARRAY = 13,
|
|
BAD_ALLOCATION_TYPE, // T_VOID = 14,
|
|
BAD_ALLOCATION_TYPE, // T_ADDRESS = 15,
|
|
BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16,
|
|
BAD_ALLOCATION_TYPE, // T_METADATA = 17,
|
|
BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
|
|
BAD_ALLOCATION_TYPE, // T_CONFLICT = 19,
|
|
BAD_ALLOCATION_TYPE, // 0
|
|
BAD_ALLOCATION_TYPE, // 1
|
|
BAD_ALLOCATION_TYPE, // 2
|
|
BAD_ALLOCATION_TYPE, // 3
|
|
STATIC_BYTE , // T_BOOLEAN = 4,
|
|
STATIC_SHORT, // T_CHAR = 5,
|
|
STATIC_WORD, // T_FLOAT = 6,
|
|
STATIC_DOUBLE, // T_DOUBLE = 7,
|
|
STATIC_BYTE, // T_BYTE = 8,
|
|
STATIC_SHORT, // T_SHORT = 9,
|
|
STATIC_WORD, // T_INT = 10,
|
|
STATIC_DOUBLE, // T_LONG = 11,
|
|
STATIC_OOP, // T_OBJECT = 12,
|
|
STATIC_OOP, // T_ARRAY = 13,
|
|
BAD_ALLOCATION_TYPE, // T_VOID = 14,
|
|
BAD_ALLOCATION_TYPE, // T_ADDRESS = 15,
|
|
BAD_ALLOCATION_TYPE, // T_NARROWOOP = 16,
|
|
BAD_ALLOCATION_TYPE, // T_METADATA = 17,
|
|
BAD_ALLOCATION_TYPE, // T_NARROWKLASS = 18,
|
|
BAD_ALLOCATION_TYPE, // T_CONFLICT = 19,
|
|
};
|
|
|
|
static FieldAllocationType basic_type_to_atype(bool is_static, BasicType type) {
|
|
assert(type >= T_BOOLEAN && type < T_VOID, "only allowable values");
|
|
FieldAllocationType result = _basic_type_to_atype[type + (is_static ? (T_CONFLICT + 1) : 0)];
|
|
assert(result != BAD_ALLOCATION_TYPE, "bad type");
|
|
return result;
|
|
}
|
|
|
|
class FieldAllocationCount: public ResourceObj {
|
|
public:
|
|
u2 count[MAX_FIELD_ALLOCATION_TYPE];
|
|
|
|
FieldAllocationCount() {
|
|
for (int i = 0; i < MAX_FIELD_ALLOCATION_TYPE; i++) {
|
|
count[i] = 0;
|
|
}
|
|
}
|
|
|
|
FieldAllocationType update(bool is_static, BasicType type) {
|
|
FieldAllocationType atype = basic_type_to_atype(is_static, type);
|
|
// Make sure there is no overflow with injected fields.
|
|
assert(count[atype] < 0xFFFF, "More than 65535 fields");
|
|
count[atype]++;
|
|
return atype;
|
|
}
|
|
};
|
|
|
|
Array<u2>* ClassFileParser::parse_fields(ClassLoaderData* loader_data,
|
|
Symbol* class_name,
|
|
constantPoolHandle cp,
|
|
bool is_interface,
|
|
FieldAllocationCount *fac,
|
|
Array<AnnotationArray*>** fields_annotations,
|
|
Array<AnnotationArray*>** fields_type_annotations,
|
|
u2* java_fields_count_ptr, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
cfs->guarantee_more(2, CHECK_NULL); // length
|
|
u2 length = cfs->get_u2_fast();
|
|
*java_fields_count_ptr = length;
|
|
|
|
int num_injected = 0;
|
|
InjectedField* injected = JavaClasses::get_injected(class_name, &num_injected);
|
|
int total_fields = length + num_injected;
|
|
|
|
// The field array starts with tuples of shorts
|
|
// [access, name index, sig index, initial value index, byte offset].
|
|
// A generic signature slot only exists for field with generic
|
|
// signature attribute. And the access flag is set with
|
|
// JVM_ACC_FIELD_HAS_GENERIC_SIGNATURE for that field. The generic
|
|
// signature slots are at the end of the field array and after all
|
|
// other fields data.
|
|
//
|
|
// f1: [access, name index, sig index, initial value index, low_offset, high_offset]
|
|
// f2: [access, name index, sig index, initial value index, low_offset, high_offset]
|
|
// ...
|
|
// fn: [access, name index, sig index, initial value index, low_offset, high_offset]
|
|
// [generic signature index]
|
|
// [generic signature index]
|
|
// ...
|
|
//
|
|
// Allocate a temporary resource array for field data. For each field,
|
|
// a slot is reserved in the temporary array for the generic signature
|
|
// index. After parsing all fields, the data are copied to a permanent
|
|
// array and any unused slots will be discarded.
|
|
ResourceMark rm(THREAD);
|
|
u2* fa = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2, total_fields * (FieldInfo::field_slots + 1));
|
|
|
|
AnnotationArray* field_annotations = NULL;
|
|
AnnotationArray* field_type_annotations = NULL;
|
|
// The generic signature slots start after all other fields' data.
|
|
int generic_signature_slot = total_fields * FieldInfo::field_slots;
|
|
int num_generic_signature = 0;
|
|
for (int n = 0; n < length; n++) {
|
|
cfs->guarantee_more(8, CHECK_NULL); // access_flags, name_index, descriptor_index, attributes_count
|
|
|
|
AccessFlags access_flags;
|
|
jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_FIELD_MODIFIERS;
|
|
verify_legal_field_modifiers(flags, is_interface, CHECK_NULL);
|
|
access_flags.set_flags(flags);
|
|
|
|
u2 name_index = cfs->get_u2_fast();
|
|
int cp_size = cp->length();
|
|
check_property(
|
|
valid_cp_range(name_index, cp_size) && cp->tag_at(name_index).is_utf8(),
|
|
"Invalid constant pool index %u for field name in class file %s",
|
|
name_index, CHECK_NULL);
|
|
Symbol* name = cp->symbol_at(name_index);
|
|
verify_legal_field_name(name, CHECK_NULL);
|
|
|
|
u2 signature_index = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(signature_index, cp_size) &&
|
|
cp->tag_at(signature_index).is_utf8(),
|
|
"Invalid constant pool index %u for field signature in class file %s",
|
|
signature_index, CHECK_NULL);
|
|
Symbol* sig = cp->symbol_at(signature_index);
|
|
verify_legal_field_signature(name, sig, CHECK_NULL);
|
|
|
|
u2 constantvalue_index = 0;
|
|
bool is_synthetic = false;
|
|
u2 generic_signature_index = 0;
|
|
bool is_static = access_flags.is_static();
|
|
FieldAnnotationCollector parsed_annotations;
|
|
|
|
u2 attributes_count = cfs->get_u2_fast();
|
|
if (attributes_count > 0) {
|
|
parse_field_attributes(loader_data,
|
|
cp, attributes_count, is_static, signature_index,
|
|
&constantvalue_index, &is_synthetic,
|
|
&generic_signature_index, &field_annotations,
|
|
&field_type_annotations, &parsed_annotations,
|
|
CHECK_NULL);
|
|
if (field_annotations != NULL) {
|
|
if (*fields_annotations == NULL) {
|
|
*fields_annotations = MetadataFactory::new_array<AnnotationArray*>(
|
|
loader_data, length, NULL,
|
|
CHECK_NULL);
|
|
}
|
|
(*fields_annotations)->at_put(n, field_annotations);
|
|
}
|
|
if (field_type_annotations != NULL) {
|
|
if (*fields_type_annotations == NULL) {
|
|
*fields_type_annotations = MetadataFactory::new_array<AnnotationArray*>(
|
|
loader_data, length, NULL,
|
|
CHECK_NULL);
|
|
}
|
|
(*fields_type_annotations)->at_put(n, field_type_annotations);
|
|
}
|
|
if (is_synthetic) {
|
|
access_flags.set_is_synthetic();
|
|
}
|
|
if (generic_signature_index != 0) {
|
|
access_flags.set_field_has_generic_signature();
|
|
fa[generic_signature_slot] = generic_signature_index;
|
|
generic_signature_slot ++;
|
|
num_generic_signature ++;
|
|
}
|
|
}
|
|
|
|
FieldInfo* field = FieldInfo::from_field_array(fa, n);
|
|
field->initialize(access_flags.as_short(),
|
|
name_index,
|
|
signature_index,
|
|
constantvalue_index,
|
|
0);
|
|
if (parsed_annotations.has_any_annotations())
|
|
parsed_annotations.apply_to(field);
|
|
|
|
BasicType type = cp->basic_type_for_signature_at(signature_index);
|
|
|
|
// Remember how many oops we encountered and compute allocation type
|
|
FieldAllocationType atype = fac->update(is_static, type);
|
|
|
|
// The correct offset is computed later (all oop fields will be located together)
|
|
// We temporarily store the allocation type in the offset field
|
|
field->set_offset(atype);
|
|
}
|
|
|
|
int index = length;
|
|
if (num_injected != 0) {
|
|
for (int n = 0; n < num_injected; n++) {
|
|
// Check for duplicates
|
|
if (injected[n].may_be_java) {
|
|
Symbol* name = injected[n].name();
|
|
Symbol* signature = injected[n].signature();
|
|
bool duplicate = false;
|
|
for (int i = 0; i < length; i++) {
|
|
FieldInfo* f = FieldInfo::from_field_array(fa, i);
|
|
if (name == cp->symbol_at(f->name_index()) &&
|
|
signature == cp->symbol_at(f->signature_index())) {
|
|
// Symbol is desclared in Java so skip this one
|
|
duplicate = true;
|
|
break;
|
|
}
|
|
}
|
|
if (duplicate) {
|
|
// These will be removed from the field array at the end
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Injected field
|
|
FieldInfo* field = FieldInfo::from_field_array(fa, index);
|
|
field->initialize(JVM_ACC_FIELD_INTERNAL,
|
|
injected[n].name_index,
|
|
injected[n].signature_index,
|
|
0,
|
|
0);
|
|
|
|
BasicType type = FieldType::basic_type(injected[n].signature());
|
|
|
|
// Remember how many oops we encountered and compute allocation type
|
|
FieldAllocationType atype = fac->update(false, type);
|
|
|
|
// The correct offset is computed later (all oop fields will be located together)
|
|
// We temporarily store the allocation type in the offset field
|
|
field->set_offset(atype);
|
|
index++;
|
|
}
|
|
}
|
|
|
|
// Now copy the fields' data from the temporary resource array.
|
|
// Sometimes injected fields already exist in the Java source so
|
|
// the fields array could be too long. In that case the
|
|
// fields array is trimed. Also unused slots that were reserved
|
|
// for generic signature indexes are discarded.
|
|
Array<u2>* fields = MetadataFactory::new_array<u2>(
|
|
loader_data, index * FieldInfo::field_slots + num_generic_signature,
|
|
CHECK_NULL);
|
|
{
|
|
int i = 0;
|
|
for (; i < index * FieldInfo::field_slots; i++) {
|
|
fields->at_put(i, fa[i]);
|
|
}
|
|
for (int j = total_fields * FieldInfo::field_slots;
|
|
j < generic_signature_slot; j++) {
|
|
fields->at_put(i++, fa[j]);
|
|
}
|
|
assert(i == fields->length(), "");
|
|
}
|
|
|
|
if (_need_verify && length > 1) {
|
|
// Check duplicated fields
|
|
ResourceMark rm(THREAD);
|
|
NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, NameSigHash*, HASH_ROW_SIZE);
|
|
initialize_hashtable(names_and_sigs);
|
|
bool dup = false;
|
|
{
|
|
debug_only(No_Safepoint_Verifier nsv;)
|
|
for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) {
|
|
Symbol* name = fs.name();
|
|
Symbol* sig = fs.signature();
|
|
// If no duplicates, add name/signature in hashtable names_and_sigs.
|
|
if (!put_after_lookup(name, sig, names_and_sigs)) {
|
|
dup = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dup) {
|
|
classfile_parse_error("Duplicate field name&signature in class file %s",
|
|
CHECK_NULL);
|
|
}
|
|
}
|
|
|
|
return fields;
|
|
}
|
|
|
|
|
|
static void copy_u2_with_conversion(u2* dest, u2* src, int length) {
|
|
while (length-- > 0) {
|
|
*dest++ = Bytes::get_Java_u2((u1*) (src++));
|
|
}
|
|
}
|
|
|
|
|
|
u2* ClassFileParser::parse_exception_table(ClassLoaderData* loader_data,
|
|
u4 code_length,
|
|
u4 exception_table_length,
|
|
constantPoolHandle cp,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
|
|
u2* exception_table_start = cfs->get_u2_buffer();
|
|
assert(exception_table_start != NULL, "null exception table");
|
|
cfs->guarantee_more(8 * exception_table_length, CHECK_NULL); // start_pc, end_pc, handler_pc, catch_type_index
|
|
// Will check legal target after parsing code array in verifier.
|
|
if (_need_verify) {
|
|
for (unsigned int i = 0; i < exception_table_length; i++) {
|
|
u2 start_pc = cfs->get_u2_fast();
|
|
u2 end_pc = cfs->get_u2_fast();
|
|
u2 handler_pc = cfs->get_u2_fast();
|
|
u2 catch_type_index = cfs->get_u2_fast();
|
|
guarantee_property((start_pc < end_pc) && (end_pc <= code_length),
|
|
"Illegal exception table range in class file %s",
|
|
CHECK_NULL);
|
|
guarantee_property(handler_pc < code_length,
|
|
"Illegal exception table handler in class file %s",
|
|
CHECK_NULL);
|
|
if (catch_type_index != 0) {
|
|
guarantee_property(valid_cp_range(catch_type_index, cp->length()) &&
|
|
is_klass_reference(cp, catch_type_index),
|
|
"Catch type in exception table has bad constant type in class file %s", CHECK_NULL);
|
|
}
|
|
}
|
|
} else {
|
|
cfs->skip_u2_fast(exception_table_length * 4);
|
|
}
|
|
return exception_table_start;
|
|
}
|
|
|
|
void ClassFileParser::parse_linenumber_table(
|
|
u4 code_attribute_length, u4 code_length,
|
|
CompressedLineNumberWriteStream** write_stream, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
unsigned int num_entries = cfs->get_u2(CHECK);
|
|
|
|
// Each entry is a u2 start_pc, and a u2 line_number
|
|
unsigned int length_in_bytes = num_entries * (sizeof(u2) + sizeof(u2));
|
|
|
|
// Verify line number attribute and table length
|
|
check_property(
|
|
code_attribute_length == sizeof(u2) + length_in_bytes,
|
|
"LineNumberTable attribute has wrong length in class file %s", CHECK);
|
|
|
|
cfs->guarantee_more(length_in_bytes, CHECK);
|
|
|
|
if ((*write_stream) == NULL) {
|
|
if (length_in_bytes > fixed_buffer_size) {
|
|
(*write_stream) = new CompressedLineNumberWriteStream(length_in_bytes);
|
|
} else {
|
|
(*write_stream) = new CompressedLineNumberWriteStream(
|
|
linenumbertable_buffer, fixed_buffer_size);
|
|
}
|
|
}
|
|
|
|
while (num_entries-- > 0) {
|
|
u2 bci = cfs->get_u2_fast(); // start_pc
|
|
u2 line = cfs->get_u2_fast(); // line_number
|
|
guarantee_property(bci < code_length,
|
|
"Invalid pc in LineNumberTable in class file %s", CHECK);
|
|
(*write_stream)->write_pair(bci, line);
|
|
}
|
|
}
|
|
|
|
|
|
// Class file LocalVariableTable elements.
|
|
class Classfile_LVT_Element VALUE_OBJ_CLASS_SPEC {
|
|
public:
|
|
u2 start_bci;
|
|
u2 length;
|
|
u2 name_cp_index;
|
|
u2 descriptor_cp_index;
|
|
u2 slot;
|
|
};
|
|
|
|
|
|
class LVT_Hash: public CHeapObj<mtClass> {
|
|
public:
|
|
LocalVariableTableElement *_elem; // element
|
|
LVT_Hash* _next; // Next entry in hash table
|
|
};
|
|
|
|
unsigned int hash(LocalVariableTableElement *elem) {
|
|
unsigned int raw_hash = elem->start_bci;
|
|
|
|
raw_hash = elem->length + raw_hash * 37;
|
|
raw_hash = elem->name_cp_index + raw_hash * 37;
|
|
raw_hash = elem->slot + raw_hash * 37;
|
|
|
|
return raw_hash % HASH_ROW_SIZE;
|
|
}
|
|
|
|
void initialize_hashtable(LVT_Hash** table) {
|
|
for (int i = 0; i < HASH_ROW_SIZE; i++) {
|
|
table[i] = NULL;
|
|
}
|
|
}
|
|
|
|
void clear_hashtable(LVT_Hash** table) {
|
|
for (int i = 0; i < HASH_ROW_SIZE; i++) {
|
|
LVT_Hash* current = table[i];
|
|
LVT_Hash* next;
|
|
while (current != NULL) {
|
|
next = current->_next;
|
|
current->_next = NULL;
|
|
delete(current);
|
|
current = next;
|
|
}
|
|
table[i] = NULL;
|
|
}
|
|
}
|
|
|
|
LVT_Hash* LVT_lookup(LocalVariableTableElement *elem, int index, LVT_Hash** table) {
|
|
LVT_Hash* entry = table[index];
|
|
|
|
/*
|
|
* 3-tuple start_bci/length/slot has to be unique key,
|
|
* so the following comparison seems to be redundant:
|
|
* && elem->name_cp_index == entry->_elem->name_cp_index
|
|
*/
|
|
while (entry != NULL) {
|
|
if (elem->start_bci == entry->_elem->start_bci
|
|
&& elem->length == entry->_elem->length
|
|
&& elem->name_cp_index == entry->_elem->name_cp_index
|
|
&& elem->slot == entry->_elem->slot
|
|
) {
|
|
return entry;
|
|
}
|
|
entry = entry->_next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Return false if the local variable is found in table.
|
|
// Return true if no duplicate is found.
|
|
// And local variable is added as a new entry in table.
|
|
bool LVT_put_after_lookup(LocalVariableTableElement *elem, LVT_Hash** table) {
|
|
// First lookup for duplicates
|
|
int index = hash(elem);
|
|
LVT_Hash* entry = LVT_lookup(elem, index, table);
|
|
|
|
if (entry != NULL) {
|
|
return false;
|
|
}
|
|
// No duplicate is found, allocate a new entry and fill it.
|
|
if ((entry = new LVT_Hash()) == NULL) {
|
|
return false;
|
|
}
|
|
entry->_elem = elem;
|
|
|
|
// Insert into hash table
|
|
entry->_next = table[index];
|
|
table[index] = entry;
|
|
|
|
return true;
|
|
}
|
|
|
|
void copy_lvt_element(Classfile_LVT_Element *src, LocalVariableTableElement *lvt) {
|
|
lvt->start_bci = Bytes::get_Java_u2((u1*) &src->start_bci);
|
|
lvt->length = Bytes::get_Java_u2((u1*) &src->length);
|
|
lvt->name_cp_index = Bytes::get_Java_u2((u1*) &src->name_cp_index);
|
|
lvt->descriptor_cp_index = Bytes::get_Java_u2((u1*) &src->descriptor_cp_index);
|
|
lvt->signature_cp_index = 0;
|
|
lvt->slot = Bytes::get_Java_u2((u1*) &src->slot);
|
|
}
|
|
|
|
// Function is used to parse both attributes:
|
|
// LocalVariableTable (LVT) and LocalVariableTypeTable (LVTT)
|
|
u2* ClassFileParser::parse_localvariable_table(u4 code_length,
|
|
u2 max_locals,
|
|
u4 code_attribute_length,
|
|
constantPoolHandle cp,
|
|
u2* localvariable_table_length,
|
|
bool isLVTT,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
const char * tbl_name = (isLVTT) ? "LocalVariableTypeTable" : "LocalVariableTable";
|
|
*localvariable_table_length = cfs->get_u2(CHECK_NULL);
|
|
unsigned int size = (*localvariable_table_length) * sizeof(Classfile_LVT_Element) / sizeof(u2);
|
|
// Verify local variable table attribute has right length
|
|
if (_need_verify) {
|
|
guarantee_property(code_attribute_length == (sizeof(*localvariable_table_length) + size * sizeof(u2)),
|
|
"%s has wrong length in class file %s", tbl_name, CHECK_NULL);
|
|
}
|
|
u2* localvariable_table_start = cfs->get_u2_buffer();
|
|
assert(localvariable_table_start != NULL, "null local variable table");
|
|
if (!_need_verify) {
|
|
cfs->skip_u2_fast(size);
|
|
} else {
|
|
cfs->guarantee_more(size * 2, CHECK_NULL);
|
|
for(int i = 0; i < (*localvariable_table_length); i++) {
|
|
u2 start_pc = cfs->get_u2_fast();
|
|
u2 length = cfs->get_u2_fast();
|
|
u2 name_index = cfs->get_u2_fast();
|
|
u2 descriptor_index = cfs->get_u2_fast();
|
|
u2 index = cfs->get_u2_fast();
|
|
// Assign to a u4 to avoid overflow
|
|
u4 end_pc = (u4)start_pc + (u4)length;
|
|
|
|
if (start_pc >= code_length) {
|
|
classfile_parse_error(
|
|
"Invalid start_pc %u in %s in class file %s",
|
|
start_pc, tbl_name, CHECK_NULL);
|
|
}
|
|
if (end_pc > code_length) {
|
|
classfile_parse_error(
|
|
"Invalid length %u in %s in class file %s",
|
|
length, tbl_name, CHECK_NULL);
|
|
}
|
|
int cp_size = cp->length();
|
|
guarantee_property(
|
|
valid_cp_range(name_index, cp_size) &&
|
|
cp->tag_at(name_index).is_utf8(),
|
|
"Name index %u in %s has bad constant type in class file %s",
|
|
name_index, tbl_name, CHECK_NULL);
|
|
guarantee_property(
|
|
valid_cp_range(descriptor_index, cp_size) &&
|
|
cp->tag_at(descriptor_index).is_utf8(),
|
|
"Signature index %u in %s has bad constant type in class file %s",
|
|
descriptor_index, tbl_name, CHECK_NULL);
|
|
|
|
Symbol* name = cp->symbol_at(name_index);
|
|
Symbol* sig = cp->symbol_at(descriptor_index);
|
|
verify_legal_field_name(name, CHECK_NULL);
|
|
u2 extra_slot = 0;
|
|
if (!isLVTT) {
|
|
verify_legal_field_signature(name, sig, CHECK_NULL);
|
|
|
|
// 4894874: check special cases for double and long local variables
|
|
if (sig == vmSymbols::type_signature(T_DOUBLE) ||
|
|
sig == vmSymbols::type_signature(T_LONG)) {
|
|
extra_slot = 1;
|
|
}
|
|
}
|
|
guarantee_property((index + extra_slot) < max_locals,
|
|
"Invalid index %u in %s in class file %s",
|
|
index, tbl_name, CHECK_NULL);
|
|
}
|
|
}
|
|
return localvariable_table_start;
|
|
}
|
|
|
|
|
|
void ClassFileParser::parse_type_array(u2 array_length, u4 code_length, u4* u1_index, u4* u2_index,
|
|
u1* u1_array, u2* u2_array, constantPoolHandle cp, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
u2 index = 0; // index in the array with long/double occupying two slots
|
|
u4 i1 = *u1_index;
|
|
u4 i2 = *u2_index + 1;
|
|
for(int i = 0; i < array_length; i++) {
|
|
u1 tag = u1_array[i1++] = cfs->get_u1(CHECK);
|
|
index++;
|
|
if (tag == ITEM_Long || tag == ITEM_Double) {
|
|
index++;
|
|
} else if (tag == ITEM_Object) {
|
|
u2 class_index = u2_array[i2++] = cfs->get_u2(CHECK);
|
|
guarantee_property(valid_cp_range(class_index, cp->length()) &&
|
|
is_klass_reference(cp, class_index),
|
|
"Bad class index %u in StackMap in class file %s",
|
|
class_index, CHECK);
|
|
} else if (tag == ITEM_Uninitialized) {
|
|
u2 offset = u2_array[i2++] = cfs->get_u2(CHECK);
|
|
guarantee_property(
|
|
offset < code_length,
|
|
"Bad uninitialized type offset %u in StackMap in class file %s",
|
|
offset, CHECK);
|
|
} else {
|
|
guarantee_property(
|
|
tag <= (u1)ITEM_Uninitialized,
|
|
"Unknown variable type %u in StackMap in class file %s",
|
|
tag, CHECK);
|
|
}
|
|
}
|
|
u2_array[*u2_index] = index;
|
|
*u1_index = i1;
|
|
*u2_index = i2;
|
|
}
|
|
|
|
Array<u1>* ClassFileParser::parse_stackmap_table(
|
|
ClassLoaderData* loader_data,
|
|
u4 code_attribute_length, TRAPS) {
|
|
if (code_attribute_length == 0)
|
|
return NULL;
|
|
|
|
ClassFileStream* cfs = stream();
|
|
u1* stackmap_table_start = cfs->get_u1_buffer();
|
|
assert(stackmap_table_start != NULL, "null stackmap table");
|
|
|
|
// check code_attribute_length first
|
|
stream()->skip_u1(code_attribute_length, CHECK_NULL);
|
|
|
|
if (!_need_verify && !DumpSharedSpaces) {
|
|
return NULL;
|
|
}
|
|
|
|
Array<u1>* stackmap_data =
|
|
MetadataFactory::new_array<u1>(loader_data, code_attribute_length, 0, CHECK_NULL);
|
|
|
|
memcpy((void*)stackmap_data->adr_at(0),
|
|
(void*)stackmap_table_start, code_attribute_length);
|
|
return stackmap_data;
|
|
}
|
|
|
|
u2* ClassFileParser::parse_checked_exceptions(u2* checked_exceptions_length,
|
|
u4 method_attribute_length,
|
|
constantPoolHandle cp, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
cfs->guarantee_more(2, CHECK_NULL); // checked_exceptions_length
|
|
*checked_exceptions_length = cfs->get_u2_fast();
|
|
unsigned int size = (*checked_exceptions_length) * sizeof(CheckedExceptionElement) / sizeof(u2);
|
|
u2* checked_exceptions_start = cfs->get_u2_buffer();
|
|
assert(checked_exceptions_start != NULL, "null checked exceptions");
|
|
if (!_need_verify) {
|
|
cfs->skip_u2_fast(size);
|
|
} else {
|
|
// Verify each value in the checked exception table
|
|
u2 checked_exception;
|
|
u2 len = *checked_exceptions_length;
|
|
cfs->guarantee_more(2 * len, CHECK_NULL);
|
|
for (int i = 0; i < len; i++) {
|
|
checked_exception = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(checked_exception, cp->length()) &&
|
|
is_klass_reference(cp, checked_exception),
|
|
"Exception name has bad type at constant pool %u in class file %s",
|
|
checked_exception, CHECK_NULL);
|
|
}
|
|
}
|
|
// check exceptions attribute length
|
|
if (_need_verify) {
|
|
guarantee_property(method_attribute_length == (sizeof(*checked_exceptions_length) +
|
|
sizeof(u2) * size),
|
|
"Exceptions attribute has wrong length in class file %s", CHECK_NULL);
|
|
}
|
|
return checked_exceptions_start;
|
|
}
|
|
|
|
void ClassFileParser::throwIllegalSignature(
|
|
const char* type, Symbol* name, Symbol* sig, TRAPS) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"%s \"%s\" in class %s has illegal signature \"%s\"", type,
|
|
name->as_C_string(), _class_name->as_C_string(), sig->as_C_string());
|
|
}
|
|
|
|
// Skip an annotation. Return >=limit if there is any problem.
|
|
int ClassFileParser::skip_annotation(u1* buffer, int limit, int index) {
|
|
// annotation := atype:u2 do(nmem:u2) {member:u2 value}
|
|
// value := switch (tag:u1) { ... }
|
|
index += 2; // skip atype
|
|
if ((index += 2) >= limit) return limit; // read nmem
|
|
int nmem = Bytes::get_Java_u2(buffer+index-2);
|
|
while (--nmem >= 0 && index < limit) {
|
|
index += 2; // skip member
|
|
index = skip_annotation_value(buffer, limit, index);
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Skip an annotation value. Return >=limit if there is any problem.
|
|
int ClassFileParser::skip_annotation_value(u1* buffer, int limit, int index) {
|
|
// value := switch (tag:u1) {
|
|
// case B, C, I, S, Z, D, F, J, c: con:u2;
|
|
// case e: e_class:u2 e_name:u2;
|
|
// case s: s_con:u2;
|
|
// case [: do(nval:u2) {value};
|
|
// case @: annotation;
|
|
// case s: s_con:u2;
|
|
// }
|
|
if ((index += 1) >= limit) return limit; // read tag
|
|
u1 tag = buffer[index-1];
|
|
switch (tag) {
|
|
case 'B': case 'C': case 'I': case 'S': case 'Z':
|
|
case 'D': case 'F': case 'J': case 'c': case 's':
|
|
index += 2; // skip con or s_con
|
|
break;
|
|
case 'e':
|
|
index += 4; // skip e_class, e_name
|
|
break;
|
|
case '[':
|
|
{
|
|
if ((index += 2) >= limit) return limit; // read nval
|
|
int nval = Bytes::get_Java_u2(buffer+index-2);
|
|
while (--nval >= 0 && index < limit) {
|
|
index = skip_annotation_value(buffer, limit, index);
|
|
}
|
|
}
|
|
break;
|
|
case '@':
|
|
index = skip_annotation(buffer, limit, index);
|
|
break;
|
|
default:
|
|
assert(false, "annotation tag");
|
|
return limit; // bad tag byte
|
|
}
|
|
return index;
|
|
}
|
|
|
|
// Sift through annotations, looking for those significant to the VM:
|
|
void ClassFileParser::parse_annotations(u1* buffer, int limit,
|
|
constantPoolHandle cp,
|
|
ClassFileParser::AnnotationCollector* coll,
|
|
TRAPS) {
|
|
// annotations := do(nann:u2) {annotation}
|
|
int index = 0;
|
|
if ((index += 2) >= limit) return; // read nann
|
|
int nann = Bytes::get_Java_u2(buffer+index-2);
|
|
enum { // initial annotation layout
|
|
atype_off = 0, // utf8 such as 'Ljava/lang/annotation/Retention;'
|
|
count_off = 2, // u2 such as 1 (one value)
|
|
member_off = 4, // utf8 such as 'value'
|
|
tag_off = 6, // u1 such as 'c' (type) or 'e' (enum)
|
|
e_tag_val = 'e',
|
|
e_type_off = 7, // utf8 such as 'Ljava/lang/annotation/RetentionPolicy;'
|
|
e_con_off = 9, // utf8 payload, such as 'SOURCE', 'CLASS', 'RUNTIME'
|
|
e_size = 11, // end of 'e' annotation
|
|
c_tag_val = 'c',
|
|
c_con_off = 7, // utf8 payload, such as 'I' or 'Ljava/lang/String;'
|
|
c_size = 9, // end of 'c' annotation
|
|
min_size = 6 // smallest possible size (zero members)
|
|
};
|
|
while ((--nann) >= 0 && (index-2 + min_size <= limit)) {
|
|
int index0 = index;
|
|
index = skip_annotation(buffer, limit, index);
|
|
u1* abase = buffer + index0;
|
|
int atype = Bytes::get_Java_u2(abase + atype_off);
|
|
int count = Bytes::get_Java_u2(abase + count_off);
|
|
Symbol* aname = check_symbol_at(cp, atype);
|
|
if (aname == NULL) break; // invalid annotation name
|
|
Symbol* member = NULL;
|
|
if (count >= 1) {
|
|
int member_index = Bytes::get_Java_u2(abase + member_off);
|
|
member = check_symbol_at(cp, member_index);
|
|
if (member == NULL) break; // invalid member name
|
|
}
|
|
|
|
// Here is where parsing particular annotations will take place.
|
|
AnnotationCollector::ID id = coll->annotation_index(aname);
|
|
if (id == AnnotationCollector::_unknown) continue;
|
|
coll->set_annotation(id);
|
|
// If there are no values, just set the bit and move on:
|
|
if (count == 0) continue;
|
|
|
|
// For the record, here is how annotation payloads can be collected.
|
|
// Suppose we want to capture @Retention.value. Here is how:
|
|
//if (id == AnnotationCollector::_class_Retention) {
|
|
// Symbol* payload = NULL;
|
|
// if (count == 1
|
|
// && e_size == (index0 - index) // match size
|
|
// && e_tag_val == *(abase + tag_off)
|
|
// && (check_symbol_at(cp, Bytes::get_Java_u2(abase + e_type_off))
|
|
// == vmSymbols::RetentionPolicy_signature())
|
|
// && member == vmSymbols::value_name()) {
|
|
// payload = check_symbol_at(cp, Bytes::get_Java_u2(abase + e_con_off));
|
|
// }
|
|
// check_property(payload != NULL,
|
|
// "Invalid @Retention annotation at offset %u in class file %s",
|
|
// index0, CHECK);
|
|
// if (payload != NULL) {
|
|
// payload->increment_refcount();
|
|
// coll->_class_RetentionPolicy = payload;
|
|
// }
|
|
//}
|
|
}
|
|
}
|
|
|
|
ClassFileParser::AnnotationCollector::ID ClassFileParser::AnnotationCollector::annotation_index(Symbol* name) {
|
|
vmSymbols::SID sid = vmSymbols::find_sid(name);
|
|
switch (sid) {
|
|
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_ForceInline_signature):
|
|
if (_location != _in_method) break; // only allow for methods
|
|
return _method_ForceInline;
|
|
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_DontInline_signature):
|
|
if (_location != _in_method) break; // only allow for methods
|
|
return _method_DontInline;
|
|
case vmSymbols::VM_SYMBOL_ENUM_NAME(java_lang_invoke_LambdaForm_Compiled_signature):
|
|
if (_location != _in_method) break; // only allow for methods
|
|
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
|
|
return _method_LambdaForm_Hidden;
|
|
default: break;
|
|
}
|
|
return AnnotationCollector::_unknown;
|
|
}
|
|
|
|
void ClassFileParser::FieldAnnotationCollector::apply_to(FieldInfo* f) {
|
|
fatal("no field annotations yet");
|
|
}
|
|
|
|
void ClassFileParser::MethodAnnotationCollector::apply_to(methodHandle m) {
|
|
if (has_annotation(_method_ForceInline))
|
|
m->set_force_inline(true);
|
|
if (has_annotation(_method_DontInline))
|
|
m->set_dont_inline(true);
|
|
if (has_annotation(_method_LambdaForm_Compiled) && m->intrinsic_id() == vmIntrinsics::_none)
|
|
m->set_intrinsic_id(vmIntrinsics::_compiledLambdaForm);
|
|
if (has_annotation(_method_LambdaForm_Hidden))
|
|
m->set_hidden(true);
|
|
}
|
|
|
|
void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) {
|
|
fatal("no class annotations yet");
|
|
}
|
|
|
|
|
|
#define MAX_ARGS_SIZE 255
|
|
#define MAX_CODE_SIZE 65535
|
|
#define INITIAL_MAX_LVT_NUMBER 256
|
|
|
|
// Note: the parse_method below is big and clunky because all parsing of the code and exceptions
|
|
// attribute is inlined. This is cumbersome to avoid since we inline most of the parts in the
|
|
// Method* to save footprint, so we only know the size of the resulting Method* when the
|
|
// entire method attribute is parsed.
|
|
//
|
|
// The promoted_flags parameter is used to pass relevant access_flags
|
|
// from the method back up to the containing klass. These flag values
|
|
// are added to klass's access_flags.
|
|
|
|
methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data,
|
|
constantPoolHandle cp,
|
|
bool is_interface,
|
|
AccessFlags *promoted_flags,
|
|
AnnotationArray** method_annotations,
|
|
AnnotationArray** method_parameter_annotations,
|
|
AnnotationArray** method_default_annotations,
|
|
AnnotationArray** method_type_annotations,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
methodHandle nullHandle;
|
|
ResourceMark rm(THREAD);
|
|
// Parse fixed parts
|
|
cfs->guarantee_more(8, CHECK_(nullHandle)); // access_flags, name_index, descriptor_index, attributes_count
|
|
|
|
int flags = cfs->get_u2_fast();
|
|
u2 name_index = cfs->get_u2_fast();
|
|
int cp_size = cp->length();
|
|
check_property(
|
|
valid_cp_range(name_index, cp_size) &&
|
|
cp->tag_at(name_index).is_utf8(),
|
|
"Illegal constant pool index %u for method name in class file %s",
|
|
name_index, CHECK_(nullHandle));
|
|
Symbol* name = cp->symbol_at(name_index);
|
|
verify_legal_method_name(name, CHECK_(nullHandle));
|
|
|
|
u2 signature_index = cfs->get_u2_fast();
|
|
guarantee_property(
|
|
valid_cp_range(signature_index, cp_size) &&
|
|
cp->tag_at(signature_index).is_utf8(),
|
|
"Illegal constant pool index %u for method signature in class file %s",
|
|
signature_index, CHECK_(nullHandle));
|
|
Symbol* signature = cp->symbol_at(signature_index);
|
|
|
|
AccessFlags access_flags;
|
|
if (name == vmSymbols::class_initializer_name()) {
|
|
// We ignore the other access flags for a valid class initializer.
|
|
// (JVM Spec 2nd ed., chapter 4.6)
|
|
if (_major_version < 51) { // backward compatibility
|
|
flags = JVM_ACC_STATIC;
|
|
} else if ((flags & JVM_ACC_STATIC) == JVM_ACC_STATIC) {
|
|
flags &= JVM_ACC_STATIC | JVM_ACC_STRICT;
|
|
}
|
|
} else {
|
|
verify_legal_method_modifiers(flags, is_interface, name, CHECK_(nullHandle));
|
|
}
|
|
|
|
int args_size = -1; // only used when _need_verify is true
|
|
if (_need_verify) {
|
|
args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +
|
|
verify_legal_method_signature(name, signature, CHECK_(nullHandle));
|
|
if (args_size > MAX_ARGS_SIZE) {
|
|
classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle));
|
|
}
|
|
}
|
|
|
|
access_flags.set_flags(flags & JVM_RECOGNIZED_METHOD_MODIFIERS);
|
|
|
|
// Default values for code and exceptions attribute elements
|
|
u2 max_stack = 0;
|
|
u2 max_locals = 0;
|
|
u4 code_length = 0;
|
|
u1* code_start = 0;
|
|
u2 exception_table_length = 0;
|
|
u2* exception_table_start = NULL;
|
|
Array<int>* exception_handlers = Universe::the_empty_int_array();
|
|
u2 checked_exceptions_length = 0;
|
|
u2* checked_exceptions_start = NULL;
|
|
CompressedLineNumberWriteStream* linenumber_table = NULL;
|
|
int linenumber_table_length = 0;
|
|
int total_lvt_length = 0;
|
|
u2 lvt_cnt = 0;
|
|
u2 lvtt_cnt = 0;
|
|
bool lvt_allocated = false;
|
|
u2 max_lvt_cnt = INITIAL_MAX_LVT_NUMBER;
|
|
u2 max_lvtt_cnt = INITIAL_MAX_LVT_NUMBER;
|
|
u2* localvariable_table_length;
|
|
u2** localvariable_table_start;
|
|
u2* localvariable_type_table_length;
|
|
u2** localvariable_type_table_start;
|
|
u2 method_parameters_length = 0;
|
|
u1* method_parameters_data = NULL;
|
|
bool parsed_code_attribute = false;
|
|
bool parsed_checked_exceptions_attribute = false;
|
|
bool parsed_stackmap_attribute = false;
|
|
// stackmap attribute - JDK1.5
|
|
Array<u1>* stackmap_data = NULL;
|
|
u2 generic_signature_index = 0;
|
|
MethodAnnotationCollector parsed_annotations;
|
|
u1* runtime_visible_annotations = NULL;
|
|
int runtime_visible_annotations_length = 0;
|
|
u1* runtime_invisible_annotations = NULL;
|
|
int runtime_invisible_annotations_length = 0;
|
|
u1* runtime_visible_parameter_annotations = NULL;
|
|
int runtime_visible_parameter_annotations_length = 0;
|
|
u1* runtime_invisible_parameter_annotations = NULL;
|
|
int runtime_invisible_parameter_annotations_length = 0;
|
|
u1* runtime_visible_type_annotations = NULL;
|
|
int runtime_visible_type_annotations_length = 0;
|
|
u1* runtime_invisible_type_annotations = NULL;
|
|
int runtime_invisible_type_annotations_length = 0;
|
|
u1* annotation_default = NULL;
|
|
int annotation_default_length = 0;
|
|
|
|
// Parse code and exceptions attribute
|
|
u2 method_attributes_count = cfs->get_u2_fast();
|
|
while (method_attributes_count--) {
|
|
cfs->guarantee_more(6, CHECK_(nullHandle)); // method_attribute_name_index, method_attribute_length
|
|
u2 method_attribute_name_index = cfs->get_u2_fast();
|
|
u4 method_attribute_length = cfs->get_u4_fast();
|
|
check_property(
|
|
valid_cp_range(method_attribute_name_index, cp_size) &&
|
|
cp->tag_at(method_attribute_name_index).is_utf8(),
|
|
"Invalid method attribute name index %u in class file %s",
|
|
method_attribute_name_index, CHECK_(nullHandle));
|
|
|
|
Symbol* method_attribute_name = cp->symbol_at(method_attribute_name_index);
|
|
if (method_attribute_name == vmSymbols::tag_code()) {
|
|
// Parse Code attribute
|
|
if (_need_verify) {
|
|
guarantee_property(
|
|
!access_flags.is_native() && !access_flags.is_abstract(),
|
|
"Code attribute in native or abstract methods in class file %s",
|
|
CHECK_(nullHandle));
|
|
}
|
|
if (parsed_code_attribute) {
|
|
classfile_parse_error("Multiple Code attributes in class file %s", CHECK_(nullHandle));
|
|
}
|
|
parsed_code_attribute = true;
|
|
|
|
// Stack size, locals size, and code size
|
|
if (_major_version == 45 && _minor_version <= 2) {
|
|
cfs->guarantee_more(4, CHECK_(nullHandle));
|
|
max_stack = cfs->get_u1_fast();
|
|
max_locals = cfs->get_u1_fast();
|
|
code_length = cfs->get_u2_fast();
|
|
} else {
|
|
cfs->guarantee_more(8, CHECK_(nullHandle));
|
|
max_stack = cfs->get_u2_fast();
|
|
max_locals = cfs->get_u2_fast();
|
|
code_length = cfs->get_u4_fast();
|
|
}
|
|
if (_need_verify) {
|
|
guarantee_property(args_size <= max_locals,
|
|
"Arguments can't fit into locals in class file %s", CHECK_(nullHandle));
|
|
guarantee_property(code_length > 0 && code_length <= MAX_CODE_SIZE,
|
|
"Invalid method Code length %u in class file %s",
|
|
code_length, CHECK_(nullHandle));
|
|
}
|
|
// Code pointer
|
|
code_start = cfs->get_u1_buffer();
|
|
assert(code_start != NULL, "null code start");
|
|
cfs->guarantee_more(code_length, CHECK_(nullHandle));
|
|
cfs->skip_u1_fast(code_length);
|
|
|
|
// Exception handler table
|
|
cfs->guarantee_more(2, CHECK_(nullHandle)); // exception_table_length
|
|
exception_table_length = cfs->get_u2_fast();
|
|
if (exception_table_length > 0) {
|
|
exception_table_start =
|
|
parse_exception_table(loader_data, code_length, exception_table_length, cp, CHECK_(nullHandle));
|
|
}
|
|
|
|
// Parse additional attributes in code attribute
|
|
cfs->guarantee_more(2, CHECK_(nullHandle)); // code_attributes_count
|
|
u2 code_attributes_count = cfs->get_u2_fast();
|
|
|
|
unsigned int calculated_attribute_length = 0;
|
|
|
|
if (_major_version > 45 || (_major_version == 45 && _minor_version > 2)) {
|
|
calculated_attribute_length =
|
|
sizeof(max_stack) + sizeof(max_locals) + sizeof(code_length);
|
|
} else {
|
|
// max_stack, locals and length are smaller in pre-version 45.2 classes
|
|
calculated_attribute_length = sizeof(u1) + sizeof(u1) + sizeof(u2);
|
|
}
|
|
calculated_attribute_length +=
|
|
code_length +
|
|
sizeof(exception_table_length) +
|
|
sizeof(code_attributes_count) +
|
|
exception_table_length *
|
|
( sizeof(u2) + // start_pc
|
|
sizeof(u2) + // end_pc
|
|
sizeof(u2) + // handler_pc
|
|
sizeof(u2) ); // catch_type_index
|
|
|
|
while (code_attributes_count--) {
|
|
cfs->guarantee_more(6, CHECK_(nullHandle)); // code_attribute_name_index, code_attribute_length
|
|
u2 code_attribute_name_index = cfs->get_u2_fast();
|
|
u4 code_attribute_length = cfs->get_u4_fast();
|
|
calculated_attribute_length += code_attribute_length +
|
|
sizeof(code_attribute_name_index) +
|
|
sizeof(code_attribute_length);
|
|
check_property(valid_cp_range(code_attribute_name_index, cp_size) &&
|
|
cp->tag_at(code_attribute_name_index).is_utf8(),
|
|
"Invalid code attribute name index %u in class file %s",
|
|
code_attribute_name_index,
|
|
CHECK_(nullHandle));
|
|
if (LoadLineNumberTables &&
|
|
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_line_number_table()) {
|
|
// Parse and compress line number table
|
|
parse_linenumber_table(code_attribute_length, code_length,
|
|
&linenumber_table, CHECK_(nullHandle));
|
|
|
|
} else if (LoadLocalVariableTables &&
|
|
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_table()) {
|
|
// Parse local variable table
|
|
if (!lvt_allocated) {
|
|
localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2*, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2*, INITIAL_MAX_LVT_NUMBER);
|
|
lvt_allocated = true;
|
|
}
|
|
if (lvt_cnt == max_lvt_cnt) {
|
|
max_lvt_cnt <<= 1;
|
|
REALLOC_RESOURCE_ARRAY(u2, localvariable_table_length, lvt_cnt, max_lvt_cnt);
|
|
REALLOC_RESOURCE_ARRAY(u2*, localvariable_table_start, lvt_cnt, max_lvt_cnt);
|
|
}
|
|
localvariable_table_start[lvt_cnt] =
|
|
parse_localvariable_table(code_length,
|
|
max_locals,
|
|
code_attribute_length,
|
|
cp,
|
|
&localvariable_table_length[lvt_cnt],
|
|
false, // is not LVTT
|
|
CHECK_(nullHandle));
|
|
total_lvt_length += localvariable_table_length[lvt_cnt];
|
|
lvt_cnt++;
|
|
} else if (LoadLocalVariableTypeTables &&
|
|
_major_version >= JAVA_1_5_VERSION &&
|
|
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_local_variable_type_table()) {
|
|
if (!lvt_allocated) {
|
|
localvariable_table_length = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_table_start = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2*, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_type_table_length = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2, INITIAL_MAX_LVT_NUMBER);
|
|
localvariable_type_table_start = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, u2*, INITIAL_MAX_LVT_NUMBER);
|
|
lvt_allocated = true;
|
|
}
|
|
// Parse local variable type table
|
|
if (lvtt_cnt == max_lvtt_cnt) {
|
|
max_lvtt_cnt <<= 1;
|
|
REALLOC_RESOURCE_ARRAY(u2, localvariable_type_table_length, lvtt_cnt, max_lvtt_cnt);
|
|
REALLOC_RESOURCE_ARRAY(u2*, localvariable_type_table_start, lvtt_cnt, max_lvtt_cnt);
|
|
}
|
|
localvariable_type_table_start[lvtt_cnt] =
|
|
parse_localvariable_table(code_length,
|
|
max_locals,
|
|
code_attribute_length,
|
|
cp,
|
|
&localvariable_type_table_length[lvtt_cnt],
|
|
true, // is LVTT
|
|
CHECK_(nullHandle));
|
|
lvtt_cnt++;
|
|
} else if (UseSplitVerifier &&
|
|
_major_version >= Verifier::STACKMAP_ATTRIBUTE_MAJOR_VERSION &&
|
|
cp->symbol_at(code_attribute_name_index) == vmSymbols::tag_stack_map_table()) {
|
|
// Stack map is only needed by the new verifier in JDK1.5.
|
|
if (parsed_stackmap_attribute) {
|
|
classfile_parse_error("Multiple StackMapTable attributes in class file %s", CHECK_(nullHandle));
|
|
}
|
|
stackmap_data = parse_stackmap_table(loader_data, code_attribute_length, CHECK_(nullHandle));
|
|
parsed_stackmap_attribute = true;
|
|
} else {
|
|
// Skip unknown attributes
|
|
cfs->skip_u1(code_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
// check method attribute length
|
|
if (_need_verify) {
|
|
guarantee_property(method_attribute_length == calculated_attribute_length,
|
|
"Code segment has wrong length in class file %s", CHECK_(nullHandle));
|
|
}
|
|
} else if (method_attribute_name == vmSymbols::tag_exceptions()) {
|
|
// Parse Exceptions attribute
|
|
if (parsed_checked_exceptions_attribute) {
|
|
classfile_parse_error("Multiple Exceptions attributes in class file %s", CHECK_(nullHandle));
|
|
}
|
|
parsed_checked_exceptions_attribute = true;
|
|
checked_exceptions_start =
|
|
parse_checked_exceptions(&checked_exceptions_length,
|
|
method_attribute_length,
|
|
cp, CHECK_(nullHandle));
|
|
} else if (method_attribute_name == vmSymbols::tag_method_parameters()) {
|
|
method_parameters_length = cfs->get_u1_fast();
|
|
// Track the actual size (note: this is written for clarity; a
|
|
// decent compiler will CSE and constant-fold this into a single
|
|
// expression)
|
|
u2 actual_size = 1;
|
|
method_parameters_data = cfs->get_u1_buffer();
|
|
actual_size += 2 * method_parameters_length;
|
|
cfs->skip_u2_fast(method_parameters_length);
|
|
actual_size += 4 * method_parameters_length;
|
|
cfs->skip_u4_fast(method_parameters_length);
|
|
// Enforce attribute length
|
|
if (method_attribute_length != actual_size) {
|
|
classfile_parse_error(
|
|
"Invalid MethodParameters method attribute length %u in class file %s",
|
|
method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
// ignore this attribute if it cannot be reflected
|
|
if (!SystemDictionary::Parameter_klass_loaded())
|
|
method_parameters_length = 0;
|
|
} else if (method_attribute_name == vmSymbols::tag_synthetic()) {
|
|
if (method_attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Synthetic method attribute length %u in class file %s",
|
|
method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
// Should we check that there hasn't already been a synthetic attribute?
|
|
access_flags.set_is_synthetic();
|
|
} else if (method_attribute_name == vmSymbols::tag_deprecated()) { // 4276120
|
|
if (method_attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Deprecated method attribute length %u in class file %s",
|
|
method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
} else if (_major_version >= JAVA_1_5_VERSION) {
|
|
if (method_attribute_name == vmSymbols::tag_signature()) {
|
|
if (method_attribute_length != 2) {
|
|
classfile_parse_error(
|
|
"Invalid Signature attribute length %u in class file %s",
|
|
method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
cfs->guarantee_more(2, CHECK_(nullHandle)); // generic_signature_index
|
|
generic_signature_index = cfs->get_u2_fast();
|
|
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_annotations()) {
|
|
runtime_visible_annotations_length = method_attribute_length;
|
|
runtime_visible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_annotations != NULL, "null visible annotations");
|
|
parse_annotations(runtime_visible_annotations,
|
|
runtime_visible_annotations_length, cp, &parsed_annotations,
|
|
CHECK_(nullHandle));
|
|
cfs->skip_u1(runtime_visible_annotations_length, CHECK_(nullHandle));
|
|
} else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_annotations()) {
|
|
runtime_invisible_annotations_length = method_attribute_length;
|
|
runtime_invisible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_annotations != NULL, "null invisible annotations");
|
|
cfs->skip_u1(runtime_invisible_annotations_length, CHECK_(nullHandle));
|
|
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_parameter_annotations()) {
|
|
runtime_visible_parameter_annotations_length = method_attribute_length;
|
|
runtime_visible_parameter_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_parameter_annotations != NULL, "null visible parameter annotations");
|
|
cfs->skip_u1(runtime_visible_parameter_annotations_length, CHECK_(nullHandle));
|
|
} else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_parameter_annotations()) {
|
|
runtime_invisible_parameter_annotations_length = method_attribute_length;
|
|
runtime_invisible_parameter_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_parameter_annotations != NULL, "null invisible parameter annotations");
|
|
cfs->skip_u1(runtime_invisible_parameter_annotations_length, CHECK_(nullHandle));
|
|
} else if (method_attribute_name == vmSymbols::tag_annotation_default()) {
|
|
annotation_default_length = method_attribute_length;
|
|
annotation_default = cfs->get_u1_buffer();
|
|
assert(annotation_default != NULL, "null annotation default");
|
|
cfs->skip_u1(annotation_default_length, CHECK_(nullHandle));
|
|
} else if (method_attribute_name == vmSymbols::tag_runtime_visible_type_annotations()) {
|
|
runtime_visible_type_annotations_length = method_attribute_length;
|
|
runtime_visible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_type_annotations != NULL, "null visible type annotations");
|
|
// No need for the VM to parse Type annotations
|
|
cfs->skip_u1(runtime_visible_type_annotations_length, CHECK_(nullHandle));
|
|
} else if (PreserveAllAnnotations && method_attribute_name == vmSymbols::tag_runtime_invisible_type_annotations()) {
|
|
runtime_invisible_type_annotations_length = method_attribute_length;
|
|
runtime_invisible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations");
|
|
cfs->skip_u1(runtime_invisible_type_annotations_length, CHECK_(nullHandle));
|
|
} else {
|
|
// Skip unknown attributes
|
|
cfs->skip_u1(method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
} else {
|
|
// Skip unknown attributes
|
|
cfs->skip_u1(method_attribute_length, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
|
|
if (linenumber_table != NULL) {
|
|
linenumber_table->write_terminator();
|
|
linenumber_table_length = linenumber_table->position();
|
|
}
|
|
|
|
// Make sure there's at least one Code attribute in non-native/non-abstract method
|
|
if (_need_verify) {
|
|
guarantee_property(access_flags.is_native() || access_flags.is_abstract() || parsed_code_attribute,
|
|
"Absent Code attribute in method that is not native or abstract in class file %s", CHECK_(nullHandle));
|
|
}
|
|
|
|
// All sizing information for a Method* is finally available, now create it
|
|
Method* m = Method::allocate(
|
|
loader_data, code_length, access_flags, linenumber_table_length,
|
|
total_lvt_length, exception_table_length, checked_exceptions_length,
|
|
method_parameters_length, generic_signature_index,
|
|
ConstMethod::NORMAL, CHECK_(nullHandle));
|
|
|
|
ClassLoadingService::add_class_method_size(m->size()*HeapWordSize);
|
|
|
|
// Fill in information from fixed part (access_flags already set)
|
|
m->set_constants(cp());
|
|
m->set_name_index(name_index);
|
|
m->set_signature_index(signature_index);
|
|
#ifdef CC_INTERP
|
|
// hmm is there a gc issue here??
|
|
ResultTypeFinder rtf(cp->symbol_at(signature_index));
|
|
m->set_result_index(rtf.type());
|
|
#endif
|
|
|
|
if (args_size >= 0) {
|
|
m->set_size_of_parameters(args_size);
|
|
} else {
|
|
m->compute_size_of_parameters(THREAD);
|
|
}
|
|
#ifdef ASSERT
|
|
if (args_size >= 0) {
|
|
m->compute_size_of_parameters(THREAD);
|
|
assert(args_size == m->size_of_parameters(), "");
|
|
}
|
|
#endif
|
|
|
|
// Fill in code attribute information
|
|
m->set_max_stack(max_stack);
|
|
m->set_max_locals(max_locals);
|
|
m->constMethod()->set_stackmap_data(stackmap_data);
|
|
|
|
// Copy byte codes
|
|
m->set_code(code_start);
|
|
|
|
// Copy line number table
|
|
if (linenumber_table != NULL) {
|
|
memcpy(m->compressed_linenumber_table(),
|
|
linenumber_table->buffer(), linenumber_table_length);
|
|
}
|
|
|
|
// Copy exception table
|
|
if (exception_table_length > 0) {
|
|
int size =
|
|
exception_table_length * sizeof(ExceptionTableElement) / sizeof(u2);
|
|
copy_u2_with_conversion((u2*) m->exception_table_start(),
|
|
exception_table_start, size);
|
|
}
|
|
|
|
// Copy method parameters
|
|
if (method_parameters_length > 0) {
|
|
MethodParametersElement* elem = m->constMethod()->method_parameters_start();
|
|
for(int i = 0; i < method_parameters_length; i++) {
|
|
elem[i].name_cp_index =
|
|
Bytes::get_Java_u2(method_parameters_data);
|
|
method_parameters_data += 2;
|
|
u4 flags = Bytes::get_Java_u4(method_parameters_data);
|
|
// This caused an alignment fault on Sparc, if flags was a u4
|
|
elem[i].flags_lo = extract_low_short_from_int(flags);
|
|
elem[i].flags_hi = extract_high_short_from_int(flags);
|
|
method_parameters_data += 4;
|
|
}
|
|
}
|
|
|
|
// Copy checked exceptions
|
|
if (checked_exceptions_length > 0) {
|
|
int size = checked_exceptions_length * sizeof(CheckedExceptionElement) / sizeof(u2);
|
|
copy_u2_with_conversion((u2*) m->checked_exceptions_start(), checked_exceptions_start, size);
|
|
}
|
|
|
|
/* Copy class file LVT's/LVTT's into the HotSpot internal LVT.
|
|
*
|
|
* 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.
|
|
*/
|
|
if (total_lvt_length > 0) {
|
|
int tbl_no, idx;
|
|
|
|
promoted_flags->set_has_localvariable_table();
|
|
|
|
LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE);
|
|
initialize_hashtable(lvt_Hash);
|
|
|
|
// To fill LocalVariableTable in
|
|
Classfile_LVT_Element* cf_lvt;
|
|
LocalVariableTableElement* lvt = m->localvariable_table_start();
|
|
|
|
for (tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) {
|
|
cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no];
|
|
for (idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) {
|
|
copy_lvt_element(&cf_lvt[idx], lvt);
|
|
// If no duplicates, add LVT elem in hashtable lvt_Hash.
|
|
if (LVT_put_after_lookup(lvt, lvt_Hash) == false
|
|
&& _need_verify
|
|
&& _major_version >= JAVA_1_5_VERSION ) {
|
|
clear_hashtable(lvt_Hash);
|
|
classfile_parse_error("Duplicated LocalVariableTable attribute "
|
|
"entry for '%s' in class file %s",
|
|
cp->symbol_at(lvt->name_cp_index)->as_utf8(),
|
|
CHECK_(nullHandle));
|
|
}
|
|
}
|
|
}
|
|
|
|
// To merge LocalVariableTable and LocalVariableTypeTable
|
|
Classfile_LVT_Element* cf_lvtt;
|
|
LocalVariableTableElement lvtt_elem;
|
|
|
|
for (tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) {
|
|
cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no];
|
|
for (idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) {
|
|
copy_lvt_element(&cf_lvtt[idx], &lvtt_elem);
|
|
int index = hash(&lvtt_elem);
|
|
LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash);
|
|
if (entry == NULL) {
|
|
if (_need_verify) {
|
|
clear_hashtable(lvt_Hash);
|
|
classfile_parse_error("LVTT entry for '%s' in class file %s "
|
|
"does not match any LVT entry",
|
|
cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(),
|
|
CHECK_(nullHandle));
|
|
}
|
|
} else if (entry->_elem->signature_cp_index != 0 && _need_verify) {
|
|
clear_hashtable(lvt_Hash);
|
|
classfile_parse_error("Duplicated LocalVariableTypeTable attribute "
|
|
"entry for '%s' in class file %s",
|
|
cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(),
|
|
CHECK_(nullHandle));
|
|
} else {
|
|
// to add generic signatures into LocalVariableTable
|
|
entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index;
|
|
}
|
|
}
|
|
}
|
|
clear_hashtable(lvt_Hash);
|
|
}
|
|
|
|
if (parsed_annotations.has_any_annotations())
|
|
parsed_annotations.apply_to(m);
|
|
*method_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_annotations,
|
|
runtime_visible_annotations_length,
|
|
runtime_invisible_annotations,
|
|
runtime_invisible_annotations_length,
|
|
CHECK_(nullHandle));
|
|
*method_parameter_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_parameter_annotations,
|
|
runtime_visible_parameter_annotations_length,
|
|
runtime_invisible_parameter_annotations,
|
|
runtime_invisible_parameter_annotations_length,
|
|
CHECK_(nullHandle));
|
|
*method_default_annotations = assemble_annotations(loader_data,
|
|
annotation_default,
|
|
annotation_default_length,
|
|
NULL,
|
|
0,
|
|
CHECK_(nullHandle));
|
|
*method_type_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_type_annotations,
|
|
runtime_visible_type_annotations_length,
|
|
runtime_invisible_type_annotations,
|
|
runtime_invisible_type_annotations_length,
|
|
CHECK_(nullHandle));
|
|
|
|
if (name == vmSymbols::finalize_method_name() &&
|
|
signature == vmSymbols::void_method_signature()) {
|
|
if (m->is_empty_method()) {
|
|
_has_empty_finalizer = true;
|
|
} else {
|
|
_has_finalizer = true;
|
|
}
|
|
}
|
|
if (name == vmSymbols::object_initializer_name() &&
|
|
signature == vmSymbols::void_method_signature() &&
|
|
m->is_vanilla_constructor()) {
|
|
_has_vanilla_constructor = true;
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
|
|
// The promoted_flags parameter is used to pass relevant access_flags
|
|
// from the methods back up to the containing klass. These flag values
|
|
// are added to klass's access_flags.
|
|
|
|
Array<Method*>* ClassFileParser::parse_methods(ClassLoaderData* loader_data,
|
|
constantPoolHandle cp,
|
|
bool is_interface,
|
|
AccessFlags* promoted_flags,
|
|
bool* has_final_method,
|
|
Array<AnnotationArray*>** methods_annotations,
|
|
Array<AnnotationArray*>** methods_parameter_annotations,
|
|
Array<AnnotationArray*>** methods_default_annotations,
|
|
Array<AnnotationArray*>** methods_type_annotations,
|
|
bool* has_default_methods,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
AnnotationArray* method_annotations = NULL;
|
|
AnnotationArray* method_parameter_annotations = NULL;
|
|
AnnotationArray* method_default_annotations = NULL;
|
|
AnnotationArray* method_type_annotations = NULL;
|
|
cfs->guarantee_more(2, CHECK_NULL); // length
|
|
u2 length = cfs->get_u2_fast();
|
|
if (length == 0) {
|
|
return Universe::the_empty_method_array();
|
|
} else {
|
|
// FIXME: Handle leaks at later failures.
|
|
Array<Method*>* methods = MetadataFactory::new_array<Method*>(loader_data, length, NULL, CHECK_NULL);
|
|
|
|
HandleMark hm(THREAD);
|
|
for (int index = 0; index < length; index++) {
|
|
methodHandle method = parse_method(loader_data,
|
|
cp, is_interface,
|
|
promoted_flags,
|
|
&method_annotations,
|
|
&method_parameter_annotations,
|
|
&method_default_annotations,
|
|
&method_type_annotations,
|
|
CHECK_NULL);
|
|
|
|
if (method->is_final()) {
|
|
*has_final_method = true;
|
|
}
|
|
if (is_interface && !method->is_abstract() && !method->is_static()) {
|
|
// default method
|
|
*has_default_methods = true;
|
|
}
|
|
methods->at_put(index, method());
|
|
if (*methods_annotations == NULL) {
|
|
*methods_annotations =
|
|
MetadataFactory::new_array<AnnotationArray*>(loader_data, length, NULL, CHECK_NULL);
|
|
}
|
|
(*methods_annotations)->at_put(index, method_annotations);
|
|
if (*methods_parameter_annotations == NULL) {
|
|
*methods_parameter_annotations =
|
|
MetadataFactory::new_array<AnnotationArray*>(loader_data, length, NULL, CHECK_NULL);
|
|
}
|
|
(*methods_parameter_annotations)->at_put(index, method_parameter_annotations);
|
|
if (*methods_default_annotations == NULL) {
|
|
*methods_default_annotations =
|
|
MetadataFactory::new_array<AnnotationArray*>(loader_data, length, NULL, CHECK_NULL);
|
|
}
|
|
(*methods_default_annotations)->at_put(index, method_default_annotations);
|
|
if (*methods_type_annotations == NULL) {
|
|
*methods_type_annotations =
|
|
MetadataFactory::new_array<AnnotationArray*>(loader_data, length, NULL, CHECK_NULL);
|
|
}
|
|
(*methods_type_annotations)->at_put(index, method_type_annotations);
|
|
}
|
|
|
|
if (_need_verify && length > 1) {
|
|
// Check duplicated methods
|
|
ResourceMark rm(THREAD);
|
|
NameSigHash** names_and_sigs = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, NameSigHash*, HASH_ROW_SIZE);
|
|
initialize_hashtable(names_and_sigs);
|
|
bool dup = false;
|
|
{
|
|
debug_only(No_Safepoint_Verifier nsv;)
|
|
for (int i = 0; i < length; i++) {
|
|
Method* m = methods->at(i);
|
|
// If no duplicates, add name/signature in hashtable names_and_sigs.
|
|
if (!put_after_lookup(m->name(), m->signature(), names_and_sigs)) {
|
|
dup = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dup) {
|
|
classfile_parse_error("Duplicate method name&signature in class file %s",
|
|
CHECK_NULL);
|
|
}
|
|
}
|
|
return methods;
|
|
}
|
|
}
|
|
|
|
|
|
Array<int>* ClassFileParser::sort_methods(ClassLoaderData* loader_data,
|
|
Array<Method*>* methods,
|
|
Array<AnnotationArray*>* methods_annotations,
|
|
Array<AnnotationArray*>* methods_parameter_annotations,
|
|
Array<AnnotationArray*>* methods_default_annotations,
|
|
Array<AnnotationArray*>* methods_type_annotations,
|
|
TRAPS) {
|
|
int length = methods->length();
|
|
// If JVMTI original method ordering or sharing is enabled we have to
|
|
// remember the original class file ordering.
|
|
// We temporarily use the vtable_index field in the Method* to store the
|
|
// class file index, so we can read in after calling qsort.
|
|
// Put the method ordering in the shared archive.
|
|
if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
|
|
for (int index = 0; index < length; index++) {
|
|
Method* m = methods->at(index);
|
|
assert(!m->valid_vtable_index(), "vtable index should not be set");
|
|
m->set_vtable_index(index);
|
|
}
|
|
}
|
|
// Sort method array by ascending method name (for faster lookups & vtable construction)
|
|
// Note that the ordering is not alphabetical, see Symbol::fast_compare
|
|
Method::sort_methods(methods, methods_annotations,
|
|
methods_parameter_annotations,
|
|
methods_default_annotations,
|
|
methods_type_annotations);
|
|
|
|
// If JVMTI original method ordering or sharing is enabled construct int
|
|
// array remembering the original ordering
|
|
if (JvmtiExport::can_maintain_original_method_order() || DumpSharedSpaces) {
|
|
Array<int>* method_ordering = MetadataFactory::new_array<int>(loader_data, length, CHECK_NULL);
|
|
for (int index = 0; index < length; index++) {
|
|
Method* m = methods->at(index);
|
|
int old_index = m->vtable_index();
|
|
assert(old_index >= 0 && old_index < length, "invalid method index");
|
|
method_ordering->at_put(index, old_index);
|
|
m->set_vtable_index(Method::invalid_vtable_index);
|
|
}
|
|
return method_ordering;
|
|
} else {
|
|
return Universe::the_empty_int_array();
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFileParser::parse_classfile_sourcefile_attribute(constantPoolHandle cp, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
cfs->guarantee_more(2, CHECK); // sourcefile_index
|
|
u2 sourcefile_index = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(sourcefile_index, cp->length()) &&
|
|
cp->tag_at(sourcefile_index).is_utf8(),
|
|
"Invalid SourceFile attribute at constant pool index %u in class file %s",
|
|
sourcefile_index, CHECK);
|
|
set_class_sourcefile(cp->symbol_at(sourcefile_index));
|
|
}
|
|
|
|
|
|
|
|
void ClassFileParser::parse_classfile_source_debug_extension_attribute(constantPoolHandle cp,
|
|
int length, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
u1* sde_buffer = cfs->get_u1_buffer();
|
|
assert(sde_buffer != NULL, "null sde buffer");
|
|
|
|
// Don't bother storing it if there is no way to retrieve it
|
|
if (JvmtiExport::can_get_source_debug_extension()) {
|
|
assert((length+1) > length, "Overflow checking");
|
|
u1* sde = NEW_RESOURCE_ARRAY_IN_THREAD(THREAD, u1, length+1);
|
|
for (int i = 0; i < length; i++) {
|
|
sde[i] = sde_buffer[i];
|
|
}
|
|
sde[length] = '\0';
|
|
set_class_sde_buffer((char*)sde, length);
|
|
}
|
|
// Got utf8 string, set stream position forward
|
|
cfs->skip_u1(length, CHECK);
|
|
}
|
|
|
|
|
|
// Inner classes can be static, private or protected (classic VM does this)
|
|
#define RECOGNIZED_INNER_CLASS_MODIFIERS (JVM_RECOGNIZED_CLASS_MODIFIERS | JVM_ACC_PRIVATE | JVM_ACC_PROTECTED | JVM_ACC_STATIC)
|
|
|
|
// Return number of classes in the inner classes attribute table
|
|
u2 ClassFileParser::parse_classfile_inner_classes_attribute(ClassLoaderData* loader_data,
|
|
u1* inner_classes_attribute_start,
|
|
bool parsed_enclosingmethod_attribute,
|
|
u2 enclosing_method_class_index,
|
|
u2 enclosing_method_method_index,
|
|
constantPoolHandle cp,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
u1* current_mark = cfs->current();
|
|
u2 length = 0;
|
|
if (inner_classes_attribute_start != NULL) {
|
|
cfs->set_current(inner_classes_attribute_start);
|
|
cfs->guarantee_more(2, CHECK_0); // length
|
|
length = cfs->get_u2_fast();
|
|
}
|
|
|
|
// 4-tuples of shorts of inner classes data and 2 shorts of enclosing
|
|
// method data:
|
|
// [inner_class_info_index,
|
|
// outer_class_info_index,
|
|
// inner_name_index,
|
|
// inner_class_access_flags,
|
|
// ...
|
|
// enclosing_method_class_index,
|
|
// enclosing_method_method_index]
|
|
int size = length * 4 + (parsed_enclosingmethod_attribute ? 2 : 0);
|
|
// FIXME: Will leak on exceptions.
|
|
Array<u2>* inner_classes = MetadataFactory::new_array<u2>(loader_data, size, CHECK_0);
|
|
int index = 0;
|
|
int cp_size = cp->length();
|
|
cfs->guarantee_more(8 * length, CHECK_0); // 4-tuples of u2
|
|
for (int n = 0; n < length; n++) {
|
|
// Inner class index
|
|
u2 inner_class_info_index = cfs->get_u2_fast();
|
|
check_property(
|
|
inner_class_info_index == 0 ||
|
|
(valid_cp_range(inner_class_info_index, cp_size) &&
|
|
is_klass_reference(cp, inner_class_info_index)),
|
|
"inner_class_info_index %u has bad constant type in class file %s",
|
|
inner_class_info_index, CHECK_0);
|
|
// Outer class index
|
|
u2 outer_class_info_index = cfs->get_u2_fast();
|
|
check_property(
|
|
outer_class_info_index == 0 ||
|
|
(valid_cp_range(outer_class_info_index, cp_size) &&
|
|
is_klass_reference(cp, outer_class_info_index)),
|
|
"outer_class_info_index %u has bad constant type in class file %s",
|
|
outer_class_info_index, CHECK_0);
|
|
// Inner class name
|
|
u2 inner_name_index = cfs->get_u2_fast();
|
|
check_property(
|
|
inner_name_index == 0 || (valid_cp_range(inner_name_index, cp_size) &&
|
|
cp->tag_at(inner_name_index).is_utf8()),
|
|
"inner_name_index %u has bad constant type in class file %s",
|
|
inner_name_index, CHECK_0);
|
|
if (_need_verify) {
|
|
guarantee_property(inner_class_info_index != outer_class_info_index,
|
|
"Class is both outer and inner class in class file %s", CHECK_0);
|
|
}
|
|
// Access flags
|
|
AccessFlags inner_access_flags;
|
|
jint flags = cfs->get_u2_fast() & RECOGNIZED_INNER_CLASS_MODIFIERS;
|
|
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
|
|
// Set abstract bit for old class files for backward compatibility
|
|
flags |= JVM_ACC_ABSTRACT;
|
|
}
|
|
verify_legal_class_modifiers(flags, CHECK_0);
|
|
inner_access_flags.set_flags(flags);
|
|
|
|
inner_classes->at_put(index++, inner_class_info_index);
|
|
inner_classes->at_put(index++, outer_class_info_index);
|
|
inner_classes->at_put(index++, inner_name_index);
|
|
inner_classes->at_put(index++, inner_access_flags.as_short());
|
|
}
|
|
|
|
// 4347400: make sure there's no duplicate entry in the classes array
|
|
if (_need_verify && _major_version >= JAVA_1_5_VERSION) {
|
|
for(int i = 0; i < length * 4; i += 4) {
|
|
for(int j = i + 4; j < length * 4; j += 4) {
|
|
guarantee_property((inner_classes->at(i) != inner_classes->at(j) ||
|
|
inner_classes->at(i+1) != inner_classes->at(j+1) ||
|
|
inner_classes->at(i+2) != inner_classes->at(j+2) ||
|
|
inner_classes->at(i+3) != inner_classes->at(j+3)),
|
|
"Duplicate entry in InnerClasses in class file %s",
|
|
CHECK_0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set EnclosingMethod class and method indexes.
|
|
if (parsed_enclosingmethod_attribute) {
|
|
inner_classes->at_put(index++, enclosing_method_class_index);
|
|
inner_classes->at_put(index++, enclosing_method_method_index);
|
|
}
|
|
assert(index == size, "wrong size");
|
|
|
|
// Update InstanceKlass with inner class info.
|
|
set_class_inner_classes(inner_classes);
|
|
|
|
// Restore buffer's current position.
|
|
cfs->set_current(current_mark);
|
|
|
|
return length;
|
|
}
|
|
|
|
void ClassFileParser::parse_classfile_synthetic_attribute(constantPoolHandle cp, TRAPS) {
|
|
set_class_synthetic_flag(true);
|
|
}
|
|
|
|
void ClassFileParser::parse_classfile_signature_attribute(constantPoolHandle cp, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
u2 signature_index = cfs->get_u2(CHECK);
|
|
check_property(
|
|
valid_cp_range(signature_index, cp->length()) &&
|
|
cp->tag_at(signature_index).is_utf8(),
|
|
"Invalid constant pool index %u in Signature attribute in class file %s",
|
|
signature_index, CHECK);
|
|
set_class_generic_signature(cp->symbol_at(signature_index));
|
|
}
|
|
|
|
void ClassFileParser::parse_classfile_bootstrap_methods_attribute(ClassLoaderData* loader_data,
|
|
constantPoolHandle cp,
|
|
u4 attribute_byte_length, TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
u1* current_start = cfs->current();
|
|
|
|
cfs->guarantee_more(2, CHECK); // length
|
|
int attribute_array_length = cfs->get_u2_fast();
|
|
|
|
guarantee_property(_max_bootstrap_specifier_index < attribute_array_length,
|
|
"Short length on BootstrapMethods in class file %s",
|
|
CHECK);
|
|
|
|
// The attribute contains a counted array of counted tuples of shorts,
|
|
// represending bootstrap specifiers:
|
|
// length*{bootstrap_method_index, argument_count*{argument_index}}
|
|
int operand_count = (attribute_byte_length - sizeof(u2)) / sizeof(u2);
|
|
// operand_count = number of shorts in attr, except for leading length
|
|
|
|
// The attribute is copied into a short[] array.
|
|
// The array begins with a series of short[2] pairs, one for each tuple.
|
|
int index_size = (attribute_array_length * 2);
|
|
|
|
Array<u2>* operands = MetadataFactory::new_array<u2>(loader_data, index_size + operand_count, CHECK);
|
|
|
|
int operand_fill_index = index_size;
|
|
int cp_size = cp->length();
|
|
|
|
for (int n = 0; n < attribute_array_length; n++) {
|
|
// Store a 32-bit offset into the header of the operand array.
|
|
ConstantPool::operand_offset_at_put(operands, n, operand_fill_index);
|
|
|
|
// Read a bootstrap specifier.
|
|
cfs->guarantee_more(sizeof(u2) * 2, CHECK); // bsm, argc
|
|
u2 bootstrap_method_index = cfs->get_u2_fast();
|
|
u2 argument_count = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(bootstrap_method_index, cp_size) &&
|
|
cp->tag_at(bootstrap_method_index).is_method_handle(),
|
|
"bootstrap_method_index %u has bad constant type in class file %s",
|
|
bootstrap_method_index,
|
|
CHECK);
|
|
operands->at_put(operand_fill_index++, bootstrap_method_index);
|
|
operands->at_put(operand_fill_index++, argument_count);
|
|
|
|
cfs->guarantee_more(sizeof(u2) * argument_count, CHECK); // argv[argc]
|
|
for (int j = 0; j < argument_count; j++) {
|
|
u2 argument_index = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(argument_index, cp_size) &&
|
|
cp->tag_at(argument_index).is_loadable_constant(),
|
|
"argument_index %u has bad constant type in class file %s",
|
|
argument_index,
|
|
CHECK);
|
|
operands->at_put(operand_fill_index++, argument_index);
|
|
}
|
|
}
|
|
|
|
assert(operand_fill_index == operands->length(), "exact fill");
|
|
assert(ConstantPool::operand_array_length(operands) == attribute_array_length, "correct decode");
|
|
|
|
u1* current_end = cfs->current();
|
|
guarantee_property(current_end == current_start + attribute_byte_length,
|
|
"Bad length on BootstrapMethods in class file %s",
|
|
CHECK);
|
|
|
|
cp->set_operands(operands);
|
|
}
|
|
|
|
void ClassFileParser::parse_classfile_attributes(ClassLoaderData* loader_data,
|
|
constantPoolHandle cp,
|
|
ClassFileParser::ClassAnnotationCollector* parsed_annotations,
|
|
TRAPS) {
|
|
ClassFileStream* cfs = stream();
|
|
// Set inner classes attribute to default sentinel
|
|
set_class_inner_classes(Universe::the_empty_short_array());
|
|
cfs->guarantee_more(2, CHECK); // attributes_count
|
|
u2 attributes_count = cfs->get_u2_fast();
|
|
bool parsed_sourcefile_attribute = false;
|
|
bool parsed_innerclasses_attribute = false;
|
|
bool parsed_enclosingmethod_attribute = false;
|
|
bool parsed_bootstrap_methods_attribute = false;
|
|
u1* runtime_visible_annotations = NULL;
|
|
int runtime_visible_annotations_length = 0;
|
|
u1* runtime_invisible_annotations = NULL;
|
|
int runtime_invisible_annotations_length = 0;
|
|
u1* runtime_visible_type_annotations = NULL;
|
|
int runtime_visible_type_annotations_length = 0;
|
|
u1* runtime_invisible_type_annotations = NULL;
|
|
int runtime_invisible_type_annotations_length = 0;
|
|
u1* inner_classes_attribute_start = NULL;
|
|
u4 inner_classes_attribute_length = 0;
|
|
u2 enclosing_method_class_index = 0;
|
|
u2 enclosing_method_method_index = 0;
|
|
// Iterate over attributes
|
|
while (attributes_count--) {
|
|
cfs->guarantee_more(6, CHECK); // attribute_name_index, attribute_length
|
|
u2 attribute_name_index = cfs->get_u2_fast();
|
|
u4 attribute_length = cfs->get_u4_fast();
|
|
check_property(
|
|
valid_cp_range(attribute_name_index, cp->length()) &&
|
|
cp->tag_at(attribute_name_index).is_utf8(),
|
|
"Attribute name has bad constant pool index %u in class file %s",
|
|
attribute_name_index, CHECK);
|
|
Symbol* tag = cp->symbol_at(attribute_name_index);
|
|
if (tag == vmSymbols::tag_source_file()) {
|
|
// Check for SourceFile tag
|
|
if (_need_verify) {
|
|
guarantee_property(attribute_length == 2, "Wrong SourceFile attribute length in class file %s", CHECK);
|
|
}
|
|
if (parsed_sourcefile_attribute) {
|
|
classfile_parse_error("Multiple SourceFile attributes in class file %s", CHECK);
|
|
} else {
|
|
parsed_sourcefile_attribute = true;
|
|
}
|
|
parse_classfile_sourcefile_attribute(cp, CHECK);
|
|
} else if (tag == vmSymbols::tag_source_debug_extension()) {
|
|
// Check for SourceDebugExtension tag
|
|
parse_classfile_source_debug_extension_attribute(cp, (int)attribute_length, CHECK);
|
|
} else if (tag == vmSymbols::tag_inner_classes()) {
|
|
// Check for InnerClasses tag
|
|
if (parsed_innerclasses_attribute) {
|
|
classfile_parse_error("Multiple InnerClasses attributes in class file %s", CHECK);
|
|
} else {
|
|
parsed_innerclasses_attribute = true;
|
|
}
|
|
inner_classes_attribute_start = cfs->get_u1_buffer();
|
|
inner_classes_attribute_length = attribute_length;
|
|
cfs->skip_u1(inner_classes_attribute_length, CHECK);
|
|
} else if (tag == vmSymbols::tag_synthetic()) {
|
|
// Check for Synthetic tag
|
|
// Shouldn't we check that the synthetic flags wasn't already set? - not required in spec
|
|
if (attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Synthetic classfile attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
parse_classfile_synthetic_attribute(cp, CHECK);
|
|
} else if (tag == vmSymbols::tag_deprecated()) {
|
|
// Check for Deprecatd tag - 4276120
|
|
if (attribute_length != 0) {
|
|
classfile_parse_error(
|
|
"Invalid Deprecated classfile attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
} else if (_major_version >= JAVA_1_5_VERSION) {
|
|
if (tag == vmSymbols::tag_signature()) {
|
|
if (attribute_length != 2) {
|
|
classfile_parse_error(
|
|
"Wrong Signature attribute length %u in class file %s",
|
|
attribute_length, CHECK);
|
|
}
|
|
parse_classfile_signature_attribute(cp, CHECK);
|
|
} else if (tag == vmSymbols::tag_runtime_visible_annotations()) {
|
|
runtime_visible_annotations_length = attribute_length;
|
|
runtime_visible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_annotations != NULL, "null visible annotations");
|
|
parse_annotations(runtime_visible_annotations,
|
|
runtime_visible_annotations_length,
|
|
cp,
|
|
parsed_annotations,
|
|
CHECK);
|
|
cfs->skip_u1(runtime_visible_annotations_length, CHECK);
|
|
} else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_annotations()) {
|
|
runtime_invisible_annotations_length = attribute_length;
|
|
runtime_invisible_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_annotations != NULL, "null invisible annotations");
|
|
cfs->skip_u1(runtime_invisible_annotations_length, CHECK);
|
|
} else if (tag == vmSymbols::tag_enclosing_method()) {
|
|
if (parsed_enclosingmethod_attribute) {
|
|
classfile_parse_error("Multiple EnclosingMethod attributes in class file %s", CHECK);
|
|
} else {
|
|
parsed_enclosingmethod_attribute = true;
|
|
}
|
|
cfs->guarantee_more(4, CHECK); // class_index, method_index
|
|
enclosing_method_class_index = cfs->get_u2_fast();
|
|
enclosing_method_method_index = cfs->get_u2_fast();
|
|
if (enclosing_method_class_index == 0) {
|
|
classfile_parse_error("Invalid class index in EnclosingMethod attribute in class file %s", CHECK);
|
|
}
|
|
// Validate the constant pool indices and types
|
|
if (!cp->is_within_bounds(enclosing_method_class_index) ||
|
|
!is_klass_reference(cp, enclosing_method_class_index)) {
|
|
classfile_parse_error("Invalid or out-of-bounds class index in EnclosingMethod attribute in class file %s", CHECK);
|
|
}
|
|
if (enclosing_method_method_index != 0 &&
|
|
(!cp->is_within_bounds(enclosing_method_method_index) ||
|
|
!cp->tag_at(enclosing_method_method_index).is_name_and_type())) {
|
|
classfile_parse_error("Invalid or out-of-bounds method index in EnclosingMethod attribute in class file %s", CHECK);
|
|
}
|
|
} else if (tag == vmSymbols::tag_bootstrap_methods() &&
|
|
_major_version >= Verifier::INVOKEDYNAMIC_MAJOR_VERSION) {
|
|
if (parsed_bootstrap_methods_attribute)
|
|
classfile_parse_error("Multiple BootstrapMethods attributes in class file %s", CHECK);
|
|
parsed_bootstrap_methods_attribute = true;
|
|
parse_classfile_bootstrap_methods_attribute(loader_data, cp, attribute_length, CHECK);
|
|
} else if (tag == vmSymbols::tag_runtime_visible_type_annotations()) {
|
|
runtime_visible_type_annotations_length = attribute_length;
|
|
runtime_visible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_visible_type_annotations != NULL, "null visible type annotations");
|
|
// No need for the VM to parse Type annotations
|
|
cfs->skip_u1(runtime_visible_type_annotations_length, CHECK);
|
|
} else if (PreserveAllAnnotations && tag == vmSymbols::tag_runtime_invisible_type_annotations()) {
|
|
runtime_invisible_type_annotations_length = attribute_length;
|
|
runtime_invisible_type_annotations = cfs->get_u1_buffer();
|
|
assert(runtime_invisible_type_annotations != NULL, "null invisible type annotations");
|
|
cfs->skip_u1(runtime_invisible_type_annotations_length, CHECK);
|
|
} else {
|
|
// Unknown attribute
|
|
cfs->skip_u1(attribute_length, CHECK);
|
|
}
|
|
} else {
|
|
// Unknown attribute
|
|
cfs->skip_u1(attribute_length, CHECK);
|
|
}
|
|
}
|
|
AnnotationArray* annotations = assemble_annotations(loader_data,
|
|
runtime_visible_annotations,
|
|
runtime_visible_annotations_length,
|
|
runtime_invisible_annotations,
|
|
runtime_invisible_annotations_length,
|
|
CHECK);
|
|
set_class_annotations(annotations);
|
|
AnnotationArray* type_annotations = assemble_annotations(loader_data,
|
|
runtime_visible_type_annotations,
|
|
runtime_visible_type_annotations_length,
|
|
runtime_invisible_type_annotations,
|
|
runtime_invisible_type_annotations_length,
|
|
CHECK);
|
|
set_class_type_annotations(type_annotations);
|
|
|
|
if (parsed_innerclasses_attribute || parsed_enclosingmethod_attribute) {
|
|
u2 num_of_classes = parse_classfile_inner_classes_attribute(
|
|
loader_data,
|
|
inner_classes_attribute_start,
|
|
parsed_innerclasses_attribute,
|
|
enclosing_method_class_index,
|
|
enclosing_method_method_index,
|
|
cp, CHECK);
|
|
if (parsed_innerclasses_attribute &&_need_verify && _major_version >= JAVA_1_5_VERSION) {
|
|
guarantee_property(
|
|
inner_classes_attribute_length == sizeof(num_of_classes) + 4 * sizeof(u2) * num_of_classes,
|
|
"Wrong InnerClasses attribute length in class file %s", CHECK);
|
|
}
|
|
}
|
|
|
|
if (_max_bootstrap_specifier_index >= 0) {
|
|
guarantee_property(parsed_bootstrap_methods_attribute,
|
|
"Missing BootstrapMethods attribute in class file %s", CHECK);
|
|
}
|
|
}
|
|
|
|
void ClassFileParser::apply_parsed_class_attributes(instanceKlassHandle k) {
|
|
if (_synthetic_flag)
|
|
k->set_is_synthetic();
|
|
if (_sourcefile != NULL) {
|
|
_sourcefile->increment_refcount();
|
|
k->set_source_file_name(_sourcefile);
|
|
}
|
|
if (_generic_signature != NULL) {
|
|
_generic_signature->increment_refcount();
|
|
k->set_generic_signature(_generic_signature);
|
|
}
|
|
if (_sde_buffer != NULL) {
|
|
k->set_source_debug_extension(_sde_buffer, _sde_length);
|
|
}
|
|
k->set_inner_classes(_inner_classes);
|
|
if (_annotations != NULL) {
|
|
k->annotations()->set_class_annotations(_annotations);
|
|
}
|
|
}
|
|
|
|
AnnotationArray* ClassFileParser::assemble_annotations(ClassLoaderData* loader_data,
|
|
u1* runtime_visible_annotations,
|
|
int runtime_visible_annotations_length,
|
|
u1* runtime_invisible_annotations,
|
|
int runtime_invisible_annotations_length, TRAPS) {
|
|
AnnotationArray* annotations = NULL;
|
|
if (runtime_visible_annotations != NULL ||
|
|
runtime_invisible_annotations != NULL) {
|
|
annotations = MetadataFactory::new_array<u1>(loader_data,
|
|
runtime_visible_annotations_length +
|
|
runtime_invisible_annotations_length,
|
|
CHECK_(annotations));
|
|
if (runtime_visible_annotations != NULL) {
|
|
for (int i = 0; i < runtime_visible_annotations_length; i++) {
|
|
annotations->at_put(i, runtime_visible_annotations[i]);
|
|
}
|
|
}
|
|
if (runtime_invisible_annotations != NULL) {
|
|
for (int i = 0; i < runtime_invisible_annotations_length; i++) {
|
|
int append = runtime_visible_annotations_length+i;
|
|
annotations->at_put(append, runtime_invisible_annotations[i]);
|
|
}
|
|
}
|
|
}
|
|
return annotations;
|
|
}
|
|
|
|
|
|
#ifndef PRODUCT
|
|
static void parseAndPrintGenericSignatures(
|
|
instanceKlassHandle this_klass, TRAPS) {
|
|
assert(ParseAllGenericSignatures == true, "Shouldn't call otherwise");
|
|
ResourceMark rm;
|
|
|
|
if (this_klass->generic_signature() != NULL) {
|
|
using namespace generic;
|
|
ClassDescriptor* spec = ClassDescriptor::parse_generic_signature(this_klass(), CHECK);
|
|
|
|
tty->print_cr("Parsing %s", this_klass->generic_signature()->as_C_string());
|
|
spec->print_on(tty);
|
|
|
|
for (int i = 0; i < this_klass->methods()->length(); ++i) {
|
|
Method* m = this_klass->methods()->at(i);
|
|
MethodDescriptor* method_spec = MethodDescriptor::parse_generic_signature(m, spec);
|
|
Symbol* sig = m->generic_signature();
|
|
if (sig == NULL) {
|
|
sig = m->signature();
|
|
}
|
|
tty->print_cr("Parsing %s", sig->as_C_string());
|
|
method_spec->print_on(tty);
|
|
}
|
|
}
|
|
}
|
|
#endif // ndef PRODUCT
|
|
|
|
|
|
instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
|
|
ClassLoaderData* loader_data,
|
|
Handle protection_domain,
|
|
KlassHandle host_klass,
|
|
GrowableArray<Handle>* cp_patches,
|
|
TempNewSymbol& parsed_name,
|
|
bool verify,
|
|
TRAPS) {
|
|
|
|
// When a retransformable agent is attached, JVMTI caches the
|
|
// class bytes that existed before the first retransformation.
|
|
// If RedefineClasses() was used before the retransformable
|
|
// agent attached, then the cached class bytes may not be the
|
|
// original class bytes.
|
|
unsigned char *cached_class_file_bytes = NULL;
|
|
jint cached_class_file_length;
|
|
Handle class_loader(THREAD, loader_data->class_loader());
|
|
bool has_default_methods = false;
|
|
ResourceMark rm(THREAD);
|
|
|
|
ClassFileStream* cfs = stream();
|
|
// Timing
|
|
assert(THREAD->is_Java_thread(), "must be a JavaThread");
|
|
JavaThread* jt = (JavaThread*) THREAD;
|
|
|
|
PerfClassTraceTime ctimer(ClassLoader::perf_class_parse_time(),
|
|
ClassLoader::perf_class_parse_selftime(),
|
|
NULL,
|
|
jt->get_thread_stat()->perf_recursion_counts_addr(),
|
|
jt->get_thread_stat()->perf_timers_addr(),
|
|
PerfClassTraceTime::PARSE_CLASS);
|
|
|
|
init_parsed_class_attributes();
|
|
|
|
if (JvmtiExport::should_post_class_file_load_hook()) {
|
|
// Get the cached class file bytes (if any) from the class that
|
|
// is being redefined or retransformed. We use jvmti_thread_state()
|
|
// instead of JvmtiThreadState::state_for(jt) so we don't allocate
|
|
// a JvmtiThreadState any earlier than necessary. This will help
|
|
// avoid the bug described by 7126851.
|
|
JvmtiThreadState *state = jt->jvmti_thread_state();
|
|
if (state != NULL) {
|
|
KlassHandle *h_class_being_redefined =
|
|
state->get_class_being_redefined();
|
|
if (h_class_being_redefined != NULL) {
|
|
instanceKlassHandle ikh_class_being_redefined =
|
|
instanceKlassHandle(THREAD, (*h_class_being_redefined)());
|
|
cached_class_file_bytes =
|
|
ikh_class_being_redefined->get_cached_class_file_bytes();
|
|
cached_class_file_length =
|
|
ikh_class_being_redefined->get_cached_class_file_len();
|
|
}
|
|
}
|
|
|
|
unsigned char* ptr = cfs->buffer();
|
|
unsigned char* end_ptr = cfs->buffer() + cfs->length();
|
|
|
|
JvmtiExport::post_class_file_load_hook(name, class_loader(), protection_domain,
|
|
&ptr, &end_ptr,
|
|
&cached_class_file_bytes,
|
|
&cached_class_file_length);
|
|
|
|
if (ptr != cfs->buffer()) {
|
|
// JVMTI agent has modified class file data.
|
|
// Set new class file stream using JVMTI agent modified
|
|
// class file data.
|
|
cfs = new ClassFileStream(ptr, end_ptr - ptr, cfs->source());
|
|
set_stream(cfs);
|
|
}
|
|
}
|
|
|
|
_host_klass = host_klass;
|
|
_cp_patches = cp_patches;
|
|
|
|
instanceKlassHandle nullHandle;
|
|
|
|
// Figure out whether we can skip format checking (matching classic VM behavior)
|
|
_need_verify = Verifier::should_verify_for(class_loader(), verify);
|
|
|
|
// Set the verify flag in stream
|
|
cfs->set_verify(_need_verify);
|
|
|
|
// Save the class file name for easier error message printing.
|
|
_class_name = (name != NULL) ? name : vmSymbols::unknown_class_name();
|
|
|
|
cfs->guarantee_more(8, CHECK_(nullHandle)); // magic, major, minor
|
|
// Magic value
|
|
u4 magic = cfs->get_u4_fast();
|
|
guarantee_property(magic == JAVA_CLASSFILE_MAGIC,
|
|
"Incompatible magic value %u in class file %s",
|
|
magic, CHECK_(nullHandle));
|
|
|
|
// Version numbers
|
|
u2 minor_version = cfs->get_u2_fast();
|
|
u2 major_version = cfs->get_u2_fast();
|
|
|
|
// Check version numbers - we check this even with verifier off
|
|
if (!is_supported_version(major_version, minor_version)) {
|
|
if (name == NULL) {
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_UnsupportedClassVersionError(),
|
|
"Unsupported major.minor version %u.%u",
|
|
major_version,
|
|
minor_version);
|
|
} else {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_UnsupportedClassVersionError(),
|
|
"%s : Unsupported major.minor version %u.%u",
|
|
name->as_C_string(),
|
|
major_version,
|
|
minor_version);
|
|
}
|
|
return nullHandle;
|
|
}
|
|
|
|
_major_version = major_version;
|
|
_minor_version = minor_version;
|
|
|
|
|
|
// Check if verification needs to be relaxed for this class file
|
|
// Do not restrict it to jdk1.0 or jdk1.1 to maintain backward compatibility (4982376)
|
|
_relax_verify = Verifier::relax_verify_for(class_loader());
|
|
|
|
// Constant pool
|
|
constantPoolHandle cp = parse_constant_pool(loader_data, CHECK_(nullHandle));
|
|
ConstantPoolCleaner error_handler(cp); // set constant pool to be cleaned up.
|
|
|
|
int cp_size = cp->length();
|
|
|
|
cfs->guarantee_more(8, CHECK_(nullHandle)); // flags, this_class, super_class, infs_len
|
|
|
|
// Access flags
|
|
AccessFlags access_flags;
|
|
jint flags = cfs->get_u2_fast() & JVM_RECOGNIZED_CLASS_MODIFIERS;
|
|
|
|
if ((flags & JVM_ACC_INTERFACE) && _major_version < JAVA_6_VERSION) {
|
|
// Set abstract bit for old class files for backward compatibility
|
|
flags |= JVM_ACC_ABSTRACT;
|
|
}
|
|
verify_legal_class_modifiers(flags, CHECK_(nullHandle));
|
|
access_flags.set_flags(flags);
|
|
|
|
// This class and superclass
|
|
instanceKlassHandle super_klass;
|
|
u2 this_class_index = cfs->get_u2_fast();
|
|
check_property(
|
|
valid_cp_range(this_class_index, cp_size) &&
|
|
cp->tag_at(this_class_index).is_unresolved_klass(),
|
|
"Invalid this class index %u in constant pool in class file %s",
|
|
this_class_index, CHECK_(nullHandle));
|
|
|
|
Symbol* class_name = cp->unresolved_klass_at(this_class_index);
|
|
assert(class_name != NULL, "class_name can't be null");
|
|
|
|
// It's important to set parsed_name *before* resolving the super class.
|
|
// (it's used for cleanup by the caller if parsing fails)
|
|
parsed_name = class_name;
|
|
// parsed_name is returned and can be used if there's an error, so add to
|
|
// its reference count. Caller will decrement the refcount.
|
|
parsed_name->increment_refcount();
|
|
|
|
// Update _class_name which could be null previously to be class_name
|
|
_class_name = class_name;
|
|
|
|
// Don't need to check whether this class name is legal or not.
|
|
// It has been checked when constant pool is parsed.
|
|
// However, make sure it is not an array type.
|
|
if (_need_verify) {
|
|
guarantee_property(class_name->byte_at(0) != JVM_SIGNATURE_ARRAY,
|
|
"Bad class name in class file %s",
|
|
CHECK_(nullHandle));
|
|
}
|
|
|
|
Klass* preserve_this_klass; // for storing result across HandleMark
|
|
|
|
// release all handles when parsing is done
|
|
{ HandleMark hm(THREAD);
|
|
|
|
// Checks if name in class file matches requested name
|
|
if (name != NULL && class_name != name) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_NoClassDefFoundError(),
|
|
"%s (wrong name: %s)",
|
|
name->as_C_string(),
|
|
class_name->as_C_string()
|
|
);
|
|
return nullHandle;
|
|
}
|
|
|
|
if (TraceClassLoadingPreorder) {
|
|
tty->print("[Loading %s", name->as_klass_external_name());
|
|
if (cfs->source() != NULL) tty->print(" from %s", cfs->source());
|
|
tty->print_cr("]");
|
|
}
|
|
|
|
u2 super_class_index = cfs->get_u2_fast();
|
|
if (super_class_index == 0) {
|
|
check_property(class_name == vmSymbols::java_lang_Object(),
|
|
"Invalid superclass index %u in class file %s",
|
|
super_class_index,
|
|
CHECK_(nullHandle));
|
|
} else {
|
|
check_property(valid_cp_range(super_class_index, cp_size) &&
|
|
is_klass_reference(cp, super_class_index),
|
|
"Invalid superclass index %u in class file %s",
|
|
super_class_index,
|
|
CHECK_(nullHandle));
|
|
// The class name should be legal because it is checked when parsing constant pool.
|
|
// However, make sure it is not an array type.
|
|
bool is_array = false;
|
|
if (cp->tag_at(super_class_index).is_klass()) {
|
|
super_klass = instanceKlassHandle(THREAD, cp->resolved_klass_at(super_class_index));
|
|
if (_need_verify)
|
|
is_array = super_klass->oop_is_array();
|
|
} else if (_need_verify) {
|
|
is_array = (cp->unresolved_klass_at(super_class_index)->byte_at(0) == JVM_SIGNATURE_ARRAY);
|
|
}
|
|
if (_need_verify) {
|
|
guarantee_property(!is_array,
|
|
"Bad superclass name in class file %s", CHECK_(nullHandle));
|
|
}
|
|
}
|
|
|
|
// Interfaces
|
|
u2 itfs_len = cfs->get_u2_fast();
|
|
Array<Klass*>* local_interfaces;
|
|
if (itfs_len == 0) {
|
|
local_interfaces = Universe::the_empty_klass_array();
|
|
} else {
|
|
local_interfaces = parse_interfaces(
|
|
cp, itfs_len, loader_data, protection_domain, _class_name,
|
|
&has_default_methods, CHECK_(nullHandle));
|
|
}
|
|
|
|
u2 java_fields_count = 0;
|
|
// Fields (offsets are filled in later)
|
|
FieldAllocationCount fac;
|
|
Array<AnnotationArray*>* fields_annotations = NULL;
|
|
Array<AnnotationArray*>* fields_type_annotations = NULL;
|
|
Array<u2>* fields = parse_fields(loader_data, class_name, cp, access_flags.is_interface(), &fac, &fields_annotations,
|
|
&fields_type_annotations,
|
|
&java_fields_count,
|
|
CHECK_(nullHandle));
|
|
// Methods
|
|
bool has_final_method = false;
|
|
AccessFlags promoted_flags;
|
|
promoted_flags.set_flags(0);
|
|
// These need to be oop pointers because they are allocated lazily
|
|
// inside parse_methods inside a nested HandleMark
|
|
Array<AnnotationArray*>* methods_annotations = NULL;
|
|
Array<AnnotationArray*>* methods_parameter_annotations = NULL;
|
|
Array<AnnotationArray*>* methods_default_annotations = NULL;
|
|
Array<AnnotationArray*>* methods_type_annotations = NULL;
|
|
Array<Method*>* methods = parse_methods(loader_data,
|
|
cp, access_flags.is_interface(),
|
|
&promoted_flags,
|
|
&has_final_method,
|
|
&methods_annotations,
|
|
&methods_parameter_annotations,
|
|
&methods_default_annotations,
|
|
&methods_type_annotations,
|
|
&has_default_methods,
|
|
CHECK_(nullHandle));
|
|
|
|
// Additional attributes
|
|
ClassAnnotationCollector parsed_annotations;
|
|
parse_classfile_attributes(loader_data, cp, &parsed_annotations, CHECK_(nullHandle));
|
|
|
|
// Make sure this is the end of class file stream
|
|
guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle));
|
|
|
|
// We check super class after class file is parsed and format is checked
|
|
if (super_class_index > 0 && super_klass.is_null()) {
|
|
Symbol* sk = cp->klass_name_at(super_class_index);
|
|
if (access_flags.is_interface()) {
|
|
// Before attempting to resolve the superclass, check for class format
|
|
// errors not checked yet.
|
|
guarantee_property(sk == vmSymbols::java_lang_Object(),
|
|
"Interfaces must have java.lang.Object as superclass in class file %s",
|
|
CHECK_(nullHandle));
|
|
}
|
|
Klass* k = SystemDictionary::resolve_super_or_fail(class_name, sk,
|
|
class_loader,
|
|
protection_domain,
|
|
true,
|
|
CHECK_(nullHandle));
|
|
|
|
KlassHandle kh (THREAD, k);
|
|
super_klass = instanceKlassHandle(THREAD, kh());
|
|
}
|
|
if (super_klass.not_null()) {
|
|
|
|
if (super_klass->has_default_methods()) {
|
|
has_default_methods = true;
|
|
}
|
|
|
|
if (super_klass->is_interface()) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_IncompatibleClassChangeError(),
|
|
"class %s has interface %s as super class",
|
|
class_name->as_klass_external_name(),
|
|
super_klass->external_name()
|
|
);
|
|
return nullHandle;
|
|
}
|
|
// Make sure super class is not final
|
|
if (super_klass->is_final()) {
|
|
THROW_MSG_(vmSymbols::java_lang_VerifyError(), "Cannot inherit from final class", nullHandle);
|
|
}
|
|
}
|
|
|
|
// Compute the transitive list of all unique interfaces implemented by this class
|
|
Array<Klass*>* transitive_interfaces = compute_transitive_interfaces(loader_data, super_klass, local_interfaces, CHECK_(nullHandle));
|
|
|
|
// sort methods
|
|
Array<int>* method_ordering = sort_methods(loader_data,
|
|
methods,
|
|
methods_annotations,
|
|
methods_parameter_annotations,
|
|
methods_default_annotations,
|
|
methods_type_annotations,
|
|
CHECK_(nullHandle));
|
|
|
|
// promote flags from parse_methods() to the klass' flags
|
|
access_flags.add_promoted_flags(promoted_flags.as_int());
|
|
|
|
// Size of Java vtable (in words)
|
|
int vtable_size = 0;
|
|
int itable_size = 0;
|
|
int num_miranda_methods = 0;
|
|
|
|
GrowableArray<Method*> all_mirandas(20);
|
|
|
|
klassVtable::compute_vtable_size_and_num_mirandas(
|
|
&vtable_size, &num_miranda_methods, &all_mirandas, super_klass(), methods,
|
|
access_flags, class_loader, class_name, local_interfaces,
|
|
CHECK_(nullHandle));
|
|
|
|
// Size of Java itable (in words)
|
|
itable_size = access_flags.is_interface() ? 0 : klassItable::compute_itable_size(transitive_interfaces);
|
|
|
|
// Field size and offset computation
|
|
int nonstatic_field_size = super_klass() == NULL ? 0 : super_klass->nonstatic_field_size();
|
|
#ifndef PRODUCT
|
|
int orig_nonstatic_field_size = 0;
|
|
#endif
|
|
int static_field_size = 0;
|
|
int next_static_oop_offset;
|
|
int next_static_double_offset;
|
|
int next_static_word_offset;
|
|
int next_static_short_offset;
|
|
int next_static_byte_offset;
|
|
int next_static_type_offset;
|
|
int next_nonstatic_oop_offset;
|
|
int next_nonstatic_double_offset;
|
|
int next_nonstatic_word_offset;
|
|
int next_nonstatic_short_offset;
|
|
int next_nonstatic_byte_offset;
|
|
int next_nonstatic_type_offset;
|
|
int first_nonstatic_oop_offset;
|
|
int first_nonstatic_field_offset;
|
|
int next_nonstatic_field_offset;
|
|
|
|
// Calculate the starting byte offsets
|
|
next_static_oop_offset = InstanceMirrorKlass::offset_of_static_fields();
|
|
next_static_double_offset = next_static_oop_offset +
|
|
(fac.count[STATIC_OOP] * heapOopSize);
|
|
if ( fac.count[STATIC_DOUBLE] &&
|
|
(Universe::field_type_should_be_aligned(T_DOUBLE) ||
|
|
Universe::field_type_should_be_aligned(T_LONG)) ) {
|
|
next_static_double_offset = align_size_up(next_static_double_offset, BytesPerLong);
|
|
}
|
|
|
|
next_static_word_offset = next_static_double_offset +
|
|
(fac.count[STATIC_DOUBLE] * BytesPerLong);
|
|
next_static_short_offset = next_static_word_offset +
|
|
(fac.count[STATIC_WORD] * BytesPerInt);
|
|
next_static_byte_offset = next_static_short_offset +
|
|
(fac.count[STATIC_SHORT] * BytesPerShort);
|
|
next_static_type_offset = align_size_up((next_static_byte_offset +
|
|
fac.count[STATIC_BYTE] ), wordSize );
|
|
static_field_size = (next_static_type_offset -
|
|
next_static_oop_offset) / wordSize;
|
|
|
|
first_nonstatic_field_offset = instanceOopDesc::base_offset_in_bytes() +
|
|
nonstatic_field_size * heapOopSize;
|
|
next_nonstatic_field_offset = first_nonstatic_field_offset;
|
|
|
|
unsigned int nonstatic_double_count = fac.count[NONSTATIC_DOUBLE];
|
|
unsigned int nonstatic_word_count = fac.count[NONSTATIC_WORD];
|
|
unsigned int nonstatic_short_count = fac.count[NONSTATIC_SHORT];
|
|
unsigned int nonstatic_byte_count = fac.count[NONSTATIC_BYTE];
|
|
unsigned int nonstatic_oop_count = fac.count[NONSTATIC_OOP];
|
|
|
|
bool super_has_nonstatic_fields =
|
|
(super_klass() != NULL && super_klass->has_nonstatic_fields());
|
|
bool has_nonstatic_fields = super_has_nonstatic_fields ||
|
|
((nonstatic_double_count + nonstatic_word_count +
|
|
nonstatic_short_count + nonstatic_byte_count +
|
|
nonstatic_oop_count) != 0);
|
|
|
|
|
|
// Prepare list of oops for oop map generation.
|
|
int* nonstatic_oop_offsets;
|
|
unsigned int* nonstatic_oop_counts;
|
|
unsigned int nonstatic_oop_map_count = 0;
|
|
|
|
nonstatic_oop_offsets = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, int, nonstatic_oop_count + 1);
|
|
nonstatic_oop_counts = NEW_RESOURCE_ARRAY_IN_THREAD(
|
|
THREAD, unsigned int, nonstatic_oop_count + 1);
|
|
|
|
first_nonstatic_oop_offset = 0; // will be set for first oop field
|
|
|
|
#ifndef PRODUCT
|
|
if( PrintCompactFieldsSavings ) {
|
|
next_nonstatic_double_offset = next_nonstatic_field_offset +
|
|
(nonstatic_oop_count * heapOopSize);
|
|
if ( nonstatic_double_count > 0 ) {
|
|
next_nonstatic_double_offset = align_size_up(next_nonstatic_double_offset, BytesPerLong);
|
|
}
|
|
next_nonstatic_word_offset = next_nonstatic_double_offset +
|
|
(nonstatic_double_count * BytesPerLong);
|
|
next_nonstatic_short_offset = next_nonstatic_word_offset +
|
|
(nonstatic_word_count * BytesPerInt);
|
|
next_nonstatic_byte_offset = next_nonstatic_short_offset +
|
|
(nonstatic_short_count * BytesPerShort);
|
|
next_nonstatic_type_offset = align_size_up((next_nonstatic_byte_offset +
|
|
nonstatic_byte_count ), heapOopSize );
|
|
orig_nonstatic_field_size = nonstatic_field_size +
|
|
((next_nonstatic_type_offset - first_nonstatic_field_offset)/heapOopSize);
|
|
}
|
|
#endif
|
|
bool compact_fields = CompactFields;
|
|
int allocation_style = FieldsAllocationStyle;
|
|
if( allocation_style < 0 || allocation_style > 2 ) { // Out of range?
|
|
assert(false, "0 <= FieldsAllocationStyle <= 2");
|
|
allocation_style = 1; // Optimistic
|
|
}
|
|
|
|
// The next classes have predefined hard-coded fields offsets
|
|
// (see in JavaClasses::compute_hard_coded_offsets()).
|
|
// Use default fields allocation order for them.
|
|
if( (allocation_style != 0 || compact_fields ) && class_loader.is_null() &&
|
|
(class_name == vmSymbols::java_lang_AssertionStatusDirectives() ||
|
|
class_name == vmSymbols::java_lang_Class() ||
|
|
class_name == vmSymbols::java_lang_ClassLoader() ||
|
|
class_name == vmSymbols::java_lang_ref_Reference() ||
|
|
class_name == vmSymbols::java_lang_ref_SoftReference() ||
|
|
class_name == vmSymbols::java_lang_StackTraceElement() ||
|
|
class_name == vmSymbols::java_lang_String() ||
|
|
class_name == vmSymbols::java_lang_Throwable() ||
|
|
class_name == vmSymbols::java_lang_Boolean() ||
|
|
class_name == vmSymbols::java_lang_Character() ||
|
|
class_name == vmSymbols::java_lang_Float() ||
|
|
class_name == vmSymbols::java_lang_Double() ||
|
|
class_name == vmSymbols::java_lang_Byte() ||
|
|
class_name == vmSymbols::java_lang_Short() ||
|
|
class_name == vmSymbols::java_lang_Integer() ||
|
|
class_name == vmSymbols::java_lang_Long())) {
|
|
allocation_style = 0; // Allocate oops first
|
|
compact_fields = false; // Don't compact fields
|
|
}
|
|
|
|
if( allocation_style == 0 ) {
|
|
// Fields order: oops, longs/doubles, ints, shorts/chars, bytes
|
|
next_nonstatic_oop_offset = next_nonstatic_field_offset;
|
|
next_nonstatic_double_offset = next_nonstatic_oop_offset +
|
|
(nonstatic_oop_count * heapOopSize);
|
|
} else if( allocation_style == 1 ) {
|
|
// Fields order: longs/doubles, ints, shorts/chars, bytes, oops
|
|
next_nonstatic_double_offset = next_nonstatic_field_offset;
|
|
} else if( allocation_style == 2 ) {
|
|
// Fields allocation: oops fields in super and sub classes are together.
|
|
if( nonstatic_field_size > 0 && super_klass() != NULL &&
|
|
super_klass->nonstatic_oop_map_size() > 0 ) {
|
|
int map_count = super_klass->nonstatic_oop_map_count();
|
|
OopMapBlock* first_map = super_klass->start_of_nonstatic_oop_maps();
|
|
OopMapBlock* last_map = first_map + map_count - 1;
|
|
int next_offset = last_map->offset() + (last_map->count() * heapOopSize);
|
|
if (next_offset == next_nonstatic_field_offset) {
|
|
allocation_style = 0; // allocate oops first
|
|
next_nonstatic_oop_offset = next_nonstatic_field_offset;
|
|
next_nonstatic_double_offset = next_nonstatic_oop_offset +
|
|
(nonstatic_oop_count * heapOopSize);
|
|
}
|
|
}
|
|
if( allocation_style == 2 ) {
|
|
allocation_style = 1; // allocate oops last
|
|
next_nonstatic_double_offset = next_nonstatic_field_offset;
|
|
}
|
|
} else {
|
|
ShouldNotReachHere();
|
|
}
|
|
|
|
int nonstatic_oop_space_count = 0;
|
|
int nonstatic_word_space_count = 0;
|
|
int nonstatic_short_space_count = 0;
|
|
int nonstatic_byte_space_count = 0;
|
|
int nonstatic_oop_space_offset;
|
|
int nonstatic_word_space_offset;
|
|
int nonstatic_short_space_offset;
|
|
int nonstatic_byte_space_offset;
|
|
|
|
if( nonstatic_double_count > 0 ) {
|
|
int offset = next_nonstatic_double_offset;
|
|
next_nonstatic_double_offset = align_size_up(offset, BytesPerLong);
|
|
if( compact_fields && offset != next_nonstatic_double_offset ) {
|
|
// Allocate available fields into the gap before double field.
|
|
int length = next_nonstatic_double_offset - offset;
|
|
assert(length == BytesPerInt, "");
|
|
nonstatic_word_space_offset = offset;
|
|
if( nonstatic_word_count > 0 ) {
|
|
nonstatic_word_count -= 1;
|
|
nonstatic_word_space_count = 1; // Only one will fit
|
|
length -= BytesPerInt;
|
|
offset += BytesPerInt;
|
|
}
|
|
nonstatic_short_space_offset = offset;
|
|
while( length >= BytesPerShort && nonstatic_short_count > 0 ) {
|
|
nonstatic_short_count -= 1;
|
|
nonstatic_short_space_count += 1;
|
|
length -= BytesPerShort;
|
|
offset += BytesPerShort;
|
|
}
|
|
nonstatic_byte_space_offset = offset;
|
|
while( length > 0 && nonstatic_byte_count > 0 ) {
|
|
nonstatic_byte_count -= 1;
|
|
nonstatic_byte_space_count += 1;
|
|
length -= 1;
|
|
}
|
|
// Allocate oop field in the gap if there are no other fields for that.
|
|
nonstatic_oop_space_offset = offset;
|
|
if( length >= heapOopSize && nonstatic_oop_count > 0 &&
|
|
allocation_style != 0 ) { // when oop fields not first
|
|
nonstatic_oop_count -= 1;
|
|
nonstatic_oop_space_count = 1; // Only one will fit
|
|
length -= heapOopSize;
|
|
offset += heapOopSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
next_nonstatic_word_offset = next_nonstatic_double_offset +
|
|
(nonstatic_double_count * BytesPerLong);
|
|
next_nonstatic_short_offset = next_nonstatic_word_offset +
|
|
(nonstatic_word_count * BytesPerInt);
|
|
next_nonstatic_byte_offset = next_nonstatic_short_offset +
|
|
(nonstatic_short_count * BytesPerShort);
|
|
|
|
int notaligned_offset;
|
|
if( allocation_style == 0 ) {
|
|
notaligned_offset = next_nonstatic_byte_offset + nonstatic_byte_count;
|
|
} else { // allocation_style == 1
|
|
next_nonstatic_oop_offset = next_nonstatic_byte_offset + nonstatic_byte_count;
|
|
if( nonstatic_oop_count > 0 ) {
|
|
next_nonstatic_oop_offset = align_size_up(next_nonstatic_oop_offset, heapOopSize);
|
|
}
|
|
notaligned_offset = next_nonstatic_oop_offset + (nonstatic_oop_count * heapOopSize);
|
|
}
|
|
next_nonstatic_type_offset = align_size_up(notaligned_offset, heapOopSize );
|
|
nonstatic_field_size = nonstatic_field_size + ((next_nonstatic_type_offset
|
|
- first_nonstatic_field_offset)/heapOopSize);
|
|
|
|
// Iterate over fields again and compute correct offsets.
|
|
// The field allocation type was temporarily stored in the offset slot.
|
|
// oop fields are located before non-oop fields (static and non-static).
|
|
for (AllFieldStream fs(fields, cp); !fs.done(); fs.next()) {
|
|
int real_offset;
|
|
FieldAllocationType atype = (FieldAllocationType) fs.offset();
|
|
switch (atype) {
|
|
case STATIC_OOP:
|
|
real_offset = next_static_oop_offset;
|
|
next_static_oop_offset += heapOopSize;
|
|
break;
|
|
case STATIC_BYTE:
|
|
real_offset = next_static_byte_offset;
|
|
next_static_byte_offset += 1;
|
|
break;
|
|
case STATIC_SHORT:
|
|
real_offset = next_static_short_offset;
|
|
next_static_short_offset += BytesPerShort;
|
|
break;
|
|
case STATIC_WORD:
|
|
real_offset = next_static_word_offset;
|
|
next_static_word_offset += BytesPerInt;
|
|
break;
|
|
case STATIC_DOUBLE:
|
|
real_offset = next_static_double_offset;
|
|
next_static_double_offset += BytesPerLong;
|
|
break;
|
|
case NONSTATIC_OOP:
|
|
if( nonstatic_oop_space_count > 0 ) {
|
|
real_offset = nonstatic_oop_space_offset;
|
|
nonstatic_oop_space_offset += heapOopSize;
|
|
nonstatic_oop_space_count -= 1;
|
|
} else {
|
|
real_offset = next_nonstatic_oop_offset;
|
|
next_nonstatic_oop_offset += heapOopSize;
|
|
}
|
|
// Update oop maps
|
|
if( nonstatic_oop_map_count > 0 &&
|
|
nonstatic_oop_offsets[nonstatic_oop_map_count - 1] ==
|
|
real_offset -
|
|
int(nonstatic_oop_counts[nonstatic_oop_map_count - 1]) *
|
|
heapOopSize ) {
|
|
// Extend current oop map
|
|
nonstatic_oop_counts[nonstatic_oop_map_count - 1] += 1;
|
|
} else {
|
|
// Create new oop map
|
|
nonstatic_oop_offsets[nonstatic_oop_map_count] = real_offset;
|
|
nonstatic_oop_counts [nonstatic_oop_map_count] = 1;
|
|
nonstatic_oop_map_count += 1;
|
|
if( first_nonstatic_oop_offset == 0 ) { // Undefined
|
|
first_nonstatic_oop_offset = real_offset;
|
|
}
|
|
}
|
|
break;
|
|
case NONSTATIC_BYTE:
|
|
if( nonstatic_byte_space_count > 0 ) {
|
|
real_offset = nonstatic_byte_space_offset;
|
|
nonstatic_byte_space_offset += 1;
|
|
nonstatic_byte_space_count -= 1;
|
|
} else {
|
|
real_offset = next_nonstatic_byte_offset;
|
|
next_nonstatic_byte_offset += 1;
|
|
}
|
|
break;
|
|
case NONSTATIC_SHORT:
|
|
if( nonstatic_short_space_count > 0 ) {
|
|
real_offset = nonstatic_short_space_offset;
|
|
nonstatic_short_space_offset += BytesPerShort;
|
|
nonstatic_short_space_count -= 1;
|
|
} else {
|
|
real_offset = next_nonstatic_short_offset;
|
|
next_nonstatic_short_offset += BytesPerShort;
|
|
}
|
|
break;
|
|
case NONSTATIC_WORD:
|
|
if( nonstatic_word_space_count > 0 ) {
|
|
real_offset = nonstatic_word_space_offset;
|
|
nonstatic_word_space_offset += BytesPerInt;
|
|
nonstatic_word_space_count -= 1;
|
|
} else {
|
|
real_offset = next_nonstatic_word_offset;
|
|
next_nonstatic_word_offset += BytesPerInt;
|
|
}
|
|
break;
|
|
case NONSTATIC_DOUBLE:
|
|
real_offset = next_nonstatic_double_offset;
|
|
next_nonstatic_double_offset += BytesPerLong;
|
|
break;
|
|
default:
|
|
ShouldNotReachHere();
|
|
}
|
|
fs.set_offset(real_offset);
|
|
}
|
|
|
|
// Size of instances
|
|
int instance_size;
|
|
|
|
next_nonstatic_type_offset = align_size_up(notaligned_offset, wordSize );
|
|
instance_size = align_object_size(next_nonstatic_type_offset / wordSize);
|
|
|
|
assert(instance_size == align_object_size(align_size_up((instanceOopDesc::base_offset_in_bytes() + nonstatic_field_size*heapOopSize), wordSize) / wordSize), "consistent layout helper value");
|
|
|
|
// Number of non-static oop map blocks allocated at end of klass.
|
|
const unsigned int total_oop_map_count =
|
|
compute_oop_map_count(super_klass, nonstatic_oop_map_count,
|
|
first_nonstatic_oop_offset);
|
|
|
|
// Compute reference type
|
|
ReferenceType rt;
|
|
if (super_klass() == NULL) {
|
|
rt = REF_NONE;
|
|
} else {
|
|
rt = super_klass->reference_type();
|
|
}
|
|
|
|
// We can now create the basic Klass* for this klass
|
|
int total_oop_map_size2 =
|
|
InstanceKlass::nonstatic_oop_map_size(total_oop_map_count);
|
|
|
|
Klass* ik = InstanceKlass::allocate_instance_klass(loader_data,
|
|
vtable_size,
|
|
itable_size,
|
|
static_field_size,
|
|
total_oop_map_size2,
|
|
rt,
|
|
access_flags,
|
|
name,
|
|
super_klass(),
|
|
host_klass,
|
|
CHECK_(nullHandle));
|
|
|
|
// Add all classes to our internal class loader list here,
|
|
// including classes in the bootstrap (NULL) class loader.
|
|
loader_data->add_class(ik);
|
|
|
|
instanceKlassHandle this_klass (THREAD, ik);
|
|
|
|
assert(this_klass->static_field_size() == static_field_size, "sanity");
|
|
assert(this_klass->nonstatic_oop_map_count() == total_oop_map_count,
|
|
"sanity");
|
|
|
|
// Fill in information already parsed
|
|
this_klass->set_should_verify_class(verify);
|
|
jint lh = Klass::instance_layout_helper(instance_size, false);
|
|
this_klass->set_layout_helper(lh);
|
|
assert(this_klass->oop_is_instance(), "layout is correct");
|
|
assert(this_klass->size_helper() == instance_size, "correct size_helper");
|
|
// Not yet: supers are done below to support the new subtype-checking fields
|
|
//this_klass->set_super(super_klass());
|
|
this_klass->set_class_loader_data(loader_data);
|
|
this_klass->set_nonstatic_field_size(nonstatic_field_size);
|
|
this_klass->set_has_nonstatic_fields(has_nonstatic_fields);
|
|
this_klass->set_static_oop_field_count(fac.count[STATIC_OOP]);
|
|
cp->set_pool_holder(this_klass());
|
|
error_handler.set_in_error(false); // turn off error handler for cp
|
|
this_klass->set_constants(cp());
|
|
this_klass->set_local_interfaces(local_interfaces);
|
|
this_klass->set_fields(fields, java_fields_count);
|
|
this_klass->set_methods(methods);
|
|
if (has_final_method) {
|
|
this_klass->set_has_final_method();
|
|
}
|
|
this_klass->set_method_ordering(method_ordering);
|
|
// The InstanceKlass::_methods_jmethod_ids cache and the
|
|
// InstanceKlass::_methods_cached_itable_indices cache are
|
|
// both managed on the assumption that the initial cache
|
|
// size is equal to the number of methods in the class. If
|
|
// that changes, then InstanceKlass::idnum_can_increment()
|
|
// has to be changed accordingly.
|
|
this_klass->set_initial_method_idnum(methods->length());
|
|
this_klass->set_name(cp->klass_name_at(this_class_index));
|
|
if (is_anonymous()) // I am well known to myself
|
|
cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve
|
|
|
|
// Allocate an annotation type if needed.
|
|
if (fields_annotations != NULL ||
|
|
methods_annotations != NULL ||
|
|
methods_parameter_annotations != NULL ||
|
|
methods_default_annotations != NULL ||
|
|
fields_type_annotations != NULL ||
|
|
methods_type_annotations != NULL) {
|
|
Annotations* anno = Annotations::allocate(loader_data,
|
|
fields_annotations, methods_annotations,
|
|
methods_parameter_annotations,
|
|
methods_default_annotations, CHECK_(nullHandle));
|
|
this_klass->set_annotations(anno);
|
|
} else {
|
|
this_klass->set_annotations(NULL);
|
|
}
|
|
|
|
if (fields_type_annotations != NULL ||
|
|
methods_type_annotations != NULL) {
|
|
assert(this_klass->annotations() != NULL, "annotations should have been allocated");
|
|
Annotations* anno = Annotations::allocate(loader_data,
|
|
fields_type_annotations,
|
|
methods_type_annotations,
|
|
NULL,
|
|
NULL, CHECK_(nullHandle));
|
|
this_klass->annotations()->set_type_annotations(anno);
|
|
}
|
|
|
|
this_klass->set_minor_version(minor_version);
|
|
this_klass->set_major_version(major_version);
|
|
this_klass->set_has_default_methods(has_default_methods);
|
|
|
|
// Set up Method*::intrinsic_id as soon as we know the names of methods.
|
|
// (We used to do this lazily, but now we query it in Rewriter,
|
|
// which is eagerly done for every method, so we might as well do it now,
|
|
// when everything is fresh in memory.)
|
|
if (Method::klass_id_for_intrinsics(this_klass()) != vmSymbols::NO_SID) {
|
|
for (int j = 0; j < methods->length(); j++) {
|
|
methods->at(j)->init_intrinsic_id();
|
|
}
|
|
}
|
|
|
|
if (cached_class_file_bytes != NULL) {
|
|
// JVMTI: we have an InstanceKlass now, tell it about the cached bytes
|
|
this_klass->set_cached_class_file(cached_class_file_bytes,
|
|
cached_class_file_length);
|
|
}
|
|
|
|
// Fill in field values obtained by parse_classfile_attributes
|
|
if (parsed_annotations.has_any_annotations())
|
|
parsed_annotations.apply_to(this_klass);
|
|
|
|
// Create annotations
|
|
if (_annotations != NULL && this_klass->annotations() == NULL) {
|
|
Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL);
|
|
this_klass->set_annotations(anno);
|
|
}
|
|
apply_parsed_class_attributes(this_klass);
|
|
|
|
// Create type annotations
|
|
if (_type_annotations != NULL) {
|
|
if (this_klass->annotations() == NULL) {
|
|
Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL);
|
|
this_klass->set_annotations(anno);
|
|
}
|
|
if (this_klass->annotations()->type_annotations() == NULL) {
|
|
Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL);
|
|
this_klass->annotations()->set_type_annotations(anno);
|
|
}
|
|
this_klass->annotations()->type_annotations()->set_class_annotations(_type_annotations);
|
|
}
|
|
|
|
// Miranda methods
|
|
if ((num_miranda_methods > 0) ||
|
|
// if this class introduced new miranda methods or
|
|
(super_klass.not_null() && (super_klass->has_miranda_methods()))
|
|
// super class exists and this class inherited miranda methods
|
|
) {
|
|
this_klass->set_has_miranda_methods(); // then set a flag
|
|
}
|
|
|
|
this_klass->set_transitive_interfaces(transitive_interfaces);
|
|
|
|
// Fill in information needed to compute superclasses.
|
|
this_klass->initialize_supers(super_klass(), CHECK_(nullHandle));
|
|
|
|
// Initialize itable offset tables
|
|
klassItable::setup_itable_offset_table(this_klass);
|
|
|
|
// Compute transitive closure of interfaces this class implements
|
|
// Do final class setup
|
|
fill_oop_maps(this_klass, nonstatic_oop_map_count, nonstatic_oop_offsets, nonstatic_oop_counts);
|
|
|
|
// Fill in has_finalizer, has_vanilla_constructor, and layout_helper
|
|
set_precomputed_flags(this_klass);
|
|
|
|
// reinitialize modifiers, using the InnerClasses attribute
|
|
int computed_modifiers = this_klass->compute_modifier_flags(CHECK_(nullHandle));
|
|
this_klass->set_modifier_flags(computed_modifiers);
|
|
|
|
// check if this class can access its super class
|
|
check_super_class_access(this_klass, CHECK_(nullHandle));
|
|
|
|
// check if this class can access its superinterfaces
|
|
check_super_interface_access(this_klass, CHECK_(nullHandle));
|
|
|
|
// check if this class overrides any final method
|
|
check_final_method_override(this_klass, CHECK_(nullHandle));
|
|
|
|
// check that if this class is an interface then it doesn't have static methods
|
|
if (this_klass->is_interface()) {
|
|
/* An interface in a JAVA 8 classfile can be static */
|
|
if (_major_version < JAVA_8_VERSION) {
|
|
check_illegal_static_method(this_klass, CHECK_(nullHandle));
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef ASSERT
|
|
if (ParseAllGenericSignatures) {
|
|
parseAndPrintGenericSignatures(this_klass, CHECK_(nullHandle));
|
|
}
|
|
#endif
|
|
|
|
// Generate any default methods - default methods are interface methods
|
|
// that have a default implementation. This is new with Lambda project.
|
|
if (has_default_methods && !access_flags.is_interface() &&
|
|
local_interfaces->length() > 0) {
|
|
DefaultMethods::generate_default_methods(
|
|
this_klass(), &all_mirandas, CHECK_(nullHandle));
|
|
}
|
|
|
|
// Allocate mirror and initialize static fields
|
|
java_lang_Class::create_mirror(this_klass, CHECK_(nullHandle));
|
|
|
|
// Allocate a simple java object for locking during class initialization.
|
|
// This needs to be a java object because it can be held across a java call.
|
|
typeArrayOop r = oopFactory::new_typeArray(T_INT, 0, CHECK_NULL);
|
|
this_klass->set_init_lock(r);
|
|
|
|
// TODO: Move these oops to the mirror
|
|
this_klass->set_protection_domain(protection_domain());
|
|
|
|
// Update the loader_data graph.
|
|
record_defined_class_dependencies(this_klass, CHECK_NULL);
|
|
|
|
ClassLoadingService::notify_class_loaded(InstanceKlass::cast(this_klass()),
|
|
false /* not shared class */);
|
|
|
|
if (TraceClassLoading) {
|
|
ResourceMark rm;
|
|
// print in a single call to reduce interleaving of output
|
|
if (cfs->source() != NULL) {
|
|
tty->print("[Loaded %s from %s]\n", this_klass->external_name(),
|
|
cfs->source());
|
|
} else if (class_loader.is_null()) {
|
|
if (THREAD->is_Java_thread()) {
|
|
Klass* caller = ((JavaThread*)THREAD)->security_get_caller_class(1);
|
|
tty->print("[Loaded %s by instance of %s]\n",
|
|
this_klass->external_name(),
|
|
InstanceKlass::cast(caller)->external_name());
|
|
} else {
|
|
tty->print("[Loaded %s]\n", this_klass->external_name());
|
|
}
|
|
} else {
|
|
tty->print("[Loaded %s from %s]\n", this_klass->external_name(),
|
|
InstanceKlass::cast(class_loader->klass())->external_name());
|
|
}
|
|
}
|
|
|
|
if (TraceClassResolution) {
|
|
ResourceMark rm;
|
|
// print out the superclass.
|
|
const char * from = this_klass()->external_name();
|
|
if (this_klass->java_super() != NULL) {
|
|
tty->print("RESOLVE %s %s (super)\n", from, InstanceKlass::cast(this_klass->java_super())->external_name());
|
|
}
|
|
// print out each of the interface classes referred to by this class.
|
|
Array<Klass*>* local_interfaces = this_klass->local_interfaces();
|
|
if (local_interfaces != NULL) {
|
|
int length = local_interfaces->length();
|
|
for (int i = 0; i < length; i++) {
|
|
Klass* k = local_interfaces->at(i);
|
|
InstanceKlass* to_class = InstanceKlass::cast(k);
|
|
const char * to = to_class->external_name();
|
|
tty->print("RESOLVE %s %s (interface)\n", from, to);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
if( PrintCompactFieldsSavings ) {
|
|
ResourceMark rm;
|
|
if( nonstatic_field_size < orig_nonstatic_field_size ) {
|
|
tty->print("[Saved %d of %d bytes in %s]\n",
|
|
(orig_nonstatic_field_size - nonstatic_field_size)*heapOopSize,
|
|
orig_nonstatic_field_size*heapOopSize,
|
|
this_klass->external_name());
|
|
} else if( nonstatic_field_size > orig_nonstatic_field_size ) {
|
|
tty->print("[Wasted %d over %d bytes in %s]\n",
|
|
(nonstatic_field_size - orig_nonstatic_field_size)*heapOopSize,
|
|
orig_nonstatic_field_size*heapOopSize,
|
|
this_klass->external_name());
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// preserve result across HandleMark
|
|
preserve_this_klass = this_klass();
|
|
}
|
|
|
|
// Create new handle outside HandleMark (might be needed for
|
|
// Extended Class Redefinition)
|
|
instanceKlassHandle this_klass (THREAD, preserve_this_klass);
|
|
debug_only(this_klass->verify();)
|
|
|
|
return this_klass;
|
|
}
|
|
|
|
unsigned int
|
|
ClassFileParser::compute_oop_map_count(instanceKlassHandle super,
|
|
unsigned int nonstatic_oop_map_count,
|
|
int first_nonstatic_oop_offset) {
|
|
unsigned int map_count =
|
|
super.is_null() ? 0 : super->nonstatic_oop_map_count();
|
|
if (nonstatic_oop_map_count > 0) {
|
|
// We have oops to add to map
|
|
if (map_count == 0) {
|
|
map_count = nonstatic_oop_map_count;
|
|
} else {
|
|
// Check whether we should add a new map block or whether the last one can
|
|
// be extended
|
|
OopMapBlock* const first_map = super->start_of_nonstatic_oop_maps();
|
|
OopMapBlock* const last_map = first_map + map_count - 1;
|
|
|
|
int next_offset = last_map->offset() + last_map->count() * heapOopSize;
|
|
if (next_offset == first_nonstatic_oop_offset) {
|
|
// There is no gap bettwen superklass's last oop field and first
|
|
// local oop field, merge maps.
|
|
nonstatic_oop_map_count -= 1;
|
|
} else {
|
|
// Superklass didn't end with a oop field, add extra maps
|
|
assert(next_offset < first_nonstatic_oop_offset, "just checking");
|
|
}
|
|
map_count += nonstatic_oop_map_count;
|
|
}
|
|
}
|
|
return map_count;
|
|
}
|
|
|
|
|
|
void ClassFileParser::fill_oop_maps(instanceKlassHandle k,
|
|
unsigned int nonstatic_oop_map_count,
|
|
int* nonstatic_oop_offsets,
|
|
unsigned int* nonstatic_oop_counts) {
|
|
OopMapBlock* this_oop_map = k->start_of_nonstatic_oop_maps();
|
|
const InstanceKlass* const super = k->superklass();
|
|
const unsigned int super_count = super ? super->nonstatic_oop_map_count() : 0;
|
|
if (super_count > 0) {
|
|
// Copy maps from superklass
|
|
OopMapBlock* super_oop_map = super->start_of_nonstatic_oop_maps();
|
|
for (unsigned int i = 0; i < super_count; ++i) {
|
|
*this_oop_map++ = *super_oop_map++;
|
|
}
|
|
}
|
|
|
|
if (nonstatic_oop_map_count > 0) {
|
|
if (super_count + nonstatic_oop_map_count > k->nonstatic_oop_map_count()) {
|
|
// The counts differ because there is no gap between superklass's last oop
|
|
// field and the first local oop field. Extend the last oop map copied
|
|
// from the superklass instead of creating new one.
|
|
nonstatic_oop_map_count--;
|
|
nonstatic_oop_offsets++;
|
|
this_oop_map--;
|
|
this_oop_map->set_count(this_oop_map->count() + *nonstatic_oop_counts++);
|
|
this_oop_map++;
|
|
}
|
|
|
|
// Add new map blocks, fill them
|
|
while (nonstatic_oop_map_count-- > 0) {
|
|
this_oop_map->set_offset(*nonstatic_oop_offsets++);
|
|
this_oop_map->set_count(*nonstatic_oop_counts++);
|
|
this_oop_map++;
|
|
}
|
|
assert(k->start_of_nonstatic_oop_maps() + k->nonstatic_oop_map_count() ==
|
|
this_oop_map, "sanity");
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFileParser::set_precomputed_flags(instanceKlassHandle k) {
|
|
Klass* super = k->super();
|
|
|
|
// Check if this klass has an empty finalize method (i.e. one with return bytecode only),
|
|
// in which case we don't have to register objects as finalizable
|
|
if (!_has_empty_finalizer) {
|
|
if (_has_finalizer ||
|
|
(super != NULL && super->has_finalizer())) {
|
|
k->set_has_finalizer();
|
|
}
|
|
}
|
|
|
|
#ifdef ASSERT
|
|
bool f = false;
|
|
Method* m = k->lookup_method(vmSymbols::finalize_method_name(),
|
|
vmSymbols::void_method_signature());
|
|
if (m != NULL && !m->is_empty_method()) {
|
|
f = true;
|
|
}
|
|
assert(f == k->has_finalizer(), "inconsistent has_finalizer");
|
|
#endif
|
|
|
|
// Check if this klass supports the java.lang.Cloneable interface
|
|
if (SystemDictionary::Cloneable_klass_loaded()) {
|
|
if (k->is_subtype_of(SystemDictionary::Cloneable_klass())) {
|
|
k->set_is_cloneable();
|
|
}
|
|
}
|
|
|
|
// Check if this klass has a vanilla default constructor
|
|
if (super == NULL) {
|
|
// java.lang.Object has empty default constructor
|
|
k->set_has_vanilla_constructor();
|
|
} else {
|
|
if (super->has_vanilla_constructor() &&
|
|
_has_vanilla_constructor) {
|
|
k->set_has_vanilla_constructor();
|
|
}
|
|
#ifdef ASSERT
|
|
bool v = false;
|
|
if (super->has_vanilla_constructor()) {
|
|
Method* constructor = k->find_method(vmSymbols::object_initializer_name(
|
|
), vmSymbols::void_method_signature());
|
|
if (constructor != NULL && constructor->is_vanilla_constructor()) {
|
|
v = true;
|
|
}
|
|
}
|
|
assert(v == k->has_vanilla_constructor(), "inconsistent has_vanilla_constructor");
|
|
#endif
|
|
}
|
|
|
|
// If it cannot be fast-path allocated, set a bit in the layout helper.
|
|
// See documentation of InstanceKlass::can_be_fastpath_allocated().
|
|
assert(k->size_helper() > 0, "layout_helper is initialized");
|
|
if ((!RegisterFinalizersAtInit && k->has_finalizer())
|
|
|| k->is_abstract() || k->is_interface()
|
|
|| (k->name() == vmSymbols::java_lang_Class() && k->class_loader() == NULL)
|
|
|| k->size_helper() >= FastAllocateSizeLimit) {
|
|
// Forbid fast-path allocation.
|
|
jint lh = Klass::instance_layout_helper(k->size_helper(), true);
|
|
k->set_layout_helper(lh);
|
|
}
|
|
}
|
|
|
|
// Attach super classes and interface classes to class loader data
|
|
void ClassFileParser::record_defined_class_dependencies(instanceKlassHandle defined_klass, TRAPS) {
|
|
ClassLoaderData * defining_loader_data = defined_klass->class_loader_data();
|
|
if (defining_loader_data->is_the_null_class_loader_data()) {
|
|
// Dependencies to null class loader data are implicit.
|
|
return;
|
|
} else {
|
|
// add super class dependency
|
|
Klass* super = defined_klass->super();
|
|
if (super != NULL) {
|
|
defining_loader_data->record_dependency(super, CHECK);
|
|
}
|
|
|
|
// add super interface dependencies
|
|
Array<Klass*>* local_interfaces = defined_klass->local_interfaces();
|
|
if (local_interfaces != NULL) {
|
|
int length = local_interfaces->length();
|
|
for (int i = 0; i < length; i++) {
|
|
defining_loader_data->record_dependency(local_interfaces->at(i), CHECK);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// utility method for appending and array with check for duplicates
|
|
|
|
void append_interfaces(GrowableArray<Klass*>* result, Array<Klass*>* ifs) {
|
|
// iterate over new interfaces
|
|
for (int i = 0; i < ifs->length(); i++) {
|
|
Klass* e = ifs->at(i);
|
|
assert(e->is_klass() && InstanceKlass::cast(e)->is_interface(), "just checking");
|
|
// add new interface
|
|
result->append_if_missing(e);
|
|
}
|
|
}
|
|
|
|
|
|
Array<Klass*>* ClassFileParser::compute_transitive_interfaces(ClassLoaderData* loader_data, instanceKlassHandle super, Array<Klass*>* local_ifs, TRAPS) {
|
|
// Compute maximum size for transitive interfaces
|
|
int max_transitive_size = 0;
|
|
int super_size = 0;
|
|
// Add superclass transitive interfaces size
|
|
if (super.not_null()) {
|
|
super_size = super->transitive_interfaces()->length();
|
|
max_transitive_size += super_size;
|
|
}
|
|
// Add local interfaces' super interfaces
|
|
int local_size = local_ifs->length();
|
|
for (int i = 0; i < local_size; i++) {
|
|
Klass* l = local_ifs->at(i);
|
|
max_transitive_size += InstanceKlass::cast(l)->transitive_interfaces()->length();
|
|
}
|
|
// Finally add local interfaces
|
|
max_transitive_size += local_size;
|
|
// Construct array
|
|
if (max_transitive_size == 0) {
|
|
// no interfaces, use canonicalized array
|
|
return Universe::the_empty_klass_array();
|
|
} else if (max_transitive_size == super_size) {
|
|
// no new local interfaces added, share superklass' transitive interface array
|
|
return super->transitive_interfaces();
|
|
} else if (max_transitive_size == local_size) {
|
|
// only local interfaces added, share local interface array
|
|
return local_ifs;
|
|
} else {
|
|
ResourceMark rm;
|
|
GrowableArray<Klass*>* result = new GrowableArray<Klass*>(max_transitive_size);
|
|
|
|
// Copy down from superclass
|
|
if (super.not_null()) {
|
|
append_interfaces(result, super->transitive_interfaces());
|
|
}
|
|
|
|
// Copy down from local interfaces' superinterfaces
|
|
for (int i = 0; i < local_ifs->length(); i++) {
|
|
Klass* l = local_ifs->at(i);
|
|
append_interfaces(result, InstanceKlass::cast(l)->transitive_interfaces());
|
|
}
|
|
// Finally add local interfaces
|
|
append_interfaces(result, local_ifs);
|
|
|
|
// length will be less than the max_transitive_size if duplicates were removed
|
|
int length = result->length();
|
|
assert(length <= max_transitive_size, "just checking");
|
|
Array<Klass*>* new_result = MetadataFactory::new_array<Klass*>(loader_data, length, CHECK_NULL);
|
|
for (int i = 0; i < length; i++) {
|
|
Klass* e = result->at(i);
|
|
assert(e != NULL, "just checking");
|
|
new_result->at_put(i, e);
|
|
}
|
|
return new_result;
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFileParser::check_super_class_access(instanceKlassHandle this_klass, TRAPS) {
|
|
Klass* super = this_klass->super();
|
|
if ((super != NULL) &&
|
|
(!Reflection::verify_class_access(this_klass(), super, false))) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_IllegalAccessError(),
|
|
"class %s cannot access its superclass %s",
|
|
this_klass->external_name(),
|
|
InstanceKlass::cast(super)->external_name()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFileParser::check_super_interface_access(instanceKlassHandle this_klass, TRAPS) {
|
|
Array<Klass*>* local_interfaces = this_klass->local_interfaces();
|
|
int lng = local_interfaces->length();
|
|
for (int i = lng - 1; i >= 0; i--) {
|
|
Klass* k = local_interfaces->at(i);
|
|
assert (k != NULL && k->is_interface(), "invalid interface");
|
|
if (!Reflection::verify_class_access(this_klass(), k, false)) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_IllegalAccessError(),
|
|
"class %s cannot access its superinterface %s",
|
|
this_klass->external_name(),
|
|
InstanceKlass::cast(k)->external_name()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void ClassFileParser::check_final_method_override(instanceKlassHandle this_klass, TRAPS) {
|
|
Array<Method*>* methods = this_klass->methods();
|
|
int num_methods = methods->length();
|
|
|
|
// go thru each method and check if it overrides a final method
|
|
for (int index = 0; index < num_methods; index++) {
|
|
Method* m = methods->at(index);
|
|
|
|
// skip private, static and <init> methods
|
|
if ((!m->is_private()) &&
|
|
(!m->is_static()) &&
|
|
(m->name() != vmSymbols::object_initializer_name())) {
|
|
|
|
Symbol* name = m->name();
|
|
Symbol* signature = m->signature();
|
|
Klass* k = this_klass->super();
|
|
Method* super_m = NULL;
|
|
while (k != NULL) {
|
|
// skip supers that don't have final methods.
|
|
if (k->has_final_method()) {
|
|
// lookup a matching method in the super class hierarchy
|
|
super_m = InstanceKlass::cast(k)->lookup_method(name, signature);
|
|
if (super_m == NULL) {
|
|
break; // didn't find any match; get out
|
|
}
|
|
|
|
if (super_m->is_final() &&
|
|
// matching method in super is final
|
|
(Reflection::verify_field_access(this_klass(),
|
|
super_m->method_holder(),
|
|
super_m->method_holder(),
|
|
super_m->access_flags(), false))
|
|
// this class can access super final method and therefore override
|
|
) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_VerifyError(),
|
|
"class %s overrides final method %s.%s",
|
|
this_klass->external_name(),
|
|
name->as_C_string(),
|
|
signature->as_C_string()
|
|
);
|
|
return;
|
|
}
|
|
|
|
// continue to look from super_m's holder's super.
|
|
k = super_m->method_holder()->super();
|
|
continue;
|
|
}
|
|
|
|
k = k->super();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// assumes that this_klass is an interface
|
|
void ClassFileParser::check_illegal_static_method(instanceKlassHandle this_klass, TRAPS) {
|
|
assert(this_klass->is_interface(), "not an interface");
|
|
Array<Method*>* methods = this_klass->methods();
|
|
int num_methods = methods->length();
|
|
|
|
for (int index = 0; index < num_methods; index++) {
|
|
Method* m = methods->at(index);
|
|
// if m is static and not the init method, throw a verify error
|
|
if ((m->is_static()) && (m->name() != vmSymbols::class_initializer_name())) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_VerifyError(),
|
|
"Illegal static method %s in interface %s",
|
|
m->name()->as_C_string(),
|
|
this_klass->external_name()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// utility methods for format checking
|
|
|
|
void ClassFileParser::verify_legal_class_modifiers(jint flags, TRAPS) {
|
|
if (!_need_verify) { return; }
|
|
|
|
const bool is_interface = (flags & JVM_ACC_INTERFACE) != 0;
|
|
const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0;
|
|
const bool is_final = (flags & JVM_ACC_FINAL) != 0;
|
|
const bool is_super = (flags & JVM_ACC_SUPER) != 0;
|
|
const bool is_enum = (flags & JVM_ACC_ENUM) != 0;
|
|
const bool is_annotation = (flags & JVM_ACC_ANNOTATION) != 0;
|
|
const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION;
|
|
|
|
if ((is_abstract && is_final) ||
|
|
(is_interface && !is_abstract) ||
|
|
(is_interface && major_gte_15 && (is_super || is_enum)) ||
|
|
(!is_interface && major_gte_15 && is_annotation)) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Illegal class modifiers in class %s: 0x%X",
|
|
_class_name->as_C_string(), flags
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool ClassFileParser::has_illegal_visibility(jint flags) {
|
|
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
|
|
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
|
|
const bool is_private = (flags & JVM_ACC_PRIVATE) != 0;
|
|
|
|
return ((is_public && is_protected) ||
|
|
(is_public && is_private) ||
|
|
(is_protected && is_private));
|
|
}
|
|
|
|
bool ClassFileParser::is_supported_version(u2 major, u2 minor) {
|
|
u2 max_version =
|
|
JDK_Version::is_gte_jdk17x_version() ? JAVA_MAX_SUPPORTED_VERSION :
|
|
(JDK_Version::is_gte_jdk16x_version() ? JAVA_6_VERSION : JAVA_1_5_VERSION);
|
|
return (major >= JAVA_MIN_SUPPORTED_VERSION) &&
|
|
(major <= max_version) &&
|
|
((major != max_version) ||
|
|
(minor <= JAVA_MAX_SUPPORTED_MINOR_VERSION));
|
|
}
|
|
|
|
void ClassFileParser::verify_legal_field_modifiers(
|
|
jint flags, bool is_interface, TRAPS) {
|
|
if (!_need_verify) { return; }
|
|
|
|
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
|
|
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
|
|
const bool is_private = (flags & JVM_ACC_PRIVATE) != 0;
|
|
const bool is_static = (flags & JVM_ACC_STATIC) != 0;
|
|
const bool is_final = (flags & JVM_ACC_FINAL) != 0;
|
|
const bool is_volatile = (flags & JVM_ACC_VOLATILE) != 0;
|
|
const bool is_transient = (flags & JVM_ACC_TRANSIENT) != 0;
|
|
const bool is_enum = (flags & JVM_ACC_ENUM) != 0;
|
|
const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION;
|
|
|
|
bool is_illegal = false;
|
|
|
|
if (is_interface) {
|
|
if (!is_public || !is_static || !is_final || is_private ||
|
|
is_protected || is_volatile || is_transient ||
|
|
(major_gte_15 && is_enum)) {
|
|
is_illegal = true;
|
|
}
|
|
} else { // not interface
|
|
if (has_illegal_visibility(flags) || (is_final && is_volatile)) {
|
|
is_illegal = true;
|
|
}
|
|
}
|
|
|
|
if (is_illegal) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Illegal field modifiers in class %s: 0x%X",
|
|
_class_name->as_C_string(), flags);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ClassFileParser::verify_legal_method_modifiers(
|
|
jint flags, bool is_interface, Symbol* name, TRAPS) {
|
|
if (!_need_verify) { return; }
|
|
|
|
const bool is_public = (flags & JVM_ACC_PUBLIC) != 0;
|
|
const bool is_private = (flags & JVM_ACC_PRIVATE) != 0;
|
|
const bool is_static = (flags & JVM_ACC_STATIC) != 0;
|
|
const bool is_final = (flags & JVM_ACC_FINAL) != 0;
|
|
const bool is_native = (flags & JVM_ACC_NATIVE) != 0;
|
|
const bool is_abstract = (flags & JVM_ACC_ABSTRACT) != 0;
|
|
const bool is_bridge = (flags & JVM_ACC_BRIDGE) != 0;
|
|
const bool is_strict = (flags & JVM_ACC_STRICT) != 0;
|
|
const bool is_synchronized = (flags & JVM_ACC_SYNCHRONIZED) != 0;
|
|
const bool is_protected = (flags & JVM_ACC_PROTECTED) != 0;
|
|
const bool major_gte_15 = _major_version >= JAVA_1_5_VERSION;
|
|
const bool major_gte_8 = _major_version >= JAVA_8_VERSION;
|
|
const bool is_initializer = (name == vmSymbols::object_initializer_name());
|
|
|
|
bool is_illegal = false;
|
|
|
|
if (is_interface) {
|
|
if (major_gte_8) {
|
|
// Class file version is JAVA_8_VERSION or later Methods of
|
|
// interfaces may set any of the flags except ACC_PROTECTED,
|
|
// ACC_FINAL, ACC_NATIVE, and ACC_SYNCHRONIZED; they must
|
|
// have exactly one of the ACC_PUBLIC or ACC_PRIVATE flags set.
|
|
if ((is_public == is_private) || /* Only one of private and public should be true - XNOR */
|
|
(is_native || is_protected || is_final || is_synchronized) ||
|
|
// If a specific method of a class or interface has its
|
|
// ACC_ABSTRACT flag set, it must not have any of its
|
|
// ACC_FINAL, ACC_NATIVE, ACC_PRIVATE, ACC_STATIC,
|
|
// ACC_STRICT, or ACC_SYNCHRONIZED flags set. No need to
|
|
// check for ACC_FINAL, ACC_NATIVE or ACC_SYNCHRONIZED as
|
|
// those flags are illegal irrespective of ACC_ABSTRACT being set or not.
|
|
(is_abstract && (is_private || is_static || is_strict))) {
|
|
is_illegal = true;
|
|
}
|
|
} else if (major_gte_15) {
|
|
// Class file version in the interval [JAVA_1_5_VERSION, JAVA_8_VERSION)
|
|
if (!is_public || is_static || is_final || is_synchronized ||
|
|
is_native || !is_abstract || is_strict) {
|
|
is_illegal = true;
|
|
}
|
|
} else {
|
|
// Class file version is pre-JAVA_1_5_VERSION
|
|
if (!is_public || is_static || is_final || is_native || !is_abstract) {
|
|
is_illegal = true;
|
|
}
|
|
}
|
|
} else { // not interface
|
|
if (is_initializer) {
|
|
if (is_static || is_final || is_synchronized || is_native ||
|
|
is_abstract || (major_gte_15 && is_bridge)) {
|
|
is_illegal = true;
|
|
}
|
|
} else { // not initializer
|
|
if (is_abstract) {
|
|
if ((is_final || is_native || is_private || is_static ||
|
|
(major_gte_15 && (is_synchronized || is_strict)))) {
|
|
is_illegal = true;
|
|
}
|
|
}
|
|
if (has_illegal_visibility(flags)) {
|
|
is_illegal = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (is_illegal) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Method %s in class %s has illegal modifiers: 0x%X",
|
|
name->as_C_string(), _class_name->as_C_string(), flags);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ClassFileParser::verify_legal_utf8(const unsigned char* buffer, int length, TRAPS) {
|
|
assert(_need_verify, "only called when _need_verify is true");
|
|
int i = 0;
|
|
int count = length >> 2;
|
|
for (int k=0; k<count; k++) {
|
|
unsigned char b0 = buffer[i];
|
|
unsigned char b1 = buffer[i+1];
|
|
unsigned char b2 = buffer[i+2];
|
|
unsigned char b3 = buffer[i+3];
|
|
// For an unsigned char v,
|
|
// (v | v - 1) is < 128 (highest bit 0) for 0 < v < 128;
|
|
// (v | v - 1) is >= 128 (highest bit 1) for v == 0 or v >= 128.
|
|
unsigned char res = b0 | b0 - 1 |
|
|
b1 | b1 - 1 |
|
|
b2 | b2 - 1 |
|
|
b3 | b3 - 1;
|
|
if (res >= 128) break;
|
|
i += 4;
|
|
}
|
|
for(; i < length; i++) {
|
|
unsigned short c;
|
|
// no embedded zeros
|
|
guarantee_property((buffer[i] != 0), "Illegal UTF8 string in constant pool in class file %s", CHECK);
|
|
if(buffer[i] < 128) {
|
|
continue;
|
|
}
|
|
if ((i + 5) < length) { // see if it's legal supplementary character
|
|
if (UTF8::is_supplementary_character(&buffer[i])) {
|
|
c = UTF8::get_supplementary_character(&buffer[i]);
|
|
i += 5;
|
|
continue;
|
|
}
|
|
}
|
|
switch (buffer[i] >> 4) {
|
|
default: break;
|
|
case 0x8: case 0x9: case 0xA: case 0xB: case 0xF:
|
|
classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK);
|
|
case 0xC: case 0xD: // 110xxxxx 10xxxxxx
|
|
c = (buffer[i] & 0x1F) << 6;
|
|
i++;
|
|
if ((i < length) && ((buffer[i] & 0xC0) == 0x80)) {
|
|
c += buffer[i] & 0x3F;
|
|
if (_major_version <= 47 || c == 0 || c >= 0x80) {
|
|
// for classes with major > 47, c must a null or a character in its shortest form
|
|
break;
|
|
}
|
|
}
|
|
classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK);
|
|
case 0xE: // 1110xxxx 10xxxxxx 10xxxxxx
|
|
c = (buffer[i] & 0xF) << 12;
|
|
i += 2;
|
|
if ((i < length) && ((buffer[i-1] & 0xC0) == 0x80) && ((buffer[i] & 0xC0) == 0x80)) {
|
|
c += ((buffer[i-1] & 0x3F) << 6) + (buffer[i] & 0x3F);
|
|
if (_major_version <= 47 || c >= 0x800) {
|
|
// for classes with major > 47, c must be in its shortest form
|
|
break;
|
|
}
|
|
}
|
|
classfile_parse_error("Illegal UTF8 string in constant pool in class file %s", CHECK);
|
|
} // end of switch
|
|
} // end of for
|
|
}
|
|
|
|
// Checks if name is a legal class name.
|
|
void ClassFileParser::verify_legal_class_name(Symbol* name, TRAPS) {
|
|
if (!_need_verify || _relax_verify) { return; }
|
|
|
|
char buf[fixed_buffer_size];
|
|
char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size);
|
|
unsigned int length = name->utf8_length();
|
|
bool legal = false;
|
|
|
|
if (length > 0) {
|
|
char* p;
|
|
if (bytes[0] == JVM_SIGNATURE_ARRAY) {
|
|
p = skip_over_field_signature(bytes, false, length, CHECK);
|
|
legal = (p != NULL) && ((p - bytes) == (int)length);
|
|
} else if (_major_version < JAVA_1_5_VERSION) {
|
|
if (bytes[0] != '<') {
|
|
p = skip_over_field_name(bytes, true, length);
|
|
legal = (p != NULL) && ((p - bytes) == (int)length);
|
|
}
|
|
} else {
|
|
// 4900761: relax the constraints based on JSR202 spec
|
|
// Class names may be drawn from the entire Unicode character set.
|
|
// Identifiers between '/' must be unqualified names.
|
|
// The utf8 string has been verified when parsing cpool entries.
|
|
legal = verify_unqualified_name(bytes, length, LegalClass);
|
|
}
|
|
}
|
|
if (!legal) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Illegal class name \"%s\" in class file %s", bytes,
|
|
_class_name->as_C_string()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Checks if name is a legal field name.
|
|
void ClassFileParser::verify_legal_field_name(Symbol* name, TRAPS) {
|
|
if (!_need_verify || _relax_verify) { return; }
|
|
|
|
char buf[fixed_buffer_size];
|
|
char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size);
|
|
unsigned int length = name->utf8_length();
|
|
bool legal = false;
|
|
|
|
if (length > 0) {
|
|
if (_major_version < JAVA_1_5_VERSION) {
|
|
if (bytes[0] != '<') {
|
|
char* p = skip_over_field_name(bytes, false, length);
|
|
legal = (p != NULL) && ((p - bytes) == (int)length);
|
|
}
|
|
} else {
|
|
// 4881221: relax the constraints based on JSR202 spec
|
|
legal = verify_unqualified_name(bytes, length, LegalField);
|
|
}
|
|
}
|
|
|
|
if (!legal) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Illegal field name \"%s\" in class %s", bytes,
|
|
_class_name->as_C_string()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Checks if name is a legal method name.
|
|
void ClassFileParser::verify_legal_method_name(Symbol* name, TRAPS) {
|
|
if (!_need_verify || _relax_verify) { return; }
|
|
|
|
assert(name != NULL, "method name is null");
|
|
char buf[fixed_buffer_size];
|
|
char* bytes = name->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size);
|
|
unsigned int length = name->utf8_length();
|
|
bool legal = false;
|
|
|
|
if (length > 0) {
|
|
if (bytes[0] == '<') {
|
|
if (name == vmSymbols::object_initializer_name() || name == vmSymbols::class_initializer_name()) {
|
|
legal = true;
|
|
}
|
|
} else if (_major_version < JAVA_1_5_VERSION) {
|
|
char* p;
|
|
p = skip_over_field_name(bytes, false, length);
|
|
legal = (p != NULL) && ((p - bytes) == (int)length);
|
|
} else {
|
|
// 4881221: relax the constraints based on JSR202 spec
|
|
legal = verify_unqualified_name(bytes, length, LegalMethod);
|
|
}
|
|
}
|
|
|
|
if (!legal) {
|
|
ResourceMark rm(THREAD);
|
|
Exceptions::fthrow(
|
|
THREAD_AND_LOCATION,
|
|
vmSymbols::java_lang_ClassFormatError(),
|
|
"Illegal method name \"%s\" in class %s", bytes,
|
|
_class_name->as_C_string()
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
// Checks if signature is a legal field signature.
|
|
void ClassFileParser::verify_legal_field_signature(Symbol* name, Symbol* signature, TRAPS) {
|
|
if (!_need_verify) { return; }
|
|
|
|
char buf[fixed_buffer_size];
|
|
char* bytes = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size);
|
|
unsigned int length = signature->utf8_length();
|
|
char* p = skip_over_field_signature(bytes, false, length, CHECK);
|
|
|
|
if (p == NULL || (p - bytes) != (int)length) {
|
|
throwIllegalSignature("Field", name, signature, CHECK);
|
|
}
|
|
}
|
|
|
|
// Checks if signature is a legal method signature.
|
|
// Returns number of parameters
|
|
int ClassFileParser::verify_legal_method_signature(Symbol* name, Symbol* signature, TRAPS) {
|
|
if (!_need_verify) {
|
|
// make sure caller's args_size will be less than 0 even for non-static
|
|
// method so it will be recomputed in compute_size_of_parameters().
|
|
return -2;
|
|
}
|
|
|
|
unsigned int args_size = 0;
|
|
char buf[fixed_buffer_size];
|
|
char* p = signature->as_utf8_flexible_buffer(THREAD, buf, fixed_buffer_size);
|
|
unsigned int length = signature->utf8_length();
|
|
char* nextp;
|
|
|
|
// The first character must be a '('
|
|
if ((length > 0) && (*p++ == JVM_SIGNATURE_FUNC)) {
|
|
length--;
|
|
// Skip over legal field signatures
|
|
nextp = skip_over_field_signature(p, false, length, CHECK_0);
|
|
while ((length > 0) && (nextp != NULL)) {
|
|
args_size++;
|
|
if (p[0] == 'J' || p[0] == 'D') {
|
|
args_size++;
|
|
}
|
|
length -= nextp - p;
|
|
p = nextp;
|
|
nextp = skip_over_field_signature(p, false, length, CHECK_0);
|
|
}
|
|
// The first non-signature thing better be a ')'
|
|
if ((length > 0) && (*p++ == JVM_SIGNATURE_ENDFUNC)) {
|
|
length--;
|
|
if (name->utf8_length() > 0 && name->byte_at(0) == '<') {
|
|
// All internal methods must return void
|
|
if ((length == 1) && (p[0] == JVM_SIGNATURE_VOID)) {
|
|
return args_size;
|
|
}
|
|
} else {
|
|
// Now we better just have a return value
|
|
nextp = skip_over_field_signature(p, true, length, CHECK_0);
|
|
if (nextp && ((int)length == (nextp - p))) {
|
|
return args_size;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Report error
|
|
throwIllegalSignature("Method", name, signature, CHECK_0);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// Unqualified names may not contain the characters '.', ';', '[', or '/'.
|
|
// Method names also may not contain the characters '<' or '>', unless <init>
|
|
// or <clinit>. Note that method names may not be <init> or <clinit> in this
|
|
// method. Because these names have been checked as special cases before
|
|
// calling this method in verify_legal_method_name.
|
|
bool ClassFileParser::verify_unqualified_name(
|
|
char* name, unsigned int length, int type) {
|
|
jchar ch;
|
|
|
|
for (char* p = name; p != name + length; ) {
|
|
ch = *p;
|
|
if (ch < 128) {
|
|
p++;
|
|
if (ch == '.' || ch == ';' || ch == '[' ) {
|
|
return false; // do not permit '.', ';', or '['
|
|
}
|
|
if (type != LegalClass && ch == '/') {
|
|
return false; // do not permit '/' unless it's class name
|
|
}
|
|
if (type == LegalMethod && (ch == '<' || ch == '>')) {
|
|
return false; // do not permit '<' or '>' in method names
|
|
}
|
|
} else {
|
|
char* tmp_p = UTF8::next(p, &ch);
|
|
p = tmp_p;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Take pointer to a string. Skip over the longest part of the string that could
|
|
// be taken as a fieldname. Allow '/' if slash_ok is true.
|
|
// Return a pointer to just past the fieldname.
|
|
// Return NULL if no fieldname at all was found, or in the case of slash_ok
|
|
// being true, we saw consecutive slashes (meaning we were looking for a
|
|
// qualified path but found something that was badly-formed).
|
|
char* ClassFileParser::skip_over_field_name(char* name, bool slash_ok, unsigned int length) {
|
|
char* p;
|
|
jchar ch;
|
|
jboolean last_is_slash = false;
|
|
jboolean not_first_ch = false;
|
|
|
|
for (p = name; p != name + length; not_first_ch = true) {
|
|
char* old_p = p;
|
|
ch = *p;
|
|
if (ch < 128) {
|
|
p++;
|
|
// quick check for ascii
|
|
if ((ch >= 'a' && ch <= 'z') ||
|
|
(ch >= 'A' && ch <= 'Z') ||
|
|
(ch == '_' || ch == '$') ||
|
|
(not_first_ch && ch >= '0' && ch <= '9')) {
|
|
last_is_slash = false;
|
|
continue;
|
|
}
|
|
if (slash_ok && ch == '/') {
|
|
if (last_is_slash) {
|
|
return NULL; // Don't permit consecutive slashes
|
|
}
|
|
last_is_slash = true;
|
|
continue;
|
|
}
|
|
} else {
|
|
jint unicode_ch;
|
|
char* tmp_p = UTF8::next_character(p, &unicode_ch);
|
|
p = tmp_p;
|
|
last_is_slash = false;
|
|
// Check if ch is Java identifier start or is Java identifier part
|
|
// 4672820: call java.lang.Character methods directly without generating separate tables.
|
|
EXCEPTION_MARK;
|
|
instanceKlassHandle klass (THREAD, SystemDictionary::Character_klass());
|
|
|
|
// return value
|
|
JavaValue result(T_BOOLEAN);
|
|
// Set up the arguments to isJavaIdentifierStart and isJavaIdentifierPart
|
|
JavaCallArguments args;
|
|
args.push_int(unicode_ch);
|
|
|
|
// public static boolean isJavaIdentifierStart(char ch);
|
|
JavaCalls::call_static(&result,
|
|
klass,
|
|
vmSymbols::isJavaIdentifierStart_name(),
|
|
vmSymbols::int_bool_signature(),
|
|
&args,
|
|
THREAD);
|
|
|
|
if (HAS_PENDING_EXCEPTION) {
|
|
CLEAR_PENDING_EXCEPTION;
|
|
return 0;
|
|
}
|
|
if (result.get_jboolean()) {
|
|
continue;
|
|
}
|
|
|
|
if (not_first_ch) {
|
|
// public static boolean isJavaIdentifierPart(char ch);
|
|
JavaCalls::call_static(&result,
|
|
klass,
|
|
vmSymbols::isJavaIdentifierPart_name(),
|
|
vmSymbols::int_bool_signature(),
|
|
&args,
|
|
THREAD);
|
|
|
|
if (HAS_PENDING_EXCEPTION) {
|
|
CLEAR_PENDING_EXCEPTION;
|
|
return 0;
|
|
}
|
|
|
|
if (result.get_jboolean()) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
return (not_first_ch) ? old_p : NULL;
|
|
}
|
|
return (not_first_ch) ? p : NULL;
|
|
}
|
|
|
|
|
|
// Take pointer to a string. Skip over the longest part of the string that could
|
|
// be taken as a field signature. Allow "void" if void_ok.
|
|
// Return a pointer to just past the signature.
|
|
// Return NULL if no legal signature is found.
|
|
char* ClassFileParser::skip_over_field_signature(char* signature,
|
|
bool void_ok,
|
|
unsigned int length,
|
|
TRAPS) {
|
|
unsigned int array_dim = 0;
|
|
while (length > 0) {
|
|
switch (signature[0]) {
|
|
case JVM_SIGNATURE_VOID: if (!void_ok) { return NULL; }
|
|
case JVM_SIGNATURE_BOOLEAN:
|
|
case JVM_SIGNATURE_BYTE:
|
|
case JVM_SIGNATURE_CHAR:
|
|
case JVM_SIGNATURE_SHORT:
|
|
case JVM_SIGNATURE_INT:
|
|
case JVM_SIGNATURE_FLOAT:
|
|
case JVM_SIGNATURE_LONG:
|
|
case JVM_SIGNATURE_DOUBLE:
|
|
return signature + 1;
|
|
case JVM_SIGNATURE_CLASS: {
|
|
if (_major_version < JAVA_1_5_VERSION) {
|
|
// Skip over the class name if one is there
|
|
char* p = skip_over_field_name(signature + 1, true, --length);
|
|
|
|
// The next character better be a semicolon
|
|
if (p && (p - signature) > 1 && p[0] == ';') {
|
|
return p + 1;
|
|
}
|
|
} else {
|
|
// 4900761: For class version > 48, any unicode is allowed in class name.
|
|
length--;
|
|
signature++;
|
|
while (length > 0 && signature[0] != ';') {
|
|
if (signature[0] == '.') {
|
|
classfile_parse_error("Class name contains illegal character '.' in descriptor in class file %s", CHECK_0);
|
|
}
|
|
length--;
|
|
signature++;
|
|
}
|
|
if (signature[0] == ';') { return signature + 1; }
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
case JVM_SIGNATURE_ARRAY:
|
|
array_dim++;
|
|
if (array_dim > 255) {
|
|
// 4277370: array descriptor is valid only if it represents 255 or fewer dimensions.
|
|
classfile_parse_error("Array type descriptor has more than 255 dimensions in class file %s", CHECK_0);
|
|
}
|
|
// The rest of what's there better be a legal signature
|
|
signature++;
|
|
length--;
|
|
void_ok = false;
|
|
break;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|