bool Method::_parse_code()

in vm/vmcore/src/class_support/Class_File_Loader.cpp [1374:1732]


bool Method::_parse_code(Global_Env& env, ConstantPool& cp, unsigned code_attr_len,
                         ByteReader& cfs)
{
    unsigned real_code_attr_len = 0;
    if(!cfs.parse_u2_be(&_max_stack)) {
        REPORT_FAILED_METHOD("truncated class file: failed to parse max_stack "
            << "while parsing Code");
        return false;
    }

    if(!cfs.parse_u2_be(&_max_locals)) {
        REPORT_FAILED_METHOD("truncated class file: failed to parse max_locals "
            << "while parsing Code attribute");
        return false;
    }

    //See specification 4.8.3 about Code Attribute, max_locals.
    if(_max_locals < _arguments_slot_num) {
        REPORT_FAILED_METHOD(" wrong max_locals count "
            << "while parsing Code attribute");
        return false;
    }

    if(!cfs.parse_u4_be(& _byte_code_length)) {
        REPORT_FAILED_METHOD("truncated class file: failed to parse bytecode length "
            << "while parsing Code attribute");
        return false;
    }

    // See specification 4.8.3 and 4.10.1 about code_length.
    // code length for non-abstract java methods must not be 0
    if(_byte_code_length == 0
        || (_byte_code_length >= (1<<16)))
    {
        REPORT_FAILED_METHOD(" invalid bytecode length "
            << _byte_code_length);
        return false;
    }

    //See specification 4.8.3 about attribute_length value.
    real_code_attr_len += 8;

    //
    // allocate & parse code array
    //
    _byte_codes = new U_8[_byte_code_length];
    // ppervov: FIXME: should throw OOME

    unsigned i;
    for (i=0; i<_byte_code_length; i++) {
        if(!cfs.parse_u1(&_byte_codes[i])) {
            REPORT_FAILED_METHOD("truncated class file: failed to parse bytecode");
            return false;
        }
    }
    real_code_attr_len += _byte_code_length;

    if(!cfs.parse_u2_be(&_n_handlers)) {
        REPORT_FAILED_METHOD("truncated class file: failed to parse number of exception handlers");
        return false;
    }
    real_code_attr_len += 2;

    //
    // allocate & parse exception handler table
    //
    _handlers = new Handler[_n_handlers];
    // ppervov: FIXME: should throw OOME

    for (i=0; i<_n_handlers; i++) {
        if(!_handlers[i].parse(_class, _byte_code_length, cfs, this)) {
            return false;
        }
    }
    real_code_attr_len += _n_handlers*8; // for the size of exception_table entry see JVM Spec 4.8.3

    //
    // attributes of the Code attribute
    //
    uint16 n_attrs;
    if(!cfs.parse_u2_be(&n_attrs)) {
        REPORT_FAILED_METHOD("truncated class file: failed to parse number of attributes");
        return false;
    }
    real_code_attr_len += 2;

    static bool TI_enabled = VM_Global_State::loader_env->TI->isEnabled();

    U_32 attr_len = 0;
    LocalVarOffset* offset_lvt_array = NULL;
    LocalVarOffset* lvt_iter = NULL;
    LocalVarOffset* offset_lvtt_array = NULL;
    LocalVarOffset* lvtt_iter = NULL;
    unsigned num_lvt_entries = 0;
    unsigned num_lvtt_entries = 0;

    unsigned numStackMap = 0;
    m_stackmap = 0;

    for (i=0; i<n_attrs; i++) {
        Attributes cur_attr = parse_attribute(_class, cfs, code_attrs, &attr_len);
        switch(cur_attr) {
        case ATTR_LineNumberTable:
            {
                if  (!_parse_line_numbers(attr_len, cfs)) {
                    return false;
                }
                break;
            }
        case ATTR_LocalVariableTable:
            {

                uint16 n_local_vars;
                if(!cfs.parse_u2_be(&n_local_vars)) {
                    REPORT_FAILED_METHOD("could not parse local variables number "
                            "of LocalVariableTable attribute");
                    return false;
                }
                TRACE2("classloader.spec","number of local vars:" <<n_local_vars);
                unsigned lnt_attr_len = 2 + n_local_vars * 10;
                if(lnt_attr_len != attr_len) {
                    REPORT_FAILED_METHOD("real LocalVariableTable attribute length differ "
                        "from declared length ("
                        << attr_len << " vs. " << lnt_attr_len << ")" );
                    return false;
                }
                if(n_local_vars == 0) break;

                if(offset_lvt_array == NULL){
                    offset_lvt_array = lvt_iter =
                        (LocalVarOffset*)STD_ALLOCA(sizeof(LocalVarOffset) * n_local_vars);
                } else {
                    lvt_iter->next = (LocalVarOffset*)STD_ALLOCA(sizeof(LocalVarOffset) * n_local_vars);
                    lvt_iter = lvt_iter->next;
                }
                int off = cfs.get_offset();
                
                int j = 0;
                for(j = 0; j < n_local_vars - 1; j++, lvt_iter++)
                {
                    lvt_iter->value = off + 10*j;
                    lvt_iter->next = lvt_iter + 1;
                }
                lvt_iter->value = off + 10*j;
                lvt_iter->next = NULL;
                num_lvt_entries += n_local_vars;
                if (!cfs.skip(10*n_local_vars))
                {
                    REPORT_FAILED_CLASS_FORMAT(_class,
                            "Truncated class file");
                        return false;
                }
                break;
            }
        case ATTR_LocalVariableTypeTable:
            {
                if(_class->get_version() < JAVA5_CLASS_FILE_VERSION) {
                    //skip this attribute for class files of version less than 49
                    if (!cfs.skip(attr_len))
                    {
                        REPORT_FAILED_CLASS_FORMAT(_class,
                            "Truncated class file");
                        return false;
                    }
                } else {
                    uint16 n_local_vars;
                    if(!cfs.parse_u2_be(&n_local_vars)) {
                        REPORT_FAILED_METHOD("could not parse local variables number "
                                "of LocalVariableTypeTable attribute");
                        return false;
                    }
                    unsigned lnt_attr_len = 2 + n_local_vars * 10;
                    if(lnt_attr_len != attr_len) {
                        REPORT_FAILED_METHOD("real LocalVariableTypeTable attribute length differ "
                                "from declared length ("
                                << attr_len << " vs. " << lnt_attr_len << ")" );
                        return false;
                    }
                    if(n_local_vars == 0) break;

                    if(offset_lvtt_array == NULL){
                        offset_lvtt_array = lvtt_iter =
                            (LocalVarOffset*)STD_ALLOCA(sizeof(LocalVarOffset) * n_local_vars);
                    } else {
                        lvtt_iter->next = (LocalVarOffset*)STD_ALLOCA(sizeof(LocalVarOffset) * n_local_vars);
                        lvtt_iter = lvtt_iter->next;
                    }
                    int off = cfs.get_offset();
                    int j = 0;
                    for(j = 0; j < n_local_vars - 1; j++, lvtt_iter++)
                    {
                        lvtt_iter->value = off + 10*j;
                        lvtt_iter->next = lvtt_iter + 1;
                    }
                    lvtt_iter->value = off + 10*j;
                    lvtt_iter->next = NULL;
                    num_lvtt_entries += n_local_vars;

                    if (!cfs.skip(10*n_local_vars))
                    {
                        REPORT_FAILED_CLASS_FORMAT(_class,
                            "Truncated class file");
                        return false;
                    }
                }
                break;
            }

        case ATTR_StackMapTable:
            //TODO: skip if classfile version less than 50
            numStackMap++;
            if (numStackMap > 1) {
                REPORT_FAILED_METHOD(" there is more than one StackMap attribute");
                return false;
            }

            m_stackmap = (U_8*)Method::Alloc(attr_len + 6);
            if(!cfs.skip(-6)) { // read once again attribute head
                REPORT_FAILED_CLASS_CLASS(_class->get_class_loader(), _class, "java/lang/InternalError",
                    _class->get_name()->bytes << ": inernal error: unable to read beginning of an attribute");
                return false;
            }

            unsigned i;
            for (i=0; i<attr_len + 6; i++) {
                if(!cfs.parse_u1(&m_stackmap[i])) {
                    REPORT_FAILED_METHOD("truncated class file: failed to parse bytecode");
                    return false;
                }
            }

            break;

        case ATTR_UNDEF:
            // unrecognized attribute; skipped
            break;
        case ATTR_ERROR:
            return false;
        default:
            // error occured
            REPORT_FAILED_CLASS_CLASS(_class->get_class_loader(), _class, "java/lang/InternalError",
                _class->get_name()->bytes << ": unknown error occured "
                "while parsing attributes for code of method "
                << _name->bytes << _descriptor->bytes
                << "; unprocessed attribute " << cur_attr);
            return false;
        } // switch
        real_code_attr_len += 6 + attr_len; // u2 - attribute_name_index, u4 - attribute_length
    } // for
    if(code_attr_len != real_code_attr_len) {
        REPORT_FAILED_METHOD( " Code attribute length does not match real length "
            "in class file (" << code_attr_len << " vs. " << real_code_attr_len
            << ") while parsing attributes for code");
        return false;
    }

    // we should remember this point to return here
    // after complete LVT and LVTT parsing.
    int return_point = cfs.get_offset();

    if(_class->get_version() >= JAVA5_CLASS_FILE_VERSION) {
        if(num_lvt_entries == 0 && num_lvtt_entries != 0) {
            REPORT_FAILED_METHOD("if LocalVariableTable is empty "
                    "LocalVariableTypeTable must be empty too");
            return false;
        }
    }

    if(num_lvt_entries != 0) {
        //lvt and lvtt parsing
        Local_Var_Table* lv_table = NULL;
        Local_Var_Table* generic_vars = NULL;
        static const int LV_ALLOCATION_THRESHOLD = 30;

        if(num_lvtt_entries != 0) {
            if( num_lvtt_entries < LV_ALLOCATION_THRESHOLD ){
                generic_vars = (Local_Var_Table *)STD_ALLOCA(sizeof(Local_Var_Table) +
                        sizeof(Local_Var_Entry) * (num_lvtt_entries - 1));
            } else {
                generic_vars = (Local_Var_Table *)STD_MALLOC(sizeof(Local_Var_Table) +
                        sizeof(Local_Var_Entry) * (num_lvtt_entries - 1));
            }
            generic_vars->length = num_lvtt_entries;
        }

        if(TI_enabled) {
            lv_table = (Local_Var_Table *)_class->get_class_loader()->Alloc(
                sizeof(Local_Var_Table) +
                sizeof(Local_Var_Entry) * (num_lvt_entries - 1));
        } else {
            if( num_lvt_entries < LV_ALLOCATION_THRESHOLD){
                lv_table =(Local_Var_Table *)STD_ALLOCA(sizeof(Local_Var_Table) +
                    sizeof(Local_Var_Entry) * (num_lvt_entries - 1));
            } else {
                lv_table =(Local_Var_Table *)STD_MALLOC(sizeof(Local_Var_Table) +
                    sizeof(Local_Var_Entry) * (num_lvt_entries - 1));
            }
        }
        lv_table->length = num_lvt_entries;

	//this bool variable is needed to avoid memory leak in case
	//parsing of LVT failed 
        bool failed = false; 
        if (!_parse_local_vars(lv_table, offset_lvt_array, env, cp, cfs,
                "LocalVariableTable", ATTR_LocalVariableTable)
            || (generic_vars && !_parse_local_vars(generic_vars, offset_lvtt_array, env, cp, cfs,
                "LocalVariableTypeTable", ATTR_LocalVariableTypeTable)))
        {
            failed = true;
        }
        // JVM spec hints that LocalVariableTypeTable is meant to be a supplement to LocalVariableTable
        // See specification 4.8.13 second paragraph.
        if (!failed && generic_vars) {
            unsigned j = i = 0;
            for (i = 0; i < generic_vars->length; i++) {
                for (j = 0; j < lv_table->length; j++) {
                    if (generic_vars->table[i].name == lv_table->table[j].name
                        && generic_vars->table[i].start_pc == lv_table->table[j].start_pc
                        && generic_vars->table[i].length == lv_table->table[j].length
                        && generic_vars->table[i].index == lv_table->table[j].index)
                    {
                        lv_table->table[j].generic_type = generic_vars->table[i].type;
                        break;
                    }
                }
                if(j == lv_table->length && env.verify) {
                    REPORT_FAILED_METHOD("Element "<< generic_vars->table[i].name->bytes <<
                        " of LocalVariableTypeTable does not match any of LocalVariableTable entries");
                    failed = true;
                    break;
                }
            }
        }

        if(TI_enabled) {
            _local_vars_table = lv_table;
        } else {
            if(num_lvt_entries >= LV_ALLOCATION_THRESHOLD) {
                STD_FREE(lv_table);
            }
            if( num_lvtt_entries >= LV_ALLOCATION_THRESHOLD ){
                STD_FREE(generic_vars);
            }
        }

        if (failed) {
            return false;
        }
    }


    //return to the right ByteReader point
    if(!cfs.go_to_offset(return_point))
    {
        return false;
    }

    return true;
} //Method::_parse_code