Implemented type annotation reflection for statically compiled classes.

This commit is contained in:
jfrijters 2014-06-05 12:38:09 +00:00
Родитель 3828b7df57
Коммит ff3cadecc8
3 изменённых файлов: 318 добавлений и 0 удалений

Просмотреть файл

@ -931,6 +931,10 @@ namespace IKVM.Internal
AttributeHelper.SetEnclosingMethodAttribute(typeBuilder, classFile.EnclosingMethod[0], classFile.EnclosingMethod[1], classFile.EnclosingMethod[2]);
}
}
if (classFile.RuntimeVisibleTypeAnnotations != null)
{
AttributeHelper.SetRuntimeVisibleTypeAnnotationsAttribute(typeBuilder, classFile.RuntimeVisibleTypeAnnotations);
}
if (wrapper.classLoader.EmitStackTraceInfo)
{
if (f.SourceFileAttribute != null)
@ -1637,6 +1641,10 @@ namespace IKVM.Internal
{
AttributeHelper.SetSignatureAttribute(field, fld.GenericSignature);
}
if (fld.RuntimeVisibleTypeAnnotations != null)
{
AttributeHelper.SetRuntimeVisibleTypeAnnotationsAttribute(field, fld.RuntimeVisibleTypeAnnotations);
}
}
#endif // STATIC_COMPILER
return field;
@ -2974,6 +2982,10 @@ namespace IKVM.Internal
}
AttributeHelper.SetMethodParametersAttribute(method, modifiers);
}
if (m.RuntimeVisibleTypeAnnotations != null)
{
AttributeHelper.SetRuntimeVisibleTypeAnnotationsAttribute(method, m.RuntimeVisibleTypeAnnotations);
}
#else // STATIC_COMPILER
if (setModifiers)
{
@ -4517,6 +4529,8 @@ namespace IKVM.Internal
{
AddAccessStubs();
}
AddConstantPoolAttributeIfNecessary(classFile, typeBuilder);
#endif // STATIC_COMPILER
for (int i = 0; i < classFile.Methods.Length; i++)
@ -4633,6 +4647,147 @@ namespace IKVM.Internal
}
#if STATIC_COMPILER
private static void AddConstantPoolAttributeIfNecessary(ClassFile classFile, TypeBuilder typeBuilder)
{
object[] constantPool = null;
bool[] inUse = null;
MarkConstantPoolUsage(classFile, classFile.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse);
foreach (ClassFile.Method method in classFile.Methods)
{
MarkConstantPoolUsage(classFile, method.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse);
}
foreach (ClassFile.Field field in classFile.Fields)
{
MarkConstantPoolUsage(classFile, field.RuntimeVisibleTypeAnnotations, ref constantPool, ref inUse);
}
if (constantPool != null)
{
// to save space, we clear out the items that aren't used by the RuntimeVisibleTypeAnnotations and
// use an RLE for the empty slots
AttributeHelper.SetConstantPoolAttribute(typeBuilder, ConstantPoolAttribute.Compress(constantPool, inUse));
}
}
private static void MarkConstantPoolUsage(ClassFile classFile, byte[] runtimeVisibleTypeAnnotations, ref object[] constantPool, ref bool[] inUse)
{
if (runtimeVisibleTypeAnnotations != null)
{
if (constantPool == null)
{
constantPool = classFile.GetConstantPool();
inUse = new bool[constantPool.Length];
}
try
{
BigEndianBinaryReader br = new BigEndianBinaryReader(runtimeVisibleTypeAnnotations, 0, runtimeVisibleTypeAnnotations.Length);
ushort num_annotations = br.ReadUInt16();
for (int i = 0; i < num_annotations; i++)
{
MarkConstantPoolUsageForTypeAnnotation(br, inUse);
}
return;
}
catch (ClassFormatError)
{
}
catch (IndexOutOfRangeException)
{
}
// if we fail to parse the annotations (e.g. due to a malformed attribute), we simply keep all the constant pool entries
for (int i = 0; i < inUse.Length; i++)
{
inUse[i] = true;
}
}
}
private static void MarkConstantPoolUsageForTypeAnnotation(BigEndianBinaryReader br, bool[] inUse)
{
switch (br.ReadByte()) // target_type
{
case 0x00:
case 0x01:
br.ReadByte(); // type_parameter_index
break;
case 0x10:
br.ReadUInt16(); // supertype_index
break;
case 0x11:
case 0x12:
br.ReadByte(); // type_parameter_index
br.ReadByte(); // bound_index
break;
case 0x13:
case 0x14:
case 0x15:
// empty_target
break;
case 0x16:
br.ReadByte(); // formal_parameter_index
break;
case 0x17:
br.ReadUInt16(); // throws_type_index
break;
default:
throw new ClassFormatError("");
}
byte path_length = br.ReadByte();
for (int i = 0; i < path_length; i++)
{
br.ReadByte(); // type_path_kind
br.ReadByte(); // type_argument_index
}
MarkConstantPoolUsageForAnnotation(br, inUse);
}
private static void MarkConstantPoolUsageForAnnotation(BigEndianBinaryReader br, bool[] inUse)
{
ushort type_index = br.ReadUInt16();
inUse[type_index] = true;
ushort num_components = br.ReadUInt16();
for (int i = 0; i < num_components; i++)
{
ushort component_name_index = br.ReadUInt16();
inUse[component_name_index] = true;
MarkConstantPoolUsageForAnnotationComponentValue(br, inUse);
}
}
private static void MarkConstantPoolUsageForAnnotationComponentValue(BigEndianBinaryReader br, bool[] inUse)
{
switch ((char)br.ReadByte()) // tag
{
case 'B':
case 'C':
case 'D':
case 'F':
case 'I':
case 'J':
case 'S':
case 'Z':
case 's':
case 'c':
inUse[br.ReadUInt16()] = true;
break;
case 'e':
inUse[br.ReadUInt16()] = true;
inUse[br.ReadUInt16()] = true;
break;
case '@':
MarkConstantPoolUsageForAnnotation(br, inUse);
break;
case '[':
ushort num_values = br.ReadUInt16();
for (int i = 0; i < num_values; i++)
{
MarkConstantPoolUsageForAnnotationComponentValue(br, inUse);
}
break;
default:
throw new ClassFormatError("");
}
}
private bool EmitInterlockedCompareAndSet(MethodWrapper method, string fieldName, CodeEmitter ilGenerator)
{
TypeWrapper[] parameters = method.GetParameters();

Просмотреть файл

@ -80,6 +80,8 @@ namespace IKVM.Internal
private static ConstructorInfo enclosingMethodAttribute;
private static ConstructorInfo signatureAttribute;
private static ConstructorInfo methodParametersAttribute;
private static ConstructorInfo runtimeVisibleTypeAnnotationsAttribute;
private static ConstructorInfo constantPoolAttribute;
private static CustomAttributeBuilder paramArrayAttribute;
private static ConstructorInfo nonNestedInnerClassAttribute;
private static ConstructorInfo nonNestedOuterClassAttribute;
@ -107,6 +109,8 @@ namespace IKVM.Internal
private static readonly Type typeofNonNestedOuterClassAttribute = JVM.LoadType(typeof(NonNestedOuterClassAttribute));
private static readonly Type typeofEnclosingMethodAttribute = JVM.LoadType(typeof(EnclosingMethodAttribute));
private static readonly Type typeofMethodParametersAttribute = JVM.LoadType(typeof(MethodParametersAttribute));
private static readonly Type typeofRuntimeVisibleTypeAnnotationsAttribute = JVM.LoadType(typeof(RuntimeVisibleTypeAnnotationsAttribute));
private static readonly Type typeofConstantPoolAttribute = JVM.LoadType(typeof(ConstantPoolAttribute));
private static readonly CustomAttributeBuilder hideFromJavaAttribute = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(Type.EmptyTypes), new object[0]);
private static readonly CustomAttributeBuilder hideFromReflection = new CustomAttributeBuilder(typeofHideFromJavaAttribute.GetConstructor(new Type[] { typeofHideFromJavaFlags }), new object[] { HideFromJavaFlags.Reflection | HideFromJavaFlags.StackTrace | HideFromJavaFlags.StackWalk });
@ -874,6 +878,42 @@ namespace IKVM.Internal
mb.SetCustomAttribute(new CustomAttributeBuilder(methodParametersAttribute, new object[] { modifiers }));
}
internal static void SetRuntimeVisibleTypeAnnotationsAttribute(TypeBuilder tb, byte[] data)
{
if(runtimeVisibleTypeAnnotationsAttribute == null)
{
runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() });
}
tb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data }));
}
internal static void SetRuntimeVisibleTypeAnnotationsAttribute(FieldBuilder fb, byte[] data)
{
if(runtimeVisibleTypeAnnotationsAttribute == null)
{
runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() });
}
fb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data }));
}
internal static void SetRuntimeVisibleTypeAnnotationsAttribute(MethodBuilder mb, byte[] data)
{
if(runtimeVisibleTypeAnnotationsAttribute == null)
{
runtimeVisibleTypeAnnotationsAttribute = typeofRuntimeVisibleTypeAnnotationsAttribute.GetConstructor(new Type[] { Types.Byte.MakeArrayType() });
}
mb.SetCustomAttribute(new CustomAttributeBuilder(runtimeVisibleTypeAnnotationsAttribute, new object[] { data }));
}
internal static void SetConstantPoolAttribute(TypeBuilder tb, object[] constantPool)
{
if(constantPoolAttribute == null)
{
constantPoolAttribute = typeofConstantPoolAttribute.GetConstructor(new Type[] { Types.Object.MakeArrayType() });
}
tb.SetCustomAttribute(new CustomAttributeBuilder(constantPoolAttribute, new object[] { constantPool }));
}
internal static void SetParamArrayAttribute(ParameterBuilder pb)
{
if(paramArrayAttribute == null)
@ -1017,6 +1057,34 @@ namespace IKVM.Internal
#endif
}
internal static object[] GetConstantPool(Type type)
{
#if !STATIC_COMPILER && !STUB_GENERATOR
object[] attribs = type.GetCustomAttributes(typeof(ConstantPoolAttribute), false);
return attribs.Length == 1 ? ((ConstantPoolAttribute)attribs[0]).constantPool : null;
#else
foreach(CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(type, typeofConstantPoolAttribute, false))
{
return DecodeArray<object>(cad.ConstructorArguments[0]);
}
return null;
#endif
}
internal static byte[] GetRuntimeVisibleTypeAnnotations(MemberInfo member)
{
#if !STATIC_COMPILER && !STUB_GENERATOR
object[] attribs = member.GetCustomAttributes(typeof(RuntimeVisibleTypeAnnotationsAttribute), false);
return attribs.Length == 1 ? ((RuntimeVisibleTypeAnnotationsAttribute)attribs[0]).data : null;
#else
foreach(CustomAttributeData cad in CustomAttributeData.__GetCustomAttributes(member, typeofRuntimeVisibleTypeAnnotationsAttribute, false))
{
return DecodeArray<byte>(cad.ConstructorArguments[0]);
}
return null;
#endif
}
internal static InnerClassAttribute GetInnerClass(Type type)
{
#if !STATIC_COMPILER && !STUB_GENERATOR
@ -5164,6 +5232,28 @@ namespace IKVM.Internal
{
get { return true; }
}
internal override object[] GetConstantPool()
{
return AttributeHelper.GetConstantPool(type);
}
internal override byte[] GetRawTypeAnnotations()
{
return AttributeHelper.GetRuntimeVisibleTypeAnnotations(type);
}
internal override byte[] GetMethodRawTypeAnnotations(MethodWrapper mw)
{
MethodBase mb = mw.GetMethod();
return mb == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(mb);
}
internal override byte[] GetFieldRawTypeAnnotations(FieldWrapper fw)
{
FieldInfo fi = fw.GetField();
return fi == null ? null : AttributeHelper.GetRuntimeVisibleTypeAnnotations(fi);
}
}
sealed class ArrayTypeWrapper : TypeWrapper

