| // defineclass.cc - defining a class from .class format. |
| |
| /* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 |
| Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| /* |
| Author: Kresten Krab Thorup <krab@gnu.org> |
| |
| Written using the online versions of Java Language Specification (1st |
| ed.) and The Java Virtual Machine Specification (2nd ed.). |
| |
| Future work may include reading (and handling) attributes which are |
| currently being ignored ("InnerClasses", "LineNumber", etc...). |
| */ |
| |
| #include <config.h> |
| |
| #include <java-interp.h> |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <java-cpool.h> |
| #include <gcj/cni.h> |
| #include <execution.h> |
| |
| #include <java/lang/Class.h> |
| #include <java/lang/Float.h> |
| #include <java/lang/Double.h> |
| #include <java/lang/Character.h> |
| #include <java/lang/LinkageError.h> |
| #include <java/lang/InternalError.h> |
| #include <java/lang/ClassFormatError.h> |
| #include <java/lang/NoClassDefFoundError.h> |
| #include <java/lang/ClassCircularityError.h> |
| #include <java/lang/IncompatibleClassChangeError.h> |
| #include <java/lang/reflect/Modifier.h> |
| #include <java/lang/reflect/Field.h> |
| #include <java/lang/reflect/Method.h> |
| #include <java/security/ProtectionDomain.h> |
| #include <java/io/DataOutputStream.h> |
| #include <java/io/ByteArrayOutputStream.h> |
| |
| using namespace gcj; |
| |
| #ifdef INTERPRETER |
| |
| // these go in some separate functions, to avoid having _Jv_InitClass |
| // inserted all over the place. |
| 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 (const char *msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_class_format_error (jstring msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_incompatible_class_change_error (jstring msg) |
| __attribute__ ((__noreturn__)); |
| static void throw_class_circularity_error (jstring msg) |
| __attribute__ ((__noreturn__)); |
| |
| /** |
| * 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 |
| * write all it's different structures); but also because this makes it |
| * easy to make class definition reentrant, and thus two threads can be |
| * defining classes at the same time. This class (_Jv_ClassReader) is |
| * never exposed outside this file, so we don't have to worry about |
| * public or private members here. |
| */ |
| |
| struct _Jv_ClassReader |
| { |
| |
| // do verification? Currently, there is no option to disable this. |
| // This flag just controls the verificaiton done by the class loader; |
| // i.e., checking the integrity of the constant pool; and it is |
| // 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 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 |
| // most likely to be linked in. |
| |
| bool verify; |
| |
| // original input data. |
| jbyteArray input_data; |
| jint input_offset; |
| |
| // input data. |
| unsigned char *bytes; |
| int len; |
| |
| // current input position |
| int pos; |
| |
| // the constant pool data |
| int pool_count; |
| unsigned char *tags; |
| unsigned int *offsets; |
| |
| // the class to define (see java-interp.h) |
| 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) |
| { |
| if (pos + num > len) |
| throw_class_format_error ("Premature end of data"); |
| } |
| |
| /* skip a given number of bytes in input */ |
| inline void skip (int num) |
| { |
| check (num); |
| pos += num; |
| } |
| |
| /* read an unsigned 1-byte unit */ |
| inline static jint get1u (unsigned char* bytes) |
| { |
| return bytes[0]; |
| } |
| |
| /* read an unsigned 1-byte unit */ |
| inline jint read1u () |
| { |
| skip (1); |
| return get1u (bytes+pos-1); |
| } |
| |
| /* read an unsigned 2-byte unit */ |
| inline static jint get2u (unsigned char *bytes) |
| { |
| return (((jint)bytes[0]) << 8) | ((jint)bytes[1]); |
| } |
| |
| /* read an unsigned 2-byte unit */ |
| inline jint read2u () |
| { |
| skip (2); |
| return get2u (bytes+pos-2); |
| } |
| |
| /* read a 4-byte unit */ |
| static jint get4 (unsigned char *bytes) |
| { |
| return (((jint)bytes[0]) << 24) |
| | (((jint)bytes[1]) << 16) |
| | (((jint)bytes[2]) << 8) |
| | (((jint)bytes[3]) << 0); |
| } |
| |
| /* read a 4-byte unit, (we don't do that quite so often) */ |
| inline jint read4 () |
| { |
| skip (4); |
| return get4 (bytes+pos-4); |
| } |
| |
| /* read a 8-byte unit */ |
| static jlong get8 (unsigned char* bytes) |
| { |
| return (((jlong)bytes[0]) << 56) |
| | (((jlong)bytes[1]) << 48) |
| | (((jlong)bytes[2]) << 40) |
| | (((jlong)bytes[3]) << 32) |
| | (((jlong)bytes[4]) << 24) |
| | (((jlong)bytes[5]) << 16) |
| | (((jlong)bytes[6]) << 8) |
| | (((jlong)bytes[7]) << 0); |
| } |
| |
| /* read a 8-byte unit */ |
| inline jlong read8 () |
| { |
| skip (8); |
| return get8 (bytes+pos-8); |
| } |
| |
| inline void check_tag (int index, char expected_tag) |
| { |
| if (index < 0 |
| || index > pool_count |
| || tags[index] != expected_tag) |
| throw_class_format_error ("erroneous constant pool tag"); |
| } |
| |
| 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; |
| 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, |
| 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, 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, const char *name); |
| |
| /** return the value of a utf8 entry in the passed array */ |
| int pool_Utf8_to_char_arr (int index, char **entry); |
| |
| /** here goes the class-loader members defined out-of-line */ |
| void handleConstantPool (); |
| void handleClassBegin (int, int, int); |
| void handleInterfacesBegin (int); |
| void handleInterface (int, int); |
| void handleFieldsBegin (int); |
| void handleField (int, int, int, int, int *); |
| void handleConstantValueAttribute (int, int, bool *); |
| void handleMethodsBegin (int); |
| void handleMethod (int, int, int, int); |
| void handleMethodsEnd (); |
| void handleCodeAttribute (int, int, int, int, int, int); |
| void handleExceptionTableEntry (int, int, int, int, int, int); |
| |
| void checkExtends (jclass sub, jclass super); |
| void checkImplements (jclass sub, jclass super); |
| |
| /* |
| * FIXME: we should keep a hash table of utf8-strings, since many will |
| * be the same. It's a little tricky, however, because the hash table |
| * needs to interact gracefully with the garbage collector. Much |
| * memory is to be saved by this, however! perhaps the improvement |
| * could be implemented in prims.cc (_Jv_makeUtf8Const), since it |
| * computes the hash value anyway. |
| */ |
| }; |
| |
| // 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, |
| java::security::ProtectionDomain *pd, |
| _Jv_Utf8Const **name_result) |
| { |
| _Jv_ClassReader reader (klass, data, offset, length, pd, name_result); |
| reader.parse(); |
| |
| /* that's it! */ |
| } |
| |
| |
| /** 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 |
| #define MAJOR_1_6 50 |
| #define MINOR_1_6 0 |
| |
| void |
| _Jv_ClassReader::parse () |
| { |
| int magic = read4 (); |
| 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_6 |
| || (major_version == MAJOR_1_6 && minor_version > MINOR_1_6)) |
| throw_class_format_error ("unrecognized class file version"); |
| is_15 = (major_version >= MAJOR_1_5); |
| |
| pool_count = read2u (); |
| |
| read_constpool (); |
| |
| int access_flags = read2u (); |
| int this_class = read2u (); |
| int super_class = read2u (); |
| |
| check_tag (this_class, JV_CONSTANT_Class); |
| if (super_class != 0) |
| check_tag (super_class, JV_CONSTANT_Class); |
| |
| 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++) |
| { |
| int iface = read2u (); |
| check_tag (iface, JV_CONSTANT_Class); |
| handleInterface (i, iface); |
| } |
| |
| read_fields (); |
| read_methods (); |
| |
| int attributes_count = read2u (); |
| |
| for (int i = 0; i < attributes_count; i++) |
| { |
| read_one_class_attribute (); |
| } |
| |
| if (pos != len) |
| throw_class_format_error ("unused data before end of file"); |
| |
| 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_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; |
| offsets[0] = pos; |
| for (int c = 1; c < pool_count; c++) |
| { |
| tags[c] = read1u (); |
| offsets[c] = pos; |
| |
| switch (tags[c]) |
| { |
| case JV_CONSTANT_String: |
| case JV_CONSTANT_Class: |
| skip (2); |
| break; |
| |
| case JV_CONSTANT_Fieldref: |
| case JV_CONSTANT_Methodref: |
| case JV_CONSTANT_InterfaceMethodref: |
| case JV_CONSTANT_NameAndType: |
| case JV_CONSTANT_Integer: |
| case JV_CONSTANT_Float: |
| skip (4); |
| break; |
| |
| case JV_CONSTANT_Double: |
| case JV_CONSTANT_Long: |
| skip (8); |
| tags[++c] = JV_CONSTANT_Undefined; |
| break; |
| |
| case JV_CONSTANT_Utf8: |
| { |
| int len = read2u (); |
| skip (len); |
| } |
| break; |
| |
| case JV_CONSTANT_Unicode: |
| throw_class_format_error ("unicode not supported"); |
| break; |
| |
| default: |
| throw_class_format_error ("erroneous constant pool tag"); |
| } |
| } |
| |
| handleConstantPool (); |
| } |
| |
| |
| 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, fieldmap); |
| |
| bool found_value = false; |
| for (int j = 0; j < attributes_count; j++) |
| { |
| read_one_field_attribute (fieldmap[i], &found_value); |
| } |
| } |
| } |
| |
| bool |
| _Jv_ClassReader::is_attribute_name (int index, const char *name) |
| { |
| check_tag (index, JV_CONSTANT_Utf8); |
| int len = get2u (bytes+offsets[index]); |
| if (len != (int) strlen (name)) |
| return false; |
| else |
| return !memcmp (bytes+offsets[index]+2, name, len); |
| } |
| |
| // Get a UTF8 value from the constant pool and turn it into a garbage |
| // collected char array. |
| int _Jv_ClassReader::pool_Utf8_to_char_arr (int index, char** entry) |
| { |
| check_tag (index, JV_CONSTANT_Utf8); |
| int len = get2u (bytes + offsets[index]); |
| *entry = reinterpret_cast<char *> (_Jv_AllocBytes (len + 1)); |
| (*entry)[len] = '\0'; |
| memcpy (*entry, bytes + offsets[index] + 2, len); |
| return len + 1; |
| } |
| |
| void _Jv_ClassReader::read_one_field_attribute (int field_index, |
| bool *found_value) |
| { |
| int name = read2u (); |
| int length = read4 (); |
| |
| if (is_attribute_name (name, "ConstantValue")) |
| { |
| int cv = read2u (); |
| |
| if (cv < pool_count |
| && cv > 0 |
| && (tags[cv] == JV_CONSTANT_Integer |
| || tags[cv] == JV_CONSTANT_Float |
| || tags[cv] == JV_CONSTANT_Long |
| || tags[cv] == JV_CONSTANT_Double |
| || tags[cv] == JV_CONSTANT_String)) |
| { |
| handleConstantValueAttribute (field_index, cv, found_value); |
| } |
| else |
| { |
| throw_class_format_error ("erroneous ConstantValue attribute"); |
| } |
| |
| 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 () |
| { |
| int methods_count = read2u (); |
| |
| handleMethodsBegin (methods_count); |
| |
| for (int i = 0; i < methods_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); |
| |
| handleMethod (i, access_flags, name_index, |
| descriptor_index); |
| |
| for (int j = 0; j < attributes_count; j++) |
| { |
| read_one_method_attribute (i); |
| } |
| } |
| |
| handleMethodsEnd (); |
| } |
| |
| void _Jv_ClassReader::read_one_method_attribute (int method_index) |
| { |
| int name = read2u (); |
| int length = read4 (); |
| |
| if (is_attribute_name (name, "Exceptions")) |
| { |
| _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; |
| int max_stack = read2u (); |
| int max_locals = read2u (); |
| int code_length = read4 (); |
| |
| int code_start = pos; |
| skip (code_length); |
| int exception_table_length = read2u (); |
| |
| handleCodeAttribute (method_index, |
| max_stack, max_locals, |
| code_start, code_length, |
| exception_table_length); |
| |
| |
| for (int i = 0; i < exception_table_length; i++) |
| { |
| int start_pc = read2u (); |
| int end_pc = read2u (); |
| int handler_pc = read2u (); |
| int catch_type = read2u (); |
| |
| if (start_pc > end_pc |
| || start_pc < 0 |
| // 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"); |
| |
| if (! (tags[catch_type] == JV_CONSTANT_Class |
| || tags[catch_type] == 0)) |
| { |
| throw_class_format_error ("erroneous exception handler info"); |
| } |
| |
| handleExceptionTableEntry (method_index, |
| i, |
| start_pc, |
| end_pc, |
| handler_pc, |
| catch_type); |
| |
| } |
| |
| int attributes_count = read2u (); |
| |
| for (int i = 0; i < attributes_count; i++) |
| { |
| read_one_code_attribute (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 */ |
| skip (length); |
| } |
| } |
| |
| void _Jv_ClassReader::read_one_code_attribute (int method_index) |
| { |
| int name = read2u (); |
| int length = read4 (); |
| 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 if (is_attribute_name (name, "LocalVariableTable")) |
| { |
| _Jv_InterpMethod *method = reinterpret_cast<_Jv_InterpMethod *> |
| (def_interp->interpreted_methods[method_index]); |
| if (method->local_var_table != NULL) |
| throw_class_format_error ("Method already has LocalVariableTable"); |
| |
| int table_len = read2u (); |
| _Jv_LocalVarTableEntry *table |
| = reinterpret_cast<_Jv_LocalVarTableEntry *> |
| (_Jv_AllocRawObj (table_len * sizeof (_Jv_LocalVarTableEntry))); |
| |
| for (int i = 0; i < table_len; i++) |
| { |
| table[i].bytecode_pc = read2u (); |
| table[i].length = read2u (); |
| pool_Utf8_to_char_arr (read2u (), &table[i].name); |
| pool_Utf8_to_char_arr (read2u (), &table[i].descriptor); |
| table[i].slot = read2u (); |
| |
| if (table[i].slot > method->max_locals || table[i].slot < 0) |
| throw_class_format_error ("Malformed Local Variable Table: Invalid Slot"); |
| } |
| |
| method->local_var_table_len = table_len; |
| method->local_var_table = table; |
| } |
| else |
| { |
| /* ignore unknown code attributes */ |
| skip (length); |
| } |
| } |
| |
| void _Jv_ClassReader::read_one_class_attribute () |
| { |
| int name = read2u (); |
| int length = read4 (); |
| 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); |
| } |
| } |
| |
| |
| |
| |
| /* this section defines the semantic actions of the parser */ |
| |
| void _Jv_ClassReader::handleConstantPool () |
| { |
| /** now, we actually define the class' constant pool */ |
| |
| 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; |
| |
| // Here we make a pass to collect the strings! We do this, because |
| // internally in the GCJ runtime, classes are encoded with .'s not /'s. |
| // Therefore, we first collect the strings, and then translate the rest |
| // of the utf8-entries (thus not representing strings) from /-notation |
| // to .-notation. |
| for (int i = 1; i < pool_count; i++) |
| { |
| if (tags[i] == JV_CONSTANT_String) |
| { |
| unsigned char* str_data = bytes + offsets [i]; |
| int utf_index = get2u (str_data); |
| check_tag (utf_index, JV_CONSTANT_Utf8); |
| unsigned char *utf_data = bytes + offsets[utf_index]; |
| int len = get2u (utf_data); |
| pool_data[i].utf8 = _Jv_makeUtf8Const ((char*)(utf_data+2), len); |
| pool_tags[i] = JV_CONSTANT_String; |
| } |
| else |
| { |
| pool_tags[i] = JV_CONSTANT_Undefined; |
| } |
| } |
| |
| // and now, we scan everything else but strings & utf8-entries. This |
| // leaves out those utf8-entries which are not used; which will be left |
| // with a tag of JV_CONSTANT_Undefined in the class definition. |
| for (int index = 1; index < pool_count; index++) |
| { |
| switch (tags[index]) |
| { |
| case JV_CONSTANT_Undefined: |
| case JV_CONSTANT_String: |
| case JV_CONSTANT_Utf8: |
| continue; |
| |
| default: |
| prepare_pool_entry (index, tags[index]); |
| } |
| } |
| |
| } |
| |
| /* this is a recursive procedure, which will prepare pool entries as needed. |
| 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, |
| 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; |
| _Jv_word *pool_data = def->constants.data; |
| |
| /* this entry was already prepared */ |
| if (pool_tags[index] == this_tag) |
| return; |
| |
| /* this_data points to the constant-pool information for the current |
| constant-pool entry */ |
| |
| unsigned char *this_data = bytes + offsets[index]; |
| |
| switch (this_tag) |
| { |
| case JV_CONSTANT_Utf8: |
| { |
| int len = get2u (this_data); |
| char *s = ((char*) this_data)+2; |
| pool_tags[index] = JV_CONSTANT_Utf8; |
| |
| 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] = s[i]; |
| } |
| pool_data[index].utf8 = _Jv_makeUtf8Const (buffer, len); |
| } |
| break; |
| |
| case JV_CONSTANT_Class: |
| { |
| int utf_index = get2u (this_data); |
| check_tag (utf_index, JV_CONSTANT_Utf8); |
| prepare_pool_entry (utf_index, JV_CONSTANT_Utf8); |
| |
| if (verify) |
| verify_classname (pool_data[utf_index].utf8); |
| |
| pool_data[index].utf8 = pool_data[utf_index].utf8; |
| pool_tags[index] = JV_CONSTANT_Class; |
| } |
| break; |
| |
| case JV_CONSTANT_String: |
| // already handled before... |
| break; |
| |
| case JV_CONSTANT_Fieldref: |
| case JV_CONSTANT_Methodref: |
| case JV_CONSTANT_InterfaceMethodref: |
| { |
| int class_index = get2u (this_data); |
| int nat_index = get2u (this_data+2); |
| |
| check_tag (class_index, JV_CONSTANT_Class); |
| prepare_pool_entry (class_index, JV_CONSTANT_Class); |
| |
| check_tag (nat_index, JV_CONSTANT_NameAndType); |
| prepare_pool_entry (nat_index, JV_CONSTANT_NameAndType); |
| |
| // here, verify the signature and identifier name |
| if (verify) |
| { |
| _Jv_ushort name_index, type_index; |
| _Jv_loadIndexes (&pool_data[nat_index], |
| name_index, type_index); |
| |
| if (this_tag == JV_CONSTANT_Fieldref) |
| verify_field_signature (pool_data[type_index].utf8); |
| else |
| verify_method_signature (pool_data[type_index].utf8); |
| |
| _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 |
| verify_identifier (pool_data[name_index].utf8); |
| } |
| |
| _Jv_storeIndexes (&pool_data[index], class_index, nat_index); |
| pool_tags[index] = this_tag; |
| } |
| break; |
| |
| case JV_CONSTANT_NameAndType: |
| { |
| _Jv_ushort name_index = get2u (this_data); |
| _Jv_ushort type_index = get2u (this_data+2); |
| |
| check_tag (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); |
| |
| _Jv_storeIndexes (&pool_data[index], name_index, type_index); |
| pool_tags[index] = JV_CONSTANT_NameAndType; |
| } |
| break; |
| |
| case JV_CONSTANT_Float: |
| { |
| jfloat f = java::lang::Float::intBitsToFloat ((jint) get4 (this_data)); |
| _Jv_storeFloat (&pool_data[index], f); |
| pool_tags[index] = JV_CONSTANT_Float; |
| } |
| break; |
| |
| case JV_CONSTANT_Integer: |
| { |
| int i = get4 (this_data); |
| _Jv_storeInt (&pool_data[index], i); |
| pool_tags[index] = JV_CONSTANT_Integer; |
| } |
| break; |
| |
| case JV_CONSTANT_Double: |
| { |
| jdouble d |
| = java::lang::Double::longBitsToDouble ((jlong) get8 (this_data)); |
| _Jv_storeDouble (&pool_data[index], d); |
| pool_tags[index] = JV_CONSTANT_Double; |
| } |
| break; |
| |
| case JV_CONSTANT_Long: |
| { |
| jlong i = get8 (this_data); |
| _Jv_storeLong (&pool_data[index], i); |
| pool_tags[index] = JV_CONSTANT_Long; |
| } |
| break; |
| |
| default: |
| throw_class_format_error ("erroneous constant pool tag"); |
| } |
| } |
| |
| |
| void |
| _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; |
| _Jv_word *pool_data = def->constants.data; |
| |
| check_tag (this_class, JV_CONSTANT_Class); |
| _Jv_Utf8Const *loadedName = pool_data[this_class].utf8; |
| |
| // was ClassLoader.defineClass called with an expected class name? |
| if (def->name == 0) |
| { |
| jclass orig = def->loader->findLoadedClass(loadedName->toString()); |
| |
| if (orig == 0) |
| { |
| def->name = loadedName; |
| } |
| else |
| { |
| jstring msg = JvNewStringUTF ("anonymous " |
| "class data denotes " |
| "existing class "); |
| msg = msg->concat (orig->getName ()); |
| |
| throw_no_class_def_found_error (msg); |
| } |
| } |
| |
| // assert that the loaded class has the expected name, 5.3.5 |
| else if (! _Jv_equalUtf8Consts (loadedName, def->name)) |
| { |
| jstring msg = JvNewStringUTF ("loaded class "); |
| msg = msg->concat (def->getName ()); |
| msg = msg->concat (_Jv_NewStringUTF (" was in fact named ")); |
| jstring klass_name = loadedName->toString(); |
| msg = msg->concat (klass_name); |
| |
| throw_no_class_def_found_error (msg); |
| } |
| |
| 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) |
| { |
| // 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"); |
| } |
| |
| def->state = JV_STATE_PRELOADING; |
| |
| // 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 superclass. |
| check_tag (super_class, JV_CONSTANT_Class); |
| _Jv_Utf8Const* super_name = pool_data[super_class].utf8; |
| |
| // 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. |
| checkExtends (def, 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. |
| |
| def->state = JV_STATE_LOADING; |
| def->notifyAll (); |
| } |
| |
| ///// Implements the checks described in sect. 5.3.5.3 |
| void |
| _Jv_ClassReader::checkExtends (jclass sub, jclass super) |
| { |
| 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 & Modifier::PUBLIC) == 0) |
| { |
| // With package scope, the classes must have the same class |
| // loader. |
| if ( sub->loader != super->loader |
| || !_Jv_ClassNameSamePackage (sub->name, super->name)) |
| { |
| throw_incompatible_class_change_error (sub->getName ()); |
| } |
| } |
| |
| for (; super != 0; super = super->getSuperclass ()) |
| { |
| if (super == sub) |
| throw_class_circularity_error (sub->getName ()); |
| } |
| } |
| |
| |
| |
| void _Jv_ClassReader::handleInterfacesBegin (int count) |
| { |
| def->interfaces = (jclass*) _Jv_AllocRawObj (count*sizeof (jclass)); |
| def->interface_count = count; |
| } |
| |
| void _Jv_ClassReader::handleInterface (int if_number, int offset) |
| { |
| _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 = pool_data[offset].utf8; |
| the_interface = _Jv_FindClass (name, def->loader); |
| } |
| else if (pool_tags[offset] == JV_CONSTANT_ResolvedClass) |
| { |
| the_interface = pool_data[offset].clazz; |
| } |
| else |
| { |
| throw_no_class_def_found_error ("erroneous constant pool tag"); |
| } |
| |
| // checks the validity of the_interface, and that we are in fact |
| // allowed to implement that interface. |
| checkImplements (def, the_interface); |
| |
| pool_data[offset].clazz = the_interface; |
| pool_tags[offset] = JV_CONSTANT_ResolvedClass; |
| |
| def->interfaces[if_number] = the_interface; |
| } |
| |
| void |
| _Jv_ClassReader::checkImplements (jclass sub, jclass super) |
| { |
| using namespace java::lang::reflect; |
| |
| // well, it *must* be an interface |
| 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 & Modifier::PUBLIC) == 0) |
| { |
| if ( sub->loader != super->loader |
| || !_Jv_ClassNameSamePackage (sub->name, super->name)) |
| { |
| throw_incompatible_class_change_error (sub->getName ()); |
| } |
| } |
| |
| // FIXME: add interface circularity check here |
| if (sub == super) |
| { |
| throw_class_circularity_error (sub->getName ()); |
| } |
| } |
| |
| void _Jv_ClassReader::handleFieldsBegin (int count) |
| { |
| def->fields = (_Jv_Field*) _Jv_AllocRawObj (count * sizeof (_Jv_Field)); |
| def->field_count = count; |
| def_interp->field_initializers |
| = (_Jv_ushort*) _Jv_AllocRawObj (count * sizeof (_Jv_ushort)); |
| for (int i = 0; i < count; i++) |
| def_interp->field_initializers[i] = (_Jv_ushort) 0; |
| } |
| |
| void _Jv_ClassReader::handleField (int field_no, |
| int flags, |
| int name, |
| int desc, |
| int *fieldmap) |
| { |
| using namespace java::lang::reflect; |
| |
| _Jv_word *pool_data = def->constants.data; |
| |
| _Jv_Field *field = &def->fields[fieldmap[field_no]]; |
| _Jv_Utf8Const *field_name = pool_data[name].utf8; |
| |
| field->name = field_name; |
| |
| // Ignore flags we don't know about. |
| field->flags = flags & (Field::FIELD_MODIFIERS |
| | Modifier::SYNTHETIC |
| | Modifier::ENUM); |
| |
| _Jv_Utf8Const* sig = pool_data[desc].utf8; |
| |
| if (verify) |
| { |
| 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 (verify) |
| verify_field_signature (sig); |
| |
| // field->type is really a jclass, but while it is still |
| // unresolved we keep an _Jv_Utf8Const* instead. |
| field->type = (jclass) sig; |
| field->flags |= _Jv_FIELD_UNRESOLVED_FLAG; |
| field->u.boffset = 0; |
| } |
| |
| |
| void _Jv_ClassReader::handleConstantValueAttribute (int field_index, |
| int value, |
| bool *found_value) |
| { |
| using namespace java::lang::reflect; |
| |
| _Jv_Field *field = &def->fields[field_index]; |
| |
| 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 (*found_value) |
| throw_class_format_error ("field has multiple ConstantValue attributes"); |
| |
| *found_value = true; |
| def_interp->field_initializers[field_index] = value; |
| |
| /* type check the initializer */ |
| |
| if (value <= 0 || value >= pool_count) |
| throw_class_format_error ("erroneous ConstantValue attribute"); |
| |
| /* FIXME: do the rest */ |
| } |
| |
| void |
| _Jv_ClassReader::handleMethodsBegin (int count) |
| { |
| def->methods = (_Jv_Method *) _Jv_AllocRawObj (sizeof (_Jv_Method) * count); |
| |
| def_interp->interpreted_methods |
| = (_Jv_MethodBase **) _Jv_AllocRawObj (sizeof (_Jv_MethodBase *) |
| * count); |
| |
| for (int i = 0; i < count; i++) |
| { |
| def_interp->interpreted_methods[i] = 0; |
| def->methods[i].index = (_Jv_ushort) -1; |
| } |
| |
| def->method_count = count; |
| } |
| |
| |
| void _Jv_ClassReader::handleMethod |
| (int mth_index, int accflags, int name, int desc) |
| { |
| 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, false); |
| method->name = pool_data[name].utf8; |
| |
| check_tag (desc, JV_CONSTANT_Utf8); |
| prepare_pool_entry (desc, JV_CONSTANT_Utf8); |
| method->signature = pool_data[desc].utf8; |
| |
| // ignore unknown flags |
| method->accflags = accflags & (Method::METHOD_MODIFIERS |
| | Modifier::BRIDGE |
| | Modifier::SYNTHETIC |
| | Modifier::VARARGS); |
| |
| // Initialize... |
| method->ncode = 0; |
| method->throws = NULL; |
| |
| if (verify) |
| { |
| if (_Jv_equalUtf8Consts (method->name, clinit_name) |
| || _Jv_equalUtf8Consts (method->name, init_name)) |
| /* ignore */; |
| else |
| verify_identifier (method->name); |
| |
| verify_method_signature (method->signature); |
| |
| 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. |
| } |
| } |
| |
| void _Jv_ClassReader::handleCodeAttribute |
| (int method_index, int max_stack, int max_locals, |
| int code_start, int code_length, int exc_table_length) |
| { |
| int size = _Jv_InterpMethod::size (exc_table_length, code_length); |
| _Jv_InterpMethod *method = |
| (_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; |
| #ifdef DIRECT_THREADED |
| method->thread_count = 0; |
| #endif |
| |
| // grab the byte code! |
| memcpy ((void*) method->bytecode (), |
| (void*) (bytes+code_start), |
| code_length); |
| |
| 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 (def); |
| } |
| } |
| |
| 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 = reinterpret_cast<_Jv_InterpMethod *> |
| (def_interp->interpreted_methods[method_index]); |
| _Jv_InterpException *exc = method->exceptions (); |
| |
| 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 & 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 (def); |
| } |
| } |
| } |
| else if ((method->accflags & Modifier::ABSTRACT) != 0) |
| { |
| if (def_interp->interpreted_methods[i] != 0) |
| throw_class_format_error ("code provided for abstract method"); |
| method->ncode = (void *) &_Jv_ThrowAbstractMethodError; |
| } |
| else |
| { |
| 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 */ |
| |
| #define UTF8_PEEK(PTR, LIMIT) \ |
| ({ unsigned char* xxkeep = (PTR); \ |
| int xxch = UTF8_GET(PTR,LIMIT); \ |
| PTR = xxkeep; xxch; }) |
| |
| /* Verify one element of a type descriptor or signature. */ |
| static unsigned char* |
| _Jv_VerifyOne (unsigned char* ptr, unsigned char* limit, bool void_ok) |
| { |
| if (ptr >= limit) |
| return 0; |
| |
| int ch = UTF8_GET (ptr, limit); |
| |
| switch (ch) |
| { |
| case 'V': |
| if (! void_ok) |
| return 0; |
| |
| case 'S': case 'B': case 'I': case 'J': |
| case 'Z': case 'C': case 'F': case 'D': |
| break; |
| |
| 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 != ';'); |
| 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. */ |
| bool |
| _Jv_VerifyFieldSignature (_Jv_Utf8Const*sig) |
| { |
| unsigned char* ptr = (unsigned char*) sig->chars(); |
| unsigned char* limit = ptr + sig->len(); |
| |
| ptr = _Jv_VerifyOne (ptr, limit, false); |
| |
| return ptr == limit; |
| } |
| |
| bool |
| _Jv_VerifyMethodSignature (_Jv_Utf8Const*sig) |
| { |
| unsigned char* ptr = (unsigned char*) sig->chars(); |
| unsigned char* limit = ptr + sig->len(); |
| |
| if (ptr == limit || UTF8_GET(ptr,limit) != '(') |
| return false; |
| |
| while (ptr && UTF8_PEEK (ptr, limit) != ')') |
| ptr = _Jv_VerifyOne (ptr, limit, false); |
| |
| if (! ptr || UTF8_GET (ptr, limit) != ')') |
| return false; |
| |
| // get the return type |
| ptr = _Jv_VerifyOne (ptr, limit, true); |
| |
| 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. */ |
| static __inline__ int |
| is_identifier_start (int c) |
| { |
| unsigned int ch = (unsigned)c; |
| |
| if ((ch - 0x41U) < 29U) /* A ... Z */ |
| return 1; |
| if ((ch - 0x61U) < 29U) /* a ... z */ |
| return 1; |
| if (ch == 0x5FU) /* _ */ |
| return 1; |
| |
| return java::lang::Character::isJavaIdentifierStart ((jchar) ch); |
| } |
| |
| static __inline__ int |
| is_identifier_part (int c) |
| { |
| unsigned int ch = (unsigned)c; |
| |
| if ((ch - 0x41U) < 29U) /* A ... Z */ |
| return 1; |
| if ((ch - 0x61U) < 29U) /* a ... z */ |
| return 1; |
| if ((ch - 0x30) < 10U) /* 0 .. 9 */ |
| return 1; |
| if (ch == 0x5FU || ch == 0x24U) /* _ $ */ |
| return 1; |
| |
| return java::lang::Character::isJavaIdentifierStart ((jchar) ch); |
| } |
| |
| bool |
| _Jv_VerifyIdentifier (_Jv_Utf8Const* name) |
| { |
| 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)) |
| return false; |
| |
| while (ptr != limit) |
| { |
| if ((ch = UTF8_GET (ptr, limit))==-1 |
| || ! is_identifier_part (ch)) |
| return false; |
| } |
| return true; |
| } |
| |
| 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: |
| for (;;) { |
| if ((ch = UTF8_GET (ptr, limit))==-1) |
| return false; |
| if (! is_identifier_start (ch)) |
| return false; |
| for (;;) { |
| if (ptr == limit) |
| return true; |
| else if ((ch = UTF8_GET (ptr, limit))==-1) |
| return false; |
| else if (ch == '.') |
| goto next_level; |
| else if (! is_identifier_part (ch)) |
| return false; |
| } |
| } |
| } |
| |
| bool |
| _Jv_VerifyClassName (_Jv_Utf8Const *name) |
| { |
| return _Jv_VerifyClassName ((unsigned char*)name->chars(), name->len()); |
| } |
| |
| /* 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->chars(); |
| unsigned char* limit1 = (unsigned char*) name1->limit(); |
| |
| unsigned char* last1 = ptr1; |
| |
| // scan name1, and find the last occurrence of '.' |
| while (ptr1 < limit1) { |
| int ch1 = UTF8_GET (ptr1, limit1); |
| |
| 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->chars(); |
| |
| // 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->chars(), (void*) name2->chars(), len) == 0) |
| { |
| // Check that there are no .'s after position LEN in NAME2. |
| |
| unsigned char* ptr2 = (unsigned char*) name2->chars() + len; |
| unsigned char* limit2 = (unsigned char*) name2->limit(); |
| |
| while (ptr2 < limit2) |
| { |
| int ch2 = UTF8_GET (ptr2, limit2); |
| if (ch2 == -1 || ch2 == '.') |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |