зеркало из https://github.com/mono/ikvm-fork.git
2973 строки
73 KiB
C#
2973 строки
73 KiB
C#
/*
|
|
Copyright (C) 2002-2010 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,
|
|
}
|
|
|
|
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 = JVM.SafeGetEnvironmentVariable("IKVM_EXPERIMENTAL_JDK_8") != null ? 52 : 51;
|
|
}
|
|
|
|
#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)
|
|
{
|
|
try
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
catch (NullReferenceException)
|
|
{
|
|
}
|
|
catch (InvalidCastException)
|
|
{
|
|
}
|
|
catch (IndexOutOfRangeException)
|
|
{
|
|
}
|
|
return new object[] { AnnotationDefaultAttribute.TAG_ERROR, "java.lang.IllegalArgumentException", "Wrong type at constant pool index" };
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
internal 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;
|
|
}
|
|
}
|
|
|
|
internal 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 = ClassLoaderWrapper.LoadClassNoThrow(thisType.GetClassLoader(), name);
|
|
}
|
|
}
|
|
|
|
internal string Name
|
|
{
|
|
get
|
|
{
|
|
return name;
|
|
}
|
|
}
|
|
|
|
internal TypeWrapper GetClassType()
|
|
{
|
|
return typeWrapper;
|
|
}
|
|
|
|
internal override ConstantType GetConstantType()
|
|
{
|
|
return ConstantType.Class;
|
|
}
|
|
}
|
|
|
|
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 = classLoader.FieldTypeWrapperFromSigNoThrow(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 = classLoader.ArgTypeWrapperListFromSigNoThrow(this.Signature);
|
|
TypeWrapper ret = classLoader.RetTypeWrapperFromSigNoThrow(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;"))
|
|
{
|
|
if (classFile.IsInterface)
|
|
{
|
|
StaticCompiler.IssueMessage(Message.InterfaceMethodCantBeInternal, classFile.Name, this.Name, this.Signature);
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
}
|
|
// 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
|
|
int startIndex = pcIndexMap[start_pc];
|
|
int endIndex;
|
|
if (end_pc == code_length)
|
|
{
|
|
// it is legal for end_pc to point to just after the last instruction,
|
|
// but since there isn't an entry in our pcIndexMap for that, we have
|
|
// a special case for this
|
|
endIndex = instructionIndex - 1;
|
|
}
|
|
else
|
|
{
|
|
endIndex = pcIndexMap[end_pc];
|
|
}
|
|
int handlerIndex = pcIndexMap[handler_pc];
|
|
exception_table[i] = new ExceptionTableEntry(startIndex, endIndex, handlerIndex, catch_type, i);
|
|
}
|
|
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 readonly int startIndex;
|
|
internal readonly int endIndex;
|
|
internal readonly int handlerIndex;
|
|
internal readonly ushort catch_type;
|
|
internal readonly int ordinal;
|
|
internal readonly bool isFinally;
|
|
|
|
internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal)
|
|
: this(startIndex, endIndex, handlerIndex, catch_type, ordinal, false)
|
|
{
|
|
}
|
|
|
|
internal ExceptionTableEntry(int startIndex, int endIndex, int handlerIndex, ushort catch_type, int ordinal, bool isFinally)
|
|
{
|
|
this.startIndex = startIndex;
|
|
this.endIndex = endIndex;
|
|
this.handlerIndex = handlerIndex;
|
|
this.catch_type = catch_type;
|
|
this.ordinal = ordinal;
|
|
this.isFinally = isFinally;
|
|
}
|
|
}
|
|
|
|
[Flags]
|
|
internal enum InstructionFlags : byte
|
|
{
|
|
Reachable = 1,
|
|
Processed = 2,
|
|
BranchTarget = 4,
|
|
}
|
|
|
|
internal struct Instruction
|
|
{
|
|
private ushort pc;
|
|
private NormalizedByteCode normopcode;
|
|
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 HandlerIndex
|
|
{
|
|
get { return (ushort)arg2; }
|
|
}
|
|
|
|
internal int HardErrorMessageId
|
|
{
|
|
get
|
|
{
|
|
return arg1;
|
|
}
|
|
}
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc)
|
|
{
|
|
this.normopcode = bc;
|
|
}
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc, int arg1)
|
|
{
|
|
this.normopcode = bc;
|
|
this.arg1 = arg1;
|
|
}
|
|
|
|
internal void PatchOpCode(NormalizedByteCode bc, int arg1, short arg2)
|
|
{
|
|
this.normopcode = bc;
|
|
this.arg1 = arg1;
|
|
this.arg2 = arg2;
|
|
}
|
|
|
|
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();
|
|
}
|
|
// do the last entry outside the loop, to avoid overflowing "i", if high == int.MaxValue
|
|
entries[high - low].value = high;
|
|
entries[high - 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;
|
|
}
|
|
}
|
|
}
|
|
}
|