ikvm-fork/runtime/ClassFile.cs

3070 строки
75 KiB
C#

/*
Copyright (C) 2002-2009 Jeroen Frijters
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Jeroen Frijters
jeroen@frijters.net
*/
using System;
using System.IO;
using System.Collections.Generic;
using IKVM.Attributes;
namespace IKVM.Internal
{
enum HardError : short
{
NoClassDefFoundError,
IllegalAccessError,
InstantiationError,
IncompatibleClassChangeError,
NoSuchFieldError,
AbstractMethodError,
NoSuchMethodError,
LinkageError
// if an error is added here, it must also be added to MethodAnalyzer.SetHardError()
}
[Flags]
enum ClassFileParseOptions
{
None = 0,
LocalVariableTable = 1,
LineNumberTable = 2,
}
static class StringConstants
{
internal static readonly string CLINIT = string.Intern("<clinit>");
internal static readonly string INIT = string.Intern("<init>");
internal static readonly string SIG_VOID = string.Intern("()V");
internal static readonly string FINALIZE = string.Intern("finalize");
internal static readonly string CLONE = string.Intern("clone");
}
sealed class ClassFile
{
private ConstantPoolItem[] constantpool;
private string[] utf8_cp;
// Modifiers is a ushort, so the next four fields combine into two 32 bit slots
private Modifiers access_flags;
private ushort this_class;
private ushort super_class;
private ushort flags;
private const ushort FLAG_MASK_MAJORVERSION = 0xFF;
private const ushort FLAG_MASK_DEPRECATED = 0x100;
private const ushort FLAG_MASK_INTERNAL = 0x200;
private const ushort FLAG_MASK_EFFECTIVELY_FINAL = 0x400;
private const ushort FLAG_HAS_CALLERID = 0x800;
private ConstantPoolItemClass[] interfaces;
private Field[] fields;
private Method[] methods;
private string sourceFile;
private string ikvmAssembly;
private InnerClass[] innerClasses;
private object[] annotations;
private string signature;
private string[] enclosingMethod;
private static class SupportedVersions
{
internal static readonly int Minimum = 45;
internal static readonly int Maximum = 50;
}
#if STATIC_COMPILER
// This method parses just enough of the class file to obtain its name, it doesn't
// validate the class file structure, but it may throw a ClassFormatError if it
// encounters bogus data
internal static string GetClassName(byte[] buf, int offset, int length)
{
BigEndianBinaryReader br = new BigEndianBinaryReader(buf, offset, length);
if(br.ReadUInt32() != 0xCAFEBABE)
{
throw new ClassFormatError("Bad magic number");
}
int minorVersion = br.ReadUInt16();
int 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(majorVersion + "." + minorVersion);
}
int constantpoolcount = br.ReadUInt16();
int[] cpclass = new int[constantpoolcount];
string[] utf8_cp = new string[constantpoolcount];
for(int i = 1; i < constantpoolcount; i++)
{
Constant tag = (Constant)br.ReadByte();
switch(tag)
{
case Constant.Class:
cpclass[i] = br.ReadUInt16();
break;
case Constant.Double:
case Constant.Long:
br.Skip(8);
i++;
break;
case Constant.Fieldref:
case Constant.InterfaceMethodref:
case Constant.Methodref:
case Constant.NameAndType:
case Constant.Float:
case Constant.Integer:
br.Skip(4);
break;
case Constant.String:
br.Skip(2);
break;
case Constant.Utf8:
utf8_cp[i] = br.ReadString("<unknown>");
break;
default:
throw new ClassFormatError("Illegal constant pool type 0x{0:X}", tag);
}
}
br.ReadUInt16(); // access_flags
try
{
return String.Intern(utf8_cp[cpclass[br.ReadUInt16()]].Replace('/', '.'));
}
catch(Exception x)
{
throw new ClassFormatError("{0}: {1}", x.GetType().Name, x.Message);
}
}
#endif // STATIC_COMPILER
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];
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 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, 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, 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(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(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);
}
}
break;
}
case "Signature":
if(majorVersion < 49)
{
goto default;
}
if(br.ReadUInt32() != 2)
{
throw new ClassFormatError("Signature attribute has incorrect length");
}
signature = GetConstantPoolUtf8String(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(m.name_index),
GetConstantPoolUtf8String(m.descriptor_index).Replace('/', '.')
};
}
}
break;
case "RuntimeVisibleAnnotations":
if(majorVersion < 49)
{
goto default;
}
annotations = ReadAnnotations(br, this);
break;
#if STATIC_COMPILER
case "RuntimeInvisibleAnnotations":
if(majorVersion < 49)
{
goto default;
}
foreach(object[] annot in ReadAnnotations(br, this))
{
if(annot[1].Equals("Likvm/lang/Internal;"))
{
this.access_flags &= ~Modifiers.AccessMask;
flags |= FLAG_MASK_INTERNAL;
}
}
break;
#endif
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;
}
}
// now that we've constructed the high level objects, the utf8 table isn't needed anymore
// TODO remove utf8_cp field from ClassFile object
utf8_cp = null;
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;
// }
}
private static object[] ReadAnnotations(BigEndianBinaryReader br, ClassFile classFile)
{
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
ushort num_annotations = rdr.ReadUInt16();
object[] annotations = new object[num_annotations];
for(int i = 0; i < annotations.Length; i++)
{
annotations[i] = ReadAnnotation(rdr, classFile);
}
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (RuntimeVisibleAnnotations attribute has wrong length)", classFile.Name);
}
return annotations;
}
private static object ReadAnnotation(BigEndianBinaryReader rdr, ClassFile classFile)
{
string type = classFile.GetConstantPoolUtf8String(rdr.ReadUInt16());
ushort num_element_value_pairs = rdr.ReadUInt16();
object[] annot = new object[2 + num_element_value_pairs * 2];
annot[0] = AnnotationDefaultAttribute.TAG_ANNOTATION;
annot[1] = type;
for(int i = 0; i < num_element_value_pairs; i++)
{
annot[2 + i * 2 + 0] = classFile.GetConstantPoolUtf8String(rdr.ReadUInt16());
annot[2 + i * 2 + 1] = ReadAnnotationElementValue(rdr, classFile);
}
return annot;
}
private static object ReadAnnotationElementValue(BigEndianBinaryReader rdr, ClassFile classFile)
{
byte tag = rdr.ReadByte();
switch(tag)
{
case (byte)'Z':
return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16()) != 0;
case (byte)'B':
return (byte)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
case (byte)'C':
return (char)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
case (byte)'S':
return (short)classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
case (byte)'I':
return classFile.GetConstantPoolConstantInteger(rdr.ReadUInt16());
case (byte)'F':
return classFile.GetConstantPoolConstantFloat(rdr.ReadUInt16());
case (byte)'J':
return classFile.GetConstantPoolConstantLong(rdr.ReadUInt16());
case (byte)'D':
return classFile.GetConstantPoolConstantDouble(rdr.ReadUInt16());
case (byte)'s':
return classFile.GetConstantPoolUtf8String(rdr.ReadUInt16());
case (byte)'e':
{
ushort type_name_index = rdr.ReadUInt16();
ushort const_name_index = rdr.ReadUInt16();
return new object[] {
AnnotationDefaultAttribute.TAG_ENUM,
classFile.GetConstantPoolUtf8String(type_name_index),
classFile.GetConstantPoolUtf8String(const_name_index)
};
}
case (byte)'c':
return new object[] {
AnnotationDefaultAttribute.TAG_CLASS,
classFile.GetConstantPoolUtf8String(rdr.ReadUInt16())
};
case (byte)'@':
return ReadAnnotation(rdr, classFile);
case (byte)'[':
{
ushort num_values = rdr.ReadUInt16();
object[] array = new object[num_values + 1];
array[0] = AnnotationDefaultAttribute.TAG_ARRAY;
for(int i = 0; i < num_values; i++)
{
array[i + 1] = ReadAnnotationElementValue(rdr, classFile);
}
return array;
}
default:
throw new ClassFormatError("Invalid tag {0} in annotation element_value", tag);
}
}
private void ValidateConstantPoolItemClass(string classFile, ushort index)
{
if(index >= constantpool.Length || !(constantpool[index] is ConstantPoolItemClass))
{
throw new ClassFormatError("{0} (Bad constant pool index #{1})", classFile, index);
}
}
private static bool IsValidMethodName(string name, int majorVersion)
{
if(name.Length == 0)
{
return false;
}
for(int i = 0; i < name.Length; i++)
{
if(".;/<>".IndexOf(name[i]) != -1)
{
return false;
}
}
return majorVersion >= 49 || IsValidPre49Identifier(name);
}
private static bool IsValidFieldName(string name, int majorVersion)
{
if(name.Length == 0)
{
return false;
}
for(int i = 0; i < name.Length; i++)
{
if(".;/".IndexOf(name[i]) != -1)
{
return false;
}
}
return majorVersion >= 49 || IsValidPre49Identifier(name);
}
private static bool IsValidPre49Identifier(string name)
{
if(!Char.IsLetter(name[0]) && "$_".IndexOf(name[0]) == -1)
{
return false;
}
for(int i = 1; i < name.Length; i++)
{
if(!Char.IsLetterOrDigit(name[i]) && "$_".IndexOf(name[i]) == -1)
{
return false;
}
}
return true;
}
private static bool IsValidFieldSig(string sig)
{
return IsValidFieldSigImpl(sig, 0, sig.Length);
}
private static bool IsValidFieldSigImpl(string sig, int start, int end)
{
if(start >= end)
{
return false;
}
switch(sig[start])
{
case 'L':
return sig.IndexOf(';', start + 1) == end - 1;
case '[':
while(sig[start] == '[')
{
start++;
if(start == end)
{
return false;
}
}
return IsValidFieldSigImpl(sig, start, end);
case 'B':
case 'Z':
case 'C':
case 'S':
case 'I':
case 'J':
case 'F':
case 'D':
return start == end - 1;
default:
return false;
}
}
private static bool IsValidMethodSig(string sig)
{
if(sig.Length < 3 || sig[0] != '(')
{
return false;
}
int end = sig.IndexOf(')');
if(end == -1)
{
return false;
}
if(!sig.EndsWith(")V") && !IsValidFieldSigImpl(sig, end + 1, sig.Length))
{
return false;
}
for(int i = 1; i < end; i++)
{
switch(sig[i])
{
case 'B':
case 'Z':
case 'C':
case 'S':
case 'I':
case 'J':
case 'F':
case 'D':
break;
case 'L':
i = sig.IndexOf(';', i);
break;
case '[':
while(sig[i] == '[')
{
i++;
}
if("BZCSIJFDL".IndexOf(sig[i]) == -1)
{
return false;
}
if(sig[i] == 'L')
{
i = sig.IndexOf(';', i);
}
break;
default:
return false;
}
if(i == -1 || i >= end)
{
return false;
}
}
return true;
}
internal int MajorVersion
{
get
{
return flags & FLAG_MASK_MAJORVERSION;
}
}
internal void Link(TypeWrapper thisType)
{
for(int i = 1; i < constantpool.Length; i++)
{
if(constantpool[i] != null)
{
constantpool[i].Link(thisType);
}
}
}
internal Modifiers Modifiers
{
get
{
return access_flags;
}
}
internal bool IsAbstract
{
get
{
// interfaces are implicitly abstract
return (access_flags & (Modifiers.Abstract | Modifiers.Interface)) != 0;
}
}
internal bool IsFinal
{
get
{
return (access_flags & Modifiers.Final) != 0;
}
}
internal bool IsPublic
{
get
{
return (access_flags & Modifiers.Public) != 0;
}
}
internal bool IsInterface
{
get
{
return (access_flags & Modifiers.Interface) != 0;
}
}
internal bool IsEnum
{
get
{
return (access_flags & Modifiers.Enum) != 0;
}
}
internal bool IsAnnotation
{
get
{
return (access_flags & Modifiers.Annotation) != 0;
}
}
internal bool IsSuper
{
get
{
return (access_flags & Modifiers.Super) != 0;
}
}
internal void RemoveUnusedFields()
{
List<Field> list = new List<Field>();
foreach(Field f in fields)
{
if(f.IsPrivate && f.IsStatic && f.Name != "serialVersionUID" && !IsReferenced(f))
{
// unused, so we skip it
Tracer.Info(Tracer.Compiler, "Unused field {0}::{1}", this.Name, f.Name);
}
else
{
list.Add(f);
}
}
fields = list.ToArray();
}
private bool IsReferenced(Field fld)
{
foreach(ConstantPoolItem cpi in constantpool)
{
ConstantPoolItemFieldref fieldref = cpi as ConstantPoolItemFieldref;
if(fieldref != null &&
fieldref.Class == this.Name &&
fieldref.Name == fld.Name &&
fieldref.Signature == fld.Signature)
{
return true;
}
}
return false;
}
internal ConstantPoolItemFieldref GetFieldref(int index)
{
return (ConstantPoolItemFieldref)constantpool[index];
}
// this won't throw an exception if index is invalid
// (used by IsSideEffectFreeStaticInitializer)
internal ConstantPoolItemFieldref SafeGetFieldref(int index)
{
if(index > 0 && index < constantpool.Length)
{
return constantpool[index] as ConstantPoolItemFieldref;
}
return null;
}
// NOTE this returns an MI, because it used for both normal methods and interface methods
internal ConstantPoolItemMI GetMethodref(int index)
{
return (ConstantPoolItemMI)constantpool[index];
}
// this won't throw an exception if index is invalid
// (used by IsAccessBridge)
internal ConstantPoolItemMI SafeGetMethodref(int index)
{
if (index > 0 && index < constantpool.Length)
{
return constantpool[index] as ConstantPoolItemMI;
}
return null;
}
private ConstantPoolItem GetConstantPoolItem(int index)
{
return constantpool[index];
}
internal string GetConstantPoolClass(int index)
{
return ((ConstantPoolItemClass)constantpool[index]).Name;
}
// this won't throw an exception if index is invalid
// (used by IsSideEffectFreeStaticInitializer)
internal bool SafeIsConstantPoolClass(int index)
{
if(index > 0 && index < constantpool.Length)
{
return constantpool[index] as ConstantPoolItemClass != null;
}
return false;
}
internal TypeWrapper GetConstantPoolClassType(int index)
{
return ((ConstantPoolItemClass)constantpool[index]).GetClassType();
}
internal string GetConstantPoolUtf8String(int index)
{
string s = utf8_cp[index];
if(s == null)
{
if(this_class == 0)
{
throw new ClassFormatError("Bad constant pool index #{0}", index);
}
else
{
throw new ClassFormatError("{0} (Bad constant pool index #{1})", this.Name, index);
}
}
return s;
}
internal ConstantType GetConstantPoolConstantType(int index)
{
return constantpool[index].GetConstantType();
}
internal double GetConstantPoolConstantDouble(int index)
{
return ((ConstantPoolItemDouble)constantpool[index]).Value;
}
internal float GetConstantPoolConstantFloat(int index)
{
return ((ConstantPoolItemFloat)constantpool[index]).Value;
}
internal int GetConstantPoolConstantInteger(int index)
{
return ((ConstantPoolItemInteger)constantpool[index]).Value;
}
internal long GetConstantPoolConstantLong(int index)
{
return ((ConstantPoolItemLong)constantpool[index]).Value;
}
internal string GetConstantPoolConstantString(int index)
{
return ((ConstantPoolItemString)constantpool[index]).Value;
}
internal string Name
{
get
{
return GetConstantPoolClass(this_class);
}
}
internal string SuperClass
{
get
{
return GetConstantPoolClass(super_class);
}
}
internal Field[] Fields
{
get
{
return fields;
}
}
internal Method[] Methods
{
get
{
return methods;
}
}
internal ConstantPoolItemClass[] Interfaces
{
get
{
return interfaces;
}
}
internal string SourceFileAttribute
{
get
{
return sourceFile;
}
}
internal object[] Annotations
{
get
{
return annotations;
}
}
internal string GenericSignature
{
get
{
return signature;
}
}
internal string[] EnclosingMethod
{
get
{
return enclosingMethod;
}
}
internal string IKVMAssemblyAttribute
{
get
{
return ikvmAssembly;
}
}
internal bool DeprecatedAttribute
{
get
{
return (flags & FLAG_MASK_DEPRECATED) != 0;
}
}
internal bool IsInternal
{
get
{
return (flags & FLAG_MASK_INTERNAL) != 0;
}
}
// for use by ikvmc (to implement the -privatepackage option)
internal void SetInternal()
{
access_flags &= ~Modifiers.AccessMask;
flags |= FLAG_MASK_INTERNAL;
}
internal void SetEffectivelyFinal()
{
flags |= FLAG_MASK_EFFECTIVELY_FINAL;
}
internal bool IsEffectivelyFinal
{
get
{
return (flags & FLAG_MASK_EFFECTIVELY_FINAL) != 0;
}
}
internal bool HasInitializedFields
{
get
{
foreach (Field f in fields)
{
if (f.IsStatic && !f.IsFinal && f.ConstantValue != null)
{
return true;
}
}
return false;
}
}
internal struct InnerClass
{
internal ushort innerClass; // ConstantPoolItemClass
internal ushort outerClass; // ConstantPoolItemClass
internal ushort name; // ConstantPoolItemUtf8
internal Modifiers accessFlags;
}
internal InnerClass[] InnerClasses
{
get
{
return innerClasses;
}
}
internal enum ConstantType
{
Integer,
Long,
Float,
Double,
String,
Class
}
internal abstract class ConstantPoolItem
{
internal virtual void Resolve(ClassFile classFile)
{
}
internal virtual void Link(TypeWrapper thisType)
{
}
internal virtual ConstantType GetConstantType()
{
throw new InvalidOperationException();
}
}
internal sealed class ConstantPoolItemClass : ConstantPoolItem
{
private ushort name_index;
private string name;
private TypeWrapper typeWrapper;
private static char[] invalidJava15Characters = { '.', ';' };
internal ConstantPoolItemClass(BigEndianBinaryReader br)
{
name_index = br.ReadUInt16();
}
internal override void Resolve(ClassFile classFile)
{
name = classFile.GetConstantPoolUtf8String(name_index);
if(name.Length > 0)
{
if(classFile.MajorVersion < 49)
{
char prev = name[0];
if(Char.IsLetter(prev) || prev == '$' || prev == '_' || prev == '[' || prev == '/')
{
int skip = 1;
int end = name.Length;
if(prev == '[')
{
if(!IsValidFieldSig(name))
{
goto barf;
}
while(name[skip] == '[')
{
skip++;
}
if(name.EndsWith(";"))
{
end--;
}
}
for(int i = skip; i < end; i++)
{
char c = name[i];
if(!Char.IsLetterOrDigit(c) && c != '$' && c != '_' && (c != '/' || prev == '/'))
{
goto barf;
}
prev = c;
}
name = String.Intern(name.Replace('/', '.'));
return;
}
}
else
{
// since 1.5 the restrictions on class names have been greatly reduced
int end = name.Length;
if(name[0] == '[')
{
if(!IsValidFieldSig(name))
{
goto barf;
}
// the semicolon is only allowed at the end and IsValidFieldSig enforces this,
// but since invalidJava15Characters contains the semicolon, we decrement end
// to make the following check against invalidJava15Characters ignore the
// trailing semicolon.
if(name[end - 1] == ';')
{
end--;
}
}
if(name.IndexOfAny(invalidJava15Characters, 0, end) >= 0)
{
goto barf;
}
name = String.Intern(name.Replace('/', '.'));
return;
}
}
barf:
throw new ClassFormatError("Invalid class name \"{0}\"", name);
}
internal override void Link(TypeWrapper thisType)
{
if(typeWrapper == null)
{
typeWrapper = LoadClassHelper(thisType.GetClassLoader(), name);
}
}
internal string Name
{
get
{
return name;
}
}
internal TypeWrapper GetClassType()
{
return typeWrapper;
}
internal override ConstantType GetConstantType()
{
return ConstantType.Class;
}
}
private static TypeWrapper LoadClassHelper(ClassLoaderWrapper classLoader, string name)
{
try
{
TypeWrapper wrapper = classLoader.LoadClassByDottedNameFast(name);
if(wrapper == null)
{
Tracer.Error(Tracer.ClassLoading, "Class not found: {0}", name);
wrapper = new UnloadableTypeWrapper(name);
}
return wrapper;
}
catch(RetargetableJavaException x)
{
// HACK keep the compiler from warning about unused local
GC.KeepAlive(x);
#if !STATIC_COMPILER && !FIRST_PASS
if(Tracer.ClassLoading.TraceError)
{
java.lang.ClassLoader cl = (java.lang.ClassLoader)classLoader.GetJavaClassLoader();
if(cl != null)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
string sep = "";
while(cl != null)
{
sb.Append(sep).Append(cl);
sep = " -> ";
cl = cl.getParent();
}
Tracer.Error(Tracer.ClassLoading, "ClassLoader chain: {0}", sb);
}
Exception m = ikvm.runtime.Util.mapException(x.ToJava());
Tracer.Error(Tracer.ClassLoading, m.ToString() + Environment.NewLine + m.StackTrace);
}
#endif // !STATIC_COMPILER && !FIRST_PASS
return new UnloadableTypeWrapper(name);
}
}
private static TypeWrapper SigDecoderWrapper(ClassLoaderWrapper classLoader, ref int index, string sig)
{
switch(sig[index++])
{
case 'B':
return PrimitiveTypeWrapper.BYTE;
case 'C':
return PrimitiveTypeWrapper.CHAR;
case 'D':
return PrimitiveTypeWrapper.DOUBLE;
case 'F':
return PrimitiveTypeWrapper.FLOAT;
case 'I':
return PrimitiveTypeWrapper.INT;
case 'J':
return PrimitiveTypeWrapper.LONG;
case 'L':
{
int pos = index;
index = sig.IndexOf(';', index) + 1;
return LoadClassHelper(classLoader, sig.Substring(pos, index - pos - 1));
}
case 'S':
return PrimitiveTypeWrapper.SHORT;
case 'Z':
return PrimitiveTypeWrapper.BOOLEAN;
case 'V':
return PrimitiveTypeWrapper.VOID;
case '[':
{
// TODO this can be optimized
string array = "[";
while(sig[index] == '[')
{
index++;
array += "[";
}
switch(sig[index])
{
case 'L':
{
int pos = index;
index = sig.IndexOf(';', index) + 1;
return LoadClassHelper(classLoader, array + sig.Substring(pos, index - pos));
}
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
return LoadClassHelper(classLoader, array + sig[index++]);
default:
// TODO this should never happen, because ClassFile should validate the descriptors
throw new InvalidOperationException(sig.Substring(index));
}
}
default:
// TODO this should never happen, because ClassFile should validate the descriptors
throw new InvalidOperationException(sig.Substring(index));
}
}
internal static TypeWrapper[] ArgTypeWrapperListFromSig(ClassLoaderWrapper classLoader, string sig)
{
if(sig[1] == ')')
{
return TypeWrapper.EmptyArray;
}
List<TypeWrapper> list = new List<TypeWrapper>();
for(int i = 1; sig[i] != ')';)
{
list.Add(SigDecoderWrapper(classLoader, ref i, sig));
}
return list.ToArray();
}
internal static TypeWrapper FieldTypeWrapperFromSig(ClassLoaderWrapper classLoader, string sig)
{
int index = 0;
return SigDecoderWrapper(classLoader, ref index, sig);
}
internal static TypeWrapper RetTypeWrapperFromSig(ClassLoaderWrapper classLoader, string sig)
{
int index = sig.IndexOf(')') + 1;
return SigDecoderWrapper(classLoader, ref index, sig);
}
private sealed class ConstantPoolItemDouble : ConstantPoolItem
{
private double d;
internal ConstantPoolItemDouble(BigEndianBinaryReader br)
{
d = br.ReadDouble();
}
internal override ConstantType GetConstantType()
{
return ConstantType.Double;
}
internal double Value
{
get
{
return d;
}
}
}
internal abstract class ConstantPoolItemFMI : ConstantPoolItem
{
private ushort class_index;
private ushort name_and_type_index;
private ConstantPoolItemClass clazz;
private string name;
private string descriptor;
internal ConstantPoolItemFMI(BigEndianBinaryReader br)
{
class_index = br.ReadUInt16();
name_and_type_index = br.ReadUInt16();
}
internal override void Resolve(ClassFile classFile)
{
ConstantPoolItemNameAndType name_and_type = (ConstantPoolItemNameAndType)classFile.GetConstantPoolItem(name_and_type_index);
clazz = (ConstantPoolItemClass)classFile.GetConstantPoolItem(class_index);
// if the constant pool items referred to were strings, GetConstantPoolItem returns null
if(name_and_type == null || clazz == null)
{
throw new ClassFormatError("Bad index in constant pool");
}
name = String.Intern(classFile.GetConstantPoolUtf8String(name_and_type.name_index));
descriptor = classFile.GetConstantPoolUtf8String(name_and_type.descriptor_index);
Validate(name, descriptor, classFile.MajorVersion);
descriptor = String.Intern(descriptor.Replace('/', '.'));
}
protected abstract void Validate(string name, string descriptor, int majorVersion);
internal override void Link(TypeWrapper thisType)
{
clazz.Link(thisType);
}
internal string Name
{
get
{
return name;
}
}
internal string Signature
{
get
{
return descriptor;
}
}
internal string Class
{
get
{
return clazz.Name;
}
}
internal TypeWrapper GetClassType()
{
return clazz.GetClassType();
}
}
internal sealed class ConstantPoolItemFieldref : ConstantPoolItemFMI
{
private FieldWrapper field;
private TypeWrapper fieldTypeWrapper;
internal ConstantPoolItemFieldref(BigEndianBinaryReader br) : base(br)
{
}
protected override void Validate(string name, string descriptor, int majorVersion)
{
if(!IsValidFieldSig(descriptor))
{
throw new ClassFormatError("Invalid field signature \"{0}\"", descriptor);
}
if(!IsValidFieldName(name, majorVersion))
{
throw new ClassFormatError("Invalid field name \"{0}\"", name);
}
}
internal TypeWrapper GetFieldType()
{
return fieldTypeWrapper;
}
internal override void Link(TypeWrapper thisType)
{
base.Link(thisType);
lock(this)
{
if(fieldTypeWrapper != null)
{
return;
}
}
FieldWrapper fw = null;
TypeWrapper wrapper = GetClassType();
if(!wrapper.IsUnloadable)
{
fw = wrapper.GetFieldWrapper(Name, Signature);
if(fw != null)
{
fw.Link();
}
}
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
TypeWrapper fld = FieldTypeWrapperFromSig(classLoader, this.Signature);
lock(this)
{
if(fieldTypeWrapper == null)
{
fieldTypeWrapper = fld;
field = fw;
}
}
}
internal FieldWrapper GetField()
{
return field;
}
}
internal class ConstantPoolItemMI : ConstantPoolItemFMI
{
private TypeWrapper[] argTypeWrappers;
private TypeWrapper retTypeWrapper;
protected MethodWrapper method;
protected MethodWrapper invokespecialMethod;
internal ConstantPoolItemMI(BigEndianBinaryReader br) : base(br)
{
}
protected override void Validate(string name, string descriptor, int majorVersion)
{
if(!IsValidMethodSig(descriptor))
{
throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor);
}
if(!IsValidMethodName(name, majorVersion))
{
if(!ReferenceEquals(name, StringConstants.INIT))
{
throw new ClassFormatError("Invalid method name \"{0}\"", name);
}
if(!descriptor.EndsWith("V"))
{
throw new ClassFormatError("Method {0} has invalid signature {1}", name, descriptor);
}
}
}
internal override void Link(TypeWrapper thisType)
{
base.Link(thisType);
lock(this)
{
if(argTypeWrappers != null)
{
return;
}
}
ClassLoaderWrapper classLoader = thisType.GetClassLoader();
TypeWrapper[] args = ArgTypeWrapperListFromSig(classLoader, this.Signature);
TypeWrapper ret = RetTypeWrapperFromSig(classLoader, this.Signature);
lock(this)
{
if(argTypeWrappers == null)
{
argTypeWrappers = args;
retTypeWrapper = ret;
}
}
}
internal TypeWrapper[] GetArgTypes()
{
return argTypeWrappers;
}
internal TypeWrapper GetRetType()
{
return retTypeWrapper;
}
internal MethodWrapper GetMethod()
{
return method;
}
internal MethodWrapper GetMethodForInvokespecial()
{
return invokespecialMethod != null ? invokespecialMethod : method;
}
}
internal sealed class ConstantPoolItemMethodref : ConstantPoolItemMI
{
internal ConstantPoolItemMethodref(BigEndianBinaryReader br) : base(br)
{
}
internal override void Link(TypeWrapper thisType)
{
base.Link(thisType);
TypeWrapper wrapper = GetClassType();
if(!wrapper.IsUnloadable)
{
method = wrapper.GetMethodWrapper(Name, Signature, !ReferenceEquals(Name, StringConstants.INIT));
if(method != null)
{
method.Link();
}
if(Name != StringConstants.INIT &&
(thisType.Modifiers & (Modifiers.Interface | Modifiers.Super)) == Modifiers.Super &&
thisType != wrapper && thisType.IsSubTypeOf(wrapper))
{
invokespecialMethod = thisType.BaseTypeWrapper.GetMethodWrapper(Name, Signature, true);
if(invokespecialMethod != null)
{
invokespecialMethod.Link();
}
}
}
}
}
internal sealed class ConstantPoolItemInterfaceMethodref : ConstantPoolItemMI
{
internal ConstantPoolItemInterfaceMethodref(BigEndianBinaryReader br) : base(br)
{
}
private static MethodWrapper GetInterfaceMethod(TypeWrapper wrapper, string name, string sig)
{
MethodWrapper method = wrapper.GetMethodWrapper(name, sig, false);
if(method != null)
{
return method;
}
TypeWrapper[] interfaces = wrapper.Interfaces;
for(int i = 0; i < interfaces.Length; i++)
{
method = GetInterfaceMethod(interfaces[i], name, sig);
if(method != null)
{
return method;
}
}
return null;
}
internal override void Link(TypeWrapper thisType)
{
base.Link(thisType);
TypeWrapper wrapper = GetClassType();
if(!wrapper.IsUnloadable)
{
method = GetInterfaceMethod(wrapper, Name, Signature);
if(method == null)
{
// NOTE vmspec 5.4.3.4 clearly states that an interfacemethod may also refer to a method in Object
method = CoreClasses.java.lang.Object.Wrapper.GetMethodWrapper(Name, Signature, false);
}
if(method != null)
{
method.Link();
}
}
}
}
private sealed class ConstantPoolItemFloat : ConstantPoolItem
{
private float v;
internal ConstantPoolItemFloat(BigEndianBinaryReader br)
{
v = br.ReadSingle();
}
internal override ConstantType GetConstantType()
{
return ConstantType.Float;
}
internal float Value
{
get
{
return v;
}
}
}
private sealed class ConstantPoolItemInteger : ConstantPoolItem
{
private int v;
internal ConstantPoolItemInteger(BigEndianBinaryReader br)
{
v = br.ReadInt32();
}
internal override ConstantType GetConstantType()
{
return ConstantType.Integer;
}
internal int Value
{
get
{
return v;
}
}
}
private sealed class ConstantPoolItemLong : ConstantPoolItem
{
private long l;
internal ConstantPoolItemLong(BigEndianBinaryReader br)
{
l = br.ReadInt64();
}
internal override ConstantType GetConstantType()
{
return ConstantType.Long;
}
internal long Value
{
get
{
return l;
}
}
}
private sealed class ConstantPoolItemNameAndType : ConstantPoolItem
{
internal ushort name_index;
internal ushort descriptor_index;
internal ConstantPoolItemNameAndType(BigEndianBinaryReader br)
{
name_index = br.ReadUInt16();
descriptor_index = br.ReadUInt16();
}
internal override void Resolve(ClassFile classFile)
{
if(classFile.GetConstantPoolUtf8String(name_index) == null
|| classFile.GetConstantPoolUtf8String(descriptor_index) == null)
{
throw new ClassFormatError("Illegal constant pool index");
}
}
}
private sealed class ConstantPoolItemString : ConstantPoolItem
{
private ushort string_index;
private string s;
internal ConstantPoolItemString(BigEndianBinaryReader br)
{
string_index = br.ReadUInt16();
}
internal override void Resolve(ClassFile classFile)
{
s = classFile.GetConstantPoolUtf8String(string_index);
}
internal override ConstantType GetConstantType()
{
return ConstantType.String;
}
internal string Value
{
get
{
return s;
}
}
}
internal enum Constant
{
Utf8 = 1,
Integer = 3,
Float = 4,
Long = 5,
Double = 6,
Class = 7,
String = 8,
Fieldref = 9,
Methodref = 10,
InterfaceMethodref = 11,
NameAndType = 12
}
internal abstract class FieldOrMethod
{
// Note that Modifiers is a ushort, so it combines nicely with the following ushort field
protected Modifiers access_flags;
protected ushort flags;
private string name;
private string descriptor;
protected string signature;
protected object[] annotations;
internal FieldOrMethod(ClassFile classFile, BigEndianBinaryReader br)
{
access_flags = (Modifiers)br.ReadUInt16();
name = String.Intern(classFile.GetConstantPoolUtf8String(br.ReadUInt16()));
descriptor = classFile.GetConstantPoolUtf8String(br.ReadUInt16());
ValidateSig(classFile, descriptor);
descriptor = String.Intern(descriptor.Replace('/', '.'));
}
protected abstract void ValidateSig(ClassFile classFile, string descriptor);
internal string Name
{
get
{
return name;
}
}
internal string Signature
{
get
{
return descriptor;
}
}
internal object[] Annotations
{
get
{
return annotations;
}
}
internal string GenericSignature
{
get
{
return signature;
}
}
internal Modifiers Modifiers
{
get
{
return (Modifiers)access_flags;
}
}
internal bool IsAbstract
{
get
{
return (access_flags & Modifiers.Abstract) != 0;
}
}
internal bool IsFinal
{
get
{
return (access_flags & Modifiers.Final) != 0;
}
}
internal bool IsPublic
{
get
{
return (access_flags & Modifiers.Public) != 0;
}
}
internal bool IsPrivate
{
get
{
return (access_flags & Modifiers.Private) != 0;
}
}
internal bool IsProtected
{
get
{
return (access_flags & Modifiers.Protected) != 0;
}
}
internal bool IsStatic
{
get
{
return (access_flags & Modifiers.Static) != 0;
}
}
internal bool IsSynchronized
{
get
{
return (access_flags & Modifiers.Synchronized) != 0;
}
}
internal bool IsVolatile
{
get
{
return (access_flags & Modifiers.Volatile) != 0;
}
}
internal bool IsTransient
{
get
{
return (access_flags & Modifiers.Transient) != 0;
}
}
internal bool IsNative
{
get
{
return (access_flags & Modifiers.Native) != 0;
}
}
internal bool IsEnum
{
get
{
return (access_flags & Modifiers.Enum) != 0;
}
}
internal bool DeprecatedAttribute
{
get
{
return (flags & FLAG_MASK_DEPRECATED) != 0;
}
}
internal bool IsInternal
{
get
{
return (flags & FLAG_MASK_INTERNAL) != 0;
}
}
}
internal sealed class Field : FieldOrMethod
{
private object constantValue;
private string[] propertyGetterSetter;
internal Field(ClassFile classFile, BigEndianBinaryReader br) : base(classFile, br)
{
if((IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected)
|| (IsFinal && IsVolatile)
|| (classFile.IsInterface && (!IsPublic || !IsStatic || !IsFinal || IsTransient)))
{
throw new ClassFormatError("{0} (Illegal field modifiers: 0x{1:X})", classFile.Name, access_flags);
}
int attributes_count = br.ReadUInt16();
for(int i = 0; i < attributes_count; i++)
{
switch(classFile.GetConstantPoolUtf8String(br.ReadUInt16()))
{
case "Deprecated":
if(br.ReadUInt32() != 0)
{
throw new ClassFormatError("Invalid Deprecated attribute length");
}
flags |= FLAG_MASK_DEPRECATED;
break;
case "ConstantValue":
{
if(br.ReadUInt32() != 2)
{
throw new ClassFormatError("Invalid ConstantValue attribute length");
}
ushort index = br.ReadUInt16();
try
{
switch(Signature)
{
case "I":
constantValue = classFile.GetConstantPoolConstantInteger(index);
break;
case "S":
constantValue = (short)classFile.GetConstantPoolConstantInteger(index);
break;
case "B":
constantValue = (byte)classFile.GetConstantPoolConstantInteger(index);
break;
case "C":
constantValue = (char)classFile.GetConstantPoolConstantInteger(index);
break;
case "Z":
constantValue = classFile.GetConstantPoolConstantInteger(index) != 0;
break;
case "J":
constantValue = classFile.GetConstantPoolConstantLong(index);
break;
case "F":
constantValue = classFile.GetConstantPoolConstantFloat(index);
break;
case "D":
constantValue = classFile.GetConstantPoolConstantDouble(index);
break;
case "Ljava.lang.String;":
constantValue = classFile.GetConstantPoolConstantString(index);
break;
default:
throw new ClassFormatError("{0} (Invalid signature for constant)", classFile.Name);
}
}
catch(InvalidCastException)
{
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
}
catch(IndexOutOfRangeException)
{
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
}
catch(InvalidOperationException)
{
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
}
catch(NullReferenceException)
{
throw new ClassFormatError("{0} (Bad index into constant pool)", classFile.Name);
}
break;
}
case "Signature":
if(classFile.MajorVersion < 49)
{
goto default;
}
if(br.ReadUInt32() != 2)
{
throw new ClassFormatError("Signature attribute has incorrect length");
}
signature = classFile.GetConstantPoolUtf8String(br.ReadUInt16());
break;
case "RuntimeVisibleAnnotations":
if(classFile.MajorVersion < 49)
{
goto default;
}
annotations = ReadAnnotations(br, classFile);
break;
case "RuntimeInvisibleAnnotations":
if(classFile.MajorVersion < 49)
{
goto default;
}
foreach(object[] annot in ReadAnnotations(br, classFile))
{
if(annot[1].Equals("Likvm/lang/Property;"))
{
DecodePropertyAnnotation(classFile, annot);
}
#if STATIC_COMPILER
else if(annot[1].Equals("Likvm/lang/Internal;"))
{
this.access_flags &= ~Modifiers.AccessMask;
flags |= FLAG_MASK_INTERNAL;
}
#endif
}
break;
default:
br.Skip(br.ReadUInt32());
break;
}
}
}
private void DecodePropertyAnnotation(ClassFile classFile, object[] annot)
{
if(propertyGetterSetter != null)
{
Tracer.Error(Tracer.ClassLoading, "Ignoring duplicate ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name);
return;
}
propertyGetterSetter = new string[2];
for(int i = 2; i < annot.Length - 1; i += 2)
{
string value = annot[i + 1] as string;
if(value == null)
{
propertyGetterSetter = null;
break;
}
if(annot[i].Equals("get") && propertyGetterSetter[0] == null)
{
propertyGetterSetter[0] = value;
}
else if(annot[i].Equals("set") && propertyGetterSetter[1] == null)
{
propertyGetterSetter[1] = value;
}
else
{
propertyGetterSetter = null;
break;
}
}
if(propertyGetterSetter == null || propertyGetterSetter[0] == null)
{
propertyGetterSetter = null;
Tracer.Error(Tracer.ClassLoading, "Ignoring malformed ikvm.lang.Property annotation on {0}.{1}", classFile.Name, this.Name);
return;
}
}
protected override void ValidateSig(ClassFile classFile, string descriptor)
{
if(!IsValidFieldSig(descriptor))
{
throw new ClassFormatError("{0} (Field \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor);
}
}
internal object ConstantValue
{
get
{
return constantValue;
}
}
internal bool IsProperty
{
get
{
return propertyGetterSetter != null;
}
}
internal string PropertyGetter
{
get
{
return propertyGetterSetter[0];
}
}
internal string PropertySetter
{
get
{
return propertyGetterSetter[1];
}
}
}
internal sealed class Method : FieldOrMethod
{
private Code code;
private string[] exceptions;
private object annotationDefault;
private object[][] parameterAnnotations;
internal Method(ClassFile classFile, ClassFileParseOptions options, BigEndianBinaryReader br) : base(classFile, br)
{
// vmspec 4.6 says that all flags, except ACC_STRICT are ignored on <clinit>
if(ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID))
{
access_flags &= Modifiers.Strictfp;
access_flags |= (Modifiers.Static | Modifiers.Private);
}
else
{
// LAMESPEC: vmspec 4.6 says that abstract methods can not be strictfp (and this makes sense), but
// javac (pre 1.5) is broken and marks abstract methods as strictfp (if you put the strictfp on the class)
if((ReferenceEquals(Name, StringConstants.INIT) && (IsStatic || IsSynchronized || IsFinal || IsAbstract || IsNative))
|| (IsPrivate && IsPublic) || (IsPrivate && IsProtected) || (IsPublic && IsProtected)
|| (IsAbstract && (IsFinal || IsNative || IsPrivate || IsStatic || IsSynchronized))
|| (classFile.IsInterface && (!IsPublic || !IsAbstract)))
{
throw new ClassFormatError("{0} (Illegal method modifiers: 0x{1:X})", classFile.Name, access_flags);
}
}
int attributes_count = br.ReadUInt16();
for(int i = 0; i < attributes_count; i++)
{
switch(classFile.GetConstantPoolUtf8String(br.ReadUInt16()))
{
case "Deprecated":
if(br.ReadUInt32() != 0)
{
throw new ClassFormatError("Invalid Deprecated attribute length");
}
flags |= FLAG_MASK_DEPRECATED;
break;
case "Code":
{
if(!code.IsEmpty)
{
throw new ClassFormatError("{0} (Duplicate Code attribute)", classFile.Name);
}
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
code.Read(classFile, this, rdr, options);
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (Code attribute has wrong length)", classFile.Name);
}
break;
}
case "Exceptions":
{
if(exceptions != null)
{
throw new ClassFormatError("{0} (Duplicate Exceptions attribute)", classFile.Name);
}
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
ushort count = rdr.ReadUInt16();
exceptions = new string[count];
for(int j = 0; j < count; j++)
{
exceptions[j] = classFile.GetConstantPoolClass(rdr.ReadUInt16());
}
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (Exceptions attribute has wrong length)", classFile.Name);
}
break;
}
case "Signature":
if(classFile.MajorVersion < 49)
{
goto default;
}
if(br.ReadUInt32() != 2)
{
throw new ClassFormatError("Signature attribute has incorrect length");
}
signature = classFile.GetConstantPoolUtf8String(br.ReadUInt16());
break;
case "RuntimeVisibleAnnotations":
if(classFile.MajorVersion < 49)
{
goto default;
}
annotations = ReadAnnotations(br, classFile);
break;
case "RuntimeVisibleParameterAnnotations":
{
if(classFile.MajorVersion < 49)
{
goto default;
}
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
byte num_parameters = rdr.ReadByte();
parameterAnnotations = new object[num_parameters][];
for(int j = 0; j < num_parameters; j++)
{
ushort num_annotations = rdr.ReadUInt16();
parameterAnnotations[j] = new object[num_annotations];
for(int k = 0; k < num_annotations; k++)
{
parameterAnnotations[j][k] = ReadAnnotation(rdr, classFile);
}
}
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (RuntimeVisibleParameterAnnotations attribute has wrong length)", classFile.Name);
}
break;
}
case "AnnotationDefault":
{
if(classFile.MajorVersion < 49)
{
goto default;
}
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
annotationDefault = ReadAnnotationElementValue(rdr, classFile);
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (AnnotationDefault attribute has wrong length)", classFile.Name);
}
break;
}
#if STATIC_COMPILER
case "RuntimeInvisibleAnnotations":
if(classFile.MajorVersion < 49)
{
goto default;
}
foreach(object[] annot in ReadAnnotations(br, classFile))
{
if(annot[1].Equals("Likvm/lang/Internal;"))
{
this.access_flags &= ~Modifiers.AccessMask;
flags |= FLAG_MASK_INTERNAL;
}
if(annot[1].Equals("Likvm/internal/HasCallerID;"))
{
flags |= FLAG_HAS_CALLERID;
}
}
break;
#endif
default:
br.Skip(br.ReadUInt32());
break;
}
}
if(IsAbstract || IsNative)
{
if(!code.IsEmpty)
{
throw new ClassFormatError("Abstract or native method cannot have a Code attribute");
}
}
else
{
if(code.IsEmpty)
{
if(ReferenceEquals(this.Name, StringConstants.CLINIT))
{
code.verifyError = string.Format("Class {0}, method {1} signature {2}: No Code attribute", classFile.Name, this.Name, this.Signature);
return;
}
throw new ClassFormatError("Method has no Code attribute");
}
}
}
protected override void ValidateSig(ClassFile classFile, string descriptor)
{
if(!IsValidMethodSig(descriptor))
{
throw new ClassFormatError("{0} (Method \"{1}\" has invalid signature \"{2}\")", classFile.Name, this.Name, descriptor);
}
}
internal bool IsStrictfp
{
get
{
return (access_flags & Modifiers.Strictfp) != 0;
}
}
// Is this the <clinit>()V method?
internal bool IsClassInitializer
{
get
{
return ReferenceEquals(Name, StringConstants.CLINIT) && ReferenceEquals(Signature, StringConstants.SIG_VOID);
}
}
// for use by ikvmc only
internal bool HasCallerIDAnnotation
{
get
{
return (flags & FLAG_HAS_CALLERID) != 0;
}
}
internal string[] ExceptionsAttribute
{
get
{
return exceptions;
}
}
internal object[][] ParameterAnnotations
{
get
{
return parameterAnnotations;
}
}
internal object AnnotationDefault
{
get
{
return annotationDefault;
}
}
internal string VerifyError
{
get
{
return code.verifyError;
}
}
// maps argument 'slot' (as encoded in the xload/xstore instructions) into the ordinal
internal int[] ArgMap
{
get
{
return code.argmap;
}
}
internal int MaxStack
{
get
{
return code.max_stack;
}
}
internal int MaxLocals
{
get
{
return code.max_locals;
}
}
internal Instruction[] Instructions
{
get
{
return code.instructions;
}
set
{
code.instructions = value;
}
}
internal ExceptionTableEntry[] ExceptionTable
{
get
{
return code.exception_table;
}
set
{
code.exception_table = value;
}
}
internal LineNumberTableEntry[] LineNumberTableAttribute
{
get
{
return code.lineNumberTable;
}
}
internal LocalVariableTableEntry[] LocalVariableTableAttribute
{
get
{
return code.localVariableTable;
}
}
internal bool HasJsr
{
get
{
return code.hasJsr;
}
}
private struct Code
{
internal bool hasJsr;
internal string verifyError;
internal ushort max_stack;
internal ushort max_locals;
internal Instruction[] instructions;
internal ExceptionTableEntry[] exception_table;
internal int[] argmap;
internal LineNumberTableEntry[] lineNumberTable;
internal LocalVariableTableEntry[] localVariableTable;
internal void Read(ClassFile classFile, Method method, BigEndianBinaryReader br, ClassFileParseOptions options)
{
max_stack = br.ReadUInt16();
max_locals = br.ReadUInt16();
uint code_length = br.ReadUInt32();
if(code_length > 65535)
{
throw new ClassFormatError("{0} (Invalid Code length {1})", classFile.Name, code_length);
}
Instruction[] instructions = new Instruction[code_length + 1];
int basePosition = br.Position;
int instructionIndex = 0;
try
{
BigEndianBinaryReader rdr = br.Section(code_length);
while(!rdr.IsAtEnd)
{
instructions[instructionIndex].Read((ushort)(rdr.Position - basePosition), rdr);
hasJsr |= instructions[instructionIndex].NormalizedOpCode == NormalizedByteCode.__jsr;
instructionIndex++;
}
// we add an additional nop instruction to make it easier for consumers of the code array
instructions[instructionIndex++].SetTermNop((ushort)(rdr.Position - basePosition));
}
catch(ClassFormatError x)
{
// any class format errors in the code block are actually verify errors
verifyError = x.Message;
}
this.instructions = new Instruction[instructionIndex];
Array.Copy(instructions, 0, this.instructions, 0, instructionIndex);
// build the pcIndexMap
int[] pcIndexMap = new int[this.instructions[instructionIndex - 1].PC + 1];
for(int i = 0; i < pcIndexMap.Length; i++)
{
pcIndexMap[i] = -1;
}
for(int i = 0; i < instructionIndex - 1; i++)
{
pcIndexMap[this.instructions[i].PC] = i;
}
// convert branch offsets to indexes
for(int i = 0; i < instructionIndex - 1; i++)
{
switch(this.instructions[i].NormalizedOpCode)
{
case NormalizedByteCode.__ifeq:
case NormalizedByteCode.__ifne:
case NormalizedByteCode.__iflt:
case NormalizedByteCode.__ifge:
case NormalizedByteCode.__ifgt:
case NormalizedByteCode.__ifle:
case NormalizedByteCode.__if_icmpeq:
case NormalizedByteCode.__if_icmpne:
case NormalizedByteCode.__if_icmplt:
case NormalizedByteCode.__if_icmpge:
case NormalizedByteCode.__if_icmpgt:
case NormalizedByteCode.__if_icmple:
case NormalizedByteCode.__if_acmpeq:
case NormalizedByteCode.__if_acmpne:
case NormalizedByteCode.__ifnull:
case NormalizedByteCode.__ifnonnull:
case NormalizedByteCode.__goto:
case NormalizedByteCode.__jsr:
this.instructions[i].SetTargetIndex(pcIndexMap[this.instructions[i].Arg1 + this.instructions[i].PC]);
break;
case NormalizedByteCode.__tableswitch:
case NormalizedByteCode.__lookupswitch:
this.instructions[i].MapSwitchTargets(pcIndexMap);
break;
}
}
// read exception table
ushort exception_table_length = br.ReadUInt16();
exception_table = new ExceptionTableEntry[exception_table_length];
for(int i = 0; i < exception_table_length; i++)
{
ushort start_pc = br.ReadUInt16();
ushort end_pc = br.ReadUInt16();
ushort handler_pc = br.ReadUInt16();
ushort catch_type = br.ReadUInt16();
if(start_pc >= end_pc
|| end_pc > code_length
|| handler_pc >= code_length
|| (catch_type != 0 && !classFile.SafeIsConstantPoolClass(catch_type)))
{
throw new ClassFormatError("Illegal exception table: {0}.{1}{2}", classFile.Name, method.Name, method.Signature);
}
exception_table[i] = new ExceptionTableEntry();
exception_table[i].catch_type = catch_type;
exception_table[i].ordinal = i;
// if start_pc, end_pc or handler_pc is invalid (i.e. doesn't point to the start of an instruction),
// the index will be -1 and this will be handled by the verifier
exception_table[i].startIndex = pcIndexMap[start_pc];
exception_table[i].endIndex = pcIndexMap[end_pc];
exception_table[i].handlerIndex = pcIndexMap[handler_pc];
}
ushort attributes_count = br.ReadUInt16();
for(int i = 0; i < attributes_count; i++)
{
switch(classFile.GetConstantPoolUtf8String(br.ReadUInt16()))
{
case "LineNumberTable":
if((options & ClassFileParseOptions.LineNumberTable) != 0)
{
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
int count = rdr.ReadUInt16();
lineNumberTable = new LineNumberTableEntry[count];
for(int j = 0; j < count; j++)
{
lineNumberTable[j].start_pc = rdr.ReadUInt16();
lineNumberTable[j].line_number = rdr.ReadUInt16();
if(lineNumberTable[j].start_pc >= code_length)
{
throw new ClassFormatError("{0} (LineNumberTable has invalid pc)", classFile.Name);
}
}
if(!rdr.IsAtEnd)
{
throw new ClassFormatError("{0} (LineNumberTable attribute has wrong length)", classFile.Name);
}
}
else
{
br.Skip(br.ReadUInt32());
}
break;
case "LocalVariableTable":
if((options & ClassFileParseOptions.LocalVariableTable) != 0)
{
BigEndianBinaryReader rdr = br.Section(br.ReadUInt32());
int count = rdr.ReadUInt16();
localVariableTable = new LocalVariableTableEntry[count];
for(int j = 0; j < count; j++)
{
localVariableTable[j].start_pc = rdr.ReadUInt16();
localVariableTable[j].length = rdr.ReadUInt16();
localVariableTable[j].name = classFile.GetConstantPoolUtf8String(rdr.ReadUInt16());
localVariableTable[j].descriptor = classFile.GetConstantPoolUtf8String(rdr.ReadUInt16()).Replace('/', '.');
localVariableTable[j].index = rdr.ReadUInt16();
}
// NOTE we're intentionally not checking that we're at the end of the section
// (optional attributes shouldn't cause ClassFormatError)
}
else
{
br.Skip(br.ReadUInt32());
}
break;
default:
br.Skip(br.ReadUInt32());
break;
}
}
// build the argmap
string sig = method.Signature;
List<int> args = new List<int>();
int pos = 0;
if(!method.IsStatic)
{
args.Add(pos++);
}
for(int i = 1; sig[i] != ')'; i++)
{
args.Add(pos++);
switch(sig[i])
{
case 'L':
i = sig.IndexOf(';', i);
break;
case 'D':
case 'J':
args.Add(-1);
break;
case '[':
{
while(sig[i] == '[')
{
i++;
}
if(sig[i] == 'L')
{
i = sig.IndexOf(';', i);
}
break;
}
}
}
argmap = args.ToArray();
if(args.Count > max_locals)
{
throw new ClassFormatError("{0} (Arguments can't fit into locals)", classFile.Name);
}
}
internal bool IsEmpty
{
get
{
return instructions == null;
}
}
}
internal sealed class ExceptionTableEntry
{
internal int startIndex;
internal int endIndex;
internal int handlerIndex;
internal ushort catch_type;
internal int ordinal;
}
[Flags]
internal enum InstructionFlags : byte
{
Reachable = 1,
Processed = 2,
BranchTarget = 4,
}
internal struct Instruction
{
private ushort pc;
private NormalizedByteCode normopcode;
internal InstructionFlags flags;
private int arg1;
private short arg2;
private SwitchEntry[] switch_entries;
struct SwitchEntry
{
internal int value;
internal int target;
}
internal void SetHardError(HardError error, int messageId)
{
normopcode = NormalizedByteCode.__static_error;
arg2 = (short)error;
arg1 = messageId;
}
internal HardError HardError
{
get
{
return (HardError)arg2;
}
}
internal int HardErrorMessageId
{
get
{
return arg1;
}
}
internal bool IsReachable
{
get
{
return (flags & InstructionFlags.Reachable) != 0;
}
}
internal bool IsBranchTarget
{
get
{
return (flags & InstructionFlags.BranchTarget) != 0;
}
}
internal void PatchOpCode(NormalizedByteCode bc)
{
this.normopcode = bc;
}
internal void PatchOpCode(NormalizedByteCode bc, int arg1)
{
this.normopcode = bc;
this.arg1 = arg1;
}
internal void SetPC(int pc)
{
this.pc = (ushort)pc;
}
internal void SetTargetIndex(int targetIndex)
{
this.arg1 = targetIndex;
}
internal void SetTermNop(ushort pc)
{
// TODO what happens if we already have exactly the maximum number of instructions?
this.pc = pc;
this.normopcode = NormalizedByteCode.__nop;
}
internal void MapSwitchTargets(int[] pcIndexMap)
{
arg1 = pcIndexMap[arg1 + pc];
for (int i = 0; i < switch_entries.Length; i++)
{
switch_entries[i].target = pcIndexMap[switch_entries[i].target + pc];
}
}
internal void Read(ushort pc, BigEndianBinaryReader br)
{
this.pc = pc;
ByteCode bc = (ByteCode)br.ReadByte();
switch(ByteCodeMetaData.GetMode(bc))
{
case ByteCodeMode.Simple:
break;
case ByteCodeMode.Constant_1:
case ByteCodeMode.Local_1:
arg1 = br.ReadByte();
break;
case ByteCodeMode.Constant_2:
arg1 = br.ReadUInt16();
break;
case ByteCodeMode.Branch_2:
arg1 = br.ReadInt16();
break;
case ByteCodeMode.Branch_4:
arg1 = br.ReadInt32();
break;
case ByteCodeMode.Constant_2_1_1:
arg1 = br.ReadUInt16();
arg2 = br.ReadByte();
if(br.ReadByte() != 0)
{
throw new ClassFormatError("invokeinterface filler must be zero");
}
break;
case ByteCodeMode.Immediate_1:
arg1 = br.ReadSByte();
break;
case ByteCodeMode.Immediate_2:
arg1 = br.ReadInt16();
break;
case ByteCodeMode.Local_1_Immediate_1:
arg1 = br.ReadByte();
arg2 = br.ReadSByte();
break;
case ByteCodeMode.Constant_2_Immediate_1:
arg1 = br.ReadUInt16();
arg2 = br.ReadSByte();
break;
case ByteCodeMode.Tableswitch:
{
// skip the padding
uint p = pc + 1u;
uint align = ((p + 3) & 0x7ffffffc) - p;
br.Skip(align);
int default_offset = br.ReadInt32();
this.arg1 = default_offset;
int low = br.ReadInt32();
int high = br.ReadInt32();
if(low > high || high > 16384L + low)
{
throw new ClassFormatError("Incorrect tableswitch");
}
SwitchEntry[] entries = new SwitchEntry[high - low + 1];
for(int i = low; i <= high; i++)
{
entries[i - low].value = i;
entries[i - low].target = br.ReadInt32();
}
this.switch_entries = entries;
break;
}
case ByteCodeMode.Lookupswitch:
{
// skip the padding
uint p = pc + 1u;
uint align = ((p + 3) & 0x7ffffffc) - p;
br.Skip(align);
int default_offset = br.ReadInt32();
this.arg1 = default_offset;
int count = br.ReadInt32();
if(count < 0 || count > 16384)
{
throw new ClassFormatError("Incorrect lookupswitch");
}
SwitchEntry[] entries = new SwitchEntry[count];
for(int i = 0; i < count; i++)
{
entries[i].value = br.ReadInt32();
entries[i].target = br.ReadInt32();
}
this.switch_entries = entries;
break;
}
case ByteCodeMode.WidePrefix:
bc = (ByteCode)br.ReadByte();
// NOTE the PC of a wide instruction is actually the PC of the
// wide prefix, not the following instruction (vmspec 4.9.2)
switch(ByteCodeMetaData.GetWideMode(bc))
{
case ByteCodeModeWide.Local_2:
arg1 = br.ReadUInt16();
break;
case ByteCodeModeWide.Local_2_Immediate_2:
arg1 = br.ReadUInt16();
arg2 = br.ReadInt16();
break;
default:
throw new ClassFormatError("Invalid wide prefix on opcode: {0}", bc);
}
break;
default:
throw new ClassFormatError("Invalid opcode: {0}", bc);
}
this.normopcode = ByteCodeMetaData.GetNormalizedByteCode(bc);
arg1 = ByteCodeMetaData.GetArg(bc, arg1);
}
internal int PC
{
get
{
return pc;
}
}
internal NormalizedByteCode NormalizedOpCode
{
get
{
return normopcode;
}
}
internal int Arg1
{
get
{
return arg1;
}
}
internal int TargetIndex
{
get
{
return arg1;
}
set
{
arg1 = value;
}
}
internal int Arg2
{
get
{
return arg2;
}
}
internal int NormalizedArg1
{
get
{
return arg1;
}
}
internal int DefaultTarget
{
get
{
return arg1;
}
set
{
arg1 = value;
}
}
internal int SwitchEntryCount
{
get
{
return switch_entries.Length;
}
}
internal int GetSwitchValue(int i)
{
return switch_entries[i].value;
}
internal int GetSwitchTargetIndex(int i)
{
return switch_entries[i].target;
}
internal void SetSwitchTargets(int[] targets)
{
SwitchEntry[] newEntries = (SwitchEntry[])switch_entries.Clone();
for (int i = 0; i < newEntries.Length; i++)
{
newEntries[i].target = targets[i];
}
switch_entries = newEntries;
}
}
internal struct LineNumberTableEntry
{
internal ushort start_pc;
internal ushort line_number;
}
internal struct LocalVariableTableEntry
{
internal ushort start_pc;
internal ushort length;
internal string name;
internal string descriptor;
internal ushort index;
}
}
internal Field GetField(string name, string sig)
{
for (int i = 0; i < fields.Length; i++)
{
if (fields[i].Name == name && fields[i].Signature == sig)
{
return fields[i];
}
}
return null;
}
internal bool HasSerialVersionUID
{
get
{
Field field = GetField("serialVersionUID", "J");
return field != null && field.IsStatic && field.IsFinal;
}
}
}
}