From ff3cadecc81e286e285a2766e753bc67759db8f8 Mon Sep 17 00:00:00 2001 From: jfrijters Date: Thu, 5 Jun 2014 12:38:09 +0000 Subject: [PATCH] Implemented type annotation reflection for statically compiled classes. --- runtime/DynamicTypeWrapper.cs | 155 ++++++++++++++++++++++++++++++++++ runtime/TypeWrapper.cs | 90 ++++++++++++++++++++ runtime/attributes.cs | 73 ++++++++++++++++ 3 files changed, 318 insertions(+) diff --git a/runtime/DynamicTypeWrapper.cs b/runtime/DynamicTypeWrapper.cs index a662e57c..5796f0e9 100644 --- a/runtime/DynamicTypeWrapper.cs +++ b/runtime/DynamicTypeWrapper.cs @@ -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(); diff --git a/runtime/TypeWrapper.cs b/runtime/TypeWrapper.cs index ad4e7eda..9ff5ef1d 100644 --- a/runtime/TypeWrapper.cs +++ b/runtime/TypeWrapper.cs @@ -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(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(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 diff --git a/runtime/attributes.cs b/runtime/attributes.cs index 307cfdff..dc2a646a 100644 --- a/runtime/attributes.cs +++ b/runtime/attributes.cs @@ -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 list = new List(); + 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 { } }