blob: ae5272bf582e763c944f9f72748798a0a563ffca [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_DOMAIN LOG_CLASS_INFO
#include "cxxlog.h"
#include "port_filepath.h"
#include "environment.h"
#include "classloader.h"
#include "Class.h"
#include "class_member.h"
#include "vm_strings.h"
#include "open/vm_util.h"
#include "bytereader.h"
#include "compile.h"
#include "interpreter_exports.h"
#include "jarfile_util.h"
#include "unicode/uchar.h"
/*
* References to the JVM spec below are the references to Java Virtual Machine
* Specification, Second Edition
*/
#define REPORT_FAILED_CLASS_FORMAT(klass, msg) \
{ \
std::stringstream ss; \
ss << klass->get_name()->bytes << " : " << msg; \
klass->get_class_loader()->ReportFailedClass(klass, "java/lang/ClassFormatError", ss); \
}
//returns constant pool tag symbolic name
static const char* get_tag_name(ConstPoolTags tag) {
switch(tag) {
case CONSTANT_Utf8:
return "CONSTANT_Utf8";
case CONSTANT_Integer:
return "CONSTANT_Integer";
case CONSTANT_Float:
return "CONSTANT_Float";
case CONSTANT_Long:
return "CONSTANT_Long";
case CONSTANT_Double:
return "CONSTANT_Double";
case CONSTANT_Class:
return "CONSTANT_Class";
case CONSTANT_String:
return "CONSTANT_String";
case CONSTANT_Fieldref:
return "CONSTANT_Fieldref";
case CONSTANT_Methodref:
return "CONSTANT_Methodref";
case CONSTANT_InterfaceMethodref:
return "CONSTANT_InterfaceMethodref";
case CONSTANT_NameAndType:
return "CONSTANT_NameAndType";
case CONSTANT_UnusedEntry:
return "CONSTANT_UnusedEntry";
}
return "(Unknown tag)";
}
//checks if constant pool index and tag are valid
inline static bool
valid_cpi(Class* clss, uint16 idx, ConstPoolTags tag, const char* msg) {
if(!clss->get_constant_pool().is_valid_index(idx)) {
//report error message about wrong index
REPORT_FAILED_CLASS_FORMAT(clss, "invalid constant pool index: "
<< idx << " " << msg);
return false;
}
if(clss->get_constant_pool().get_tag(idx) != tag) {
//report error message about wrong tag
REPORT_FAILED_CLASS_FORMAT(clss, "invalid constant pool tag "
"(expected " << get_tag_name(tag) << " got "
<< get_tag_name((ConstPoolTags)clss->get_constant_pool().get_tag(idx)) << ") "
<< msg);
return false;
}
return true;
}
struct AttributeID {
const char* name;
String* interned_name;
Attributes attr_id;
};
AttributeID field_attrs[] = {
{"ConstantValue", NULL, ATTR_ConstantValue},
{"Synthetic", NULL, ATTR_Synthetic},
{"Deprecated", NULL, ATTR_Deprecated},
{"Signature", NULL, ATTR_Signature},
{"RuntimeVisibleAnnotations", NULL, ATTR_RuntimeVisibleAnnotations},
{"RuntimeInvisibleAnnotations", NULL, ATTR_RuntimeInvisibleAnnotations},
{NULL, NULL, ATTR_UNDEF}
};
AttributeID method_attrs[] = {
{"Code", NULL, ATTR_Code},
{"Exceptions", NULL, ATTR_Exceptions},
{"RuntimeVisibleParameterAnnotations", NULL, ATTR_RuntimeVisibleParameterAnnotations},
{"RuntimeInvisibleParameterAnnotations", NULL, ATTR_RuntimeInvisibleParameterAnnotations},
{"AnnotationDefault", NULL, ATTR_AnnotationDefault},
{"Synthetic", NULL, ATTR_Synthetic},
{"Deprecated", NULL, ATTR_Deprecated},
{"Signature", NULL, ATTR_Signature},
{"RuntimeVisibleAnnotations", NULL, ATTR_RuntimeVisibleAnnotations},
{"RuntimeInvisibleAnnotations", NULL, ATTR_RuntimeInvisibleAnnotations},
{NULL, NULL, ATTR_UNDEF}
};
AttributeID class_attrs[] = {
{"SourceFile", NULL, ATTR_SourceFile},
{"InnerClasses", NULL, ATTR_InnerClasses},
{"SourceDebugExtension", NULL, ATTR_SourceDebugExtension},
{"EnclosingMethod", NULL, ATTR_EnclosingMethod},
{"Synthetic", NULL, ATTR_Synthetic},
{"Deprecated", NULL, ATTR_Deprecated},
{"Signature", NULL, ATTR_Signature},
{"RuntimeVisibleAnnotations", NULL, ATTR_RuntimeVisibleAnnotations},
{"RuntimeInvisibleAnnotations", NULL, ATTR_RuntimeInvisibleAnnotations},
{NULL, NULL, ATTR_UNDEF}
};
AttributeID code_attrs[] = {
{"LineNumberTable", NULL, ATTR_LineNumberTable},
{"LocalVariableTable", NULL, ATTR_LocalVariableTable},
{"LocalVariableTypeTable", NULL, ATTR_LocalVariableTypeTable},
{"StackMapTable", NULL, ATTR_StackMapTable},
{NULL, NULL, ATTR_UNDEF}
};
static void preload(String_Pool& string_pool, AttributeID* attrs) {
for(int i = 0; attrs[i].name != NULL; i++)
{
attrs[i].interned_name = string_pool.lookup(attrs[i].name);
}
}
//
// initializes string pool by preloading it with commonly used strings
//
static bool preload_attrs(String_Pool& string_pool)
{
preload(string_pool, method_attrs);
preload(string_pool, field_attrs);
preload(string_pool, class_attrs);
preload(string_pool, code_attrs);
return true;
}
static String* parse_signature_attr(ByteReader &cfs,
U_32 attr_len,
Class* clss)
{
//See specification 4.8.8 about attribute length
if (attr_len != 2) {
REPORT_FAILED_CLASS_FORMAT(clss,
"unexpected length of Signature attribute : " << attr_len);
return NULL;
}
uint16 idx;
if (!cfs.parse_u2_be(&idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse Signature index");
return NULL;
}
if(!valid_cpi(clss, idx, CONSTANT_Utf8, "for signature at Signature attribute"))
return NULL;
return clss->get_constant_pool().get_utf8_string(idx);
}
static
Attributes parse_attribute(Class *clss,
ByteReader &cfs,
AttributeID* attrs,
U_32 *attr_len)
{
static bool UNUSED init = preload_attrs(VM_Global_State::loader_env->string_pool);
//See specification 4.8 about Attributes
uint16 attr_name_index;
if (!cfs.parse_u2_be(&attr_name_index))
{
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse attr_name_index");
return ATTR_ERROR;
}
if (!cfs.parse_u4_be(attr_len))
{
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse attribute length");
return ATTR_ERROR;
}
if(!valid_cpi(clss, attr_name_index, CONSTANT_Utf8, "for attribute name"))
return ATTR_ERROR;
String* attr_name = clss->get_constant_pool().get_utf8_string(attr_name_index);
for (unsigned i=0; attrs[i].interned_name != NULL ; i++) {
if (attrs[i].interned_name == attr_name)
return attrs[i].attr_id;
}
//
// unrecognized attribute; skip
//
if(!cfs.skip(*attr_len))
{
REPORT_FAILED_CLASS_FORMAT(clss,
"Truncated class file");
return ATTR_ERROR;
}
return ATTR_UNDEF;
} //parse_attribute
// forward declaration
static U_32
parse_annotation_value(AnnotationValue& value, ByteReader& cfs, Class* clss);
// returns number of read bytes, 0 if error occurred
static U_32
parse_annotation(Annotation** value, ByteReader& cfs, Class* clss)
{
unsigned initial_offset = cfs.get_offset();
uint16 type_idx;
if (!cfs.parse_u2_be(&type_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse type index of annotation");
return 0;
}
if(!valid_cpi(clss, type_idx, CONSTANT_Utf8, "for annotation type"))
return 0;
String* type = clss->get_constant_pool().get_utf8_string(type_idx);
uint16 num_elements;
if (!cfs.parse_u2_be(&num_elements)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse number of annotation elements");
return 0;
}
Annotation* antn = (Annotation*) clss->get_class_loader()->Alloc(
sizeof(Annotation) + num_elements * sizeof(AnnotationElement));
//FIXME: verav should throw OOM
antn->type = type;
antn->num_elements = num_elements;
antn->elements = (AnnotationElement*)((POINTER_SIZE_INT)antn + sizeof(Annotation));
*value = antn;
for (unsigned j = 0; j < num_elements; j++)
{
uint16 name_idx;
if (!cfs.parse_u2_be(&name_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse element_name_index of annotation element");
return 0;
}
if(!valid_cpi(clss, name_idx, CONSTANT_Utf8, "for annotation element name"))
return 0;
antn->elements[j].name = clss->get_constant_pool().get_utf8_string(name_idx);
if (parse_annotation_value(antn->elements[j].value, cfs, clss) == 0) {
return 0;
}
}
return cfs.get_offset() - initial_offset;
}
// returns number of read bytes, 0 if error occurred
static U_32
parse_annotation_value(AnnotationValue& value, ByteReader& cfs, Class* clss)
{
unsigned initial_offset = cfs.get_offset();
U_8 tag;
if (!cfs.parse_u1(&tag)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse annotation value tag");
return 0;
}
value.tag = (AnnotationValueType)tag;
ConstantPool& cp = clss->get_constant_pool();
unsigned cp_size = cp.get_size();
switch(tag) {
case AVT_BOOLEAN:
case AVT_BYTE:
case AVT_SHORT:
case AVT_CHAR:
case AVT_INT:
case AVT_LONG:
case AVT_FLOAT:
case AVT_DOUBLE:
case AVT_STRING:
{
uint16 const_idx;
if (!cfs.parse_u2_be(&const_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse const index of annotation value");
return 0;
}
switch (tag) {
case AVT_BOOLEAN:
case AVT_BYTE:
case AVT_SHORT:
case AVT_CHAR:
case AVT_INT:
if (!valid_cpi(clss, const_idx, CONSTANT_Integer, "of const_value for annotation element value"))
return 0;
value.const_value.i = cp.get_int(const_idx);
break;
case AVT_FLOAT:
if (!valid_cpi(clss, const_idx, CONSTANT_Float, "of const_value for annotation element value"))
return 0;
value.const_value.f = cp.get_float(const_idx);
break;
case AVT_LONG:
if (!valid_cpi(clss, const_idx, CONSTANT_Long, "of const_value for annotation element value"))
return 0;
value.const_value.l.lo_bytes = cp.get_8byte_low_word(const_idx);
value.const_value.l.hi_bytes = cp.get_8byte_high_word(const_idx);
break;
case AVT_DOUBLE:
if (!valid_cpi(clss, const_idx, CONSTANT_Double, "of const_value for annotation element value"))
return 0;
value.const_value.l.lo_bytes = cp.get_8byte_low_word(const_idx);
value.const_value.l.hi_bytes = cp.get_8byte_high_word(const_idx);
break;
case AVT_STRING:
if (!valid_cpi(clss, const_idx, CONSTANT_Utf8, "of const_value for annotation element value"))
return 0;
value.const_value.string = cp.get_utf8_string(const_idx);
break;
default:
LDIE(68, "Annotation parsing internal error");
}
}
break;
case AVT_CLASS:
{
uint16 class_idx;
if (!cfs.parse_u2_be(&class_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse class_info_index of annotation value");
return 0;
}
if(!valid_cpi(clss, class_idx, CONSTANT_Utf8, " of class for annotation element value"))
return 0;
value.class_name = cp.get_utf8_string(class_idx);
}
break;
case AVT_ENUM:
{
uint16 type_idx;
if (!cfs.parse_u2_be(&type_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse type_name_index of annotation enum value");
return 0;
}
if(!valid_cpi(clss, type_idx, CONSTANT_Utf8, "of type_name for annotation enum element value"))
return 0;
value.enum_const.type = cp.get_utf8_string(type_idx);
uint16 name_idx;
if (!cfs.parse_u2_be(&name_idx)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse const_name_index of annotation enum value");
return 0;
}
if(!valid_cpi(clss, name_idx, CONSTANT_Utf8, "of const_name for annotation enum element value"))
return 0;
value.enum_const.name = cp.get_utf8_string(name_idx);
}
break;
case AVT_ANNOTN:
{
if (parse_annotation(&value.nested, cfs, clss) == 0) {
return 0;
}
}
break;
case AVT_ARRAY:
{
uint16 num;
if (!cfs.parse_u2_be(&num)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse num_values of annotation array value");
return 0;
}
value.array.length = num;
if (num) {
value.array.items = (AnnotationValue*) clss->get_class_loader()->Alloc(
num * sizeof(AnnotationValue));
//FIXME: verav should throw OOM
for (int i = 0; i < num; i++) {
if (parse_annotation_value(value.array.items[i], cfs, clss) == 0) {
return 0;
}
}
}
}
break;
default:
REPORT_FAILED_CLASS_FORMAT(clss,
"unrecognized annotation value tag : " << (int)tag);
return 0;
}
return cfs.get_offset() - initial_offset;
}
// returns number of read bytes, 0 if error occurred
static U_32
parse_annotation_table(AnnotationTable ** table, ByteReader& cfs, Class* clss)
{
unsigned initial_offset = cfs.get_offset();
uint16 num_annotations;
if (!cfs.parse_u2_be(&num_annotations)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse number of Annotations");
return 0;
}
if (num_annotations) {
*table = (AnnotationTable*) clss->get_class_loader()->Alloc(
sizeof (AnnotationTable) + (num_annotations - 1)*sizeof(Annotation*));
//FIXME:verav should throw OOM
(*table)->length = num_annotations;
for (unsigned i = 0; i < num_annotations; i++)
{
if (parse_annotation((*table)->table + i, cfs, clss) == 0) {
return 0;
}
}
} else {
*table = NULL;
}
return cfs.get_offset() - initial_offset;
}
static U_32
parse_parameter_annotations(AnnotationTable *** table,
U_8 num_annotations,
ByteReader& cfs, Class* clss)
{
unsigned initial_offset = cfs.get_offset();
*table = (AnnotationTable**)clss->get_class_loader()->Alloc(
num_annotations * sizeof (AnnotationTable*));
//FIXME: verav should throw OOM
for (unsigned i = 0; i < num_annotations; i++)
{
if(parse_annotation_table(*table + i, cfs, clss) == 0)
return 0;
}
return cfs.get_offset() - initial_offset;
}
void* Class_Member::Alloc(size_t size) {
ClassLoader* cl = get_class()->get_class_loader();
assert(cl);
return cl->Alloc(size);
}
bool Class_Member::parse(Class* clss, ByteReader &cfs)
{
if (!cfs.parse_u2_be(&_access_flags)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: "
<< "failed to parse member access flags");
return false;
}
_class = clss;
uint16 name_index;
if (!cfs.parse_u2_be(&name_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: "
<< "failed to parse member name index");
return false;
}
uint16 descriptor_index;
if (!cfs.parse_u2_be(&descriptor_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: "
<< "failed to parse member descriptor index");
return false;
}
ConstantPool& cp = clss->get_constant_pool();
//
// Look up the name_index and descriptor_index
// utf8 string const pool entries.
// See specification 4.6 about name_index and descriptor_index.
//
if(!valid_cpi(clss, name_index, CONSTANT_Utf8, "for member name"))
return false;
if(!valid_cpi(clss, descriptor_index, CONSTANT_Utf8, "for member descriptor"))
return false;
_name = cp.get_utf8_string(name_index);
_descriptor = cp.get_utf8_string(descriptor_index);
return true;
} //Class_Member::parse
static bool
is_identifier(const char *name, unsigned len)
{
U_32 u_ch;
for(unsigned i = 0; i < len;) {
unsigned ch_len = 0;
if(name[i] & 0x80) {
assert(name[i] & 0x40);
if(name[i] & 0x20) {
U_32 x = name[i];
U_32 y = name[i + 1];
U_32 z = name[i + 2];
u_ch = (U_32)(((0x0f & x) << 12) + ((0x3f & y) << 6) + ((0x3f & z)));
ch_len = 3;
} else {
U_32 x = name[i];
U_32 y = name[i + 1];
u_ch = (U_32)(((0x1f & x) << 6) + (0x3f & y));
ch_len = 2;
}
} else {
u_ch = name[i];
ch_len = 1;
}
if(i == 0) {
if(!(u_isalpha(u_ch) || u_ch == '$' || u_ch == '_'))
return false;
} else {
if(!(u_isalnum(u_ch) || u_ch == '$' || u_ch == '_'))
return false;
}
i += ch_len;
}
return true;
}
// JVM spec:
// Unqualified names must not contain the characters ".", ";", "[" or "/". Method names are
// further constrained so that, with the exception of the special method names (§3.9)
// <init> and <clinit>, they must not contain the characters "<" or ">".
// <init> and <clinit> are not passed to this function
static inline bool
is_valid_member_name(const char *name, unsigned len, bool old_version, bool is_method)
{
if(old_version) {
return is_identifier(name, len);
}else {
for (unsigned i = 0; i < len; i++) {
//check if symbol has byte size
if(!(name[i] & 0x80)) {
switch(name[i]) {
case '.':
case ';':
case '[':
case '/':
return false;
case '<':
case '>':
if(is_method)
return false;
}
} else { //skip other symbols, they are not in exception list
assert(name[i] & 0x40);
if(name[i] & 0x20) {
if(len < i + 3) { //check array bound
return false;
}
i += 2;
} else {
if(len < i + 2) {
return false;
}
i++;
}
}
}
}
return true;
}
static inline bool
is_valid_member_descriptor( const char *descriptor,
const char **next,
bool is_void_legal,
bool old_version)
{
switch (*descriptor)
{
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
*next = descriptor + 1;
return true;
case 'V':
if( is_void_legal ) {
*next = descriptor + 1;
return true;
} else {
return false;
}
case 'L':
{
unsigned id_len = 0;
//See specification 4.4.2 about field descriptors that
//classname represents a fully qualified class or interface name in internal form.
const char* iterator;
for(iterator = ++descriptor;
*iterator != ';';
iterator++)
{
if( *iterator == '\0' ) {
// bad Java descriptor
return false;
}
if(*iterator == '/') {
if(!is_valid_member_name(descriptor, id_len, old_version, false))
return false;
id_len = 0;
descriptor = iterator + 1;
} else {
id_len++;
}
}
if(!is_valid_member_name(descriptor, id_len, old_version, false))
return false;
*next = iterator + 1;
return true;
}
case '[':
{
//See specification 4.4.2 or 4.5.1
//that array descriptor should represent array with 255 or fewer dimensions
unsigned dim = 1;
while(*(++descriptor) == '[') dim++;
if (dim > 255) return false;
return is_valid_member_descriptor(descriptor, next, is_void_legal, old_version);
}
default:
// bad Java descriptor
return false;
}
}
static bool is_magic_type_name(const String* name) {
static String* MAGIC_TYPE_NAMES[]={
VM_Global_State::loader_env->string_pool.lookup("Lorg/vmmagic/unboxed/Address;"),
VM_Global_State::loader_env->string_pool.lookup("Lorg/vmmagic/unboxed/Offset;"),
VM_Global_State::loader_env->string_pool.lookup("Lorg/vmmagic/unboxed/Word;"),
VM_Global_State::loader_env->string_pool.lookup("Lorg/vmmagic/unboxed/Extent;"),
NULL
};
for (int i=0;;i++) {
String* magicClassName = MAGIC_TYPE_NAMES[i];
if (magicClassName == NULL) break;
if (magicClassName == name) {
return true;
}
}
return false;
}
static bool
is_trusted_classloader(const String *name) {
if(name == NULL) //bootstrap classloader
return true;
if(name->len < 5)
return false;
static const char* buf = "java/";
if (0 == strncmp(buf, name->bytes, 5)) {
return true;
} else {
return false;
}
}
//define constant for class file version
static const uint16 JAVA5_CLASS_FILE_VERSION = 49;
static const uint16 JAVA6_CLASS_FILE_VERSION = 50;
bool Field::parse(Global_Env& env, Class *clss, ByteReader &cfs, bool is_trusted_cl)
{
if(!Class_Member::parse(clss, cfs))
return false;
bool old_version = clss->get_version() < JAVA5_CLASS_FILE_VERSION;
//check field name
if(is_trusted_cl) {
if(env.verify_all
&& !is_valid_member_name(_name->bytes, _name->len, old_version, false))
{
REPORT_FAILED_CLASS_FORMAT(clss, "illegal field name : " << _name->bytes);
return false;
}
} else {//always check field name if classloader is not trusted
if(!is_valid_member_name(_name->bytes, _name->len, old_version, false))
{
REPORT_FAILED_CLASS_FORMAT(clss, "illegal field name : " << _name->bytes);
return false;
}
}
// check field descriptor
//See specification 4.4.2 about field descriptors.
const char* next;
if(!is_valid_member_descriptor(_descriptor->bytes, &next, false, old_version)
|| *next != '\0')
{
REPORT_FAILED_CLASS_FORMAT(clss, "illegal field descriptor : " << _descriptor->bytes);
return false;
}
// check fields access flags
//See specification 4.6 about access flags
if(clss->is_interface()) {
// check interface fields access flags
if(!(is_public() && is_static() && is_final())){
REPORT_FAILED_CLASS_FORMAT(clss, "interface field " << get_name()->bytes
<< " has invalid combination of access flags: "
<< "0x" << std::hex << _access_flags);
return false;
}
if(_access_flags & ~(ACC_FINAL | ACC_PUBLIC | ACC_STATIC | ACC_SYNTHETIC)){
REPORT_FAILED_CLASS_FORMAT(clss, "interface field " << get_name()->bytes
<< " has invalid combination of access flags: "
<< "0x"<< std::hex << _access_flags);
return false;
}
if(old_version) {
//for class file version lower than 49 these two flags should be set to zero
//See specification 4.5 Fields, for 1.4 Java.
_access_flags &= ~(ACC_SYNTHETIC | ACC_ENUM);
}
} else if((is_public() && is_protected()
|| is_protected() && is_private()
|| is_public() && is_private())
|| (is_final() && is_volatile())) {
REPORT_FAILED_CLASS_FORMAT(clss, "field " << get_name()->bytes
<< " has invalid combination of access flags: "
<< "0x" << std::hex << _access_flags);
return false;
}
//check if field is magic type
if (is_magic_type_name(_descriptor)) {
_is_magic_type = 1;
}
//check field attributes
uint16 attr_count;
if(!cfs.parse_u2_be(&attr_count)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: "
<< "failed to parse attribute count for field " << get_name()->bytes);
return false;
}
_offset_computed = 0;
unsigned numConstantValue = 0;
unsigned numRuntimeVisibleAnnotations = 0;
unsigned numRuntimeInvisibleAnnotations = 0;
U_32 attr_len = 0;
ConstantPool& cp = clss->get_constant_pool();
for (unsigned j=0; j<attr_count; j++)
{
// See specification 4.6 about attributes[]
Attributes cur_attr = parse_attribute(clss, cfs, field_attrs, &attr_len);
switch (cur_attr) {
case ATTR_ConstantValue:
{ // constant value attribute
// a field can have at most 1 ConstantValue attribute
// See specification 4.8.2 about ConstantValueAttribute.
numConstantValue++;
if (numConstantValue > 1) {
REPORT_FAILED_CLASS_FORMAT(clss, " field " <<
get_name()->bytes << " has more then one ConstantValue attribute");
return false;
}
// attribute length must be two (vm spec reference 4.7.3)
if (attr_len != 2) {
REPORT_FAILED_CLASS_FORMAT(clss, " ConstantValue attribute has invalid length for field "
<< get_name()->bytes);
return false;
}
//For non-static field ConstantValue attribute must be silently ignored
//See specification 4.8.2, second paragraph
if(!is_static())
{
if(!cfs.skip(attr_len))
{
REPORT_FAILED_CLASS_FORMAT(clss,
"Truncated class file");
return false;
}
}
else
{
if(!cfs.parse_u2_be(&_const_value_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse "
<< "ConstantValue index for field " << get_name()->bytes);
return false;
}
if(!cp.is_valid_index(_const_value_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "invalid ConstantValue index for field " << get_name()->bytes);
return false;
}
Java_Type java_type = get_java_type();
switch(cp.get_tag(_const_value_index)) {
case CONSTANT_Long:
{
if (java_type != JAVA_TYPE_LONG) {
REPORT_FAILED_CLASS_FORMAT(clss, " data type CONSTANT_Long of ConstantValue "
<< "does not correspond to the type of field " << get_name()->bytes);
return false;
}
const_value.l.lo_bytes = cp.get_8byte_low_word(_const_value_index);
const_value.l.hi_bytes = cp.get_8byte_high_word(_const_value_index);
break;
}
case CONSTANT_Float:
{
if (java_type != JAVA_TYPE_FLOAT) {
REPORT_FAILED_CLASS_FORMAT(clss, " data type CONSTANT_Float of ConstantValue "
<< "does not correspond to the type of field " << get_name()->bytes);
return false;
}
const_value.f = cp.get_float(_const_value_index);
break;
}
case CONSTANT_Double:
{
if (java_type != JAVA_TYPE_DOUBLE) {
REPORT_FAILED_CLASS_FORMAT(clss, " data type CONSTANT_Double of ConstantValue "
<< "does not correspond to the type of field " << get_name()->bytes);
return false;
}
const_value.l.lo_bytes = cp.get_8byte_low_word(_const_value_index);
const_value.l.hi_bytes = cp.get_8byte_high_word(_const_value_index);
break;
}
case CONSTANT_Integer:
{
if ( !(java_type == JAVA_TYPE_INT ||
java_type == JAVA_TYPE_SHORT ||
java_type == JAVA_TYPE_BOOLEAN ||
java_type == JAVA_TYPE_BYTE ||
java_type == JAVA_TYPE_CHAR) )
{
REPORT_FAILED_CLASS_FORMAT(clss, " data type CONSTANT_Integer of ConstantValue "
<< "does not correspond to the type of field " << get_name()->bytes);
return false;
}
const_value.i = cp.get_int(_const_value_index);
break;
}
case CONSTANT_String:
{
if (java_type != JAVA_TYPE_CLASS) {
REPORT_FAILED_CLASS_FORMAT(clss, " data type CONSTANT_String of ConstantValue "
<< "does not correspond to the type of field " << get_name()->bytes);
return false;
}
const_value.string = cp.get_string(_const_value_index);
break;
}
case CONSTANT_UnusedEntry:
{
//do nothing here
break;
}
default:
{
REPORT_FAILED_CLASS_FORMAT(clss, " invalid data type tag of ConstantValue "
<< "for field " << get_name()->bytes);
return false;
}
}//switch
}//else for static field
}//case ATTR_ConstantValue
break;
case ATTR_Synthetic:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(clss,
"attribute Synthetic has non-zero length");
return false;
}
_access_flags |= ACC_SYNTHETIC;
}
break;
case ATTR_Deprecated:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(clss,
"attribute Deprecated has non-zero length");
return false;
}
_deprecated = true;
}
break;
case ATTR_Signature:
{
if(_signature != NULL) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one Signature attribute for the class");
return false;
}
if (!(_signature = parse_signature_attr(cfs, attr_len, clss))) {
return false;
}
}
break;
case ATTR_RuntimeVisibleAnnotations:
{
// Each field_info structure may contain at most one RuntimeVisibleAnnotations attribute.
// See specification 4.8.14.
numRuntimeVisibleAnnotations++;
if(numRuntimeVisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one RuntimeVisibleAnnotations attribute");
return false;
}
U_32 read_len = parse_annotation_table(&_annotations, cfs, clss);
if(read_len == 0)
return false;
if (attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(clss,
"error parsing Annotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
}
break;
case ATTR_RuntimeInvisibleAnnotations:
{
// Each field_info structure may contain at most one RuntimeInvisibleAnnotations attribute.
numRuntimeInvisibleAnnotations++;
if(numRuntimeInvisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one RuntimeVisibleAnnotations attribute");
return false;
}
if(env.retain_invisible_annotations) {
U_32 read_len =
parse_annotation_table(&_invisible_annotations, cfs, clss);
if(read_len == 0)
return false;
if(attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(clss,
"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(_class,
"Truncated class file");
return false;
}
}
}
break;
case ATTR_UNDEF:
// unrecognized attribute; skipped
break;
case ATTR_ERROR:
return false;
default:
REPORT_FAILED_CLASS_CLASS(_class->get_class_loader(), _class, "java/lang/InternalError",
_class->get_name()->bytes << ": unknown error occured "
"while parsing attributes for field "
<< _name->bytes << _descriptor->bytes
<< "; unprocessed attribute " << cur_attr);
return false;
} // switch
} // for
TypeDesc* td = type_desc_create_from_java_descriptor(get_descriptor()->bytes, clss->get_class_loader());
if( td == NULL ) {
exn_raise_object(VM_Global_State::loader_env->java_lang_OutOfMemoryError);
return false;
}
set_field_type_desc(td);
return true;
} //Field::parse
bool Handler::parse(Class* clss, unsigned code_length,
ByteReader& cfs, Method* method)
{
//See specification 4.8.3 about exception_table
uint16 start = 0;
if(!cfs.parse_u2_be(&start)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse start_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
_start_pc = (unsigned) start;
if (_start_pc >= code_length){
REPORT_FAILED_CLASS_FORMAT(clss, " illegal start_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
uint16 end;
if (!cfs.parse_u2_be(&end)){
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse end_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
_end_pc = (unsigned) end;
if (_end_pc > code_length){
REPORT_FAILED_CLASS_FORMAT(clss, " illegal end_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
if (_start_pc >= _end_pc){
REPORT_FAILED_CLASS_FORMAT(clss, " start_pc is not less than end_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
uint16 handler;
if (!cfs.parse_u2_be(&handler)){
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse handler_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
_handler_pc = (unsigned) handler;
if (_handler_pc >= code_length){
REPORT_FAILED_CLASS_FORMAT(clss, " illegal handler_pc"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
uint16 catch_index;
if (!cfs.parse_u2_be(&catch_index)){
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse catch_type"
<< " for exception handler of code attribute for method "
<< method->get_name()->bytes << method->get_descriptor()->bytes);
return false;
}
_catch_type_index = catch_index;
if (catch_index == 0) {
_catch_type = NULL;
} else {
if(!valid_cpi(clss, catch_index, CONSTANT_Class, "for exception handler class of code attribute"))
return false;
ConstantPool& cp = clss->get_constant_pool();
if(!valid_cpi(clss, cp.get_class_name_index(catch_index), CONSTANT_Utf8, "for exception handler class name of code attribute"))
return false;
_catch_type = cp.get_utf8_string(cp.get_class_name_index(catch_index));
}
return true;
} //Handler::parse
bool Method::get_line_number_entry(unsigned index, jlong* pc, jint* line) {
if (_line_number_table && index < _line_number_table->length) {
*pc = _line_number_table->table[index].start_pc;
*line = _line_number_table->table[index].line_number;
return true;
} else {
return false;
}
}
bool Method::get_local_var_entry(unsigned index, jlong* pc,
jint* length, jint* slot, String** name,
String** type, String** generic_type) {
if ((_local_vars_table) && (index < _local_vars_table->length)) {
*pc = _local_vars_table->table[index].start_pc;
*length = _local_vars_table->table[index].length;
*slot = _local_vars_table->table[index].index;
*name = _local_vars_table->table[index].name;
*type = _local_vars_table->table[index].type;
*generic_type = _local_vars_table->table[index].generic_type;
return true;
} else {
return false;
}
}
#define REPORT_FAILED_METHOD(msg) REPORT_FAILED_CLASS_CLASS(_class->get_class_loader(), \
_class, "java/lang/ClassFormatError", \
_class->get_name()->bytes << " : " << msg << " for method "\
<< _name->bytes << _descriptor->bytes);
bool Method::_parse_exceptions(ConstantPool& cp, unsigned attr_len,
ByteReader& cfs)
{
if(!cfs.parse_u2_be(&_n_exceptions)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse "
<< "number of exceptions");
return false;
}
if (attr_len != _n_exceptions * sizeof(uint16) + sizeof(_n_exceptions) ) {
REPORT_FAILED_METHOD(" invalid Exceptions attribute length "
<< "while parsing exceptions");
return false;
}
_exceptions = new String*[_n_exceptions];
//FIXME: verav Should throw OOM
for (unsigned i=0; i<_n_exceptions; i++) {
uint16 index;
if(!cfs.parse_u2_be(&index)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse "
<< "exception class index while parsing exceptions");
return false;
}
//See specification 4.8.4 about exception_index_table[].
if(!valid_cpi(_class, index, CONSTANT_Class, "of exception_table entry for Exception attrubute"))
return false;
if(!valid_cpi(_class, cp.get_class_name_index(index), CONSTANT_Utf8, "of exception_table entry for Exception attrubute"))
return false;
_exceptions[i] = cp.get_utf8_string(cp.get_class_name_index(index));
}
return true;
} //Method::_parse_exceptions
bool Method::_parse_line_numbers(unsigned attr_len, ByteReader &cfs) {
uint16 n_line_numbers;
if(!cfs.parse_u2_be(&n_line_numbers)) {
REPORT_FAILED_METHOD("could not parse line_number_table_length "
"while parsing LineNumberTable attribute");
return false;
}
//See specification 4.8.10 about attribute_length.
unsigned real_lnt_attr_len = sizeof(n_line_numbers) + n_line_numbers * 2 * sizeof(uint16);
if(real_lnt_attr_len != attr_len) {
REPORT_FAILED_METHOD("LineNumberTable attribute has wrong length ("
<< attr_len << " vs. " << real_lnt_attr_len << ")" );
return false;
}
_line_number_table =
(Line_Number_Table *)STD_MALLOC(sizeof(Line_Number_Table) +
sizeof(Line_Number_Entry) * (n_line_numbers - 1));
// ppervov: FIXME: should throw OOME
_line_number_table->length = n_line_numbers;
for (unsigned j = 0; j < n_line_numbers; j++) {
uint16 start_pc;
uint16 line_number;
if(!cfs.parse_u2_be(&start_pc)) {
REPORT_FAILED_METHOD("could not parse start_pc "
"while parsing LineNumberTable");
return false;
}
if(start_pc >= _byte_code_length) {
REPORT_FAILED_METHOD("start_pc in LineNumberTable "
"points outside the code");
return false;
}
if(!cfs.parse_u2_be(&line_number)) {
REPORT_FAILED_METHOD("could not parse line_number "
"while parsing LineNumberTable");
return false;
}
_line_number_table->table[j].start_pc = start_pc;
_line_number_table->table[j].line_number = line_number;
}
return true;
} //Method::_parse_line_numbers
bool Method::_parse_local_vars(Local_Var_Table* table, LocalVarOffset *offset_list,
Global_Env& env, ConstantPool& cp, ByteReader &cfs, const char *attr_name, Attributes attribute)
{
for (unsigned j = 0; j < table->length; j++) {
//go to the start of entry
if(!cfs.go_to_offset(offset_list->value)){
REPORT_FAILED_CLASS_FORMAT(_class, "could not go to the start of LVT entry");
return false;
}
uint16 start_pc;
if(!cfs.parse_u2_be(&start_pc)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse start_pc "
"in " << attr_name << " attribute");
return false;
}
uint16 length;
if(!cfs.parse_u2_be(&length)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse length entry "
"in " << attr_name << " attribute");
return false;
}
if( (start_pc >= _byte_code_length)
|| (start_pc + (unsigned)length) > _byte_code_length ) {
REPORT_FAILED_METHOD(attr_name << " entry "
"[start_pc, start_pc + length) = [" << start_pc << ", " << start_pc + length <<
") points outside bytecode range");
return false;
}
uint16 name_index;
if(!cfs.parse_u2_be(&name_index)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse name index "
"in " << attr_name << " attribute");
return false;
}
uint16 descriptor_index;
if(!cfs.parse_u2_be(&descriptor_index)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse descriptor index "
"in " << attr_name << " attribute");
return false;
}
if(!valid_cpi(_class, name_index, CONSTANT_Utf8, "for name of CONSTANT_Utf8 entry"))
return false;
String* name = cp.get_utf8_string(name_index);
if(!is_valid_member_name(name->bytes, name->len,
_class->get_version() < JAVA5_CLASS_FILE_VERSION, false))
{
REPORT_FAILED_METHOD("name of local variable: " << name->bytes <<
" in " << attr_name << " attribute is not stored as unqualified name");
return false;
}
if(!valid_cpi(_class, descriptor_index, CONSTANT_Utf8, "for descriptor of CONSTANT_Utf8 entry"))
return false;
String* descriptor = cp.get_utf8_string(descriptor_index);
if(attribute == ATTR_LocalVariableTable)
{
const char *next;
if(!is_valid_member_descriptor(descriptor->bytes, &next, false, _class->get_version() < JAVA5_CLASS_FILE_VERSION)
|| *next != '\0')
{
REPORT_FAILED_METHOD("illegal field descriptor: " << descriptor->bytes <<
" in " << attr_name << " attribute for local variable: " << name->bytes);
return false;
}
}
uint16 index;
if(!cfs.parse_u2_be(&index)) {
REPORT_FAILED_METHOD("could not parse index "
"in " << attr_name << " attribute");
return false;
}
//See specification about index value 4.8.11 and 4.8.12
if((descriptor->bytes[0] == 'D' || descriptor->bytes[0] == 'J')
&& index >= _max_locals - 1)
{
REPORT_FAILED_METHOD("invalid local index "
<< index << " in " << attr_name << " attribute");
return false;
}
if (index >= _max_locals) {
REPORT_FAILED_METHOD("invalid local index "
<< index << " in " << attr_name << " attribute");
return false;
}
//ensure that table has no duplicates
unsigned i;
for(i = 0; i < j; i++)
{
//RI does not check descriptors
if(index == table->table[i].index
&& name == table->table[i].name
&& start_pc == table->table[i].start_pc
&& length == table->table[i].length)
{
break;
}
}
if (j != 0 && i != j) {
// duplicate entry found
if (!env.verify || _class->get_version() < JAVA5_CLASS_FILE_VERSION) {
//just ignore the entry
--j;
--table->length;
} else {
REPORT_FAILED_METHOD("Duplicate local variable "<< name->bytes
<< " in attribute " << attr_name);
return false;
}
} else {
table->table[j].start_pc = start_pc;
table->table[j].length = length;
table->table[j].index = index;
table->table[j].name = name;
table->table[j].type = descriptor;
table->table[j].generic_type = NULL;
}
offset_list = offset_list->next;
}
return true;
} //Method::_parse_local_vars
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
static inline bool
check_method_descriptor(const char *descriptor, bool old_version)
{
const char *next;
bool result;
if( *descriptor != '(' )
return false;
next = ++descriptor;
while( descriptor[0] != ')' )
{
result = is_valid_member_descriptor(descriptor, &next, false, old_version);
if( !result || *next == '\0' ) {
return false;
}
descriptor = next;
}
next = ++descriptor;
result = is_valid_member_descriptor(descriptor, &next, true, old_version);
if( *next != '\0' )
return false;
return result;
}
bool Method::parse(Global_Env& env, Class* clss,
ByteReader &cfs, bool is_trusted_cl)
{
if(!Class_Member::parse(clss, cfs))
return false;
//check method name
if(is_trusted_cl && env.verify_all && !(_name == env.Init_String || _name == env.Clinit_String)
&& !is_valid_member_name(_name->bytes, _name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, true)) {
REPORT_FAILED_CLASS_FORMAT(clss, "illegal method name : " << _name->bytes);
return false;
} else {
if(!(_name == env.Init_String || _name == env.Clinit_String)
&& !is_valid_member_name(_name->bytes, _name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, true))
{
REPORT_FAILED_CLASS_FORMAT(clss, "illegal method name : " << _name->bytes);
return false;
}
}
// check method descriptor
if(!check_method_descriptor(_descriptor->bytes,
clss->get_version() < JAVA5_CLASS_FILE_VERSION))
{
REPORT_FAILED_METHOD( " invalid descriptor");
return false;
}
calculate_arguments_slot_num();
//The total length of method parameters should be 255 or less.
//See 4.4.3 in specification.
if(_arguments_slot_num > 255) {
REPORT_FAILED_METHOD("more than 255 arguments");
return false;
}
// checked method descriptor
_intf_method_for_fake_method = NULL;
// set the has_finalizer, is_clinit and is_init flags
if(_name == env.FinalizeName_String && _descriptor == env.VoidVoidDescriptor_String) {
_flags.is_finalize = 1;
}
else if(_name == env.Init_String)
_flags.is_init = 1;
else if(_name == env.Clinit_String)
_flags.is_clinit = 1;
// check method access flags
if(!is_clinit())
{
if(_class->is_interface())
{
if(!(is_abstract() && is_public())){
REPORT_FAILED_CLASS_FORMAT(_class, " Interface method "
<< _name->bytes << _descriptor->bytes
<< "must have both access flags ACC_ABSTRACT and ACC_PUBLIC set"
<< "0x" << std::hex << _access_flags);
return false;
}
if(_access_flags & ~(ACC_ABSTRACT | ACC_PUBLIC | ACC_VARARGS
| ACC_BRIDGE | ACC_SYNTHETIC)){
REPORT_FAILED_CLASS_FORMAT(_class, " Interface method "
<< _name->bytes << _descriptor->bytes
<< " has invalid combination of access flags "
<< "0x" << std::hex << _access_flags);
return false;
}
//for class file version lower than 49 these three flags should be set to zero
//See specification 4.6 Methods, for 1.4 Java.
if(_class->get_version() < JAVA5_CLASS_FILE_VERSION){
_access_flags &= ~(ACC_BRIDGE | ACC_VARARGS | ACC_SYNTHETIC);
}
} else {
if(is_private() && is_protected()
|| is_private() && is_public()
|| is_protected() && is_public())
{
//See specification 4.7 Methods about access_flags
REPORT_FAILED_CLASS_FORMAT(_class," Method "
<< _name->bytes << _descriptor->bytes
<< " has invalid combination of access flags "
<< "0x" << std::hex << _access_flags);
return false;
}
if(is_abstract()
&& (is_final() || is_native() || is_private()
|| is_static() || is_strict() || is_synchronized()))
{
bool bout = false;
REPORT_FAILED_CLASS_FORMAT(_class, " Method "
<< _name->bytes << _descriptor->bytes
<< " has invalid combination of access flags "
<< "0x" << std::hex << _access_flags);
return false;
}
if(is_init()) {
if(_access_flags & ~(ACC_STRICT | ACC_VARARGS | ACC_SYNTHETIC
| ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED))
{
REPORT_FAILED_CLASS_FORMAT(_class, " Method "
<< _name->bytes << _descriptor->bytes
<< " has invalid combination of access flags "
<< "0x" << std::hex << _access_flags);
return false;
}
}
}
} else {
// Java VM specification
// 4.7 Methods
// "Class and interface initialization methods (3.9) are called
// implicitly by the Java virtual machine; the value of their
// access_flags item is ignored except for the settings of the
// ACC_STRICT flag"
_access_flags &= ACC_STRICT;
// compiler assumes that <clinit> has ACC_STATIC
// but VM specification does not require this flag to be present
// so, enforce it
_access_flags |= ACC_STATIC;
}
//check method attributes
uint16 attr_count;
if(!cfs.parse_u2_be(&attr_count)) {
REPORT_FAILED_METHOD("truncated class file: failed to parse attributes count");
return false;
}
unsigned numCode = 0;
unsigned numExceptions = 0;
unsigned numRuntimeVisibleAnnotations = 0;
unsigned numRuntimeInvisibleAnnotations = 0;
unsigned numRuntimeInvisibleParameterAnnotations = 0;
U_32 attr_len = 0;
ConstantPool& cp = clss->get_constant_pool();
for (unsigned j=0; j<attr_count; j++) {
Attributes cur_attr = parse_attribute(clss, cfs, method_attrs, &attr_len);
switch(cur_attr) {
case ATTR_Code:
numCode++;
if (numCode > 1) {
REPORT_FAILED_METHOD(" there is more than one Code attribute");
return false;
}
if(is_abstract() || is_native()) {
REPORT_FAILED_CLASS_FORMAT(_class, " Method " << _name->bytes << _descriptor->bytes
<< ": " << (is_abstract()?"abstract":(is_native()?"native":""))
<< " should not have Code attribute present");
return false;
}
if(!_parse_code(env, cp, attr_len, cfs))
return false;
break;
case ATTR_Exceptions:
numExceptions++;
if(numExceptions > 1) {
REPORT_FAILED_METHOD(" there is more than one Exceptions attribute");
return false;
}
if(!_parse_exceptions(cp, attr_len, cfs))
return false;
break;
case ATTR_RuntimeInvisibleParameterAnnotations:
{
//RuntimeInvisibleParameterAnnotations attribute is parsed only if
//command line option -Xinvisible is set. See specification 4.8.17.
if(env.retain_invisible_annotations) {
numRuntimeInvisibleParameterAnnotations++;
if(numRuntimeInvisibleParameterAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one RuntimeInvisibleParameterAnnotations attribute");
return false;
}
if (!cfs.parse_u1(&_num_invisible_param_annotations)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"truncated class file: failed to parse number of InvisibleParameterAnnotations");
return false;
}
U_32 read_len = 1;
if (_num_invisible_param_annotations) {
U_32 len =
parse_parameter_annotations(&_invisible_param_annotations,
_num_invisible_param_annotations, cfs, _class);
if(len == 0)
return false;
read_len += len;
}
if (attr_len != read_len) {
REPORT_FAILED_METHOD(
"error parsing InvisibleParameterAnnotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
} else {
if (!cfs.skip(attr_len))
{
REPORT_FAILED_CLASS_FORMAT(_class,
"Truncated class file");
return false;
}
}
}
break;
case ATTR_RuntimeVisibleParameterAnnotations:
{
// See specification 4.8.16.
if (_param_annotations) {
REPORT_FAILED_METHOD(
"more than one RuntimeVisibleParameterAnnotations attribute");
return false;
}
if (!cfs.parse_u1(&_num_param_annotations)) {
REPORT_FAILED_CLASS_FORMAT(clss,
"cannot parse number of ParameterAnnotations");
return false;
}
U_32 read_len = 1;
if (_num_param_annotations) {
U_32 len = parse_parameter_annotations(&_param_annotations,
_num_param_annotations, cfs, _class);
if(len == 0)
return false;
read_len += len;
}
if (attr_len != read_len) {
REPORT_FAILED_METHOD(
"error parsing ParameterAnnotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
}
break;
case ATTR_AnnotationDefault:
{
//See specification 4.8.18 about default_value
if (_default_value) {
REPORT_FAILED_METHOD("more than one AnnotationDefault attribute");
return false;
}
_default_value = (AnnotationValue *)_class->get_class_loader()->Alloc(
sizeof(AnnotationValue));
//FIXME: verav should throw OOM
U_32 read_len = parse_annotation_value(*_default_value, cfs, clss);
if (read_len == 0) {
return false;
} else if (read_len != attr_len) {
REPORT_FAILED_METHOD(
"declared length " << attr_len
<< " of AnnotationDefault attribute "
<< " does not match actual " << read_len);
return false;
}
}
break;
case ATTR_Synthetic:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(clss,
"attribute Synthetic has non-zero length");
return false;
}
_access_flags |= ACC_SYNTHETIC;
}
break;
case ATTR_Deprecated:
{
if(attr_len != 0) {
REPORT_FAILED_CLASS_FORMAT(clss,
"attribute Deprecated has non-zero length");
return false;
}
_deprecated = true;
}
break;
case ATTR_Signature:
{
if(_signature != NULL) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one Signature attribute for the class");
return false;
}
if (!(_signature = parse_signature_attr(cfs, attr_len, clss))) {
return false;
}
}
break;
case ATTR_RuntimeVisibleAnnotations:
{
//Each method_info structure may contain at most one RuntimeVisibleAnnotations attribute.
// See specification 4.8.14.
numRuntimeVisibleAnnotations++;
if(numRuntimeVisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one RuntimeVisibleAnnotations attribute");
return false;
}
U_32 read_len = parse_annotation_table(&_annotations, cfs, clss);
if(read_len == 0)
return false;
if (attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(clss,
"error parsing Annotations attribute"
<< "; declared length " << attr_len
<< " does not match actual " << read_len);
return false;
}
}
break;
case ATTR_RuntimeInvisibleAnnotations:
{
//Each method_info structure may contain at most one RuntimeInvisibleAnnotations attribute.
numRuntimeInvisibleAnnotations++;
if(numRuntimeInvisibleAnnotations > 1) {
REPORT_FAILED_CLASS_FORMAT(clss,
"more than one RuntimeInvisibleAnnotations attribute");
return false;
}
//RuntimeInvisibleAnnotations attribute is parsed only if
//command line option -Xinvisible is set. See specification 4.8.15.
if(env.retain_invisible_annotations) {
U_32 read_len =
parse_annotation_table(&_invisible_annotations, cfs, clss);
if(read_len == 0)
return false;
if (attr_len != read_len) {
REPORT_FAILED_CLASS_FORMAT(clss,
"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(clss,
"Truncated class file");
return false;
}
}
}
break;
case ATTR_UNDEF:
// unrecognized attribute; skipped
break;
case ATTR_ERROR:
return false;
default:
REPORT_FAILED_CLASS_CLASS(clss->get_class_loader(), _class, "java/lang/InternalError",
_class->get_name()->bytes << ": unknown error occured "
"while parsing attributes for method "
<< _name->bytes << _descriptor->bytes
<< "; unprocessed attribute " << cur_attr);
return false;
} // switch
} // for
if(!(is_abstract() || is_native()) && numCode == 0) {
REPORT_FAILED_CLASS_FORMAT(_class, " Method " << _name->bytes << _descriptor->bytes
<< " should have Code attribute present");
return false;
}
return true;
} //Method::parse
bool Class::parse_fields(Global_Env* env, ByteReader& cfs, bool is_trusted_cl)
{
// Those fields are added by the loader even though they are nor defined
// in their corresponding class files.
static struct VMExtraFieldDescription {
const String* classname;
String* fieldname;
String* descriptor;
uint16 accessflags;
} vm_extra_fields[] = {
{ env->string_pool.lookup("java/lang/Thread"),
env->string_pool.lookup("vm_thread"),
env->string_pool.lookup("J"), ACC_PRIVATE},
{ env->string_pool.lookup("java/lang/Throwable"),
env->string_pool.lookup("vm_stacktrace"),
env->string_pool.lookup("[J"), ACC_PRIVATE|ACC_TRANSIENT},
{ env->string_pool.lookup("java/lang/Class"),
env->string_pool.lookup("vm_class"),
env->string_pool.lookup("J"), ACC_PRIVATE},
};
if(!cfs.parse_u2_be(&m_num_fields)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: faield to parse number of fields");
return false;
}
int num_fields_in_class_file = m_num_fields;
int i;
for(i = 0; i < int(sizeof(vm_extra_fields)/sizeof(VMExtraFieldDescription)); i++) {
if(get_name() == vm_extra_fields[i].classname) {
m_num_fields++;
}
}
m_fields = new Field[m_num_fields];
// ppervov: FIXME: should throw OOME
m_num_static_fields = 0;
unsigned short last_nonstatic_field = (unsigned short)num_fields_in_class_file;
for(i=0; i < num_fields_in_class_file; i++) {
Field fd;
if(!fd.parse(*env, this, cfs, is_trusted_cl))
return false;
if(fd.is_static()) {
m_fields[m_num_static_fields] = fd;
m_num_static_fields++;
} else {
last_nonstatic_field--;
m_fields[last_nonstatic_field] = fd;
}
}
assert(last_nonstatic_field == m_num_static_fields);
//See specification 4.6 Fields:
//No two fields in one class file may have the same name and descriptor.
for (int j = 0; j < num_fields_in_class_file; j++){
for(int k = j + 1; k < num_fields_in_class_file; k++){
if((m_fields[j].get_name() == m_fields[k].get_name())
&& (m_fields[j].get_descriptor() == m_fields[k].get_descriptor()))
{
REPORT_FAILED_CLASS_FORMAT(this, "duplicate field " << get_name()->bytes << "."
<< m_fields[j].get_name()->bytes << " " << m_fields[j].get_descriptor()->bytes);
return false;
}
}
}
for(i = 0; i < int(sizeof(vm_extra_fields)/sizeof(VMExtraFieldDescription)); i++) {
if(get_name() == vm_extra_fields[i].classname) {
Field& f = m_fields[num_fields_in_class_file];
f.set(this, vm_extra_fields[i].fieldname,
vm_extra_fields[i].descriptor, vm_extra_fields[i].accessflags);
f.set_injected();
TypeDesc* td = type_desc_create_from_java_descriptor(
vm_extra_fields[i].descriptor->bytes, m_class_loader);
if( td == NULL ) {
// error occured
// ppervov: FIXME: should throw OOME
return false;
}
f.set_field_type_desc(td);
num_fields_in_class_file++;
}
}
return true; // success
} //class_parse_fields
long _total_method_bytes = 0;
bool Class::parse_methods(Global_Env* env, ByteReader &cfs, bool is_trusted_cl)
{
if(!cfs.parse_u2_be(&m_num_methods)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse number of methods");
return false;
}
m_methods = new Method[m_num_methods];
//FIXME: should throw OOME
_total_method_bytes += sizeof(Method)*m_num_methods;
for(unsigned i = 0; i < m_num_methods; i++) {
if(!m_methods[i].parse(*env, this, cfs, is_trusted_cl)) {
return false;
}
Method* m = &m_methods[i];
if(m->is_clinit()) {
// There can be at most one clinit per class.
if(m_static_initializer) {
REPORT_FAILED_CLASS_FORMAT(this, ": there is more than one class initialization method");
return false;
}
m_static_initializer = &(m_methods[i]);
}
// to cache the default constructor
if (m->get_name() == VM_Global_State::loader_env->Init_String
&& m->get_descriptor() == VM_Global_State::loader_env->VoidVoidDescriptor_String)
{
m_default_constructor = &m_methods[i];
}
}
//See specification 4.7 Methods:
//No two methods in one class file may have the same name and descriptor.
for (int j = 0; j < m_num_methods; j++){
for(int k = j + 1; k < m_num_methods; k++){
if((m_methods[j].get_name() == m_methods[k].get_name())
&& (m_methods[j].get_descriptor() == m_methods[k].get_descriptor()))
{
REPORT_FAILED_CLASS_FORMAT(this, "duplicate method " << get_name()->bytes << "."
<< m_methods[j].get_name()->bytes << m_methods[j].get_descriptor()->bytes);
return false;
}
}
}
return true; // success
} //class_parse_methods
static inline bool
check_class_name(const char *name, unsigned len, bool old_version)
{
if(len == 0)
return false;
unsigned id_len = 0;
const char* iterator = name;
if(name[0] == '[')
{
const char *next = name + 1;
if(!is_valid_member_descriptor(name, &next, false, old_version) || *next != '\0') {
return false;
} else {
return true;
}
} else {
for(unsigned i = 0; i < len ; i++, iterator++)
{
if(*iterator != '/') {
id_len++;
} else {
if(!is_valid_member_name(name, id_len, old_version, false))
return false;
id_len = 0;
name = iterator;
name++;
}
}
return is_valid_member_name(name, id_len, old_version, false);
}
return false; //unreachable code
}
static String* class_file_parse_utf8data(String_Pool& string_pool, ByteReader& cfs,
uint16 len)
{
// See specification 4.5.7 about CONSTANT_Utf8 string format.
// buffer ends before len
if(!cfs.have(len))
return NULL;
//define bytes of UTF8
const U_8 HIGH_NONZERO_BIT = 0x80; // 10000000
const U_8 HIGH_TWO_NONZERO_BITS = 0xc0; // 11000000
const U_8 HIGH_THREE_NONZERO_BITS = 0xe0; // 11100000
const U_8 HIGH_FOUR_NONZERO_BITS = 0xf0; // 11110000
// get utf8 bytes and move buffer pointer
U_8* utf8data = (U_8*)cfs.get_and_skip(len);
// FIXME: decode 6-byte Java 1.5 encoding
uint16 read_len = 0;
// check utf8 correctness
for(int i = 0; i < len; i++) {
if((utf8data[i] & HIGH_NONZERO_BIT) == 0x00)
{
if(utf8data[i] == 0x00)
return NULL;
read_len++;
} else if((utf8data[i] & HIGH_THREE_NONZERO_BITS) == HIGH_TWO_NONZERO_BITS) {
read_len += 2;
if(read_len > len)
return NULL;
i++;
if((utf8data[i] & HIGH_TWO_NONZERO_BITS) != HIGH_NONZERO_BIT)
return NULL;
} else if((utf8data[i] & HIGH_FOUR_NONZERO_BITS) == HIGH_THREE_NONZERO_BITS) {
read_len += 3;
if(read_len > len)
return NULL;
i++;
if(((utf8data[i] & HIGH_TWO_NONZERO_BITS) != HIGH_NONZERO_BIT))
return NULL;
i++;
if(((utf8data[i] & HIGH_TWO_NONZERO_BITS) != HIGH_NONZERO_BIT))
return NULL;
}
else {
return NULL;
}
}
// then lookup on utf8 bytes and return string
return string_pool.lookup((const char*)utf8data, len);
}
static String* class_file_parse_utf8(String_Pool& string_pool,
ByteReader& cfs)
{
uint16 len;
if(!cfs.parse_u2_be(&len))
return NULL;
return class_file_parse_utf8data(string_pool, cfs, len);
}
bool ConstantPool::parse(Class* clss,
String_Pool& string_pool,
ByteReader& cfs)
{
if(!cfs.parse_u2_be(&m_size)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse constant pool size");
return false;
}
unsigned char* cp_tags = new unsigned char[m_size];
// ppervov: FIXME: should throw OOME
m_entries = new ConstPoolEntry[m_size];
// ppervov: FIXME: should throw OOME
//
// 0'th constant pool entry is a pointer to the tags array
// See specification 4.5 about Constant Pool
//
m_entries[0].tags = cp_tags;
cp_tags[0] = CONSTANT_Tags;
for(unsigned i = 1; i < m_size; i++) {
// parse tag into tag array
U_8 tag;
if(!cfs.parse_u1(&tag)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse constant pool tag for index " << i);
return false;
}
switch(cp_tags[i] = tag) {
case CONSTANT_Class:
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_Class.name_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse name index "
<< "for CONSTANT_Class entry");
return false;
}
break;
case CONSTANT_Methodref:
case CONSTANT_Fieldref:
case CONSTANT_InterfaceMethodref:
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_ref.class_index)) {
REPORT_FAILED_CLASS_FORMAT(clss,"truncated class file: failed to parse class index "
<< "for CONSTANT_*ref entry");
return false;
}
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_ref.name_and_type_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse "
<< "name-and-type index for CONSTANT_*ref entry");
return false;
}
break;
case CONSTANT_String:
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_String.string_index)) {
REPORT_FAILED_CLASS_FORMAT(clss,"truncated class file: failed to parse string "
<< "index for CONSTANT_String entry");
return false;
}
break;
case CONSTANT_Float:
case CONSTANT_Integer:
if(!cfs.parse_u4_be(&m_entries[i].int_value)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse value for "
<< (tag==CONSTANT_Integer?"CONSTANT_Integer":"CONSTANT_Float") << " entry");
return false;
}
break;
case CONSTANT_Double:
case CONSTANT_Long:
// longs and doubles take up two entries
// on both IA32 & IPF, first constant pool element is used, second element - unused
if(!cfs.parse_u4_be(&m_entries[i].CONSTANT_8byte.high_bytes)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse high four bytes for "
<< (tag==CONSTANT_Long?"CONSTANT_Integer":"CONSTANT_Float") << " entry");
return false;
}
if(!cfs.parse_u4_be(&m_entries[i].CONSTANT_8byte.low_bytes)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to not parse low four bytes for "
<< (tag==CONSTANT_Long?"CONSTANT_Long":"CONSTANT_Double") << " entry");
return false;
}
// skip next constant pool entry as it is used by next 4 bytes of Long/Double
if(i + 1 < m_size) {
cp_tags[i+1] = CONSTANT_UnusedEntry;
m_entries[i+1].CONSTANT_8byte.high_bytes = m_entries[i].CONSTANT_8byte.high_bytes;
m_entries[i+1].CONSTANT_8byte.low_bytes = m_entries[i].CONSTANT_8byte.low_bytes;
}
i++;
break;
case CONSTANT_NameAndType:
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_NameAndType.name_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse name index "
"for CONSTANT_NameAndType entry");
return false;
}
if(!cfs.parse_u2_be(&m_entries[i].CONSTANT_NameAndType.descriptor_index)) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: failed to parse descriptor index "
"for CONSTANT_NameAndType entry");
return false;
}
break;
case CONSTANT_Utf8:
{
// parse and insert string into string table
String* str = class_file_parse_utf8(string_pool, cfs);
if(!str) {
REPORT_FAILED_CLASS_FORMAT(clss, "truncated class file: faile to parse CONSTANT_Utf8 entry");
return false;
}
m_entries[i].CONSTANT_Utf8.string = str;
}
break;
default:
REPORT_FAILED_CLASS_FORMAT(clss, "unknown constant pool tag " << "0x" << std::hex << (int)cp_tags[i]);
return false;
}
}
return true;
} // ConstantPool::parse
bool ConstantPool::check(Global_Env* env, Class* clss, bool is_trusted_cl)
{
for(unsigned i = 1; i < m_size; i++) {
switch(unsigned char tag = get_tag(i))
{
case CONSTANT_Class:
{
unsigned name_index = get_class_name_index(i);
if (!valid_cpi(clss, name_index, CONSTANT_Utf8, "for class name at CONSTANT_Class entry")) {
// illegal name index
return false;
}
if(!check_class_name(get_utf8_string(name_index)->bytes, get_utf8_string(name_index)->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION))
{
REPORT_FAILED_CLASS_FORMAT(clss," illegal CONSTANT_Class name "
<< "\"" << get_utf8_string(name_index)->bytes << "\"");
return false;
}
break;
}
case CONSTANT_Methodref:
case CONSTANT_Fieldref:
case CONSTANT_InterfaceMethodref:
{
unsigned class_index = get_ref_class_index(i);
if (!valid_cpi(clss, class_index, CONSTANT_Class, "for class name at CONSTANT_*ref entry")) {
return false;
}
unsigned name_type_index = get_ref_name_and_type_index(i);
if (!valid_cpi(clss, name_type_index, CONSTANT_NameAndType, "for name-and-type at CONSTANT_*ref entry")) {
return false;
}
const char *next = NULL;
String *name;
String *descriptor;
unsigned name_index = get_name_and_type_name_index(name_type_index);
if(!valid_cpi(clss, name_index, CONSTANT_Utf8, "for name at CONSTANT_*ref entry")) {
return false;
}
unsigned descriptor_index = get_name_and_type_descriptor_index(name_type_index);
if (!valid_cpi(clss, descriptor_index, CONSTANT_Utf8, "for descriptor at CONSTANT_*ref entry")) {
return false;
}
name = get_utf8_string(name_index);
descriptor = get_utf8_string(descriptor_index);
if(tag == CONSTANT_Methodref)
{
//check method name
if(is_trusted_cl) {
if(env->verify_all && (name != env->Init_String)
&& !is_valid_member_name(name->bytes,name->len, clss->get_version() < JAVA5_CLASS_FILE_VERSION, true))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method name for CONSTANT_Methodref entry: " << name->bytes);
return false;
}
if(name->bytes[0] == '<' && name != env->Init_String) {
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method name "<< name->bytes
<< " at constant pool index " << name_index);
return false;
}
} else { //always check method name if classloader is not system
if((name != env->Init_String)
&& !is_valid_member_name(name->bytes,name->len, clss->get_version() < JAVA5_CLASS_FILE_VERSION, true))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method name for CONSTANT_Methodref entry: "
<< name->bytes);
return false;
}
}
//check method descriptor
if(!check_method_descriptor(descriptor->bytes, clss->get_version() < JAVA5_CLASS_FILE_VERSION))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method descriptor at CONSTANT_Methodref entry: "
<< descriptor->bytes);
return false;
}
//for <init> method return type must be void
//See specification 4.5.2
if(name == env->Init_String)
{
if(descriptor->bytes[descriptor->len - 1] != 'V')
{
REPORT_FAILED_CLASS_FORMAT(clss, " return type of <init> method"
" is not void at CONSTANT_Methodref entry");
return false;
}
}
}
if(tag == CONSTANT_Fieldref)
{
//check field name
if(is_trusted_cl) {
if(env->verify_all && !is_valid_member_name(name->bytes, name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, false))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal field name for CONSTANT_Filedref entry: "
<< name->bytes);
return false;
}
} else {
if(!is_valid_member_name(name->bytes, name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, false))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal field name for CONSTANT_Filedref entry: "
<< name->bytes);
return false;
}
}
//check field descriptor
if(!is_valid_member_descriptor(descriptor->bytes, &next, false,
clss->get_version() < JAVA5_CLASS_FILE_VERSION) || *next != '\0' )
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal field descriptor at CONSTANT_Fieldref entry: "
<< descriptor->bytes);
return false;
}
}
if(tag == CONSTANT_InterfaceMethodref)
{
//check method name, name can't be <init>
//See specification 4.5.2 about name_and_type_index last sentence.
if(is_trusted_cl) {
if(env->verify_all && (name != env->Clinit_String)
&& !is_valid_member_name(name->bytes, name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, true))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method name for CONSTANT_InterfaceMethod entry: "
<< name->bytes);
return false;
}
} else {
if(!is_valid_member_name(name->bytes, name->len,
clss->get_version() < JAVA5_CLASS_FILE_VERSION, true))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method name for CONSTANT_InterfaceMethod entry: "
<< name->bytes);
return false;
}
}
//check method descriptor
if(!check_method_descriptor(descriptor->bytes, clss->get_version() < JAVA5_CLASS_FILE_VERSION))
{
REPORT_FAILED_CLASS_FORMAT(clss, " illegal method descriptor at CONSTANT_InterfaceMethodref entry: "
<< descriptor->bytes);
return false;
}
}
break;
}
case CONSTANT_String:
{
unsigned string_index = get_string_index(i);
if (!valid_cpi(clss, string_index, CONSTANT_Utf8, "for string at CONSTANT_String entry")) {
// illegal string index
return false;
}
// set entry to the actual string
resolve_entry(i, get_utf8_string(string_index));
break;
}
case CONSTANT_Integer:
case CONSTANT_Float:
// not much to do here
break;
case CONSTANT_Long:
case CONSTANT_Double:
//check Long and Double indexes, n+1 index should be valid too.
//See specification 4.5.5
if(i + 1 == m_size){
REPORT_FAILED_CLASS_FORMAT(clss, " illegal indexes for Long or Double " << i << " and " << i + 1);
return false;
}
i++;
break;
case CONSTANT_NameAndType:
{
//See specification 4.5.6
unsigned name_index = get_name_and_type_name_index(i);
unsigned descriptor_index = get_name_and_type_descriptor_index(i);
if(!valid_cpi(clss, name_index , CONSTANT_Utf8, "for name at CONSTANT_NameAndType entry")) {
return false;
}
if (!valid_cpi(clss, descriptor_index, CONSTANT_Utf8, "for descriptor at CONSTANT_NameAndType entry")) {
return false;
}
resolve_entry(i, get_utf8_string(name_index), get_utf8_string(descriptor_index));
break;
}
case CONSTANT_Utf8:
// nothing to do here
break;
default:
REPORT_FAILED_CLASS_FORMAT(clss, " wrong constant pool tag " << get_tag(i));
return false;
}
}
return true;
} // ConstantPool::check
bool Class::parse_interfaces(ByteReader &cfs)
{
if(!cfs.parse_u2_be(&m_num_superinterfaces)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse number of superinterfaces");
return false;
}
m_superinterfaces = (Class_Super*)m_class_loader->
Alloc(sizeof(Class_Super)*m_num_superinterfaces);
// ppervov: FIXME: should throw OOME
for (unsigned i=0; i<m_num_superinterfaces; i++) {
uint16 interface_index;
if(!cfs.parse_u2_be(&interface_index)) {
REPORT_FAILED_CLASS_FORMAT(this, "truncated class file: failed to parse superinterface index");
return false;
}
//
//Verify that interface index is valid and entry in constant pool is of type CONSTANT_Class
//See specification 4.2 about interfaces.
if(!valid_cpi(this, interface_index, CONSTANT_Class, "for superinterface"))
return false;
if(!valid_cpi(this, m_const_pool.get_class_name_index(interface_index), CONSTANT_Utf8, "for superinterface name"))
return false;
m_superinterfaces[i].name = m_const_pool.get_utf8_string(m_const_pool.get_class_name_index(interface_index));
m_superinterfaces[i].cp_index = interface_index;
}
return true;
} // Class::parse_interfaces
/*
* Parses and verifies the classfile. Format is (from JVM spec):
*
* ClassFile {
* u4 magic;
* u2 minor_version;
* u2 major_version;
* u2 constant_pool_count;
* cp_info constant_pool[constant_pool_count-1];
* u2 access_flags;
* u2 this_class;
* u2 super_class;
* u2 interfaces_count;
* u2 interfaces[interfaces_count];
* u2 fields_count;
* field_info fields[fields_count];
* u2 methods_count;
* method_info methods[methods_count];
* u2 attributes_count;
* attribute_info attributes[attributes_count];
* }
*/
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
static bool const_pool_find_entry(ByteReader& cp, uint16 cp_count, uint16 index)
{
U_8 tag;
// cp must be at the beginning of constant pool
for(uint16 cp_index = 1; cp_index < cp_count; cp_index++) {
if(cp_index == index) return true;
if(!cp.parse_u1(&tag))
return false;
switch(tag) {
case CONSTANT_Class:
if(!cp.skip(2))
return false;
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
if(!cp.skip(4))
return false;
break;
case CONSTANT_String:
if(!cp.skip(2))
return false;
break;
case CONSTANT_Integer:
case CONSTANT_Float:
if(!cp.skip(4))
return false;
break;
case CONSTANT_Long:
case CONSTANT_Double:
if(!cp.skip(8))
return false;
cp_index++;
break;
case CONSTANT_NameAndType:
if(!cp.skip(4))
return false;
break;
case CONSTANT_Utf8:
{
uint16 dummy16;
if(!cp.parse_u2_be(&dummy16))
return false;
if(!cp.skip(dummy16))
return false;
}
break;
}
}
return false; // not found
}
const String* class_extract_name(Global_Env* env,
U_8* buffer, unsigned offset, unsigned length)
{
ByteReader cfs(buffer, offset, length);
U_32 magic;
// check magic
if(!cfs.parse_u4_be(&magic) || magic != CLASSFILE_MAGIC)
return NULL;
// skip minor_version and major_version
if(!cfs.skip(4))
return NULL;
uint16 cp_count;
// get constant pool entry number
if(!cfs.parse_u2_be(&cp_count))
return NULL;
// skip constant pool
U_8 tag;
uint16 utf8_len;
offset = cfs.get_offset(); // offset now contains the start of constant pool
uint16 cp_index;
for(cp_index = 1; cp_index < cp_count; cp_index++) {
if(!cfs.parse_u1(&tag))
return NULL;
switch(tag) {
case CONSTANT_Class:
if(!cfs.skip(2))
return NULL;
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
if(!cfs.skip(4))
return NULL;
break;
case CONSTANT_String:
if(!cfs.skip(2))
return NULL;
break;
case CONSTANT_Integer:
case CONSTANT_Float:
if(!cfs.skip(4))
return NULL;
break;
case CONSTANT_Long:
case CONSTANT_Double:
if(!cfs.skip(8))
return NULL;
cp_index++;
break;
case CONSTANT_NameAndType:
if(!cfs.skip(4))
return NULL;
break;
case CONSTANT_Utf8:
if(!cfs.parse_u2_be(&utf8_len))
return NULL;
if(!cfs.skip(utf8_len))
return NULL;
break;
}
}
// skip access_flags
if(!cfs.skip(2))
return NULL;
// get this_index in constant pool
uint16 this_class_idx;
if(!cfs.parse_u2_be(&this_class_idx))
return NULL;
// find needed entry
if(!cfs.go_to_offset(offset))
return NULL;
if(!const_pool_find_entry(cfs, cp_count, this_class_idx))
return NULL;
// now cfs is at CONSTANT_Class entry
if(!cfs.parse_u1(&tag) && tag != CONSTANT_Class)
return NULL;
// set this_class_idx to class_name index in constant pool
if(!cfs.parse_u2_be(&this_class_idx))
return NULL;
// find entry class_name
if(!cfs.go_to_offset(offset))
return NULL;
if(!const_pool_find_entry(cfs, cp_count, this_class_idx))
return NULL;
// now cfs is at CONSTANT_Utf8 entry
if(!cfs.parse_u1(&tag) && tag != CONSTANT_Utf8)
return NULL;
// parse class name
const String* class_name = class_file_parse_utf8(env->string_pool, cfs);
return class_name;
}
Class *class_load_verify_prepare_by_loader_jni(Global_Env* env,
const String* classname,
ClassLoader* cl)
{
assert(hythread_is_suspend_enabled());
// if no class loader passed, re-route to bootstrap
if(!cl) cl = env->bootstrap_class_loader;
Class* clss = cl->LoadVerifyAndPrepareClass(env, classname);
return clss;
}
Class *class_load_verify_prepare_from_jni(Global_Env *env, const String *classname)
{
assert(hythread_is_suspend_enabled());
Class *clss = env->bootstrap_class_loader->LoadVerifyAndPrepareClass(env, classname);
return clss;
}