X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=libjava%2Fdefineclass.cc;h=12c6032862da0ab7ce72e7eab8838347b7cda717;hb=15b3f4099d549c102dddb15df7fd1261bdbb8e0d;hp=09f8f47fc843ce3cb7da2b178afdaa1250f5827d;hpb=9a6624c49770e9b5e630a7c51eb0a13002d9df83;p=pf3gnuchains%2Fgcc-fork.git diff --git a/libjava/defineclass.cc b/libjava/defineclass.cc index 09f8f47fc84..12c6032862d 100644 --- a/libjava/defineclass.cc +++ b/libjava/defineclass.cc @@ -1,6 +1,6 @@ // defineclass.cc - defining a class from .class format. -/* Copyright (C) 1999 Cygnus Solutions +/* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation This file is part of libgcj. @@ -18,12 +18,15 @@ details. */ currently being ignored ("InnerClasses", "LineNumber", etc...). */ -#include +#include -#ifdef INTERPRETER +#include +#include +#include #include -#include +#include +#include #include #include @@ -34,41 +37,33 @@ details. */ #include #include #include -#include #include +#include +#include +#include +#include +#include +#include -#define ClassClass _CL_Q34java4lang5Class -extern java::lang::Class ClassClass; -#define StringClass _CL_Q34java4lang6String -extern java::lang::Class StringClass; -#define ClassObject _CL_Q34java4lang6Object -extern java::lang::Class ClassObject; - -// we don't verify method names that match these. -static _Jv_Utf8Const *clinit_name = _Jv_makeUtf8Const ("", 8); -static _Jv_Utf8Const *init_name = _Jv_makeUtf8Const ("", 6); +using namespace gcj; +#ifdef INTERPRETER -// these go in some seperate functions, to avoid having _Jv_InitClass +// these go in some separate functions, to avoid having _Jv_InitClass // inserted all over the place. -static void throw_internal_error (char *msg) +static void throw_internal_error (const char *msg) __attribute__ ((__noreturn__)); static void throw_no_class_def_found_error (jstring msg) __attribute__ ((__noreturn__)); -static void throw_no_class_def_found_error (char *msg) +static void throw_no_class_def_found_error (const char *msg) __attribute__ ((__noreturn__)); static void throw_class_format_error (jstring msg) __attribute__ ((__noreturn__)); -static void throw_class_format_error (char *msg) - __attribute__ ((__noreturn__)); static void throw_incompatible_class_change_error (jstring msg) __attribute__ ((__noreturn__)); static void throw_class_circularity_error (jstring msg) __attribute__ ((__noreturn__)); -static jdouble long_bits_to_double (jlong); -static jfloat int_bits_to_float (jint); - /** * We define class reading using a class. It is practical, since then * the entire class-reader can be a friend of class Class (it needs to @@ -79,7 +74,8 @@ static jfloat int_bits_to_float (jint); * public or private members here. */ -struct _Jv_ClassReader { +struct _Jv_ClassReader +{ // do verification? Currently, there is no option to disable this. // This flag just controls the verificaiton done by the class loader; @@ -87,7 +83,7 @@ struct _Jv_ClassReader { // allways on. You always want this as far as I can see, but it also // controls weither identifiers and type descriptors/signatures are // verified as legal. This could be somewhat more expensive since it - // will call Characher.isJavaIdentifier{Start,Part} for each character + // will call Character.isJavaIdentifier{Start,Part} for each character // in any identifier (field name or method name) it comes by. Thus, // it might be useful to turn off this verification for classes that // come from a trusted source. However, for GCJ, trusted classes are @@ -95,6 +91,10 @@ struct _Jv_ClassReader { bool verify; + // original input data. + jbyteArray input_data; + jint input_offset; + // input data. unsigned char *bytes; int len; @@ -108,7 +108,21 @@ struct _Jv_ClassReader { unsigned int *offsets; // the class to define (see java-interp.h) - _Jv_InterpClass *def; + jclass def; + + // the classes associated interpreter data. + _Jv_InterpClass *def_interp; + + // The name we found. + _Jv_Utf8Const **found_name; + + // True if this is a 1.5 class file. + bool is_15; + + // Buffer holding extra reflection data. + ::java::io::ByteArrayOutputStream *reflection_data; + ::java::io::DataOutputStream *data_stream; + /* check that the given number of input bytes are available */ inline void check (int num) @@ -124,7 +138,7 @@ struct _Jv_ClassReader { pos += num; } - /* read an unsignend 1-byte unit */ + /* read an unsigned 1-byte unit */ inline static jint get1u (unsigned char* bytes) { return bytes[0]; @@ -194,31 +208,97 @@ struct _Jv_ClassReader { throw_class_format_error ("erroneous constant pool tag"); } - _Jv_ClassReader (jclass klass, jbyteArray data, jint offset, jint length) + inline void verify_identifier (_Jv_Utf8Const* name) + { + if (! _Jv_VerifyIdentifier (name)) + throw_class_format_error ("erroneous identifier"); + } + + inline void verify_classname (unsigned char* ptr, _Jv_ushort length) + { + if (! _Jv_VerifyClassName (ptr, length)) + throw_class_format_error ("erroneous class name"); + } + + inline void verify_classname (_Jv_Utf8Const *name) + { + if (! _Jv_VerifyClassName (name)) + throw_class_format_error ("erroneous class name"); + } + + inline void verify_field_signature (_Jv_Utf8Const *sig) + { + if (! _Jv_VerifyFieldSignature (sig)) + throw_class_format_error ("erroneous type descriptor"); + } + + inline void verify_method_signature (_Jv_Utf8Const *sig) + { + if (! _Jv_VerifyMethodSignature (sig)) + throw_class_format_error ("erroneous type descriptor"); + } + + ::java::io::DataOutputStream *get_reflection_stream () + { + if (reflection_data == NULL) + { + reflection_data = new ::java::io::ByteArrayOutputStream(); + data_stream = new ::java::io::DataOutputStream(reflection_data); + } + return data_stream; + } + + _Jv_ClassReader (jclass klass, jbyteArray data, jint offset, jint length, + java::security::ProtectionDomain *pd, + _Jv_Utf8Const **name_result) { if (klass == 0 || length < 0 || offset+length > data->length) throw_internal_error ("arguments to _Jv_DefineClass"); verify = true; + input_data = data; + input_offset = offset; bytes = (unsigned char*) (elements (data)+offset); len = length; pos = 0; - def = (_Jv_InterpClass*) klass; + is_15 = false; + + def = klass; + found_name = name_result; + reflection_data = NULL; + data_stream = NULL; + + def->size_in_bytes = -1; + def->vtable_method_count = -1; + def->engine = &_Jv_soleInterpreterEngine; + def->protectionDomain = pd; } /** and here goes the parser members defined out-of-line */ void parse (); void read_constpool (); - void prepare_pool_entry (int index, unsigned char tag); + void prepare_pool_entry (int index, unsigned char tag, + bool rewrite = true); void read_fields (); void read_methods (); void read_one_class_attribute (); void read_one_method_attribute (int method); void read_one_code_attribute (int method); - void read_one_field_attribute (int field); + void read_one_field_attribute (int field, bool *); + void throw_class_format_error (const char *msg); + + void handleEnclosingMethod(int); + void handleGenericSignature(jv_attr_type, unsigned short, int); + void handleAnnotationElement(); + void handleAnnotation(); + void handleAnnotations(); + void handleMemberAnnotations(jv_attr_type, int, int); + void handleAnnotationDefault(int, int); + void handleParameterAnnotations(int, int); + void finish_reflection_data (); /** check an utf8 entry, without creating a Utf8Const object */ - bool is_attribute_name (int index, char *name); + bool is_attribute_name (int index, const char *name); /** here goes the class-loader members defined out-of-line */ void handleConstantPool (); @@ -226,9 +306,8 @@ struct _Jv_ClassReader { void handleInterfacesBegin (int); void handleInterface (int, int); void handleFieldsBegin (int); - void handleField (int, int, int, int); - void handleFieldsEnd (); - void handleConstantValueAttribute (int,int); + void handleField (int, int, int, int, int *); + void handleConstantValueAttribute (int, int, bool *); void handleMethodsBegin (int); void handleMethod (int, int, int, int); void handleMethodsEnd (); @@ -246,62 +325,51 @@ struct _Jv_ClassReader { * could be implemented in prims.cc (_Jv_makeUtf8Const), since it * computes the hash value anyway. */ - - static const int PUBLIC = 0x001; - static const int PRIVATE = 0x002; - static const int PROTECTED = 0x004; - static const int STATIC = 0x008; - static const int FINAL = 0x010; - static const int SYNCHRONIZED = 0x020; - static const int VOLATILE = 0x040; - static const int TRANSIENT = 0x080; - static const int NATIVE = 0x100; - static const int INTERFACE = 0x200; - static const int ABSTRACT = 0x400; - static const int ALL_FLAGS = 0x7FF; - }; -/* This is used for the isJavaIdentifierStart & isJavaIdentifierPart - methods, so we avoid doing _Jv_InitClass all the time */ - -static const java::lang::Character *character = 0; -static void prepare_character (); - +// Note that *NAME_RESULT will only be set if the class is registered +// with the class loader. This is how the caller can know whether +// unregistration is require. void -_Jv_DefineClass (jclass klass, jbyteArray data, jint offset, jint length) +_Jv_DefineClass (jclass klass, jbyteArray data, jint offset, jint length, + java::security::ProtectionDomain *pd, + _Jv_Utf8Const **name_result) { - if (character == 0) - prepare_character (); - - _Jv_ClassReader reader (klass, data, offset, length); + _Jv_ClassReader reader (klass, data, offset, length, pd, name_result); reader.parse(); /* that's it! */ } -/** put it after _Jv_DefineClass, so it doesn't get inlined */ -static void prepare_character () -{ - character = new java::lang::Character ('!'); -} - /** This section defines the parsing/scanning of the class data */ +// Major and minor version numbers for various releases. +#define MAJOR_1_1 45 +#define MINOR_1_1 3 +#define MAJOR_1_2 46 +#define MINOR_1_2 0 +#define MAJOR_1_3 47 +#define MINOR_1_3 0 +#define MAJOR_1_4 48 +#define MINOR_1_4 0 +#define MAJOR_1_5 49 +#define MINOR_1_5 0 + void _Jv_ClassReader::parse () { int magic = read4 (); - - /* FIXME: Decide which range of version numbers to allow */ - - /* int minor_version = */ read2u (); - /* int major_verson = */ read2u (); - if (magic != (int) 0xCAFEBABE) throw_class_format_error ("bad magic number"); + int minor_version = read2u (); + int major_version = read2u (); + if (major_version < MAJOR_1_1 || major_version > MAJOR_1_5 + || (major_version == MAJOR_1_5 && minor_version > MINOR_1_5)) + throw_class_format_error ("unrecognized class file version"); + is_15 = (major_version == MAJOR_1_5); + pool_count = read2u (); read_constpool (); @@ -316,8 +384,13 @@ _Jv_ClassReader::parse () handleClassBegin (access_flags, this_class, super_class); + // Allocate our aux_info here, after the name is set, to fulfill our + // contract with the collector interface. + def->aux_info = (void *) _Jv_AllocRawObj (sizeof (_Jv_InterpClass)); + def_interp = (_Jv_InterpClass *) def->aux_info; + int interfaces_count = read2u (); - + handleInterfacesBegin (interfaces_count); for (int i = 0; i < interfaces_count; i++) @@ -340,17 +413,246 @@ _Jv_ClassReader::parse () if (pos != len) throw_class_format_error ("unused data before end of file"); - // tell everyone we're done. - def->state = JV_STATE_LOADED; + finish_reflection_data (); + + // Tell everyone we're done. + def->state = JV_STATE_READ; + if (gcj::verbose_class_flag) + _Jv_Linker::print_class_loaded (def); + ++gcj::loadedClasses; def->notifyAll (); +} + +void +_Jv_ClassReader::finish_reflection_data () +{ + if (data_stream == NULL) + return; + data_stream->writeByte(JV_DONE_ATTR); + data_stream->flush(); + int nbytes = reflection_data->count; + unsigned char *new_bytes = (unsigned char *) _Jv_AllocBytes (nbytes); + memcpy (new_bytes, elements (reflection_data->buf), nbytes); + def->reflection_data = new_bytes; +} + +void +_Jv_ClassReader::handleEnclosingMethod (int len) +{ + if (len != 4) + throw_class_format_error ("invalid EnclosingMethod attribute"); + // FIXME: only allow one... + + int class_index = read2u(); + check_tag (class_index, JV_CONSTANT_Class); + prepare_pool_entry (class_index, JV_CONSTANT_Class); + + int method_index = read2u(); + // Zero is ok and means no enclosing method. + if (method_index != 0) + { + check_tag (method_index, JV_CONSTANT_NameAndType); + prepare_pool_entry (method_index, JV_CONSTANT_NameAndType); + } + + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(JV_CLASS_ATTR); + stream->writeInt(5); + stream->writeByte(JV_ENCLOSING_METHOD_KIND); + stream->writeShort(class_index); + stream->writeShort(method_index); +} + +void +_Jv_ClassReader::handleGenericSignature (jv_attr_type type, + unsigned short index, + int len) +{ + if (len != 2) + throw_class_format_error ("invalid Signature attribute"); + + int cpool_idx = read2u(); + check_tag (cpool_idx, JV_CONSTANT_Utf8); + prepare_pool_entry (cpool_idx, JV_CONSTANT_Utf8, false); + + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(type); + int attrlen = 3; + if (type != JV_CLASS_ATTR) + attrlen += 2; + stream->writeInt(attrlen); + if (type != JV_CLASS_ATTR) + stream->writeShort(index); + stream->writeByte(JV_SIGNATURE_KIND); + stream->writeShort(cpool_idx); +} + +void +_Jv_ClassReader::handleAnnotationElement() +{ + int tag = read1u(); + switch (tag) + { + case 'B': + case 'C': + case 'S': + case 'Z': + case 'I': + { + int index = read2u(); + check_tag (index, JV_CONSTANT_Integer); + prepare_pool_entry (index, JV_CONSTANT_Integer); + } + break; + case 'D': + { + int index = read2u(); + check_tag (index, JV_CONSTANT_Double); + prepare_pool_entry (index, JV_CONSTANT_Double); + } + break; + case 'F': + { + int index = read2u(); + check_tag (index, JV_CONSTANT_Float); + prepare_pool_entry (index, JV_CONSTANT_Float); + } + break; + case 'J': + { + int index = read2u(); + check_tag (index, JV_CONSTANT_Long); + prepare_pool_entry (index, JV_CONSTANT_Long); + } + break; + case 's': + { + int index = read2u(); + // Despite what the JVM spec says, compilers generate a Utf8 + // constant here, not a String. + check_tag (index, JV_CONSTANT_Utf8); + prepare_pool_entry (index, JV_CONSTANT_Utf8, false); + } + break; + + case 'e': + { + int type_name_index = read2u(); + int const_name_index = read2u (); + check_tag (type_name_index, JV_CONSTANT_Utf8); + prepare_pool_entry (type_name_index, JV_CONSTANT_Utf8); + check_tag (const_name_index, JV_CONSTANT_Utf8); + prepare_pool_entry (const_name_index, JV_CONSTANT_Utf8, false); + } + break; + case 'c': + { + int index = read2u(); + check_tag (index, JV_CONSTANT_Utf8); + prepare_pool_entry (index, JV_CONSTANT_Utf8); + } + break; + case '@': + handleAnnotation(); + break; + case '[': + { + int n_array_elts = read2u (); + for (int i = 0; i < n_array_elts; ++i) + handleAnnotationElement(); + } + break; + default: + throw_class_format_error ("invalid annotation element"); + } +} + +void +_Jv_ClassReader::handleAnnotation() +{ + int type_index = read2u(); + check_tag (type_index, JV_CONSTANT_Utf8); + prepare_pool_entry (type_index, JV_CONSTANT_Utf8); + + int npairs = read2u(); + for (int i = 0; i < npairs; ++i) + { + int name_index = read2u(); + check_tag (name_index, JV_CONSTANT_Utf8); + prepare_pool_entry (name_index, JV_CONSTANT_Utf8, false); + handleAnnotationElement(); + } +} + +void +_Jv_ClassReader::handleAnnotations() +{ + int num = read2u(); + while (num--) + handleAnnotation(); +} + +void +_Jv_ClassReader::handleMemberAnnotations(jv_attr_type member_type, + int member_index, + int len) +{ + // We're going to copy the bytes in verbatim. But first we want to + // make sure the attribute is well-formed, and we want to prepare + // the constant pool. So, we save our starting point. + int orig_pos = pos; + + handleAnnotations(); + // FIXME: check that we read all LEN bytes? + + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(member_type); + int newLen = len + 1; + if (member_type != JV_CLASS_ATTR) + newLen += 2; + stream->writeInt(newLen); + stream->writeByte(JV_ANNOTATIONS_KIND); + if (member_type != JV_CLASS_ATTR) + stream->writeShort(member_index); + // Write the data as-is. + stream->write(input_data, input_offset + orig_pos, len); +} + +void +_Jv_ClassReader::handleAnnotationDefault(int member_index, int len) +{ + int orig_pos = pos; + handleAnnotationElement(); + + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(JV_METHOD_ATTR); + stream->writeInt(len + 3); + stream->writeByte(JV_ANNOTATION_DEFAULT_KIND); + stream->writeShort(member_index); + stream->write(input_data, input_offset + orig_pos, len); +} +void +_Jv_ClassReader::handleParameterAnnotations(int member_index, int len) +{ + int orig_pos = pos; + + int n_params = read1u(); + for (int i = 0; i < n_params; ++i) + handleAnnotations(); + + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(JV_METHOD_ATTR); + stream->writeInt(len + 3); + stream->writeByte(JV_PARAMETER_ANNOTATIONS_KIND); + stream->writeShort(member_index); + stream->write(input_data, input_offset + orig_pos, len); } void _Jv_ClassReader::read_constpool () { - tags = (unsigned char*) _Jv_AllocBytesChecked (pool_count); - offsets = (unsigned int *) _Jv_AllocBytesChecked (sizeof (int) - * pool_count) ; + tags = (unsigned char*) _Jv_AllocBytes (pool_count); + offsets = (unsigned int *) _Jv_AllocBytes (sizeof (int) * pool_count) ; /** first, we scan the constant pool, collecting tags and offsets */ tags[0] = JV_CONSTANT_Undefined; @@ -407,32 +709,72 @@ void _Jv_ClassReader::read_fields () int fields_count = read2u (); handleFieldsBegin (fields_count); + // We want to sort the fields so that static fields come first, + // followed by instance fields. We do this before parsing the + // fields so that we can have the new indices available when + // creating the annotation data structures. + + // Allocate this on the heap in case there are a large number of + // fields. + int *fieldmap = (int *) _Jv_AllocBytes (fields_count * sizeof (int)); + int save_pos = pos; + int static_count = 0, instance_count = -1; + for (int i = 0; i < fields_count; ++i) + { + using namespace java::lang::reflect; + + int access_flags = read2u (); + skip (4); + int attributes_count = read2u (); + + if ((access_flags & Modifier::STATIC) != 0) + fieldmap[i] = static_count++; + else + fieldmap[i] = instance_count--; + + for (int j = 0; j < attributes_count; ++j) + { + skip (2); + int length = read4 (); + skip (length); + } + } + pos = save_pos; + + // In the loop above, instance fields are represented by negative + // numbers. Here we rewrite these to be proper offsets. + for (int i = 0; i < fields_count; ++i) + { + if (fieldmap[i] < 0) + fieldmap[i] = static_count - 1 - fieldmap[i]; + } + def->static_field_count = static_count; + for (int i = 0; i < fields_count; i++) { int access_flags = read2u (); int name_index = read2u (); int descriptor_index = read2u (); int attributes_count = read2u (); - + check_tag (name_index, JV_CONSTANT_Utf8); prepare_pool_entry (name_index, JV_CONSTANT_Utf8); check_tag (descriptor_index, JV_CONSTANT_Utf8); prepare_pool_entry (descriptor_index, JV_CONSTANT_Utf8); - - handleField (i, access_flags, name_index, descriptor_index); - + + handleField (i, access_flags, name_index, descriptor_index, fieldmap); + + bool found_value = false; for (int j = 0; j < attributes_count; j++) { - read_one_field_attribute (i); + read_one_field_attribute (fieldmap[i], &found_value); } } - - handleFieldsEnd (); } bool -_Jv_ClassReader::is_attribute_name (int index, char *name) +_Jv_ClassReader::is_attribute_name (int index, const char *name) { check_tag (index, JV_CONSTANT_Utf8); int len = get2u (bytes+offsets[index]); @@ -442,7 +784,8 @@ _Jv_ClassReader::is_attribute_name (int index, char *name) return !memcmp (bytes+offsets[index]+2, name, len); } -void _Jv_ClassReader::read_one_field_attribute (int field_index) +void _Jv_ClassReader::read_one_field_attribute (int field_index, + bool *found_value) { int name = read2u (); int length = read4 (); @@ -458,22 +801,23 @@ void _Jv_ClassReader::read_one_field_attribute (int field_index) || tags[cv] == JV_CONSTANT_Long || tags[cv] == JV_CONSTANT_Double || tags[cv] == JV_CONSTANT_String)) - { - handleConstantValueAttribute (field_index, cv); - } - else - { - throw_class_format_error ("erroneous ConstantValue attribute"); - } - - if (length != 2) + { + handleConstantValueAttribute (field_index, cv, found_value); + } + else + { throw_class_format_error ("erroneous ConstantValue attribute"); - } + } - else - { - skip (length); - } + if (length != 2) + throw_class_format_error ("erroneous ConstantValue attribute"); + } + else if (is_attribute_name (name, "Signature")) + handleGenericSignature(JV_FIELD_ATTR, field_index, length); + else if (is_attribute_name (name, "RuntimeVisibleAnnotations")) + handleMemberAnnotations(JV_FIELD_ATTR, field_index, length); + else + skip (length); } void _Jv_ClassReader::read_methods () @@ -490,14 +834,14 @@ void _Jv_ClassReader::read_methods () int attributes_count = read2u (); check_tag (name_index, JV_CONSTANT_Utf8); - prepare_pool_entry (descriptor_index, JV_CONSTANT_Utf8); + prepare_pool_entry (name_index, JV_CONSTANT_Utf8); - check_tag (name_index, JV_CONSTANT_Utf8); + check_tag (descriptor_index, JV_CONSTANT_Utf8); prepare_pool_entry (descriptor_index, JV_CONSTANT_Utf8); - + handleMethod (i, access_flags, name_index, descriptor_index); - + for (int j = 0; j < attributes_count; j++) { read_one_method_attribute (i); @@ -514,10 +858,32 @@ void _Jv_ClassReader::read_one_method_attribute (int method_index) if (is_attribute_name (name, "Exceptions")) { - /* we ignore this for now */ - skip (length); + _Jv_Method *method = reinterpret_cast<_Jv_Method *> + (&def->methods[method_index]); + if (method->throws != NULL) + throw_class_format_error ("only one Exceptions attribute allowed per method"); + + int num_exceptions = read2u (); + _Jv_Utf8Const **exceptions = + (_Jv_Utf8Const **) _Jv_AllocBytes ((num_exceptions + 1) + * sizeof (_Jv_Utf8Const *)); + + int out = 0; + _Jv_word *pool_data = def->constants.data; + for (int i = 0; i < num_exceptions; ++i) + { + int ndx = read2u (); + // JLS 2nd Ed. 4.7.5 requires that the tag not be 0. + if (ndx != 0) + { + check_tag (ndx, JV_CONSTANT_Class); + exceptions[out++] = pool_data[ndx].utf8; + } + } + exceptions[out] = NULL; + method->throws = exceptions; } - + else if (is_attribute_name (name, "Code")) { int start_off = pos; @@ -544,7 +910,9 @@ void _Jv_ClassReader::read_one_method_attribute (int method_index) if (start_pc > end_pc || start_pc < 0 - || end_pc >= code_length + // END_PC can be equal to CODE_LENGTH. + // See JVM Spec 4.7.4. + || end_pc > code_length || handler_pc >= code_length) throw_class_format_error ("erroneous exception handler info"); @@ -573,7 +941,14 @@ void _Jv_ClassReader::read_one_method_attribute (int method_index) if ((pos - start_off) != length) throw_class_format_error ("code attribute too short"); } - + else if (is_attribute_name (name, "Signature")) + handleGenericSignature(JV_METHOD_ATTR, method_index, length); + else if (is_attribute_name (name, "RuntimeVisibleAnnotations")) + handleMemberAnnotations(JV_METHOD_ATTR, method_index, length); + else if (is_attribute_name (name, "RuntimeVisibleParameterAnnotations")) + handleParameterAnnotations(method_index, length); + else if (is_attribute_name (name, "AnnotationDefault")) + handleAnnotationDefault(method_index, length); else { /* ignore unknown attributes */ @@ -581,26 +956,68 @@ void _Jv_ClassReader::read_one_method_attribute (int method_index) } } -void _Jv_ClassReader::read_one_code_attribute (int /*method*/) +void _Jv_ClassReader::read_one_code_attribute (int method_index) { - /* ignore for now, ... later we may want to pick up - line number information, for debugging purposes; - in fact, the whole debugger issue is open! */ - - /* int name = */ read2u (); + int name = read2u (); int length = read4 (); - skip (length); - + if (is_attribute_name (name, "LineNumberTable")) + { + _Jv_InterpMethod *method = reinterpret_cast<_Jv_InterpMethod *> + (def_interp->interpreted_methods[method_index]); + if (method->line_table != NULL) + throw_class_format_error ("Method already has LineNumberTable"); + + int table_len = read2u (); + _Jv_LineTableEntry* table + = (_Jv_LineTableEntry *) _Jv_AllocBytes (table_len + * sizeof (_Jv_LineTableEntry)); + for (int i = 0; i < table_len; i++) + { + table[i].bytecode_pc = read2u (); + table[i].line = read2u (); + } + method->line_table_len = table_len; + method->line_table = table; + } + else + { + /* ignore unknown code attributes */ + skip (length); + } } void _Jv_ClassReader::read_one_class_attribute () { - /* we also ignore the class attributes, ... - some day we'll add inner-classes support. */ - - /* int name = */ read2u (); + int name = read2u (); int length = read4 (); - skip (length); + if (is_attribute_name (name, "SourceFile")) + { + int source_index = read2u (); + check_tag (source_index, JV_CONSTANT_Utf8); + prepare_pool_entry (source_index, JV_CONSTANT_Utf8, false); + def_interp->source_file_name = _Jv_NewStringUtf8Const + (def->constants.data[source_index].utf8); + } + else if (is_attribute_name (name, "Signature")) + handleGenericSignature(JV_CLASS_ATTR, 0, length); + else if (is_attribute_name (name, "EnclosingMethod")) + handleEnclosingMethod(length); + else if (is_attribute_name (name, "RuntimeVisibleAnnotations")) + handleMemberAnnotations(JV_CLASS_ATTR, 0, length); + else if (is_attribute_name (name, "InnerClasses")) + { + ::java::io::DataOutputStream *stream = get_reflection_stream (); + stream->writeByte(JV_CLASS_ATTR); + stream->writeInt(length + 1); + stream->writeByte(JV_INNER_CLASSES_KIND); + stream->write(input_data, input_offset + pos, length); + skip (length); + } + else + { + /* Currently, we ignore most class attributes. */ + skip (length); + } } @@ -612,10 +1029,10 @@ void _Jv_ClassReader::handleConstantPool () { /** now, we actually define the class' constant pool */ - // the pool is scanned explicitly by the collector - jbyte *pool_tags = (jbyte*) _Jv_AllocBytesChecked (pool_count); - void **pool_data = (void**) _Jv_AllocBytesChecked (pool_count * sizeof (void*)); - + jbyte *pool_tags = (jbyte*) _Jv_AllocBytes (pool_count); + _Jv_word *pool_data + = (_Jv_word*) _Jv_AllocRawObj (pool_count * sizeof (_Jv_word)); + def->constants.tags = pool_tags; def->constants.data = pool_data; def->constants.size = pool_count; @@ -634,7 +1051,7 @@ void _Jv_ClassReader::handleConstantPool () check_tag (utf_index, JV_CONSTANT_Utf8); unsigned char *utf_data = bytes + offsets[utf_index]; int len = get2u (utf_data); - pool_data[i] = (void*)_Jv_makeUtf8Const ((char*)(utf_data+2), len); + pool_data[i].utf8 = _Jv_makeUtf8Const ((char*)(utf_data+2), len); pool_tags[i] = JV_CONSTANT_String; } else @@ -663,15 +1080,21 @@ void _Jv_ClassReader::handleConstantPool () } /* this is a recursive procedure, which will prepare pool entries as needed. - Which is how we avoid initializing those entries which go unused. */ + Which is how we avoid initializing those entries which go unused. + + REWRITE is true iff this pool entry is the Utf8 representation of a + class name or a signature. +*/ + void -_Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) +_Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag, + bool rewrite) { /* these two, pool_data and pool_tags, point into the class structure we are currently defining */ unsigned char *pool_tags = (unsigned char*) def->constants.tags; - void **pool_data = (void**) def->constants.data; + _Jv_word *pool_data = def->constants.data; /* this entry was already prepared */ if (pool_tags[index] == this_tag) @@ -686,25 +1109,29 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) { case JV_CONSTANT_Utf8: { - // If we came here, it is because some other tag needs this - // utf8-entry for type information! Thus, we translate /'s to .'s in - // order to accomondate gcj's internal representation. - int len = get2u (this_data); - char *buffer = (char*) alloca (len); char *s = ((char*) this_data)+2; + pool_tags[index] = JV_CONSTANT_Utf8; - /* FIXME: avoid using a buffer here */ + if (! rewrite) + { + pool_data[index].utf8 = _Jv_makeUtf8Const (s, len); + break; + } + + // If REWRITE is set, it is because some other tag needs this + // utf8-entry for type information: it is a class or a + // signature. Thus, we translate /'s to .'s in order to + // accomondate gcj's internal representation. + char *buffer = (char*) __builtin_alloca (len); for (int i = 0; i < len; i++) { if (s[i] == '/') buffer[i] = '.'; else - buffer[i] = (char) s[i]; + buffer[i] = s[i]; } - - pool_data[index] = (void*)_Jv_makeUtf8Const (buffer, len); - pool_tags[index] = JV_CONSTANT_Utf8; + pool_data[index].utf8 = _Jv_makeUtf8Const (buffer, len); } break; @@ -715,9 +1142,9 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) prepare_pool_entry (utf_index, JV_CONSTANT_Utf8); if (verify) - _Jv_VerifyClassName ((_Jv_Utf8Const*)pool_data[utf_index]); + verify_classname (pool_data[utf_index].utf8); - pool_data[index] = pool_data[utf_index]; + pool_data[index].utf8 = pool_data[utf_index].utf8; pool_tags[index] = JV_CONSTANT_Class; } break; @@ -743,24 +1170,22 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) if (verify) { _Jv_ushort name_index, type_index; - _Jv_loadIndexes ((const void**)&pool_data[nat_index], + _Jv_loadIndexes (&pool_data[nat_index], name_index, type_index); if (this_tag == JV_CONSTANT_Fieldref) - _Jv_VerifyFieldSignature - ((_Jv_Utf8Const*)pool_data[type_index]); + verify_field_signature (pool_data[type_index].utf8); else - _Jv_VerifyMethodSignature - ((_Jv_Utf8Const*)pool_data[type_index]); + verify_method_signature (pool_data[type_index].utf8); - _Jv_Utf8Const* name = (_Jv_Utf8Const*)pool_data[name_index]; + _Jv_Utf8Const* name = pool_data[name_index].utf8; if (this_tag != JV_CONSTANT_Fieldref && ( _Jv_equalUtf8Consts (name, clinit_name) || _Jv_equalUtf8Consts (name, init_name))) /* ignore */; else - _Jv_VerifyIdentifier ((_Jv_Utf8Const*)pool_data[name_index]); + verify_identifier (pool_data[name_index].utf8); } _Jv_storeIndexes (&pool_data[index], class_index, nat_index); @@ -774,8 +1199,7 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) _Jv_ushort type_index = get2u (this_data+2); check_tag (name_index, JV_CONSTANT_Utf8); - prepare_pool_entry (name_index, JV_CONSTANT_Utf8); - + prepare_pool_entry (name_index, JV_CONSTANT_Utf8, false); check_tag (type_index, JV_CONSTANT_Utf8); prepare_pool_entry (type_index, JV_CONSTANT_Utf8); @@ -786,7 +1210,7 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) case JV_CONSTANT_Float: { - jfloat f = int_bits_to_float ((jint) get4 (this_data)); + jfloat f = java::lang::Float::intBitsToFloat ((jint) get4 (this_data)); _Jv_storeFloat (&pool_data[index], f); pool_tags[index] = JV_CONSTANT_Float; } @@ -802,7 +1226,8 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) case JV_CONSTANT_Double: { - jdouble d = long_bits_to_double ((jlong) get8 (this_data)); + jdouble d + = java::lang::Double::longBitsToDouble ((jlong) get8 (this_data)); _Jv_storeDouble (&pool_data[index], d); pool_tags[index] = JV_CONSTANT_Double; } @@ -823,19 +1248,20 @@ _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) void -_Jv_ClassReader::handleClassBegin - (int access_flags, int this_class, int super_class) +_Jv_ClassReader::handleClassBegin (int access_flags, int this_class, int super_class) { + using namespace java::lang::reflect; + unsigned char *pool_tags = (unsigned char*) def->constants.tags; - void **pool_data = (void**) def->constants.data; + _Jv_word *pool_data = def->constants.data; check_tag (this_class, JV_CONSTANT_Class); - _Jv_Utf8Const *loadedName = (_Jv_Utf8Const*)pool_data[this_class]; + _Jv_Utf8Const *loadedName = pool_data[this_class].utf8; // was ClassLoader.defineClass called with an expected class name? if (def->name == 0) { - jclass orig = _Jv_FindClassInCache (loadedName, def->loader); + jclass orig = def->loader->findLoadedClass(loadedName->toString()); if (orig == 0) { @@ -858,80 +1284,85 @@ _Jv_ClassReader::handleClassBegin jstring msg = JvNewStringUTF ("loaded class "); msg = msg->concat (def->getName ()); msg = msg->concat (_Jv_NewStringUTF (" was in fact named ")); - jstring klass_name = _Jv_NewStringUTF (loadedName->data); + jstring klass_name = loadedName->toString(); msg = msg->concat (klass_name); throw_no_class_def_found_error (msg); } - def->accflags = access_flags; - pool_data[this_class] = (void*)def; + def->accflags = access_flags | java::lang::reflect::Modifier::INTERPRETED; + pool_data[this_class].clazz = def; pool_tags[this_class] = JV_CONSTANT_ResolvedClass; if (super_class == 0) { - // interfaces have java.lang.Object as super. - if (access_flags & INTERFACE) - { - def->superclass = (jclass)&ClassObject; - } - - // FIXME: Consider this carefully! - else if (!_Jv_equalUtf8Consts (def->name, ClassObject.name)) - { - throw_no_class_def_found_error ("loading java.lang.Object"); - } + // Note that this is ok if we are defining java.lang.Object. + // But there is no way to have this class be interpreted. + throw_class_format_error ("no superclass reference"); } - // In the pre-loading state, it can be looked up in the - // cache only by this thread! This allows the super-class - // to include references to this class. - def->state = JV_STATE_PRELOADING; - _Jv_RegisterClass (def); + + // Register this class with its defining loader as well (despite the + // name of the function we're calling), so that super class lookups + // work properly. If there is an error, our caller will unregister + // this class from the class loader. Also, we don't need to hold a + // lock here, as our caller has acquired it. + _Jv_RegisterInitiatingLoader (def, def->loader); + + // Note that we found a name so that unregistration can happen if + // needed. + *found_name = def->name; if (super_class != 0) { - // load the super class + // Load the superclass. check_tag (super_class, JV_CONSTANT_Class); - _Jv_Utf8Const* super_name = - (_Jv_Utf8Const*)pool_data[super_class]; + _Jv_Utf8Const* super_name = pool_data[super_class].utf8; - // load the super class using our defining loader - jclass the_super = _Jv_FindClass (super_name, - def->loader); + // Load the superclass using our defining loader. + jclass the_super = _Jv_FindClass (super_name, def->loader); // This will establish that we are allowed to be a subclass, - // and check for class circularity error + // and check for class circularity error. checkExtends (def, the_super); - def->superclass = the_super; - pool_data[super_class] = (void*) the_super; + // Note: for an interface we will find Object as the + // superclass. We still check it above to ensure class file + // validity, but we simply assign `null' to the actual field in + // this case. + def->superclass = (((access_flags & Modifier::INTERFACE)) + ? NULL : the_super); + pool_data[super_class].clazz = the_super; pool_tags[super_class] = JV_CONSTANT_ResolvedClass; } - - // now we've come past the circularity problem, we can - // now say that we're loading... + + // Now we've come past the circularity problem, we can + // now say that we're loading. def->state = JV_STATE_LOADING; def->notifyAll (); } -///// implements the checks described in sect. 5.3.5.3 +///// Implements the checks described in sect. 5.3.5.3 void _Jv_ClassReader::checkExtends (jclass sub, jclass super) { - // having an interface or a final class as a superclass is no good - if ((super->accflags & (INTERFACE | FINAL)) != 0) + using namespace java::lang::reflect; + + _Jv_Linker::wait_for_state (super, JV_STATE_LOADING); + + // Having an interface or a final class as a superclass is no good. + if ((super->accflags & (Modifier::INTERFACE | Modifier::FINAL)) != 0) { throw_incompatible_class_change_error (sub->getName ()); } - // if the super class is not public, we need to check some more - if ((super->accflags & PUBLIC) == 0) + // If the super class is not public, we need to check some more. + if ((super->accflags & Modifier::PUBLIC) == 0) { - // With package scope, the classes must have the same - // class loader. + // With package scope, the classes must have the same class + // loader. if ( sub->loader != super->loader || !_Jv_ClassNameSamePackage (sub->name, super->name)) { @@ -939,7 +1370,7 @@ _Jv_ClassReader::checkExtends (jclass sub, jclass super) } } - for (; super != 0; super = super->superclass) + for (; super != 0; super = super->getSuperclass ()) { if (super == sub) throw_class_circularity_error (sub->getName ()); @@ -950,25 +1381,25 @@ _Jv_ClassReader::checkExtends (jclass sub, jclass super) void _Jv_ClassReader::handleInterfacesBegin (int count) { - def->interfaces = (jclass*) _Jv_AllocBytesChecked (count*sizeof (jclass)); + def->interfaces = (jclass*) _Jv_AllocRawObj (count*sizeof (jclass)); def->interface_count = count; } void _Jv_ClassReader::handleInterface (int if_number, int offset) { - void ** pool_data = def->constants.data; + _Jv_word * pool_data = def->constants.data; unsigned char * pool_tags = (unsigned char*) def->constants.tags; jclass the_interface; if (pool_tags[offset] == JV_CONSTANT_Class) { - _Jv_Utf8Const* name = (_Jv_Utf8Const*) pool_data[offset]; + _Jv_Utf8Const* name = pool_data[offset].utf8; the_interface = _Jv_FindClass (name, def->loader); } else if (pool_tags[offset] == JV_CONSTANT_ResolvedClass) { - the_interface = (jclass)pool_data[offset]; + the_interface = pool_data[offset].clazz; } else { @@ -979,7 +1410,7 @@ void _Jv_ClassReader::handleInterface (int if_number, int offset) // allowed to implement that interface. checkImplements (def, the_interface); - pool_data[offset] = (void*)the_interface; + pool_data[offset].clazz = the_interface; pool_tags[offset] = JV_CONSTANT_ResolvedClass; def->interfaces[if_number] = the_interface; @@ -988,15 +1419,17 @@ void _Jv_ClassReader::handleInterface (int if_number, int offset) void _Jv_ClassReader::checkImplements (jclass sub, jclass super) { + using namespace java::lang::reflect; + // well, it *must* be an interface - if ((super->accflags & INTERFACE) == 0) + if ((super->accflags & Modifier::INTERFACE) == 0) { throw_incompatible_class_change_error (sub->getName ()); } // if it has package scope, it must also be defined by the // same loader. - if ((super->accflags & PUBLIC) == 0) + if ((super->accflags & Modifier::PUBLIC) == 0) { if ( sub->loader != super->loader || !_Jv_ClassNameSamePackage (sub->name, super->name)) @@ -1014,52 +1447,63 @@ _Jv_ClassReader::checkImplements (jclass sub, jclass super) void _Jv_ClassReader::handleFieldsBegin (int count) { - def->fields = (_Jv_Field*) - _Jv_AllocBytesChecked (count * sizeof (_Jv_Field)); + def->fields = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field)); def->field_count = count; - def->field_initializers = (_Jv_ushort*) - _Jv_AllocBytesChecked (count * sizeof (_Jv_ushort)); + def_interp->field_initializers + = (_Jv_ushort*) _Jv_AllocRawObj (count * sizeof (_Jv_ushort)); for (int i = 0; i < count; i++) - def->field_initializers[i] = (_Jv_ushort) 0; + def_interp->field_initializers[i] = (_Jv_ushort) 0; } void _Jv_ClassReader::handleField (int field_no, int flags, int name, - int desc) + int desc, + int *fieldmap) { - void **const pool_data = def->constants.data; + using namespace java::lang::reflect; + + _Jv_word *pool_data = def->constants.data; - _Jv_Field *field = &def->fields[field_no]; - _Jv_Utf8Const *field_name = (_Jv_Utf8Const*) pool_data[name]; + _Jv_Field *field = &def->fields[fieldmap[field_no]]; + _Jv_Utf8Const *field_name = pool_data[name].utf8; -#ifndef COMPACT_FIELDS field->name = field_name; -#else - field->nameIndex = name; -#endif - if (verify) - _Jv_VerifyIdentifier (field_name); + // Ignore flags we don't know about. + field->flags = flags & (Field::FIELD_MODIFIERS + | Modifier::SYNTHETIC + | Modifier::ENUM); - // ignore flags we don't know about. - field->flags = flags & ALL_FLAGS; + _Jv_Utf8Const* sig = pool_data[desc].utf8; if (verify) { - if (field->flags & (SYNCHRONIZED|NATIVE|INTERFACE|ABSTRACT)) + verify_identifier (field_name); + + for (int i = 0; i < field_no; ++i) + { + if (_Jv_equalUtf8Consts (field_name, def->fields[fieldmap[i]].name) + && _Jv_equalUtf8Consts (sig, + // We know the other fields are + // unresolved. + (_Jv_Utf8Const *) def->fields[i].type)) + throw_class_format_error ("duplicate field name"); + } + + // At most one of PUBLIC, PRIVATE, or PROTECTED is allowed. + if (1 < ( ((field->flags & Modifier::PUBLIC) ? 1 : 0) + +((field->flags & Modifier::PRIVATE) ? 1 : 0) + +((field->flags & Modifier::PROTECTED) ? 1 : 0))) throw_class_format_error ("erroneous field access flags"); + + // FIXME: JVM spec S4.5: Verify ACC_FINAL and ACC_VOLATILE are not + // both set. Verify modifiers for interface fields. - if (1 < ( ((field->flags & PUBLIC) ? 1 : 0) - +((field->flags & PRIVATE) ? 1 : 0) - +((field->flags & PROTECTED) ? 1 : 0))) - throw_class_format_error ("erroneous field access flags"); } - _Jv_Utf8Const* sig = (_Jv_Utf8Const*) pool_data[desc]; - if (verify) - _Jv_VerifyFieldSignature (sig); + verify_field_signature (sig); // field->type is really a jclass, but while it is still // unresolved we keep an _Jv_Utf8Const* instead. @@ -1070,22 +1514,27 @@ void _Jv_ClassReader::handleField (int field_no, void _Jv_ClassReader::handleConstantValueAttribute (int field_index, - int value) + int value, + bool *found_value) { + using namespace java::lang::reflect; + _Jv_Field *field = &def->fields[field_index]; - if ((field->flags & (STATIC|FINAL|PRIVATE)) == 0) + if ((field->flags & (Modifier::STATIC + | Modifier::FINAL + | Modifier::PRIVATE)) == 0) { // Ignore, as per vmspec #4.7.2 return; } // do not allow multiple constant fields! - if (field->flags & _Jv_FIELD_CONSTANT_VALUE) + if (*found_value) throw_class_format_error ("field has multiple ConstantValue attributes"); - field->flags |= _Jv_FIELD_CONSTANT_VALUE; - def->field_initializers[field_index] = value; + *found_value = true; + def_interp->field_initializers[field_index] = value; /* type check the initializer */ @@ -1095,61 +1544,20 @@ void _Jv_ClassReader::handleConstantValueAttribute (int field_index, /* FIXME: do the rest */ } -void _Jv_ClassReader::handleFieldsEnd () +void +_Jv_ClassReader::handleMethodsBegin (int count) { - // We need to reorganize the fields so that the static ones are first, - // to conform to GCJ class layout. + def->methods = (_Jv_Method *) _Jv_AllocRawObj (sizeof (_Jv_Method) * count); - int low = 0; - int high = def->field_count-1; - _Jv_Field *fields = def->fields; - _Jv_ushort *inits = def->field_initializers; + def_interp->interpreted_methods + = (_Jv_MethodBase **) _Jv_AllocRawObj (sizeof (_Jv_MethodBase *) + * count); - // this is kind of a raw version of quicksort. - while (low < high) + for (int i = 0; i < count; i++) { - // go forward on low, while it's a static - while (low < high && (fields[low].flags & STATIC) != 0) - low++; - - // go backwards on high, while it's a non-static - while (low < high && (fields[high].flags & STATIC) == 0) - high--; - - if (low==high) - break; - - _Jv_Field tmp = fields[low]; - _Jv_ushort itmp = inits[low]; - - fields[low] = fields[high]; - inits[low] = inits[high]; - - fields[high] = tmp; - inits[high] = itmp; - - high -= 1; - low += 1; + def_interp->interpreted_methods[i] = 0; + def->methods[i].index = (_Jv_ushort) -1; } - - if ((fields[low].flags & STATIC) != 0) - low += 1; - - def->static_field_count = low; -} - - - -void _Jv_ClassReader::handleMethodsBegin (int count) -{ - def->methods = (_Jv_Method*) - _Jv_AllocBytesChecked (sizeof (_Jv_Method)*count); - - def->interpreted_methods = (_Jv_InterpMethod**) - _Jv_AllocBytesChecked (sizeof (_Jv_InterpMethod*) * count); - - for (int i = 0; i < count; i++) - def->interpreted_methods[i] = 0; def->method_count = count; } @@ -1158,22 +1566,28 @@ void _Jv_ClassReader::handleMethodsBegin (int count) void _Jv_ClassReader::handleMethod (int mth_index, int accflags, int name, int desc) { - void **const pool_data = def->constants.data; + using namespace java::lang::reflect; + + _Jv_word *pool_data = def->constants.data; _Jv_Method *method = &def->methods[mth_index]; check_tag (name, JV_CONSTANT_Utf8); - prepare_pool_entry (name, JV_CONSTANT_Utf8); - method->name = (_Jv_Utf8Const*)pool_data[name]; + prepare_pool_entry (name, JV_CONSTANT_Utf8, false); + method->name = pool_data[name].utf8; check_tag (desc, JV_CONSTANT_Utf8); prepare_pool_entry (desc, JV_CONSTANT_Utf8); - method->signature = (_Jv_Utf8Const*)pool_data[desc]; + method->signature = pool_data[desc].utf8; // ignore unknown flags - method->accflags = accflags & ALL_FLAGS; + method->accflags = accflags & (Method::METHOD_MODIFIERS + | Modifier::BRIDGE + | Modifier::SYNTHETIC + | Modifier::VARARGS); - // intialize... + // Initialize... method->ncode = 0; + method->throws = NULL; if (verify) { @@ -1181,17 +1595,27 @@ void _Jv_ClassReader::handleMethod || _Jv_equalUtf8Consts (method->name, init_name)) /* ignore */; else - _Jv_VerifyIdentifier (method->name); + verify_identifier (method->name); - _Jv_VerifyMethodSignature (method->signature); + verify_method_signature (method->signature); - if (method->accflags & (VOLATILE|TRANSIENT|INTERFACE)) - throw_class_format_error ("erroneous method access flags"); - - if (1 < ( ((method->accflags & PUBLIC) ? 1 : 0) - +((method->accflags & PRIVATE) ? 1 : 0) - +((method->accflags & PROTECTED) ? 1 : 0))) + for (int i = 0; i < mth_index; ++i) + { + if (_Jv_equalUtf8Consts (method->name, def->methods[i].name) + && _Jv_equalUtf8Consts (method->signature, + def->methods[i].signature)) + throw_class_format_error ("duplicate method"); + } + + // At most one of PUBLIC, PRIVATE, or PROTECTED is allowed. + if (1 < ( ((method->accflags & Modifier::PUBLIC) ? 1 : 0) + +((method->accflags & Modifier::PRIVATE) ? 1 : 0) + +((method->accflags & Modifier::PROTECTED) ? 1 : 0))) throw_class_format_error ("erroneous method access flags"); + + // FIXME: JVM spec S4.6: if ABSTRACT modifier is set, verify other + // flags are not set. Verify flags for interface methods. Verify + // modifiers for initializers. } } @@ -1201,60 +1625,173 @@ void _Jv_ClassReader::handleCodeAttribute { int size = _Jv_InterpMethod::size (exc_table_length, code_length); _Jv_InterpMethod *method = - (_Jv_InterpMethod*) (_Jv_AllocBytesChecked (size)); + (_Jv_InterpMethod*) (_Jv_AllocRawObj (size)); method->max_stack = max_stack; method->max_locals = max_locals; method->code_length = code_length; method->exc_count = exc_table_length; + method->is_15 = is_15; method->defining_class = def; method->self = &def->methods[method_index]; + method->prepared = NULL; + method->line_table_len = 0; + method->line_table = NULL; + // grab the byte code! memcpy ((void*) method->bytecode (), (void*) (bytes+code_start), code_length); - - def->interpreted_methods[method_index] = method; - /* that's all we do for now */ + def_interp->interpreted_methods[method_index] = method; + + if ((method->self->accflags & java::lang::reflect::Modifier::STATIC)) + { + // Precompute the ncode field for a static method. This lets us + // call a static method of an interpreted class from precompiled + // code without first resolving the class (that will happen + // during class initialization instead). + method->self->ncode = method->ncode (); + } } -void _Jv_ClassReader::handleExceptionTableEntry +void _Jv_ClassReader::handleExceptionTableEntry (int method_index, int exc_index, int start_pc, int end_pc, int handler_pc, int catch_type) { - _Jv_InterpMethod *method = def->interpreted_methods[method_index]; + _Jv_InterpMethod *method = reinterpret_cast<_Jv_InterpMethod *> + (def_interp->interpreted_methods[method_index]); _Jv_InterpException *exc = method->exceptions (); - exc[exc_index].start_pc = start_pc; - exc[exc_index].end_pc = end_pc; - exc[exc_index].handler_pc = handler_pc; - exc[exc_index].handler_type = catch_type; + exc[exc_index].start_pc.i = start_pc; + exc[exc_index].end_pc.i = end_pc; + exc[exc_index].handler_pc.i = handler_pc; + exc[exc_index].handler_type.i = catch_type; } void _Jv_ClassReader::handleMethodsEnd () { + using namespace java::lang::reflect; + for (int i = 0; i < def->method_count; i++) { _Jv_Method *method = &def->methods[i]; - if (method->accflags & (NATIVE|ABSTRACT)) + if ((method->accflags & Modifier::NATIVE) != 0) + { + if (def_interp->interpreted_methods[i] != 0) + throw_class_format_error ("code provided for native method"); + else + { + _Jv_JNIMethod *m = (_Jv_JNIMethod *) + _Jv_AllocRawObj (sizeof (_Jv_JNIMethod)); + m->defining_class = def; + m->self = method; + m->function = NULL; + def_interp->interpreted_methods[i] = m; + + if ((method->accflags & Modifier::STATIC)) + { + // Precompute the ncode field for a static method. + // This lets us call a static method of an + // interpreted class from precompiled code without + // first resolving the class (that will happen + // during class initialization instead). + method->ncode = m->ncode (); + } + } + } + else if ((method->accflags & Modifier::ABSTRACT) != 0) { - if (def->interpreted_methods[i] != 0) - throw_class_format_error ("code provided " - "for abstract or native method"); + if (def_interp->interpreted_methods[i] != 0) + throw_class_format_error ("code provided for abstract method"); + method->ncode = (void *) &_Jv_ThrowAbstractMethodError; } else { - if (def->interpreted_methods[i] == 0) - throw_class_format_error ("abstract or native method " - "with no code"); + if (def_interp->interpreted_methods[i] == 0) + throw_class_format_error ("method with no code"); + } + } +} + +void _Jv_ClassReader::throw_class_format_error (const char *msg) +{ + jstring str; + if (def->name != NULL) + { + jsize mlen = strlen (msg); + unsigned char* data = (unsigned char*) def->name->chars(); + int ulen = def->name->len(); + unsigned char* limit = data + ulen; + jsize nlen = _Jv_strLengthUtf8 ((char *) data, ulen); + jsize len = nlen + mlen + 3; + str = JvAllocString(len); + jchar *chrs = JvGetStringChars(str); + while (data < limit) + *chrs++ = UTF8_GET(data, limit); + *chrs++ = ' '; + *chrs++ = '('; + for (;;) + { + char c = *msg++; + if (c == 0) + break; + *chrs++ = c & 0xFFFF; } + *chrs++ = ')'; } + else + str = JvNewStringLatin1 (msg); + ::throw_class_format_error (str); +} + +/** Here we define the exceptions that can be thrown */ + +static void +throw_no_class_def_found_error (jstring msg) +{ + throw (msg + ? new java::lang::NoClassDefFoundError (msg) + : new java::lang::NoClassDefFoundError); +} +static void +throw_no_class_def_found_error (const char *msg) +{ + throw_no_class_def_found_error (JvNewStringLatin1 (msg)); +} + +static void +throw_class_format_error (jstring msg) +{ + throw (msg + ? new java::lang::ClassFormatError (msg) + : new java::lang::ClassFormatError); +} + +static void +throw_internal_error (const char *msg) +{ + throw new java::lang::InternalError (JvNewStringLatin1 (msg)); } +static void +throw_incompatible_class_change_error (jstring msg) +{ + throw new java::lang::IncompatibleClassChangeError (msg); +} + +static void +throw_class_circularity_error (jstring msg) +{ + throw new java::lang::ClassCircularityError (msg); +} + +#endif /* INTERPRETER */ + + /** This section takes care of verifying integrity of identifiers, signatures, field ddescriptors, and class names */ @@ -1263,7 +1800,7 @@ void _Jv_ClassReader::handleMethodsEnd () int xxch = UTF8_GET(PTR,LIMIT); \ PTR = xxkeep; xxch; }) -/* verify one element of a type descriptor or signature */ +/* Verify one element of a type descriptor or signature. */ static unsigned char* _Jv_VerifyOne (unsigned char* ptr, unsigned char* limit, bool void_ok) { @@ -1275,7 +1812,8 @@ _Jv_VerifyOne (unsigned char* ptr, unsigned char* limit, bool void_ok) switch (ch) { case 'V': - if (! void_ok) return 0; + if (! void_ok) + return 0; case 'S': case 'B': case 'I': case 'J': case 'Z': case 'C': case 'F': case 'D': @@ -1284,78 +1822,69 @@ _Jv_VerifyOne (unsigned char* ptr, unsigned char* limit, bool void_ok) case 'L': { unsigned char *start = ptr, *end; - do { - if (ptr > limit) - return 0; - - end = ptr; - - if ((ch = UTF8_GET (ptr, limit)) == -1) - return 0; - - } while (ch != ';'); - _Jv_VerifyClassName (start, (unsigned short) (end-start)); + do + { + if (ptr > limit) + return 0; + + end = ptr; + + if ((ch = UTF8_GET (ptr, limit)) == -1) + return 0; + + } + while (ch != ';'); + if (! _Jv_VerifyClassName (start, (unsigned short) (end-start))) + return 0; } break; case '[': return _Jv_VerifyOne (ptr, limit, false); break; - + default: return 0; } return ptr; - } - -/** verification and loading procedures **/ - -void +/* Verification and loading procedures. */ +bool _Jv_VerifyFieldSignature (_Jv_Utf8Const*sig) { - unsigned char* ptr = (unsigned char*) sig->data; - unsigned char* limit = ptr + sig->length; + unsigned char* ptr = (unsigned char*) sig->chars(); + unsigned char* limit = ptr + sig->len(); ptr = _Jv_VerifyOne (ptr, limit, false); - if (ptr != limit) - throw_class_format_error ("erroneous type descriptor"); + return ptr == limit; } -void +bool _Jv_VerifyMethodSignature (_Jv_Utf8Const*sig) { - unsigned char* ptr = (unsigned char*) sig->data; - unsigned char* limit = ptr + sig->length; - - if (ptr == limit) - throw_class_format_error ("erroneous type descriptor"); + unsigned char* ptr = (unsigned char*) sig->chars(); + unsigned char* limit = ptr + sig->len(); - if (UTF8_GET(ptr,limit) != '(') - throw_class_format_error ("erroneous type descriptor"); + if (ptr == limit || UTF8_GET(ptr,limit) != '(') + return false; while (ptr && UTF8_PEEK (ptr, limit) != ')') ptr = _Jv_VerifyOne (ptr, limit, false); - - if (UTF8_GET (ptr, limit) != ')') - throw_class_format_error ("erroneous type descriptor"); + + if (! ptr || UTF8_GET (ptr, limit) != ')') + return false; // get the return type ptr = _Jv_VerifyOne (ptr, limit, true); - if (ptr != limit) - throw_class_format_error ("erroneous type descriptor"); - - return; - + return ptr == limit; } -/* we try to avoid calling the Character methods all the time, - in fact, they will only be called for non-standard things */ - +/* We try to avoid calling the Character methods all the time, in + fact, they will only be called for non-standard things. */ static __inline__ int is_identifier_start (int c) { @@ -1368,7 +1897,7 @@ is_identifier_start (int c) if (ch == 0x5FU) /* _ */ return 1; - return character->isJavaIdentifierStart ((jchar) ch); + return java::lang::Character::isJavaIdentifierStart ((jchar) ch); } static __inline__ int @@ -1385,71 +1914,78 @@ is_identifier_part (int c) if (ch == 0x5FU || ch == 0x24U) /* _ $ */ return 1; - return character->isJavaIdentifierStart ((jchar) ch); + return java::lang::Character::isJavaIdentifierStart ((jchar) ch); } -void +bool _Jv_VerifyIdentifier (_Jv_Utf8Const* name) { - unsigned char *ptr = (unsigned char*) name->data; - unsigned char *limit = ptr + name->length; + unsigned char *ptr = (unsigned char*) name->chars(); + unsigned char *limit = (unsigned char*) name->limit(); int ch; if ((ch = UTF8_GET (ptr, limit))==-1 || ! is_identifier_start (ch)) - throw_class_format_error ("erroneous identifier"); + return false; while (ptr != limit) { if ((ch = UTF8_GET (ptr, limit))==-1 || ! is_identifier_part (ch)) - throw_class_format_error ("erroneous identifier"); + return false; } + return true; } - -void +bool _Jv_VerifyClassName (unsigned char* ptr, _Jv_ushort length) { unsigned char *limit = ptr+length; int ch; + if ('[' == UTF8_PEEK (ptr, limit)) + { + unsigned char *end = _Jv_VerifyOne (++ptr, limit, false); + // _Jv_VerifyOne must leave us looking at the terminating nul + // byte. + if (! end || *end) + return false; + else + return true; + } + next_level: - do { + for (;;) { if ((ch = UTF8_GET (ptr, limit))==-1) - throw_class_format_error ("erroneous class name"); + return false; if (! is_identifier_start (ch)) - throw_class_format_error ("erroneous class name"); - do { + return false; + for (;;) { if (ptr == limit) - return; + return true; else if ((ch = UTF8_GET (ptr, limit))==-1) - throw_class_format_error ("erroneous class name"); + return false; else if (ch == '.') goto next_level; else if (! is_identifier_part (ch)) - throw_class_format_error ("erroneous class name"); - } while (true); - } while (true); - + return false; + } + } } -void +bool _Jv_VerifyClassName (_Jv_Utf8Const *name) { - _Jv_VerifyClassName ((unsigned char*)&name->data[0], - (_Jv_ushort) name->length); + return _Jv_VerifyClassName ((unsigned char*)name->chars(), name->len()); } - -/** returns true, if name1 and name2 represents classes in the same - package. */ - +/* Returns true, if NAME1 and NAME2 represent classes in the same + package. Neither NAME2 nor NAME2 may name an array type. */ bool _Jv_ClassNameSamePackage (_Jv_Utf8Const *name1, _Jv_Utf8Const *name2) { - unsigned char* ptr1 = (unsigned char*) name1->data; - unsigned char* limit1 = ptr1 + name1->length; + unsigned char* ptr1 = (unsigned char*) name1->chars(); + unsigned char* limit1 = (unsigned char*) name1->limit(); unsigned char* last1 = ptr1; @@ -1459,26 +1995,25 @@ _Jv_ClassNameSamePackage (_Jv_Utf8Const *name1, _Jv_Utf8Const *name2) if (ch1 == '.') last1 = ptr1; - + else if (ch1 == -1) return false; } - // now the length of name1's package name is len - int len = last1 - (unsigned char*) name1->data; + // Now the length of NAME1's package name is LEN. + int len = last1 - (unsigned char*) name1->chars(); - // if this is longer than name2, then we're off - if (len > name2->length) + // If this is longer than NAME2, then we're off. + if (len > name2->len()) return false; - // then compare the first len bytes for equality - if (memcmp ((void*) name1->data, (void*) name2->data, len) == 0) + // Then compare the first len bytes for equality. + if (memcmp ((void*) name1->chars(), (void*) name2->chars(), len) == 0) { - // check that there are no .'s after position len in name2 + // Check that there are no .'s after position LEN in NAME2. - unsigned char* ptr2 = (unsigned char*) name2->data + len; - unsigned char* limit2 = - (unsigned char*) name2->data + name2->length; + unsigned char* ptr2 = (unsigned char*) name2->chars() + len; + unsigned char* limit2 = (unsigned char*) name2->limit(); while (ptr2 < limit2) { @@ -1490,67 +2025,3 @@ _Jv_ClassNameSamePackage (_Jv_Utf8Const *name1, _Jv_Utf8Const *name2) } return false; } - - - -/** Here we define the exceptions that can be thrown */ - -static void -throw_no_class_def_found_error (jstring msg) -{ - if (msg == 0) - JvThrow (new java::lang::NoClassDefFoundError); - else - JvThrow (new java::lang::NoClassDefFoundError (msg)); -} - -static void -throw_no_class_def_found_error (char *msg) -{ - throw_no_class_def_found_error (JvNewStringLatin1 (msg)); -} - -static void -throw_class_format_error (jstring msg) -{ - if (msg == 0) - JvThrow (new java::lang::ClassFormatError); - else - JvThrow (new java::lang::ClassFormatError (msg)); -} - -static void -throw_class_format_error (char *msg) -{ - throw_class_format_error (JvNewStringLatin1 (msg)); -} - -static void -throw_internal_error (char *msg) -{ - JvThrow - (new java::lang::InternalError (JvNewStringLatin1 (msg))); -} - -static jfloat int_bits_to_float (jint value) -{ - return java::lang::Float::intBitsToFloat (value); -} - -static jdouble long_bits_to_double (jlong value) -{ - return java::lang::Double::longBitsToDouble (value); -} - -static void throw_incompatible_class_change_error (jstring msg) -{ - JvThrow (new java::lang::IncompatibleClassChangeError (msg)); -} - -static void throw_class_circularity_error (jstring msg) -{ - JvThrow (new java::lang::ClassCircularityError (msg)); -} - -#endif /* INTERPRETER */ -