in vm/vmcore/src/class_support/Class_File_Loader.cpp [2761:3327]
bool Class::parse(Global_Env* env,
ByteReader& cfs)
{
/*
* find out if classloader is system or user defined
*/
bool is_trusted_cl = is_trusted_classloader(m_class_loader->GetName());
TRACE2("classloader.name", "classloader_name: " << m_class_loader->GetName() << " is trusted: " << is_trusted_cl);
/*
* get and check magic number (Oxcafebabe)
*/
U_32 magic;
if (!cfs.parse_u4_be(&magic)) {
REPORT_FAILED_CLASS_FORMAT(this, "class is not a valid Java class file");
return false;
}
//See 4.2 in specification about value of magic number
if (magic != CLASSFILE_MAGIC) {
REPORT_FAILED_CLASS_FORMAT(this, "invalid magic");
return false;
}
/*
* get and check major/minor version of classfile
* 1.1 (45.0-3) 1.2 (46.???) 1.3 (47.???) 1.4 (48.?) 5 (49.0)
* See 4.2 in specification about minor_version, major_version of classfile.
*/
uint16 minor_version;
if (!cfs.parse_u2_be(&minor_version)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse minor version");
return false;
}
if (!cfs.parse_u2_be(&m_version)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse major version");
return false;
}
//See comment in specification 4.2 about supported versions.
if (!(m_version >= CLASSFILE_MAJOR_MIN
&& m_version <= CLASSFILE_MAJOR_MAX))
{
REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/UnsupportedClassVersionError",
"class has version number " << m_version);
return false;
}
if(m_version == JAVA5_CLASS_FILE_VERSION && minor_version > 0)
{
REPORT_FAILED_CLASS_FORMAT(this, "unsupported class file version "
<< m_version << "." << minor_version);
return false;
}
/*
* allocate and parse constant pool
*/
if(!m_const_pool.parse(this, env->string_pool, cfs))
return false;
/*
* check and preprocess the constant pool
*/
if(!m_const_pool.check(env, this, is_trusted_cl))
return false;
/*
* parse access flags
*/
if(!cfs.parse_u2_be(&m_access_flags)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse access flags");
return false;
}
//If class is interface, it must be abstract.
//See specification 4.2 about access_flags.
if(is_interface()) {
// NOTE: Fix for the statement that an interface should have
// abstract flag set.
// spec/harness/BenchmarkDone has interface flag, but it does not
// have abstract flag.
m_access_flags |= ACC_ABSTRACT;
}
//for class file version lower than 49 these three flags should be set to zero
//See specification 4.5 Fields, for 1.4 Java.
if(m_version < JAVA5_CLASS_FILE_VERSION) {
m_access_flags &= ~(ACC_SYNTHETIC | ACC_ENUM | ACC_ANNOTATION);
}
/*
* can't be both final and interface, or both final and abstract
* See specification 4.2 about access_flags.
*/
if(is_final() && is_interface())
{
REPORT_FAILED_CLASS_FORMAT(this, "interface cannot be final");
return false;
}
// not only ACC_FINAL flag is prohibited if is_interface, also
// ACC_SYNTHETIC and ACC_ENUM. But in Java6 there appears to be an
// exception to this rule, the package annotation interfaces
// package-info can be interfaces with synthetic flag. So the
// check is different for different class file versions
if ((m_version <= JAVA5_CLASS_FILE_VERSION && is_interface() && (is_synthetic() || is_enum())) ||
(m_version == JAVA6_CLASS_FILE_VERSION && is_interface() && is_enum()))
{
REPORT_FAILED_CLASS_FORMAT(this,
"if class is interface, no flags except ACC_ABSTRACT or ACC_PUBLIC can be set");
return false;
}
if(is_final() && is_abstract()) {
REPORT_FAILED_CLASS_FORMAT(this, "abstract class cannot be final");
return false;
}
if(is_annotation() && !is_interface())
{
REPORT_FAILED_CLASS_FORMAT(this, "annotation type must be interface");
return false;
}
if(!is_interface() && is_annotation())
{
REPORT_FAILED_CLASS_FORMAT(this, "not interface can't be annotation");
return false;
}
/*
* parse this_class & super_class & verify their constant pool entries
*/
uint16 this_class;
if (!cfs.parse_u2_be(&this_class)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse this class index");
return false;
}
//See specification 4.2 about this_class.
if(!valid_cpi(this, this_class, CONSTANT_Class, "for this class"))
return false;
if(!valid_cpi(this, m_const_pool.get_class_name_index(this_class), CONSTANT_Utf8, "for this class name"))
return false;
String * class_name = m_const_pool.get_utf8_string(m_const_pool.get_class_name_index(this_class));
/*
* When defineClass from byte stream, there are cases that clss->name is null,
* so we should add a check here
*/
if(m_name != NULL && class_name != m_name) {
REPORT_FAILED_CLASS_CLASS(m_class_loader, this,
VM_Global_State::loader_env->JavaLangNoClassDefFoundError_String->bytes,
m_name->bytes << ": class name in class data does not match class name passed");
return false;
}
if(m_name == NULL) {
m_name = class_name;
}
/*
* Mark the current class as resolved.
*/
m_const_pool.resolve_entry(this_class, this);
/*
* parse the super class name
*/
uint16 super_class;
if (!cfs.parse_u2_be(&super_class)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse super class index");
return false;
}
m_super_class.cp_index = super_class;
if (super_class == 0) {
//
// This class must represent java.lang.Object
// See 4.2 in specification about super_class.
//
if(m_name != env->JavaLangObject_String) {
REPORT_FAILED_CLASS_FORMAT(this, " class does not contain super class "
<< "but is not java.lang.Object class.");
return false;
}
m_super_class.name = NULL;
} else {
if(!valid_cpi(this, super_class, CONSTANT_Class, "for super class"))
return false;
if(!valid_cpi(this, m_const_pool.get_class_name_index(super_class), CONSTANT_Utf8, "for super class name"))
return false;
String* super_name = m_const_pool.get_utf8_string(m_const_pool.get_class_name_index(super_class));
if(!check_class_name(super_name->bytes, super_name->len, m_version < JAVA5_CLASS_FILE_VERSION)) {
REPORT_FAILED_CLASS_FORMAT(this, " Illegal super class name "
<< super_name->bytes);
return false;
}
m_super_class.name = super_name;
if(is_interface() && m_super_class.name != env->JavaLangObject_String){
REPORT_FAILED_CLASS_FORMAT(this, " the super class of interface is "
<< m_super_class.name << "; must be java/lang/Object");
return false;
}
}
/*
* allocate and parse class' interfaces
*/
if(!parse_interfaces(cfs))
return false;
/*
* allocate and parse class' fields
*/
if(!parse_fields(env, cfs, is_trusted_cl))
return false;
/*
* allocate and parse class' methods
*/
if(!parse_methods(env, cfs, is_trusted_cl))
return false;
/*
* parse attributes
*/
uint16 n_attrs;
if (!cfs.parse_u2_be(&n_attrs)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse number of attributes");
return false;
}
unsigned numSourceFile = 0;
unsigned numSourceDebugExtensions = 0;
unsigned numEnclosingMethods = 0;
unsigned numRuntimeVisibleAnnotations = 0;
unsigned numRuntimeInvisibleAnnotations = 0;
U_32 attr_len = 0;
for (unsigned i=0; i<n_attrs; i++) {
Attributes cur_attr = parse_attribute(this, cfs, class_attrs, &attr_len);
switch(cur_attr){
case ATTR_SourceFile:
{
// a class file can have at most one source file attribute
numSourceFile++;
if (numSourceFile > 1) {
REPORT_FAILED_CLASS_FORMAT(this, "there is more than one SourceFile attribute");
return false;
}
// attribute length must be two (vm spec 4.8.2)
if (attr_len != 2) {
REPORT_FAILED_CLASS_FORMAT(this, " SourceFile attribute has incorrect length ("
<< attr_len << " bytes, should be 2 bytes)");
return false;
}
// constant value attribute
uint16 filename_index;
if(!cfs.parse_u2_be(&filename_index)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse filename index"
<< " while parsing SourceFile attribute");
return false;
}
if(!valid_cpi(this, filename_index, CONSTANT_Utf8, "for source file name at SourceFile attribute")) {
return false;
}
m_src_file_name = m_const_pool.get_utf8_string(filename_index);
break;
}
case ATTR_InnerClasses:
{
//See specification 4.8.5 about InnerClasses Attribute
if (m_declaring_class_index || m_innerclasses) {
REPORT_FAILED_CLASS_FORMAT(this, "more than one InnerClasses attribute");
return false;
}
bool isinner = false;
// found_myself == 2: myself is not inner class or has passed myself when iterating inner class attribute arrays
// found_myself == 1: myself is inner class, current index of inner class attribute arrays is just myself
// found_myself == 0: myself is inner class, hasn't met myself in inner class attribute arrays
int found_myself = 2;
if(strchr(m_name->bytes, '$')){
isinner = true;
found_myself = 0;
}
unsigned read_len = 0;
//Only handle inner class
uint16 num_of_classes;
if(!cfs.parse_u2_be(&num_of_classes)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse "
<< "number of classes while parsing InnerClasses attribute");
return false;
}
read_len += 2;
if(isinner)
m_num_innerclasses = (uint16)(num_of_classes - 1); //exclude itself
else
m_num_innerclasses = num_of_classes;
if(num_of_classes)
m_innerclasses = (InnerClass*) m_class_loader->
Alloc(2*sizeof(InnerClass)*m_num_innerclasses);
// ppervov: FIXME: should throw OOME
int index = 0;
for(int i = 0; i < num_of_classes; i++){
uint16 inner_clss_info_idx;
if(!cfs.parse_u2_be(&inner_clss_info_idx)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse "
<< "inner class info index while parsing InnerClasses attribute");
return false;
}
if(inner_clss_info_idx
&& !valid_cpi(this, inner_clss_info_idx, CONSTANT_Class, "for inner class at InnerClasses attribute"))
{
return false;
}
if(!found_myself){
if(!valid_cpi(this,m_const_pool.get_class_name_index(inner_clss_info_idx),
CONSTANT_Utf8, "for inner class name at InnerClasses attribute"))
return false;
String* clssname = m_const_pool.get_utf8_string(m_const_pool.get_class_name_index(inner_clss_info_idx));
// Only handle this class
if(m_name == clssname)
found_myself = 1;
}
if(found_myself != 1)
m_innerclasses[index].index = inner_clss_info_idx;
uint16 outer_clss_info_idx;
if(!cfs.parse_u2_be(&outer_clss_info_idx)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse "
<< "outer class info index while parsing InnerClasses attribute");
return false;
}
if(outer_clss_info_idx
&& !valid_cpi(this, outer_clss_info_idx, CONSTANT_Class, "for outer class at InnerClasses attribute"))
{
return false;
}
if(found_myself == 1 && outer_clss_info_idx){
m_declaring_class_index = outer_clss_info_idx;
}
uint16 inner_name_idx;
if(!cfs.parse_u2_be(&inner_name_idx)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse "
<< "inner name index while parsing InnerClasses attribute");
return false;
}
if(inner_name_idx && !valid_cpi(this, inner_name_idx, CONSTANT_Utf8,
"for inner name of InnerClass attribute"))
{
return false;
}
if(found_myself == 1){
if (inner_name_idx) {
m_simple_name = m_const_pool.get_utf8_string(inner_name_idx);
} else {
//anonymous class
m_simple_name = env->string_pool.lookup("");
}
}
uint16 inner_clss_access_flag;
if(!cfs.parse_u2_be(&inner_clss_access_flag)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse "
<< "inner class access flags while parsing InnerClasses attribute");
return false;
}
if(found_myself == 1) {
found_myself = 2;
m_access_flags = inner_clss_access_flag;
} else
m_innerclasses[index++].access_flags = inner_clss_access_flag;
} // for num_of_classes
read_len += num_of_classes * 8;
if(read_len != attr_len){
REPORT_FAILED_CLASS_FORMAT(this,
"unexpected length of InnerClass attribute: " << read_len << ", expected: " << attr_len);
return false;
}
}break; //case ATTR_InnerClasses
case ATTR_SourceDebugExtension:
{
// attribute length is already recorded in attr_len
// now reading debug extension information
numSourceDebugExtensions++;
if( numSourceDebugExtensions > 1 ) {
REPORT_FAILED_CLASS_FORMAT(this, " there is more than one SourceDebugExtension attribute");
return false;
}
// cfs is at debug_extension[] which is:
// The debug_extension array holds a string, which must be in UTF-8 format.
// There is no terminating zero byte.
m_sourceDebugExtension = class_file_parse_utf8data(env->string_pool, cfs, attr_len);
if(!m_sourceDebugExtension) {
REPORT_FAILED_CLASS_FORMAT(this, "invalid SourceDebugExtension attribute");
return false;
}
}
break;
case ATTR_EnclosingMethod:
{
//See specification 4.8.6
numEnclosingMethods++;
if ( numEnclosingMethods > 1 ) {
REPORT_FAILED_CLASS_FORMAT(this, "more than one EnclosingMethod attribute");
return false;
}
if (attr_len != 4) {
REPORT_FAILED_CLASS_FORMAT(this,
"unexpected length of EnclosingMethod attribute: " << attr_len);
return false;
}
uint16 class_idx;
if(!cfs.parse_u2_be(&class_idx)) {
REPORT_FAILED_CLASS_FORMAT(this,
"could not parse class index of EnclosingMethod attribute");
return false;
}
if(!valid_cpi(this, class_idx, CONSTANT_Class,
"for EnclosingMethod attribute"))
{
return false;
}
m_enclosing_class_index = class_idx;
//See specification 4.8.6 about method_index.
uint16 method_idx;
if(!cfs.parse_u2_be(&method_idx)) {
REPORT_FAILED_CLASS_FORMAT(this,
"could not parse method index of EnclosingMethod attribute");
return false;
}
if(method_idx && !valid_cpi(this, method_idx, CONSTANT_NameAndType,
"for EnclosingMethod attribute"))
{
return false;
}
m_enclosing_method_index = method_idx;
}
break;
case ATTR_Synthetic:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(this,
"attribute Synthetic has non-zero length");
return false;
}
m_access_flags |= ACC_SYNTHETIC;
}
break;
case ATTR_Deprecated:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(this,
"attribute Deprecated has non-zero length");
return false;
}
m_deprecated = true;
}
break;
case ATTR_Signature:
{
if(m_signature != NULL) {
REPORT_FAILED_CLASS_FORMAT(this,
"more than one Signature attribute for the class");
return false;
}
if (!(m_signature = parse_signature_attr(cfs, attr_len, this))) {
return false;
}
}
break;
case ATTR_RuntimeVisibleAnnotations:
{
//ClassFile may contain at most one RuntimeVisibleAnnotations attribute.
// See specification 4.8.14.
numRuntimeVisibleAnnotations++;
if(numRuntimeVisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(this,
"more than one RuntimeVisibleAnnotations attribute");
return false;
}
U_32 read_len = parse_annotation_table(&m_annotations, cfs, this);
if(attr_len == 0)
return false;
if (attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(this,
"error parsing RuntimeVisibleAnnotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
}
break;
case ATTR_RuntimeInvisibleAnnotations:
{
if(env->retain_invisible_annotations) {
//ClassFile may contain at most one RuntimeInvisibleAnnotations attribute.
numRuntimeInvisibleAnnotations++;
if(numRuntimeInvisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(this,
"more than one RuntimeInvisibleAnnotations attribute");
return false;
}
U_32 read_len = parse_annotation_table(&m_invisible_annotations, cfs, this);
if(read_len == 0)
return false;
if (attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(this,
"error parsing RuntimeInvisibleAnnotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
}else {
if(!cfs.skip(attr_len)) {
REPORT_FAILED_CLASS_FORMAT(this,
"Truncated class file");
return false;
}
}
}
break;
case ATTR_UNDEF:
// unrecognized attribute; skipped
break;
case ATTR_ERROR:
return false;
break;
default:
// error occured
REPORT_FAILED_CLASS_CLASS(m_class_loader, this, "java/lang/InternalError",
m_name->bytes << ": unknown error occured"
" while parsing attributes for class"
<< "; unprocessed attribute " << cur_attr);
return false;
} // switch
} // for
if (cfs.have(1)) {
REPORT_FAILED_CLASS_FORMAT(this, "Extra bytes at the end of class file");
return false;
}
if (m_enclosing_class_index && m_simple_name == NULL) {
LWARN(3, "Attention: EnclosingMethod attribute does not imply "
"InnerClasses presence for class {0}" << m_name->bytes);
}
return true;
} // Class::parse