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;
			//		}
		}
Example #2
0
	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;
//		}
	}