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