Просмотреть файл

@ -881,6 +881,79 @@ namespace IKVM.Attributes
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public sealed class ConstantPoolAttribute : Attribute
{
internal readonly object[] constantPool;
public ConstantPoolAttribute(object[] constantPool)
{
this.constantPool = Decompress(constantPool);
}
private static object[] Decompress(object[] constantPool)
{
List<object> list = new List<object>();
foreach (object obj in constantPool)
{
int emptySlots = obj as byte? ?? obj as ushort? ?? 0;
if (emptySlots == 0)
{
list.Add(obj);
}
else
{
for (int i = 0; i < emptySlots; i++)
{
list.Add(null);
}
}
}
return list.ToArray();
}
internal static object[] Compress(object[] constantPool, bool[] inUse)
{
int length = constantPool.Length;
while (!inUse[length - 1])
{
length--;
}
int write = 0;
for (int read = 0; read < length; read++)
{
int start = read;
while (!inUse[read])
{
read++;
}
int emptySlots = read - start;
if (emptySlots > 255)
{
constantPool[write++] = (ushort)emptySlots;
}
else if (emptySlots > 0)
{
constantPool[write++] = (byte)emptySlots;
}
constantPool[write++] = constantPool[read];
}
Array.Resize(ref constantPool, write);
return constantPool;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Field)]
public sealed class RuntimeVisibleTypeAnnotationsAttribute : Attribute
{
internal readonly byte[] data;
public RuntimeVisibleTypeAnnotationsAttribute(byte[] data)
{
this.data = data;
}
}
// used in custom modifier for access stubs
public static class AccessStub { }
}