| // defineclass.cc - defining a class from .class format. | |
| /* Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 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/security/ProtectionDomain.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; | |
| // 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; | |
| /* 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 unsignend 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"); | |
| } | |
| _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; | |
| bytes = (unsigned char*) (elements (data)+offset); | |
| len = length; | |
| pos = 0; | |
| is_15 = false; | |
| def = klass; | |
| found_name = name_result; | |
| 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 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 throw_class_format_error (const char *msg); | |
| /** check an utf8 entry, without creating a Utf8Const object */ | |
| bool is_attribute_name (int index, const char *name); | |
| /** 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); | |
| void handleFieldsEnd (); | |
| void handleConstantValueAttribute (int,int); | |
| 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 | |
| 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_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 (); | |
| 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"); | |
| // Tell everyone we're done. | |
| def->state = JV_STATE_READ; | |
| if (gcj::verbose_class_flag) | |
| _Jv_Linker::print_class_loaded (def); | |
| def->notifyAll (); | |
| } | |
| 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); | |
| 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); | |
| for (int j = 0; j < attributes_count; j++) | |
| { | |
| read_one_field_attribute (i); | |
| } | |
| } | |
| handleFieldsEnd (); | |
| } | |
| 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); | |
| } | |
| void _Jv_ClassReader::read_one_field_attribute (int field_index) | |
| { | |
| 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); | |
| } | |
| else | |
| { | |
| throw_class_format_error ("erroneous ConstantValue attribute"); | |
| } | |
| if (length != 2) | |
| throw_class_format_error ("erroneous ConstantValue attribute"); | |
| } | |
| 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 | |
| { | |
| /* 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 | |
| { | |
| /* 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); | |
| def_interp->source_file_name = _Jv_NewStringUtf8Const | |
| (def->constants.data[source_index].utf8); | |
| } | |
| else | |
| { | |
| /* Currently, we ignore most class attributes. | |
| FIXME: Add inner-classes attributes support. */ | |
| 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. */ | |
| void | |
| _Jv_ClassReader::prepare_pool_entry (int index, unsigned char this_tag) | |
| { | |
| /* 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: | |
| { | |
| // 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*) __builtin_alloca (len); | |
| char *s = ((char*) this_data)+2; | |
| /* FIXME: avoid using a buffer here */ | |
| for (int i = 0; i < len; i++) | |
| { | |
| if (s[i] == '/') | |
| buffer[i] = '.'; | |
| else | |
| buffer[i] = (char) s[i]; | |
| } | |
| pool_data[index].utf8 = _Jv_makeUtf8Const (buffer, len); | |
| pool_tags[index] = JV_CONSTANT_Utf8; | |
| } | |
| 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); | |
| 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) | |
| { | |
| using namespace java::lang::reflect; | |
| _Jv_word *pool_data = def->constants.data; | |
| _Jv_Field *field = &def->fields[field_no]; | |
| _Jv_Utf8Const *field_name = pool_data[name].utf8; | |
| field->name = field_name; | |
| // Ignore flags we don't know about. | |
| field->flags = flags & Modifier::ALL_FLAGS; | |
| _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[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) | |
| { | |
| 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 (field->flags & _Jv_FIELD_CONSTANT_VALUE) | |
| throw_class_format_error ("field has multiple ConstantValue attributes"); | |
| field->flags |= _Jv_FIELD_CONSTANT_VALUE; | |
| 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::handleFieldsEnd () | |
| { | |
| using namespace java::lang::reflect; | |
| // We need to reorganize the fields so that the static ones are first, | |
| // to conform to GCJ class layout. | |
| int low = 0; | |
| int high = def->field_count-1; | |
| _Jv_Field *fields = def->fields; | |
| _Jv_ushort *inits = def_interp->field_initializers; | |
| // this is kind of a raw version of quicksort. | |
| while (low < high) | |
| { | |
| // go forward on low, while it's a static | |
| while (low < high && (fields[low].flags & Modifier::STATIC) != 0) | |
| low++; | |
| // go backwards on high, while it's a non-static | |
| while (low < high && (fields[high].flags & Modifier::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; | |
| } | |
| if ((fields[low].flags & Modifier::STATIC) != 0) | |
| low += 1; | |
| def->static_field_count = low; | |
| } | |
| 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); | |
| 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 & Modifier::ALL_FLAGS; | |
| // 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; | |
| // 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 (); | |
| } | |
| } | |
| 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 (); | |
| } | |
| } | |
| } | |
| 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; | |
| } |