bool Class::parse()

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