internal ClassFile(byte[] buf, int offset, int length, string inputClassName, ClassFileParseOptions options) { try { BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length); if(br.ReadUInt32() != 0xCAFEBABE) { throw new ClassFormatError("{0} (Bad magic number)", inputClassName); } ushort minorVersion = br.ReadUInt16(); ushort majorVersion = br.ReadUInt16(); if((majorVersion & FLAG_MASK_MAJORVERSION) != majorVersion || majorVersion < SupportedVersions.Minimum || majorVersion > SupportedVersions.Maximum || (majorVersion == SupportedVersions.Minimum && minorVersion < 3) || (majorVersion == SupportedVersions.Maximum && minorVersion != 0)) { throw new UnsupportedClassVersionError(inputClassName + " (" + majorVersion + "." + minorVersion + ")"); } flags = majorVersion; int constantpoolcount = br.ReadUInt16(); constantpool = new ConstantPoolItem[constantpoolcount]; string[] utf8_cp = new string[constantpoolcount]; for(int i = 1; i < constantpoolcount; i++) { Constant tag = (Constant)br.ReadByte(); switch(tag) { case Constant.Class: constantpool[i] = new ConstantPoolItemClass(br); break; case Constant.Double: constantpool[i] = new ConstantPoolItemDouble(br); i++; break; case Constant.Fieldref: constantpool[i] = new ConstantPoolItemFieldref(br); break; case Constant.Float: constantpool[i] = new ConstantPoolItemFloat(br); break; case Constant.Integer: constantpool[i] = new ConstantPoolItemInteger(br); break; case Constant.InterfaceMethodref: constantpool[i] = new ConstantPoolItemInterfaceMethodref(br); break; case Constant.Long: constantpool[i] = new ConstantPoolItemLong(br); i++; break; case Constant.Methodref: constantpool[i] = new ConstantPoolItemMethodref(br); break; case Constant.NameAndType: constantpool[i] = new ConstantPoolItemNameAndType(br); break; case Constant.MethodHandle: if (majorVersion < 51) goto default; constantpool[i] = new ConstantPoolItemMethodHandle(br); break; case Constant.MethodType: if (majorVersion < 51) goto default; constantpool[i] = new ConstantPoolItemMethodType(br); break; case Constant.InvokeDynamic: if (majorVersion < 51) goto default; constantpool[i] = new ConstantPoolItemInvokeDynamic(br); break; case Constant.String: constantpool[i] = new ConstantPoolItemString(br); break; case Constant.Utf8: utf8_cp[i] = br.ReadString(inputClassName); break; default: throw new ClassFormatError("{0} (Illegal constant pool type 0x{1:X})", inputClassName, tag); } } for(int i = 1; i < constantpoolcount; i++) { if(constantpool[i] != null) { try { constantpool[i].Resolve(this, utf8_cp, options); } catch(ClassFormatError x) { // HACK at this point we don't yet have the class name, so any exceptions throw // are missing the class name throw new ClassFormatError("{0} ({1})", inputClassName, x.Message); } catch(IndexOutOfRangeException) { throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); } catch(InvalidCastException) { throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); } } } access_flags = (Modifiers)br.ReadUInt16(); // NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of // javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule for older class files. // NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this // for older class files. // (See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6320322) if((IsInterface && IsFinal) || (IsAbstract && IsFinal) || (majorVersion >= 49 && IsAnnotation && !IsInterface) || (majorVersion >= 49 && IsInterface && (!IsAbstract || IsSuper || IsEnum))) { throw new ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags); } this_class = br.ReadUInt16(); ValidateConstantPoolItemClass(inputClassName, this_class); super_class = br.ReadUInt16(); ValidateConstantPoolItemClass(inputClassName, super_class); if(IsInterface && (super_class == 0 || this.SuperClass != "java.lang.Object")) { throw new ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); } // most checks are already done by ConstantPoolItemClass.Resolve, but since it allows // array types, we do need to check for that if(this.Name[0] == '[') { throw new ClassFormatError("Bad name"); } int interfaces_count = br.ReadUInt16(); interfaces = new ConstantPoolItemClass[interfaces_count]; for(int i = 0; i < interfaces.Length; i++) { int index = br.ReadUInt16(); if(index == 0 || index >= constantpool.Length) { throw new ClassFormatError("{0} (Illegal constant pool index)", Name); } ConstantPoolItemClass cpi = constantpool[index] as ConstantPoolItemClass; if(cpi == null) { throw new ClassFormatError("{0} (Interface name has bad constant type)", Name); } interfaces[i] = cpi; for(int j = 0; j < i; j++) { if(ReferenceEquals(interfaces[j].Name, cpi.Name)) { throw new ClassFormatError("{0} (Repetitive interface name)", Name); } } } int fields_count = br.ReadUInt16(); fields = new Field[fields_count]; for(int i = 0; i < fields_count; i++) { fields[i] = new Field(this, utf8_cp, br); string name = fields[i].Name; if(!IsValidFieldName(name, majorVersion)) { throw new ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name); } for(int j = 0; j < i; j++) { if(ReferenceEquals(fields[j].Name, name) && ReferenceEquals(fields[j].Signature, fields[i].Signature)) { throw new ClassFormatError("{0} (Repetitive field name/signature)", Name); } } } int methods_count = br.ReadUInt16(); methods = new Method[methods_count]; for(int i = 0; i < methods_count; i++) { methods[i] = new Method(this, utf8_cp, options, br); string name = methods[i].Name; string sig = methods[i].Signature; if(!IsValidMethodName(name, majorVersion)) { if(!ReferenceEquals(name, StringConstants.INIT) && !ReferenceEquals(name, StringConstants.CLINIT)) { throw new ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); } if(!sig.EndsWith("V")) { throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); } } for(int j = 0; j < i; j++) { if(ReferenceEquals(methods[j].Name, name) && ReferenceEquals(methods[j].Signature, sig)) { throw new ClassFormatError("{0} (Repetitive method name/signature)", Name); } } } int attributes_count = br.ReadUInt16(); for(int i = 0; i < attributes_count; i++) { switch(GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16())) { case "Deprecated": if(br.ReadUInt32() != 0) { throw new ClassFormatError("Invalid Deprecated attribute length"); } flags |= FLAG_MASK_DEPRECATED; break; case "SourceFile": if(br.ReadUInt32() != 2) { throw new ClassFormatError("SourceFile attribute has incorrect length"); } sourceFile = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); break; case "InnerClasses": { BigEndianBinaryReader rdr = br; uint attribute_length = br.ReadUInt32(); ushort count = rdr.ReadUInt16(); if(this.MajorVersion >= 49 && attribute_length != 2 + count * (2 + 2 + 2 + 2)) { throw new ClassFormatError("{0} (InnerClasses attribute has incorrect length)", this.Name); } innerClasses = new InnerClass[count]; for(int j = 0; j < innerClasses.Length; j++) { innerClasses[j].innerClass = rdr.ReadUInt16(); innerClasses[j].outerClass = rdr.ReadUInt16(); innerClasses[j].name = rdr.ReadUInt16(); innerClasses[j].accessFlags = (Modifiers)rdr.ReadUInt16(); if(innerClasses[j].innerClass != 0 && !(GetConstantPoolItem(innerClasses[j].innerClass) is ConstantPoolItemClass)) { throw new ClassFormatError("{0} (inner_class_info_index has bad constant pool index)", this.Name); } if(innerClasses[j].outerClass != 0 && !(GetConstantPoolItem(innerClasses[j].outerClass) is ConstantPoolItemClass)) { throw new ClassFormatError("{0} (outer_class_info_index has bad constant pool index)", this.Name); } if(innerClasses[j].name != 0 && utf8_cp[innerClasses[j].name] == null) { throw new ClassFormatError("{0} (inner class name has bad constant pool index)", this.Name); } if(innerClasses[j].innerClass == innerClasses[j].outerClass) { throw new ClassFormatError("{0} (Class is both inner and outer class)", this.Name); } if(innerClasses[j].innerClass != 0 && innerClasses[j].outerClass != 0) { MarkLinkRequiredConstantPoolItem(innerClasses[j].innerClass); MarkLinkRequiredConstantPoolItem(innerClasses[j].outerClass); } } break; } case "Signature": if(majorVersion < 49) { goto default; } if(br.ReadUInt32() != 2) { throw new ClassFormatError("Signature attribute has incorrect length"); } signature = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); break; case "EnclosingMethod": if(majorVersion < 49) { goto default; } if(br.ReadUInt32() != 4) { throw new ClassFormatError("EnclosingMethod attribute has incorrect length"); } else { int class_index = br.ReadUInt16(); int method_index = br.ReadUInt16(); if(method_index == 0) { enclosingMethod = new string[] { GetConstantPoolClass(class_index), null, null }; } else { ConstantPoolItemNameAndType m = (ConstantPoolItemNameAndType)GetConstantPoolItem(method_index); enclosingMethod = new string[] { GetConstantPoolClass(class_index), GetConstantPoolUtf8String(utf8_cp, m.name_index), GetConstantPoolUtf8String(utf8_cp, m.descriptor_index).Replace('/', '.') }; } } break; case "RuntimeVisibleAnnotations": if(majorVersion < 49) { goto default; } annotations = ReadAnnotations(br, this, utf8_cp); break; #if STATIC_COMPILER case "RuntimeInvisibleAnnotations": if(majorVersion < 49) { goto default; } foreach(object[] annot in ReadAnnotations(br, this, utf8_cp)) { if(annot[1].Equals("Likvm/lang/Internal;")) { this.access_flags &= ~Modifiers.AccessMask; flags |= FLAG_MASK_INTERNAL; } } break; #endif case "BootstrapMethods": if(majorVersion < 51) { goto default; } bootstrapMethods = ReadBootstrapMethods(br, this); break; case "IKVM.NET.Assembly": if(br.ReadUInt32() != 2) { throw new ClassFormatError("IKVM.NET.Assembly attribute has incorrect length"); } ikvmAssembly = GetConstantPoolUtf8String(utf8_cp, br.ReadUInt16()); break; default: br.Skip(br.ReadUInt32()); break; } } // validate the invokedynamic entries to point into the bootstrapMethods array for(int i = 1; i < constantpoolcount; i++) { ConstantPoolItemInvokeDynamic cpi; if(constantpool[i] != null && (cpi = constantpool[i] as ConstantPoolItemInvokeDynamic) != null) { if(bootstrapMethods == null || cpi.BootstrapMethod >= bootstrapMethods.Length) { throw new ClassFormatError("Short length on BootstrapMethods in class file"); } } } if(br.Position != offset + length) { throw new ClassFormatError("Extra bytes at the end of the class file"); } } catch(OverflowException) { throw new ClassFormatError("Truncated class file (or section)"); } catch(IndexOutOfRangeException) { // TODO we should throw more specific errors throw new ClassFormatError("Unspecified class file format error"); } // catch(Exception x) // { // Console.WriteLine(x); // FileStream fs = File.Create(inputClassName + ".broken"); // fs.Write(buf, offset, length); // fs.Close(); // throw; // } }
internal ClassFile(byte[] buf, int offset, int length, string inputClassName, bool allowJavaLangObject) { try { BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length); if (br.ReadUInt32() != 0xCAFEBABE) { throw new ClassFormatError("{0} (Bad magic number)", inputClassName); } int minorVersion = br.ReadUInt16(); majorVersion = br.ReadUInt16(); if (majorVersion < SupportedVersions.Minimum || majorVersion > SupportedVersions.Maximum) { throw new UnsupportedClassVersionError(inputClassName + " (" + majorVersion + "." + minorVersion + ")"); } int constantpoolcount = br.ReadUInt16(); constantpool = new ConstantPoolItem[constantpoolcount]; utf8_cp = new string[constantpoolcount]; for (int i = 1; i < constantpoolcount; i++) { Constant tag = (Constant) br.ReadByte(); switch (tag) { case Constant.Class: constantpool[i] = new ConstantPoolItemClass(br); break; case Constant.Double: constantpool[i] = new ConstantPoolItemDouble(br); i++; break; case Constant.Fieldref: constantpool[i] = new ConstantPoolItemFieldref(br); break; case Constant.Float: constantpool[i] = new ConstantPoolItemFloat(br); break; case Constant.Integer: constantpool[i] = new ConstantPoolItemInteger(br); break; case Constant.InterfaceMethodref: constantpool[i] = new ConstantPoolItemInterfaceMethodref(br); break; case Constant.Long: constantpool[i] = new ConstantPoolItemLong(br); i++; break; case Constant.Methodref: constantpool[i] = new ConstantPoolItemMethodref(br); break; case Constant.NameAndType: constantpool[i] = new ConstantPoolItemNameAndType(br); break; case Constant.String: constantpool[i] = new ConstantPoolItemString(br); break; case Constant.Utf8: utf8_cp[i] = br.ReadString(inputClassName); break; default: throw new ClassFormatError("{0} (Illegal constant pool type 0x{1:X})", inputClassName, tag); } } for (int i = 1; i < constantpoolcount; i++) { if (constantpool[i] != null) { try { constantpool[i].Resolve(this); } catch (ClassFormatError x) { // HACK at this point we don't yet have the class name, so any exceptions throw // are missing the class name throw new ClassFormatError("{0} ({1})", inputClassName, x.Message); } catch (IndexOutOfRangeException) { throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); } catch (InvalidCastException) { throw new ClassFormatError("{0} (Invalid constant pool item #{1})", inputClassName, i); } } } access_flags = (__Modifiers) br.ReadUInt16(); // NOTE although the vmspec says (in 4.1) that interfaces must be marked abstract, earlier versions of // javac (JDK 1.1) didn't do this, so the VM doesn't enforce this rule // NOTE although the vmspec implies (in 4.1) that ACC_SUPER is illegal on interfaces, it doesn't enforce this if ((IsInterface && IsFinal) || (IsAbstract && IsFinal)) { throw new ClassFormatError("{0} (Illegal class modifiers 0x{1:X})", inputClassName, access_flags); } this_class = br.ReadUInt16(); ValidateConstantPoolItemClass(inputClassName, this_class); super_class = br.ReadUInt16(); // NOTE for convenience we allow parsing java/lang/Object (which has no super class), so // we check for super_class != 0 if (super_class != 0) { ValidateConstantPoolItemClass(inputClassName, super_class); } else { if (this.Name != "java.lang.Object" || !allowJavaLangObject) { throw new ClassFormatError("{0} (Bad superclass index)", Name); } } if (IsInterface && (super_class == 0 || this.SuperClass != "java.lang.Object")) { throw new ClassFormatError("{0} (Interfaces must have java.lang.Object as superclass)", Name); } // most checks are already done by ConstantPoolItemClass.Resolve, but since it allows // array types, we do need to check for that if (this.Name[0] == '[') { throw new ClassFormatError("Bad name"); } int interfaces_count = br.ReadUInt16(); interfaces = new ConstantPoolItemClass[interfaces_count]; for (int i = 0; i < interfaces.Length; i++) { int index = br.ReadUInt16(); if (index == 0 || index >= constantpool.Length) { throw new ClassFormatError("{0} (Illegal constant pool index)", Name); } ConstantPoolItemClass cpi = constantpool[index] as ConstantPoolItemClass; if (cpi == null) { throw new ClassFormatError("{0} (Interface name has bad constant type)", Name); } interfaces[i] = cpi; for (int j = 0; j < i; j++) { if (interfaces[j].Name == cpi.Name) { throw new ClassFormatError("{0} (Repetitive interface name)", Name); } } } int fields_count = br.ReadUInt16(); fields = new Field[fields_count]; Hashtable fieldNameSigs = new Hashtable(); for (int i = 0; i < fields_count; i++) { fields[i] = new Field(this, br); string name = fields[i].Name; if (!IsValidIdentifier(name)) { throw new ClassFormatError("{0} (Illegal field name \"{1}\")", Name, name); } try { fieldNameSigs.Add(name + fields[i].Signature, null); } catch (ArgumentException) { throw new ClassFormatError("{0} (Repetitive field name/signature)", Name); } } int methods_count = br.ReadUInt16(); methods = new Method[methods_count]; Hashtable methodNameSigs = new Hashtable(); for (int i = 0; i < methods_count; i++) { methods[i] = new Method(this, br); string name = methods[i].Name; string sig = methods[i].Signature; if (!IsValidIdentifier(name) && name != "<init>" && name != "<clinit>") { throw new ClassFormatError("{0} (Illegal method name \"{1}\")", Name, name); } if ((name == "<init>" || name == "<clinit>") && !sig.EndsWith("V")) { throw new ClassFormatError("{0} (Method \"{1}\" has illegal signature \"{2}\")", Name, name, sig); } try { methodNameSigs.Add(name + sig, null); } catch (ArgumentException) { throw new ClassFormatError("{0} (Repetitive method name/signature)", Name); } } int attributes_count = br.ReadUInt16(); for (int i = 0; i < attributes_count; i++) { switch (GetConstantPoolUtf8String(br.ReadUInt16())) { case "Deprecated": deprecated = true; #if FUZZ_HACK br.Skip(br.ReadUInt32()); #else if(br.ReadUInt32() != 0) { throw new ClassFormatError("Deprecated attribute has non-zero length"); } #endif break; case "SourceFile": if (br.ReadUInt32() != 2) { throw new ClassFormatError("SourceFile attribute has incorrect length"); } sourceFile = GetConstantPoolUtf8String(br.ReadUInt16()); break; case "InnerClasses": #if FUZZ_HACK // Sun totally ignores the length of InnerClasses attribute, // so when we're running Fuzz this shows up as lots of differences. // To get rid off these differences define the FUZZ_HACK symbol. BigEndianBinaryReader rdr = br; br.ReadUInt32(); #else BigEndianBinaryReader rdr = br.Section(br.ReadUInt32()); #endif ushort count = rdr.ReadUInt16(); innerClasses = new InnerClass[count]; for (int j = 0; j < innerClasses.Length; j++) { innerClasses[j].innerClass = rdr.ReadUInt16(); innerClasses[j].outerClass = rdr.ReadUInt16(); innerClasses[j].name = rdr.ReadUInt16(); innerClasses[j].accessFlags = (__Modifiers) rdr.ReadUInt16(); if (innerClasses[j].innerClass != 0 && !(GetConstantPoolItem(innerClasses[j].innerClass) is ConstantPoolItemClass)) { throw new ClassFormatError("{0} (inner_class_info_index has bad constant pool index)", this.Name); } if (innerClasses[j].outerClass != 0 && !(GetConstantPoolItem(innerClasses[j].outerClass) is ConstantPoolItemClass)) { throw new ClassFormatError("{0} (outer_class_info_index has bad constant pool index)", this.Name); } if (innerClasses[j].name != 0 && utf8_cp[innerClasses[j].name] == null) { throw new ClassFormatError("{0} (inner class name has bad constant pool index)", this.Name); } if (innerClasses[j].innerClass == innerClasses[j].outerClass) { throw new ClassFormatError("{0} (Class is both inner and outer class)", this.Name); } } #if !FUZZ_HACK if(!rdr.IsAtEnd) { throw new ClassFormatError("{0} (InnerClasses attribute has wrong length)", this.Name); } #endif break; case "IKVM.NET.Assembly": if (br.ReadUInt32() != 2) { throw new ClassFormatError("IKVM.NET.Assembly attribute has incorrect length"); } ikvmAssembly = GetConstantPoolUtf8String(br.ReadUInt16()); break; default: br.Skip(br.ReadUInt32()); break; } } if (br.Position != offset + length) { throw new ClassFormatError("Extra bytes at the end of the class file"); } } catch (OverflowException) { throw new ClassFormatError("Truncated class file (or section)"); } catch (IndexOutOfRangeException) { // TODO we should throw more specific errors throw new ClassFormatError("Unspecified class file format error"); } // catch(Exception x) // { // Console.WriteLine(x); // FileStream fs = File.Create(inputClassName + ".broken"); // fs.Write(buf, offset, length); // fs.Close(); // throw; // } }