private void ProcessComplexSerializerMembers(TypeReference type, SerializableTypeInfo serializableTypeInfo, string profile = "Default") { // Process base type (for complex serializers) // If it's a closed type and there is a serializer, we'll serialize parent SerializableTypeInfo parentSerializableTypeInfo; var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (!parentType.ContainsGenericParameter() && (parentSerializableTypeInfo = GenerateSerializer(parentType, false, profile)) != null && parentSerializableTypeInfo.SerializerType != null) { serializableTypeInfo.ComplexSerializerProcessParentType = true; } // Process members foreach (var serializableItem in ComplexClassSerializerGenerator.GetSerializableItems(type, true)) { var resolvedType = serializableItem.Type.Resolve(); // Check that all closed types have a proper serializer if (serializableItem.Attributes.Any(x => x.AttributeType.FullName == "SiliconStudio.Core.DataMemberCustomSerializerAttribute") || (resolvedType != null && resolvedType.IsInterface) || serializableItem.Type.ContainsGenericParameter()) { continue; } if (GenerateSerializer(serializableItem.Type, profile: profile) == null) { ComplexClassSerializerGenerator.IgnoreMember(serializableItem.MemberInfo); log.Log(new LogMessage(log.Module, LogMessageType.Warning, string.Format("Member {0} does not have a valid serializer. Add [DataMemberIgnore], turn the member non-public, or add a [DataContract] to it's type.", serializableItem.MemberInfo))); } } }
private void MarkExternalMethod(MethodReference methodReference) { // If a method contains generic parameters, skip it if (ResolveGenericsVisitor.ContainsGenericParameters(methodReference)) { return; } // Handle circular assembly references if (methodReference.Module == assembly.MainModule) { return; } var methodDefinition = methodReference.Resolve(); if (methodDefinition == null || methodDefinition.Module == assembly.MainModule) { return; } // Register method if (referencedMethods.Add(methodReference)) { markedMethods.Add(methodReference); } }
private void ProcessComplexSerializerMembers(TypeReference type, SerializableTypeInfo serializableTypeInfo, string profile = "Default") { // Process base type (for complex serializers) // If it's a closed type and there is a serializer, we'll serialize parent SerializableTypeInfo parentSerializableTypeInfo; var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (!parentType.ContainsGenericParameter() && (parentSerializableTypeInfo = GenerateSerializer(parentType, false, profile)) != null && parentSerializableTypeInfo.SerializerType != null) { serializableTypeInfo.ComplexSerializerProcessParentType = true; } // Process members foreach (var serializableItem in ComplexClassSerializerGenerator.GetSerializableItems(type, true)) { var resolvedType = serializableItem.Type.Resolve(); // Check that all closed types have a proper serializer if (serializableItem.Attributes.Any(x => x.AttributeType.FullName == "SiliconStudio.Core.DataMemberCustomSerializerAttribute") || (resolvedType != null && resolvedType.IsInterface) || serializableItem.Type.ContainsGenericParameter()) { continue; } if (GenerateSerializer(serializableItem.Type, profile: profile) == null) { throw new InvalidOperationException(string.Format("Member {0} (type: {1}) doesn't seem to have a valid serializer.", serializableItem.MemberInfo, serializableItem.Type.ConvertCSharp())); } } }
public static TypeReference FindSerializerDataType(TypeReference dataSerializerType) { // Find "DataSerializer<T>" base and its dataType (T) TypeReference dataType = null; var dataSerializerTypeCurrent = dataSerializerType; while (dataSerializerTypeCurrent != null) { var genericInstanceType = dataSerializerTypeCurrent as GenericInstanceType; if (genericInstanceType != null) { if (genericInstanceType.ElementType.FullName == "SiliconStudio.Core.Serialization.DataSerializer`1") { dataType = genericInstanceType.GenericArguments[0]; break; } } var baseType = ResolveGenericsVisitor.Process(dataSerializerTypeCurrent, dataSerializerTypeCurrent.Resolve().BaseType); dataSerializerTypeCurrent = baseType; } if (dataType == null) { throw new InvalidOperationException(string.Format("Could not determine data type for {0}.", dataSerializerType)); } return(dataType); }
private void RegisterExternalType(TypeReference typeReference) { var typeDefinition = typeReference.Resolve(); if (typeDefinition.Module == assembly.MainModule) { return; } // Process its base type if (typeDefinition.BaseType != null) { MarkExternalType(ResolveGenericsVisitor.Process(typeReference, typeDefinition.BaseType)); } // Process nested types if (typeDefinition.HasNestedTypes) { foreach (var nestedType in typeDefinition.NestedTypes) { MarkExternalType(ResolveGenericsVisitor.Process(typeReference, nestedType)); } } // Process its fields if (typeDefinition.HasFields) { foreach (var field in typeDefinition.Fields) { MarkExternalType(ResolveGenericsVisitor.Process(typeReference, field.FieldType)); } } // Process its properties if (typeDefinition.HasProperties) { foreach (var property in typeDefinition.Properties) { MarkExternalType(ResolveGenericsVisitor.Process(typeReference, property.PropertyType)); } } // Process its methods if (typeDefinition.HasMethods) { foreach (var method in typeDefinition.Methods) { var methodReference = ResolveGenericMethod(typeReference, method); MarkExternalMethod(methodReference); } } }
private int ComputeClassSize(TypeDefinition typeDefinition, TypeReference typeReference) { // Maybe class size is explicitely declared? var classSize = typeDefinition.ClassSize; // If not, use fields if (classSize == -1 || classSize == 0) { if (typeDefinition.PackingSize > 4) { throw new NotImplementedException("Only pack size 1, 2 and 4 are supported."); } // TODO: Check if type is blittable, otherwise I think it is supposed to affect marshalled version only (or affect managed version differently?) if (typeDefinition.IsExplicitLayout) { // Find offset of last field foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } // TODO: Align using pack size? Need to study .NET behavior. var fieldType = ResolveGenericsVisitor.Process(typeReference, field.FieldType); classSize = Math.Max(classSize, field.Offset + GetSize(fieldType)); } } else if (typeDefinition.IsSequentialLayout) { foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } // Align for next field, according to packing size classSize = (classSize + typeDefinition.PackingSize - 1) & ~(typeDefinition.PackingSize - 1); // Add size of field var fieldType = ResolveGenericsVisitor.Process(typeReference, field.FieldType); classSize += GetSize(fieldType); } } } return(classSize); }
private void EmitRet(List <StackValue> stack, MethodReference method) { if (method.ReturnType.MetadataType == MetadataType.Void) { // Emit ret void LLVM.BuildRetVoid(builder); } else { // Get last item from stack var stackItem = stack.Pop(); // Get return type var returnType = CreateType(ResolveGenericsVisitor.Process(method, method.ReturnType)); LLVM.BuildRet(builder, ConvertFromStackToLocal(returnType, stackItem)); } }
private void ProcessComplexSerializerMembers(TypeReference type, SerializableTypeInfo serializableTypeInfo, string profile = "Default") { // Process base type (for complex serializers) // If it's a closed type and there is a serializer, we'll serialize parent SerializableTypeInfo parentSerializableTypeInfo; var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (!parentType.ContainsGenericParameter() && (parentSerializableTypeInfo = GenerateSerializer(parentType, false, profile)) != null && parentSerializableTypeInfo.SerializerType != null) { serializableTypeInfo.ComplexSerializerProcessParentType = true; } // Process members foreach (var serializableItem in ComplexSerializerRegistry.GetSerializableItems(type, true)) { // Check that all closed types have a proper serializer if (serializableItem.Attributes.Any(x => x.AttributeType.FullName == "Xenko.Core.DataMemberCustomSerializerAttribute") || serializableItem.Type.ContainsGenericParameter()) { continue; } var resolvedType = serializableItem.Type.Resolve(); var isInterface = resolvedType != null && resolvedType.IsInterface; try { if (GenerateSerializer(serializableItem.Type, profile: profile) == null) { ComplexSerializerRegistry.IgnoreMember(serializableItem.MemberInfo); if (!isInterface) { log.Write( $"Warning: Member {serializableItem.MemberInfo} does not have a valid serializer. Add [DataMemberIgnore], turn the member non-public, or add a [DataContract] to it's type."); } } } catch (Exception e) { throw new InvalidOperationException($"Could not process serialization for member {serializableItem.MemberInfo}", e); } } }
private void RegisterExternalMethod(MethodReference methodReference) { var methodDefinition = methodReference.Resolve(); if (methodDefinition == null || methodDefinition.Module == assembly.MainModule) { return; } // Process method parameters foreach (var parameter in methodDefinition.Parameters) { MarkExternalType(ResolveGenericsVisitor.Process(methodReference, parameter.ParameterType)); } if (methodDefinition.HasBody) { // Process method variables and locals foreach (var variable in methodDefinition.Body.Variables) { MarkExternalType(ResolveGenericsVisitor.Process(methodReference, variable.VariableType)); } // Process method opcodes foreach (var instruction in methodDefinition.Body.Instructions) { var typeToken = instruction.Operand as TypeReference; if (typeToken != null) { MarkExternalType(ResolveGenericsVisitor.Process(methodReference, typeToken)); } var methodToken = instruction.Operand as MethodReference; if (methodToken != null) { MarkExternalMethod(ResolveGenericsVisitor.Process(methodReference, methodToken)); } } } }
/// <summary> /// Determines whether the specified value type is blittable. /// </summary> /// <param name="typeReference">The type reference.</param> /// <param name="marshalInfo">The marshal information.</param> /// <returns></returns> private static bool IsValueTypeBlittable(TypeReference typeReference, MarshalInfo marshalInfo) { bool isBlittable; if (blittableValueTypes.TryGetValue(typeReference, out isBlittable)) { return(isBlittable); } var typeDefinition = typeReference.Resolve(); // Only value types are blittable if (typeDefinition.IsValueType) { isBlittable = true; if (!typeDefinition.IsEnum && !typeDefinition.IsExplicitLayout) { // Check if every field is blittable foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } var fieldType = ResolveGenericsVisitor.Process(typeReference, field.FieldType); if (!IsBlittable(fieldType, field.HasMarshalInfo ? field.MarshalInfo : null)) { isBlittable = false; break; } } } } blittableValueTypes[typeReference] = isBlittable; return(isBlittable); }
public void AddSerializableType(TypeReference dataType, SerializableTypeInfo serializableTypeInfo, string profile = "Default") { SerializableTypeInfo currentValue; // Check if declaring type is generics var resolvedType = dataType.Resolve(); if (resolvedType != null && resolvedType.DeclaringType != null && (resolvedType.HasGenericParameters || resolvedType.DeclaringType.HasGenericParameters)) { throw new NotSupportedException(String.Format("Serialization of nested types referencing parent's generic parameters is not currently supported. " + "[Nested type={0} Parent={1}]", resolvedType.FullName, resolvedType.DeclaringType)); } var profileInfo = GetSerializableTypes(profile); if (profileInfo.TryGetSerializableTypeInfo(dataType, serializableTypeInfo.Mode != DataSerializerGenericMode.None, out currentValue)) { // TODO: Doesn't work in some generic case if (//currentValue.SerializerType.ConvertCSharp() != serializableTypeInfo.SerializerType.ConvertCSharp() || currentValue.Mode != serializableTypeInfo.Mode) { throw new InvalidOperationException(string.Format("Incompatible serializer found for same type in different assemblies for {0}", dataType.ConvertCSharp())); } return; } // Check that we don't simply try to add the same serializer than Default profile (optimized) SerializableTypeInfo defaultValue; if (profile != "Default" && SerializableTypes.TryGetSerializableTypeInfo(dataType, serializableTypeInfo.Mode != DataSerializerGenericMode.None, out defaultValue)) { if (defaultValue.SerializerType.FullName == serializableTypeInfo.SerializerType.FullName) { // Already added in default profile, early exit return; } } profileInfo.AddSerializableTypeInfo(dataType, serializableTypeInfo); // Scan and add dependencies (stored in EnumerateGenericInstantiations() functions). if (serializableTypeInfo.Local && serializableTypeInfo.SerializerType != null) { var resolvedSerializerType = serializableTypeInfo.SerializerType.Resolve(); if (resolvedSerializerType != null) { var enumerateGenericInstantiationsMethod = resolvedSerializerType.Methods.FirstOrDefault(x => x.Name == "EnumerateGenericInstantiations"); if (enumerateGenericInstantiationsMethod != null) { // Detect all ldtoken (attributes would have been better, but unfortunately C# doesn't allow generics in attributes) foreach (var inst in enumerateGenericInstantiationsMethod.Body.Instructions) { if (inst.OpCode.Code == Code.Ldtoken) { var type = (TypeReference)inst.Operand; // Try to "close" generics type with serializer type as a context var dependentType = ResolveGenericsVisitor.Process(serializableTypeInfo.SerializerType, type); if (!dependentType.ContainsGenericParameter()) { // Import type so that it becomes local to the assembly // (otherwise SerializableTypeInfo.Local will be false and it won't be instantiated) var importedType = Assembly.MainModule.ImportReference(dependentType); if (GenerateSerializer(importedType) == null) { throw new InvalidOperationException(string.Format("Could not find serializer for generic dependent type {0} when processing {1}", dependentType, dataType)); } } } } } } } }
/// <summary> /// Finds the serializer information. /// </summary> /// <param name="type">The type.</param> /// <param name="isGenericType"> /// If set to <c>true</c>, when using <see cref="DataSerializerGenericMode.Type"/>, it will return the generic version /// instead of actual type one. /// </param> /// <returns>Serializer information for the specified type.</returns> /// <exception cref="InvalidOperationException">Not sure how to process this inherited serializer.</exception> internal SerializableTypeInfo FindSerializerInfo(TypeReference type, bool isGenericType) { if (type is null || type.FullName == typeof(object).FullName || type.FullName == typeof(ValueType).FullName || type.IsGenericParameter) { return(null); } var resolvedType = type.Resolve(); // Nested type if (resolvedType.IsNested) { // Check public/private flags if (!resolvedType.IsNestedPublic && !resolvedType.IsNestedAssembly) { return(null); } } if (resolvedType.IsEnum) { // Enum // Let's generate an EnumSerializer var enumSerializerType = StrideCoreModule.GetTypeResolved("Stride.Core.Serialization.Serializers.EnumSerializer`1"); var serializerType = new GenericInstanceType(enumSerializerType); serializerType.GenericArguments.Add(type); var serializableTypeInfo = new SerializableTypeInfo(serializerType, isLocal: true, DataSerializerGenericMode.None); AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } // 1. Check if there is DataSerializerAttribute on this type (if yes, it is serializable, but not a "complex type") var dataSerializerAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "Stride.Core.Serialization.DataSerializerAttribute"); if (dataSerializerAttribute != null) { var modeField = dataSerializerAttribute.Fields.FirstOrDefault(x => x.Name == "Mode"); var mode = (modeField.Name != null) ? (DataSerializerGenericMode)modeField.Argument.Value : DataSerializerGenericMode.None; var dataSerializerType = (TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value; // Reading from custom arguments doesn't have its ValueType properly set dataSerializerType = dataSerializerType.FixupValueType(); if (mode == DataSerializerGenericMode.Type || (mode == DataSerializerGenericMode.TypeAndGenericArguments && type is GenericInstanceType)) { var genericSerializableTypeInfo = new SerializableTypeInfo(dataSerializerType, isLocal: true, mode) { IsInherited = true }; AddSerializableType(type, genericSerializableTypeInfo); var actualSerializerType = new GenericInstanceType(dataSerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (mode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)type).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Special case for GenericMode == DataSerializerGenericMode.Type: // We store actual serializer instantiation in SerializerType (alongside the generic version in GenericSerializerType) var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, isLocal: true); AddSerializableType(type, serializableTypeInfo); if (!isGenericType) { return(serializableTypeInfo); } return(genericSerializableTypeInfo); } else { var serializableTypeInfo = new SerializableTypeInfo(dataSerializerType, isLocal: true, mode) { IsInherited = false }; AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } } // 2. Check if SerializableExtendedAttribute is set on this class, or any of its base class with ApplyHierarchy var serializableExtendedAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "Stride.Core.DataContractAttribute"); if (dataSerializerAttribute is null && serializableExtendedAttribute != null) { // CHeck if ApplyHierarchy is active, otherwise it needs to be the exact type var inherited = serializableExtendedAttribute.Properties .Where(x => x.Name == "Inherited") .Select(x => (bool)x.Argument.Value) .FirstOrDefault(); var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.IsInherited = inherited; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } // Check if parent type contains Inherited attribute var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (parentType != null) { // Generate serializer for parent type var parentSerializableInfoType = GenerateSerializer(parentType.Resolve(), force: false, isGenericType: true); // If Inherited flag is on, we also generate a serializer for this type if (parentSerializableInfoType != null && parentSerializableInfoType.IsInherited) { if (parentSerializableInfoType.IsComplexSerializer) { var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.IsInherited = true; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } else if (parentSerializableInfoType.GenericsMode == DataSerializerGenericMode.Type || parentSerializableInfoType.GenericsMode == DataSerializerGenericMode.TypeAndGenericArguments) { // Register generic version var genericSerializableTypeInfo = new SerializableTypeInfo(parentSerializableInfoType.SerializerType, isLocal: true, parentSerializableInfoType.GenericsMode); AddSerializableType(type, genericSerializableTypeInfo); if (!type.HasGenericParameters) { var actualSerializerType = new GenericInstanceType(parentSerializableInfoType.SerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (parentSerializableInfoType.GenericsMode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)parentType).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Register actual type var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, isLocal: true); AddSerializableType(type, serializableTypeInfo); if (!isGenericType) { return(serializableTypeInfo); } } return(genericSerializableTypeInfo); } else { throw new InvalidOperationException("Not sure how to process inherited serializer."); } } } return(null); }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class CreateClass(TypeReference typeReference) { Class @class; if (classes.TryGetValue(typeReference, out @class)) { return(@class); } bool processFields = false; switch (typeReference.MetadataType) { case MetadataType.Void: case MetadataType.Boolean: case MetadataType.Char: case MetadataType.Byte: case MetadataType.SByte: case MetadataType.Int16: case MetadataType.UInt16: case MetadataType.Int32: case MetadataType.UInt32: case MetadataType.Int64: case MetadataType.UInt64: case MetadataType.IntPtr: case MetadataType.UIntPtr: case MetadataType.Single: case MetadataType.Double: case MetadataType.String: { break; } case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process non-static fields processFields = true; break; } default: throw new NotImplementedException(); } var type = GetType(typeReference); // Create class version (boxed version with VTable) var boxedType = type.ObjectType; var dataType = type.DataType; var stackType = type.StackType; @class = new Class(type, typeReference, dataType, boxedType, stackType); classes.Add(typeReference, @class); if (processFields) { var typeDefinition = typeReference.Resolve(); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = typeDefinition.BaseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, typeDefinition.BaseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent classes @class.VirtualTable.AddRange(parentClass.VirtualTable); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } } // Build methods slots // TODO: This will trigger their compilation, but maybe we might want to defer that later // (esp. since vtable is not built yet => recursion issues) CompileClassMethods(@class); if (typeDefinition.IsInterface) { // Interface: No need for vtable, we can just use object's one var vtableGlobal = GetClass(assembly.MainModule.Import(typeof(object))).GeneratedRuntimeTypeInfoGlobal; LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } else { // Get parent type RTTI var runtimeTypeInfoType = LLVM.PointerType(LLVM.Int8TypeInContext(context), 0); var parentRuntimeTypeInfo = parentClass != null ? LLVM.ConstPointerCast(parentClass.GeneratedRuntimeTypeInfoGlobal, runtimeTypeInfoType) : LLVM.ConstPointerNull(runtimeTypeInfoType); // Build vtable var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false); // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in typeDefinition.Interfaces) { var resolvedInterface = ResolveGenericsVisitor.Process(typeReference, @interface); @class.Interfaces.Add(GetClass(resolvedInterface)); // TODO: Add any inherited interface inherited by the resolvedInterface as well } foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.TypeReference.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.TypeReference, interfaceMethod); var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = methodId, SlotIndex = imtSlotIndex }); } } var interfaceMethodTableConstant = LLVM.ConstArray(imtEntryType, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(imtEntryType)); } if (imtSlot.Count > 1) { throw new NotImplementedException("IMT with more than one entry per slot is not implemented yet."); } var imtEntry = imtSlot.First.Value; return(LLVM.ConstNamedStruct(imtEntryType, new[] { LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType), // i8* functionPtr LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false), // i32 functionId LLVM.ConstPointerNull(LLVM.PointerType(imtEntryType, 0)), // IMTEntry* nextSlot })); }).ToArray()); // Build static fields foreach (var field in typeDefinition.Fields) { if (!field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } var staticFieldsEmpty = LLVM.ConstNull(LLVM.StructTypeInContext(context, fieldTypes.ToArray(), false)); fieldTypes.Clear(); // Reused for non-static fields after // Build RTTI var runtimeTypeInfoConstant = LLVM.ConstStructInContext(context, new[] { parentRuntimeTypeInfo, interfaceMethodTableConstant, vtableConstant, staticFieldsEmpty }, false); var vtableGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(runtimeTypeInfoConstant), string.Empty); LLVM.SetInitializer(vtableGlobal, runtimeTypeInfoConstant); LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), dataType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } // Build actual type data (fields) // Add fields and vtable slots from parent class if (parentClass != null && typeReference.MetadataType == MetadataType.Class) { fieldTypes.Add(parentClass.DataType); } foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } LLVM.StructSetBody(dataType, fieldTypes.ToArray(), false); } return(@class); }
private void BuildRuntimeType(Class @class) { if (@class.IsEmitted) { return; } @class.IsEmitted = true; // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.Type.TypeReference.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReference, interfaceMethod); // If method is not fully resolved (generic method in interface), ignore it // We are waiting for actual closed uses. if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod)) { continue; } var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); if (resolvedFunction == null && @class.Type.TypeReference is ArrayType) { var arrayType = corlib.MainModule.GetType(typeof(Array).FullName); var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name)); if (matchingMethod != null) { if (matchingMethod.HasGenericParameters) { matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReference).ElementType); } resolvedFunction = GetFunction(matchingMethod); // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists). EmitFunction(resolvedFunction); } } if (resolvedFunction == null) { throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class)); } var isInterface = resolvedFunction.DeclaringType.TypeReference.Resolve().IsInterface; if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1) { // We might have found a base virtual method matching this interface method. // Let's get the actual method override for this virtual slot. resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot]; } // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % interfaceMethodTable.Length); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = methodId, SlotIndex = imtSlotIndex }); } } var interfaceMethodTableConstant = LLVM.ConstArray(intPtrType, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(intPtrType)); } if (imtSlot.Count == 1) { // Single entry var imtEntry = imtSlot.First.Value; return(LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType)); } else { // Multiple entries, create IMT array with all entries // TODO: Support covariance/contravariance? var imtEntries = LLVM.ConstArray(imtEntryType, imtSlot.Select(imtEntry => { return(LLVM.ConstNamedStruct(imtEntryType, new[] { LLVM.ConstInt(int32Type, (ulong)imtEntry.MethodId, false), // i32 functionId LLVM.ConstPointerCast(imtEntry.Function.GeneratedValue, intPtrType), // i8* functionPtr })); }) .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryType), 1)).ToArray()); // Append { 0, 0 } terminator var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReference.MangledName() + ".imt"); LLVM.SetInitializer(imtEntryGlobal, imtEntries); // Add 1 to differentiate between single entry and IMT array return(LLVM.ConstIntToPtr( LLVM.ConstAdd( LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntType), LLVM.ConstInt(nativeIntType, 1, false)), intPtrType)); } }).ToArray()); // Build list of super types var superTypes = new List <Class>(@class.Depth); var currentClass = @class; while (currentClass != null) { superTypes.Add(currentClass); currentClass = currentClass.BaseType; } // Reverse so that the list start with most inherited object // (allows faster type checking since a given type will always be at a given index) superTypes.Reverse(); // Build super types // Helpful for fast is/as checks on class hierarchy var superTypeCount = LLVM.ConstInt(int32Type, (ulong)@class.Depth + 1, false); var interfacesCount = LLVM.ConstInt(int32Type, (ulong)@class.Interfaces.Count, false); var zero = LLVM.ConstInt(int32Type, 0, false); // Super types global var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)superTypes.Count), @class.Type.TypeReference.MangledName() + ".supertypes"); var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero }); // Interface map global var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrType, (uint)@class.Interfaces.Count), @class.Type.TypeReference.MangledName() + ".interfaces"); var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero }); // Build VTable var vtableConstant = LLVM.ConstStructInContext(context, @class.VirtualTable.Select(x => x.GeneratedValue).ToArray(), false); // Build RTTI var runtimeTypeInfoGlobal = @class.GeneratedRuntimeTypeInfoGlobal; var runtimeTypeInfoType = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal)); var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)]; LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements); var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, new[] { @class.BaseType != null ? @class.BaseType.GeneratedRuntimeTypeInfoGlobal : LLVM.ConstPointerNull(intPtrType), superTypeCount, interfacesCount, superTypesGlobal, interfacesGlobal, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 0, false), // Class initialized? interfaceMethodTableConstant, vtableConstant, LLVM.ConstNull(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields]), }); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); // Build super type list (after RTTI since we need pointer to RTTI) var superTypesConstant = LLVM.ConstArray(intPtrType, superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedRuntimeTypeInfoGlobal, intPtrType)) .ToArray()); LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant); // Build interface map var interfacesConstant = LLVM.ConstArray(intPtrType, @class.Interfaces.Select( @interface => LLVM.ConstPointerCast(@interface.GeneratedRuntimeTypeInfoGlobal, intPtrType)).ToArray()); LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant); // Mark RTTI as external LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage); }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class GetClass(Type type) { bool processClass = false; bool processFields = false; var typeReference = type.TypeReference; switch (typeReference.MetadataType) { case MetadataType.ByReference: case MetadataType.Void: case MetadataType.Pointer: // Should return something similar to IntPtr? return(null); case MetadataType.Boolean: case MetadataType.Char: case MetadataType.Byte: case MetadataType.SByte: case MetadataType.Int16: case MetadataType.UInt16: case MetadataType.Int32: case MetadataType.UInt32: case MetadataType.Int64: case MetadataType.UInt64: case MetadataType.IntPtr: case MetadataType.UIntPtr: case MetadataType.Single: case MetadataType.Double: { processClass = true; processFields = true; break; } case MetadataType.Array: case MetadataType.String: case MetadataType.TypedByReference: case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process class and instance fields processClass = true; processFields = true; break; } default: throw new NotImplementedException(); } // Create class version (boxed version with VTable) var boxedType = type.ObjectType; var dataType = type.DataType; var valueType = type.ValueType; if (type.Class != null) { return(type.Class); } var @class = type.Class = new Class(type); if (processClass) { var baseType = GetBaseTypeDefinition(typeReference); var typeDefinition = GetMethodTypeDefinition(typeReference); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = baseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, baseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent virtual methods @class.VirtualTable.AddRange(parentClass.VirtualTable.TakeWhile(x => x.MethodReference.Resolve().IsVirtual)); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } @class.Depth = parentClass.Depth + 1; } if (typeReference is ArrayType) { var elementType = ResolveGenericsVisitor.Process(typeReference, ((ArrayType)typeReference).ElementType); // Array types implicitely inherits from IList<T>, ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T> and IEnumerable<T> foreach (var interfaceType in new[] { typeof(IList <>), typeof(ICollection <>), typeof(IReadOnlyCollection <>), typeof(IReadOnlyList <>), typeof(IEnumerable <>) }) { var @interfaceGeneric = corlib.MainModule.GetType(interfaceType.FullName); var @interface = @interfaceGeneric.MakeGenericInstanceType(elementType); @class.Interfaces.Add(GetClass(@interface)); } } // Build methods slots // TODO: This will trigger their compilation, but maybe we might want to defer that later // (esp. since vtable is not built yet => recursion issues) PrepareClassMethods(type); if (typeDefinition.IsInterface) { // Interface: No need for vtable, we can just use object's one var vtableGlobal = GetClass(assembly.MainModule.Import(typeof(object))).GeneratedRuntimeTypeInfoGlobal; LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(vtableGlobal), valueType }, false); @class.GeneratedRuntimeTypeInfoGlobal = vtableGlobal; } else { // Get parent type RTTI var parentRuntimeTypeInfoType = parentClass != null ? LLVM.TypeOf(parentClass.GeneratedRuntimeTypeInfoGlobal) : intPtrType; // Build vtable var vtableType = LLVM.StructTypeInContext(context, @class.VirtualTable.Select(x => LLVM.TypeOf(x.GeneratedValue)).ToArray(), false); foreach (var @interface in typeDefinition.Interfaces) { var resolvedInterface = ResolveGenericsVisitor.Process(typeReference, @interface); @class.Interfaces.Add(GetClass(resolvedInterface)); // TODO: Add any inherited interface inherited by the resolvedInterface as well } // Build static fields foreach (var field in typeDefinition.Fields) { if (!field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } var staticFieldsType = LLVM.StructTypeInContext(context, fieldTypes.ToArray(), false); fieldTypes.Clear(); // Reused for non-static fields after var runtimeTypeInfoType = LLVM.StructTypeInContext(context, new [] { parentRuntimeTypeInfoType, int32Type, int32Type, LLVM.PointerType(intPtrType, 0), LLVM.PointerType(intPtrType, 0), LLVM.Int1TypeInContext(context), LLVM.ArrayType(intPtrType, InterfaceMethodTableSize), vtableType, staticFieldsType, }, false); var runtimeTypeInfoGlobal = LLVM.AddGlobal(module, runtimeTypeInfoType, typeReference.MangledName() + ".rtti"); @class.GeneratedRuntimeTypeInfoGlobal = runtimeTypeInfoGlobal; if (@class.Type.IsLocal) { BuildRuntimeType(@class); } else { LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalWeakLinkage); } LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(runtimeTypeInfoGlobal), valueType }, false); } // Prepare class initializer if (@class.StaticCtor != null || typeDefinition.Methods.Any(x => x.HasPInvokeInfo)) { // void EnsureClassInitialized() // { // //lock (initMutex) < TODO: not implemented yet // { // if (!classInitialized) // { // InitializeClass(); // classInitialized = true; // } // } // } var initTypeFunction = LLVM.AddFunction(module, typeReference.MangledName() + "_inittype", LLVM.FunctionType(LLVM.VoidTypeInContext(context), new TypeRef[0], false)); var block = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.PositionBuilderAtEnd(builder2, block); // Check if class is initialized var indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.TypeInitialized, false), // Type initialized flag }; var classInitializedAddress = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty); var classInitialized = LLVM.BuildLoad(builder2, classInitializedAddress, string.Empty); var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.BuildCondBr(builder2, classInitialized, nextBlock, typeNeedInitBlock); // Initialize class (first time) LLVM.PositionBuilderAtEnd(builder2, typeNeedInitBlock); // Static ctor if (@class.StaticCtor != null) { LLVM.BuildCall(builder2, @class.StaticCtor.GeneratedValue, new ValueRef[0], string.Empty); } // TODO: PInvoke initialization foreach (var pinvokeModule in typeDefinition.Methods.Where(x => x.HasPInvokeInfo).GroupBy(x => x.PInvokeInfo.Module)) { var libraryName = CreateStringConstant(pinvokeModule.Key.Name, false, true); var pinvokeLoadLibraryResult = LLVM.BuildCall(builder2, pinvokeLoadLibraryFunction, new[] { libraryName }, string.Empty); foreach (var method in pinvokeModule) { var entryPoint = CreateStringConstant(method.PInvokeInfo.EntryPoint, false, true); var pinvokeGetProcAddressResult = LLVM.BuildCall(builder2, pinvokeGetProcAddressFunction, new[] { pinvokeLoadLibraryResult, entryPoint, }, string.Empty); // TODO: Resolve method using generic context. indices = new[] { LLVM.ConstInt(int32Type, 0, false), // Pointer indirection LLVM.ConstInt(int32Type, (int)RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32Type, (ulong)GetFunction(method).VirtualSlot, false), // Access specific vtable slot }; // Get vtable slot and cast to proper type var vtableSlot = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedRuntimeTypeInfoGlobal, indices, string.Empty); pinvokeGetProcAddressResult = LLVM.BuildPointerCast(builder2, pinvokeGetProcAddressResult, LLVM.GetElementType(LLVM.TypeOf(vtableSlot)), string.Empty); // Store value LLVM.BuildStore(builder2, pinvokeGetProcAddressResult, vtableSlot); } } // Set flag so that it won't be initialized again LLVM.BuildStore(builder2, LLVM.ConstInt(LLVM.Int1TypeInContext(context), 1, false), classInitializedAddress); LLVM.BuildBr(builder2, nextBlock); LLVM.PositionBuilderAtEnd(builder2, nextBlock); LLVM.BuildRetVoid(builder2); @class.InitializeType = initTypeFunction; } // Sometime, GetType might already define DataType (for standard CLR types such as int, enum, string, array, etc...). // In that case, do not process fields. if (processFields && LLVM.GetTypeKind(valueType) == TypeKind.StructTypeKind && LLVM.IsOpaqueStruct(valueType)) { // Build actual type data (fields) // Add fields and vtable slots from parent class if (parentClass != null && type.StackType == StackValueType.Object) { fieldTypes.Add(parentClass.Type.DataType); } // Special cases: Array if (typeReference.MetadataType == MetadataType.Array) { // String: length (native int) + first element pointer var arrayType = (ArrayType)typeReference; var elementType = CreateType(arrayType.ElementType); fieldTypes.Add(intPtrType); fieldTypes.Add(LLVM.PointerType(elementType.DefaultType, 0)); } else { foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } var fieldType = CreateType(assembly.MainModule.Import(ResolveGenericsVisitor.Process(typeReference, field.FieldType))); @class.Fields.Add(field, new Field(field, @class, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultType); } } LLVM.StructSetBody(valueType, fieldTypes.ToArray(), false); } } return(@class); }
private void CompleteType(Type type) { var typeReference = type.TypeReferenceCecil; switch (typeReference.MetadataType) { case MetadataType.Pointer: case MetadataType.ByReference: case MetadataType.RequiredModifier: return; } var valueType = type.ValueTypeLLVM; var typeDefinition = GetMethodTypeDefinition(typeReference); var stackType = type.StackType; // Sometime, GetType might already define DataType (for standard CLR types such as int, enum, string, array, etc...). // In that case, do not process fields. if (type.Fields == null && (LLVM.GetTypeKind(valueType) == TypeKind.StructTypeKind || LLVM.GetTypeKind(valueType) == TypeKind.ArrayTypeKind)) { var fields = new Dictionary <FieldDefinition, Field>(MemberEqualityComparer.Default); // Avoid recursion (need a better way?) type.Fields = fields; var baseType = GetBaseTypeDefinition(typeReference); var parentType = baseType != null?GetType(ResolveGenericsVisitor.Process(typeReference, baseType), TypeState.TypeComplete) : null; // Build actual type data (fields) // Add fields and vtable slots from parent class var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count + 1); if (parentType != null && stackType == StackValueType.Object) { fieldTypes.Add(parentType.DataTypeLLVM); } // Special cases: Array if (typeReference.MetadataType == MetadataType.Array) { // String: length (native int) + first element pointer var arrayType = (ArrayType)typeReference; var elementType = GetType(arrayType.ElementType, TypeState.StackComplete); fieldTypes.Add(intPtrLLVM); fieldTypes.Add(LLVM.PointerType(elementType.DefaultTypeLLVM, 0)); } else { bool isCustomLayout = IsCustomLayout(typeDefinition); // Do we use a struct or array? int classSize = 0; // Used for sequential layout foreach (var field in typeDefinition.Fields) { if (field.IsStatic) { continue; } var fieldType = GetType(ResolveGenericsVisitor.Process(typeReference, field.FieldType), TypeState.StackComplete); // Compute struct index (that we can use to access the field). Either struct index or array offset. int structIndex; if (!isCustomLayout) { // Default case, if no custom layout (index so that we can use it in GEP) structIndex = fieldTypes.Count; } else if (typeDefinition.IsExplicitLayout) { structIndex = field.Offset; } else if (typeDefinition.IsSequentialLayout) { // Align for next field, according to packing size classSize = (classSize + typeDefinition.PackingSize - 1) & ~(typeDefinition.PackingSize - 1); structIndex = classSize; classSize += (int)LLVM.ABISizeOfType(targetData, fieldType.DefaultTypeLLVM); } else { throw new InvalidOperationException("Invalid class layouting when computing field offsets."); } fields.Add(field, new Field(field, type, fieldType, structIndex)); fieldTypes.Add(fieldType.DefaultTypeLLVM); } } // Set struct (if not custom layout with array type) if (LLVM.GetTypeKind(valueType) == TypeKind.StructTypeKind) { LLVM.StructSetBody(valueType, fieldTypes.ToArray(), false); } } }
/// <summary> /// Finds the serializer information. /// </summary> /// <param name="type">The type.</param> /// <param name="generic">If set to true, when using <see cref="DataSerializerGenericMode.Type"/>, it will returns the generic version instead of actual type one.</param> /// <returns></returns> /// <exception cref="System.InvalidOperationException">Not sure how to process this inherited serializer</exception> internal SerializableTypeInfo FindSerializerInfo(TypeReference type, bool generic) { if (type == null || type.FullName == typeof(object).FullName || type.FullName == typeof(ValueType).FullName || type.IsGenericParameter) { return(null); } var resolvedType = type.Resolve(); // Nested type if (resolvedType.IsNested) { // Check public/private flags if (!resolvedType.IsNestedPublic && !resolvedType.IsNestedAssembly) { return(null); } } if (resolvedType.IsEnum) { // Enum // Let's generate a EnumSerializer var enumSerializerType = SiliconStudioCoreAssembly.MainModule.GetTypeResolved("SiliconStudio.Core.Serialization.Serializers.EnumSerializer`1"); var serializerType = new GenericInstanceType(enumSerializerType); serializerType.GenericArguments.Add(type); var serializableTypeInfo = new SerializableTypeInfo(serializerType, true, DataSerializerGenericMode.None); AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } // 1. Check if there is a Serializable attribute // Note: Not anymore since we don't want all system types to have unknown complex serializers. //if (((resolvedType.Attributes & TypeAttributes.Serializable) == TypeAttributes.Serializable) || resolvedType.CustomAttributes.Any(x => x.AttributeType.FullName == typeof(SerializableAttribute).FullName)) //{ // serializerInfo.Serializable = true; // serializerInfo.ComplexSerializer = true; // serializerInfo.ComplexSerializerName = SerializerTypeName(resolvedType); // return serializerInfo; //} // 2.1. Check if there is DataSerializerAttribute on this type (if yes, it is serializable, but not a "complex type") var dataSerializerAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "SiliconStudio.Core.Serialization.DataSerializerAttribute"); if (dataSerializerAttribute != null) { var modeField = dataSerializerAttribute.Fields.FirstOrDefault(x => x.Name == "Mode"); var mode = (modeField.Name != null) ? (DataSerializerGenericMode)modeField.Argument.Value : DataSerializerGenericMode.None; // TODO: Replace with ResolveGenericsVisitor var dataSerializerType = ((TypeReference)dataSerializerAttribute.ConstructorArguments[0].Value); if (mode == DataSerializerGenericMode.Type || (mode == DataSerializerGenericMode.TypeAndGenericArguments && type is GenericInstanceType)) { var genericSerializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = true }; AddSerializableType(type, genericSerializableTypeInfo); var actualSerializerType = new GenericInstanceType(dataSerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (mode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)type).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Special case for GenericMode == DataSerializerGenericMode.Type: we store actual serializer instantiation in SerializerType (alongside the generic version in GenericSerializerType). var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true); AddSerializableType(type, serializableTypeInfo); if (!generic) { return(serializableTypeInfo); } return(genericSerializableTypeInfo); } else { var serializableTypeInfo = new SerializableTypeInfo(dataSerializerType, true, mode) { Inherited = false }; AddSerializableType(type, serializableTypeInfo); return(serializableTypeInfo); } } // 2.2. Check if SerializableExtendedAttribute is set on this class, or any of its base class with ApplyHierarchy var serializableExtendedAttribute = resolvedType.CustomAttributes.FirstOrDefault( x => x.AttributeType.FullName == "SiliconStudio.Core.DataContractAttribute"); if (dataSerializerAttribute == null && serializableExtendedAttribute != null) { // CHeck if ApplyHierarchy is active, otherwise it needs to be the exact type. var inherited = serializableExtendedAttribute.Properties.Where(x => x.Name == "Inherited") .Select(x => (bool)x.Argument.Value) .FirstOrDefault(); var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.Inherited = inherited; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } // Check if parent type contains Inherited attribute var parentType = ResolveGenericsVisitor.Process(type, type.Resolve().BaseType); if (parentType != null) { // Generate serializer for parent type var parentSerializableInfoType = GenerateSerializer(parentType.Resolve(), false, generic: true); // If Inherited flag is on, we also generate a serializer for this type if (parentSerializableInfoType != null && parentSerializableInfoType.Inherited) { if (parentSerializableInfoType.ComplexSerializer) { var serializableTypeInfo = CreateComplexSerializer(type); serializableTypeInfo.Inherited = true; // Process members ProcessComplexSerializerMembers(type, serializableTypeInfo); return(serializableTypeInfo); } else if (parentSerializableInfoType.Mode == DataSerializerGenericMode.Type || parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments) { // Register generic version var genericSerializableTypeInfo = new SerializableTypeInfo(parentSerializableInfoType.SerializerType, true, parentSerializableInfoType.Mode); AddSerializableType(type, genericSerializableTypeInfo); if (!type.HasGenericParameters) { var actualSerializerType = new GenericInstanceType(parentSerializableInfoType.SerializerType); // Add Type as generic arguments actualSerializerType.GenericArguments.Add(type); // If necessary, add generic arguments too if (parentSerializableInfoType.Mode == DataSerializerGenericMode.TypeAndGenericArguments) { foreach (var genericArgument in ((GenericInstanceType)parentType).GenericArguments) { actualSerializerType.GenericArguments.Add(genericArgument); } } // Register actual type var serializableTypeInfo = new SerializableTypeInfo(actualSerializerType, true); AddSerializableType(type, serializableTypeInfo); if (!generic) { return(serializableTypeInfo); } } return(genericSerializableTypeInfo); } else { throw new InvalidOperationException("Not sure how to process this inherited serializer"); } } } return(null); }
/// <summary> /// Compiles the specified class. /// </summary> /// <param name="typeReference">The type definition.</param> /// <returns></returns> private Class GetClass(Type type) { bool processClass = false; bool processFields = false; var typeReference = type.TypeReferenceCecil; // Need complete type GetType(type.TypeReferenceCecil, TypeState.TypeComplete); switch (typeReference.MetadataType) { case MetadataType.ByReference: case MetadataType.Void: case MetadataType.Pointer: // Should return something similar to IntPtr? return(null); case MetadataType.Boolean: case MetadataType.Char: case MetadataType.Byte: case MetadataType.SByte: case MetadataType.Int16: case MetadataType.UInt16: case MetadataType.Int32: case MetadataType.UInt32: case MetadataType.Int64: case MetadataType.UInt64: case MetadataType.IntPtr: case MetadataType.UIntPtr: case MetadataType.Single: case MetadataType.Double: { processClass = true; break; } case MetadataType.Array: case MetadataType.String: case MetadataType.TypedByReference: case MetadataType.ValueType: case MetadataType.Class: case MetadataType.Object: case MetadataType.GenericInstance: { // Process class and instance fields processClass = true; break; } default: throw new NotImplementedException(); } // Create class version (boxed version with VTable) var boxedType = type.ObjectTypeLLVM; var valueType = type.ValueTypeLLVM; if (type.Class != null) { return(type.Class); } var @class = type.Class = new Class(type); if (processClass) { var baseType = GetBaseTypeDefinition(typeReference); var typeDefinition = GetMethodTypeDefinition(typeReference); var fieldTypes = new List <TypeRef>(typeDefinition.Fields.Count); var parentClass = baseType != null?GetClass(ResolveGenericsVisitor.Process(typeReference, baseType)) : null; // Add parent class if (parentClass != null) { @class.BaseType = parentClass; // Add parent virtual methods @class.VirtualTable.AddRange(parentClass.VirtualTable.TakeWhile(x => x.MethodReference.Resolve().IsVirtual)); foreach (var @interface in parentClass.Interfaces) { @class.Interfaces.Add(@interface); } @class.Depth = parentClass.Depth + 1; } if (typeReference is ArrayType) { var elementType = ResolveGenericsVisitor.Process(typeReference, ((ArrayType)typeReference).ElementType); // Array types implicitely inherits from IList<T>, ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T> and IEnumerable<T> foreach (var interfaceType in new[] { typeof(IList <>), typeof(ICollection <>), typeof(IReadOnlyCollection <>), typeof(IReadOnlyList <>), typeof(IEnumerable <>) }) { var @interfaceGeneric = corlib.MainModule.GetType(interfaceType.FullName); var @interface = @interfaceGeneric.MakeGenericInstanceType(elementType); @class.Interfaces.Add(GetClass(@interface)); } } if (baseType != null && baseType.FullName == typeof(MulticastDelegate).FullName) { // Add GetMulticastDispatchMethod runtime class on delegates var getMulticastDispatchMethod = new MethodDefinition("GetMulticastDispatchMethod", MethodAttributes.Private, intPtr.TypeReferenceCecil); getMulticastDispatchMethod.HasThis = true; getMulticastDispatchMethod.Attributes = MethodAttributes.HideBySig | MethodAttributes.Virtual | MethodAttributes.Final; getMulticastDispatchMethod.ImplAttributes = MethodImplAttributes.Runtime; typeDefinition.Methods.Add(getMulticastDispatchMethod); } // Build methods slots // TODO: This will trigger their compilation, but maybe we might want to defer that later // (esp. since vtable is not built yet => recursion issues) PrepareClassMethods(type); { // Get parent type RTTI var parentRuntimeTypeInfoType = parentClass != null ? LLVM.TypeOf(parentClass.GeneratedEETypeRuntimeLLVM) : intPtrLLVM; var runtimeTypeInfoFields = new List <TypeRef> { parentRuntimeTypeInfoType, LLVM.Int8TypeInContext(context), // IsConcreteType typeDefLLVM, intPtrLLVM, sharpLangTypeType.DefaultTypeLLVM, // CachedType }; bool isConcreteType = IsConcreteType(@class.Type); // Create runtime type (still opaque struct) var runtimeTypeInfoType = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".rtti_type"); // Remove invalid characters so that we can easily link against it from C++ var mangledRttiName = Regex.Replace(typeReference.MangledName() + ".rtti", @"(\W)", "_"); var runtimeTypeInfoGlobal = LLVM.AddGlobal(module, runtimeTypeInfoType, mangledRttiName); if (typeDefinition.IsInterface) { // Interface use object as execution engine type @class.GeneratedEETypeRuntimeLLVM = GetClass(@object).GeneratedEETypeTokenLLVM; } else { @class.GeneratedEETypeRuntimeLLVM = runtimeTypeInfoGlobal; } @class.GeneratedEETypeTokenLLVM = runtimeTypeInfoGlobal; if (isConcreteType) { // Build vtable @class.VTableTypeLLVM = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".vtable"); LLVM.StructSetBody(@class.VTableTypeLLVM, @class.VirtualTable.Select(x => LLVM.PointerType(x.VirtualFunctionType, 0)).ToArray(), false); foreach (var @interface in typeDefinition.Interfaces) { var resolvedInterface = ResolveGenericsVisitor.Process(typeReference, @interface); @class.Interfaces.Add(GetClass(resolvedInterface)); // TODO: Add any inherited interface inherited by the resolvedInterface as well } // Build static fields foreach (var field in typeDefinition.Fields) { if (!field.IsStatic) { continue; } var fieldType = GetType(ResolveGenericsVisitor.Process(typeReference, field.FieldType), TypeState.StackComplete); @class.StaticFields.Add(field, new Field(field, type, fieldType, fieldTypes.Count)); fieldTypes.Add(fieldType.DefaultTypeLLVM); } var staticFieldsType = LLVM.StructCreateNamed(context, typeReference.MangledName() + ".static"); LLVM.StructSetBody(staticFieldsType, fieldTypes.ToArray(), false); fieldTypes.Clear(); // Reused for non-static fields after runtimeTypeInfoFields.AddRange(new[] { int32LLVM, // SuperTypesCount int32LLVM, // InterfacesCount LLVM.PointerType(intPtrLLVM, 0), // SuperTypes LLVM.PointerType(intPtrLLVM, 0), // InterfaceMap LLVM.Int8TypeInContext(context), // TypeInitialized LLVM.Int32TypeInContext(context), // ObjectSize LLVM.Int32TypeInContext(context), // ElementSize LLVM.ArrayType(intPtrLLVM, InterfaceMethodTableSize), // IMT int32LLVM, // VTableSize @class.VTableTypeLLVM, // VTable staticFieldsType, // StaticFields }); } LLVM.StructSetBody(runtimeTypeInfoType, runtimeTypeInfoFields.ToArray(), false); LLVM.StructSetBody(boxedType, new[] { LLVM.TypeOf(@class.GeneratedEETypeRuntimeLLVM), valueType }, false); if (@class.Type.IsLocal) { BuildRuntimeType(@class); } // Apply linkage LLVM.SetLinkage(runtimeTypeInfoGlobal, @class.Type.Linkage); } // Prepare class initializer if (@class.StaticCtor != null || typeDefinition.Methods.Any(x => x.HasPInvokeInfo)) { // void EnsureClassInitialized() // { // //lock (initMutex) < TODO: not implemented yet // { // if (!classInitialized) // { // classInitialized = true; // InitializeClass(); // } // } // } var initTypeFunction = LLVM.AddFunction(module, typeReference.MangledName() + "_inittype", LLVM.FunctionType(LLVM.VoidTypeInContext(context), new TypeRef[0], false)); // TODO: Temporarily emit it multiple time (once per assembly), that should be fixed! LLVM.SetLinkage(initTypeFunction, Linkage.LinkOnceAnyLinkage); var block = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.PositionBuilderAtEnd(builder2, block); // Check if class is initialized var indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.TypeInitialized, false), // Type initialized flag }; var classInitializedAddress = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty); var classInitialized = LLVM.BuildLoad(builder2, classInitializedAddress, string.Empty); classInitialized = LLVM.BuildIntCast(builder2, classInitialized, LLVM.Int1TypeInContext(context), string.Empty); var typeNeedInitBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); var nextBlock = LLVM.AppendBasicBlockInContext(context, initTypeFunction, string.Empty); LLVM.BuildCondBr(builder2, classInitialized, nextBlock, typeNeedInitBlock); // Initialize class (first time) LLVM.PositionBuilderAtEnd(builder2, typeNeedInitBlock); // Set flag so that it won't be initialized again LLVM.BuildStore(builder2, LLVM.ConstInt(LLVM.Int8TypeInContext(context), 1, false), classInitializedAddress); // TODO: PInvoke initialization foreach (var pinvokeModule in typeDefinition.Methods.Where(x => x.HasPInvokeInfo).GroupBy(x => x.PInvokeInfo.Module)) { var libraryName = CreateStringConstant(pinvokeModule.Key.Name, false, true); var pinvokeLoadLibraryResult = LLVM.BuildCall(builder2, pinvokeLoadLibraryFunctionLLVM, new[] { libraryName }, string.Empty); foreach (var method in pinvokeModule) { var entryPoint = CreateStringConstant(method.PInvokeInfo.EntryPoint, false, true); var pinvokeGetProcAddressResult = LLVM.BuildCall(builder2, pinvokeGetProcAddressFunctionLLVM, new[] { pinvokeLoadLibraryResult, entryPoint, LLVM.ConstInt(int16.DataTypeLLVM, (ushort)method.PInvokeInfo.Attributes, false), }, string.Empty); // TODO: Resolve method using generic context. indices = new[] { LLVM.ConstInt(int32LLVM, 0, false), // Pointer indirection LLVM.ConstInt(int32LLVM, (int)RuntimeTypeInfoFields.VirtualTable, false), // Access vtable LLVM.ConstInt(int32LLVM, (ulong)GetFunction(method).VirtualSlot, false), // Access specific vtable slot }; // Get vtable slot and cast to proper type var vtableSlot = LLVM.BuildInBoundsGEP(builder2, @class.GeneratedEETypeRuntimeLLVM, indices, string.Empty); pinvokeGetProcAddressResult = LLVM.BuildPointerCast(builder2, pinvokeGetProcAddressResult, LLVM.GetElementType(LLVM.TypeOf(vtableSlot)), string.Empty); // Store value LLVM.BuildStore(builder2, pinvokeGetProcAddressResult, vtableSlot); } } // Static ctor if (@class.StaticCtor != null) { LLVM.BuildCall(builder2, @class.StaticCtor.GeneratedValue, new ValueRef[0], string.Empty); } LLVM.BuildBr(builder2, nextBlock); LLVM.PositionBuilderAtEnd(builder2, nextBlock); LLVM.BuildRetVoid(builder2); @class.InitializeType = initTypeFunction; } } return(@class); }
void PrepareClassMethods(Type type) { var @class = GetClass(type); // Already processed? if (@class == null || @class.MethodCompiled) { return; } @class.MethodCompiled = true; // Array: no need to do anything (its type definition, Array, has already been processed) if (@class.Type.TypeReference is ArrayType) { return; } var typeDefinition = GetMethodTypeDefinition(@class.Type.TypeReference); bool isInterface = typeDefinition.IsInterface; // Process methods, Virtual first, then non virtual, then static foreach (var method in typeDefinition.Methods.OrderBy(x => x.IsVirtual ? 0 : (!x.IsStatic ? 1 : 2))) { var methodReference = ResolveGenericMethod(@class.Type.TypeReference, method); // If a method contains generic parameters, skip it // Its closed instantiations (with generic arguments) is what needs to be generated. // (except interface methods) // Using ResolveGenericsVisitor.ContainsGenericParameters because Cecil one's doesn't seem to match what .NET Type does. // TODO: Might need a more robust generic resolver/analyzer system soon. if (ResolveGenericsVisitor.ContainsGenericParameters(methodReference)) { continue; } var function = CreateFunction(methodReference); @class.Functions.Add(function); if (method.IsSpecialName && method.Name == ".cctor") { @class.StaticCtor = function; } if (method.IsVirtual) { if (isInterface) { // Store IMT slot function.VirtualSlot = (int)(GetMethodId(methodReference) % InterfaceMethodTableSize); } else if (method.IsNewSlot) { // New slot function.VirtualSlot = @class.VirtualTable.Count; @class.VirtualTable.Add(function); } else { // Find slot in base types var baseType = @class.BaseType; Function matchedMethod = null; while (baseType != null) { matchedMethod = CecilExtensions.TryMatchMethod(baseType, methodReference); if (matchedMethod != null) { break; } baseType = baseType.BaseType; } if (matchedMethod == null) { throw new InvalidOperationException(string.Format("Could not find a slot for virtual function {0} in parents of class {1}", method, @class.Type.TypeReference)); } function.VirtualSlot = matchedMethod.VirtualSlot; @class.VirtualTable[function.VirtualSlot] = function; } } else { // New slot function.VirtualSlot = @class.VirtualTable.Count; @class.VirtualTable.Add(function); } } }
private void BuildRuntimeType(Class @class) { if (@class.IsEmitted) { return; } //Console.WriteLine("Build type {0}", @class); @class.IsEmitted = true; var zero = LLVM.ConstInt(int32LLVM, 0, false); bool isConcreteType = IsConcreteType(@class.Type); // Prepare metadata info var sharpLangModule = TestMode ? LLVM.ConstNull(intPtrLLVM) : metadataPerModule[@class.Type.TypeDefinitionCecil.Module]; var extraTypeInfo = LLVM.ConstNull(intPtrLLVM); var elementTypeSize = zero; // Build RTTI var runtimeTypeInfoGlobal = @class.GeneratedEETypeTokenLLVM; var runtimeTypeInfoType = LLVM.GetElementType(LLVM.TypeOf(runtimeTypeInfoGlobal)); var runtimeTypeInfoTypeElements = new TypeRef[LLVM.CountStructElementTypes(runtimeTypeInfoType)]; LLVM.GetStructElementTypes(runtimeTypeInfoType, runtimeTypeInfoTypeElements); var typeSpecification = @class.Type.TypeReferenceCecil as TypeSpecification; if (typeSpecification != null) { if (typeSpecification is ArrayType || typeSpecification is ByReferenceType || typeSpecification is PointerType) { // Get element type var elementType = GetType(typeSpecification.ElementType, TypeState.VTableEmitted); elementTypeSize = LLVM.ConstIntCast(LLVM.SizeOf(elementType.DefaultTypeLLVM), int32LLVM, false); extraTypeInfo = LLVM.ConstPtrToInt(elementType.Class.GeneratedEETypeRuntimeLLVM, nativeIntLLVM); if (typeSpecification is ArrayType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Array, false)); } else if (typeSpecification is ByReferenceType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.ByRef, false)); } else if (typeSpecification is PointerType) { extraTypeInfo = LLVM.ConstAdd(extraTypeInfo, LLVM.ConstInt(nativeIntLLVM, (int)ExtraTypeKind.Pointer, false)); } extraTypeInfo = LLVM.ConstIntToPtr(extraTypeInfo, intPtrLLVM); } var genericInstanceType = typeSpecification as GenericInstanceType; if (genericInstanceType != null) { // Build global with array of VTable (one for each generic argument) var genericArgumentsTypes = genericInstanceType.GenericArguments.Select(x => GetType(x, TypeState.VTableEmitted)).ToArray(); var genericArgumentesTypeGlobalType = LLVM.ArrayType(intPtrLLVM, (uint)genericArgumentsTypes.Length + 1); var genericArgumentsTypesGlobal = LLVM.AddGlobal(module, genericArgumentesTypeGlobalType, @class.Type.TypeReferenceCecil.MangledName() + ".genericarguments"); LLVM.SetLinkage(genericArgumentsTypesGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(genericArgumentsTypesGlobal, LLVM.ConstArray(intPtrLLVM, genericArgumentsTypes .Select(x => LLVM.ConstPointerCast(x.Class.GeneratedEETypeTokenLLVM, intPtrLLVM)) .Concat(new[] { LLVM.ConstPointerNull(intPtrLLVM) }) .ToArray())); extraTypeInfo = LLVM.ConstInBoundsGEP(genericArgumentsTypesGlobal, new[] { zero, zero }); extraTypeInfo = LLVM.ConstPointerCast(extraTypeInfo, intPtrLLVM); } } var runtimeTypeFields = new List <ValueRef> { @class.BaseType != null ? @class.BaseType.GeneratedEETypeTokenLLVM : LLVM.ConstPointerNull(intPtrLLVM), LLVM.ConstInt(LLVM.Int8TypeInContext(context), isConcreteType ? 1U : 0U, false), LLVM.ConstNamedStruct(typeDefLLVM, new[] { sharpLangModule, LLVM.ConstInt(int32LLVM, @class.Type.TypeDefinitionCecil.MetadataToken.ToUInt32(), false), }), extraTypeInfo, LLVM.ConstNull(sharpLangTypeType.DefaultTypeLLVM), }; // Prepare the runtime type object var mangledTypeName = Regex.Replace(@class.Type.TypeReferenceCecil.MangledName() + ".sharplangtype", @"(\W)", "_"); var sharpLangTypeGlobal = LLVM.AddGlobal(module, sharpLangTypeType.ObjectTypeLLVM, mangledTypeName); LLVM.SetLinkage(sharpLangTypeGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(sharpLangTypeGlobal, LLVM.ConstNull(sharpLangTypeType.ObjectTypeLLVM)); if (isConcreteType) { // Build IMT var interfaceMethodTable = new LinkedList <InterfaceMethodTableEntry> [InterfaceMethodTableSize]; foreach (var @interface in @class.Interfaces) { foreach (var interfaceMethod in @interface.Type.TypeReferenceCecil.Resolve().Methods) { var resolvedInterfaceMethod = ResolveGenericMethod(@interface.Type.TypeReferenceCecil, interfaceMethod); // If method is not fully resolved (generic method in interface), ignore it // We are waiting for actual closed uses. if (ResolveGenericsVisitor.ContainsGenericParameters(resolvedInterfaceMethod)) { continue; } var resolvedFunction = CecilExtensions.TryMatchMethod(@class, resolvedInterfaceMethod); if (resolvedFunction == null && @class.Type.TypeReferenceCecil is ArrayType) { var arrayType = corlib.MainModule.GetType(typeof(Array).FullName); var matchingMethod = (MethodReference)arrayType.Methods.First(x => x.Name.StartsWith("InternalArray_") && x.Name.EndsWith(resolvedInterfaceMethod.Name)); if (matchingMethod != null) { if (matchingMethod.HasGenericParameters) { matchingMethod = matchingMethod.MakeGenericMethod(((ArrayType)@class.Type.TypeReferenceCecil).ElementType); } resolvedFunction = GetFunction(matchingMethod); // Manually emit Array functions locally (until proper mscorlib + generic instantiation exists). EmitFunction(resolvedFunction); LLVM.SetLinkage(resolvedFunction.GeneratedValue, Linkage.LinkOnceAnyLinkage); } } if (resolvedFunction == null) { throw new InvalidOperationException(string.Format("Could not find matching method for {0} in {1}", resolvedInterfaceMethod, @class)); } var isInterface = resolvedFunction.DeclaringType.TypeReferenceCecil.Resolve().IsInterface; if (!isInterface && resolvedFunction.MethodReference.Resolve().IsVirtual&& resolvedFunction.VirtualSlot != -1) { // We might have found a base virtual method matching this interface method. // Let's get the actual method override for this virtual slot. resolvedFunction = @class.VirtualTable[resolvedFunction.VirtualSlot]; } // If method is not found, it could be due to covariance/contravariance if (resolvedFunction == null) { throw new InvalidOperationException("Interface method not found"); } var methodId = GetMethodId(resolvedInterfaceMethod); var imtSlotIndex = (int)(methodId % InterfaceMethodTableSize); var imtSlot = interfaceMethodTable[imtSlotIndex]; if (imtSlot == null) { interfaceMethodTable[imtSlotIndex] = imtSlot = new LinkedList <InterfaceMethodTableEntry>(); } imtSlot.AddLast(new InterfaceMethodTableEntry { Function = resolvedFunction, MethodId = GetFunction(resolvedInterfaceMethod).GeneratedValue, // Should be a fake global, that we use as IMT key }); } } var interfaceMethodTableConstant = LLVM.ConstArray(intPtrLLVM, interfaceMethodTable.Select(imtSlot => { if (imtSlot == null) { // No entries: null slot return(LLVM.ConstNull(intPtrLLVM)); } if (imtSlot.Count == 1) { // Single entry var imtEntry = imtSlot.First.Value; return(LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM)); } else { // Multiple entries, create IMT array with all entries // TODO: Support covariance/contravariance? var imtEntries = LLVM.ConstArray(imtEntryLLVM, imtSlot.Select(imtEntry => { return(LLVM.ConstNamedStruct(imtEntryLLVM, new[] { imtEntry.MethodId, // i8* functionId LLVM.ConstPointerCast(GetVirtualMethod(imtEntry.Function), intPtrLLVM), // i8* functionPtr })); }) .Concat(Enumerable.Repeat(LLVM.ConstNull(imtEntryLLVM), 1)).ToArray()); // Append { 0, 0 } terminator var imtEntryGlobal = LLVM.AddGlobal(module, LLVM.TypeOf(imtEntries), @class.Type.TypeReferenceCecil.MangledName() + ".imt"); LLVM.SetLinkage(imtEntryGlobal, Linkage.PrivateLinkage); LLVM.SetInitializer(imtEntryGlobal, imtEntries); // Add 1 to differentiate between single entry and IMT array return(LLVM.ConstIntToPtr( LLVM.ConstAdd( LLVM.ConstPtrToInt(imtEntryGlobal, nativeIntLLVM), LLVM.ConstInt(nativeIntLLVM, 1, false)), intPtrLLVM)); } }).ToArray()); // Build list of super types var superTypes = new List <Class>(@class.Depth); var currentClass = @class; while (currentClass != null) { superTypes.Add(currentClass); currentClass = currentClass.BaseType; } // Reverse so that the list start with most inherited object // (allows faster type checking since a given type will always be at a given index) superTypes.Reverse(); // Build super types // Helpful for fast is/as checks on class hierarchy var superTypeCount = LLVM.ConstInt(int32LLVM, (ulong)@class.Depth + 1, false); var interfacesCount = LLVM.ConstInt(int32LLVM, (ulong)@class.Interfaces.Count, false); // Super types global var superTypesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)superTypes.Count), @class.Type.TypeReferenceCecil.MangledName() + ".supertypes"); LLVM.SetLinkage(superTypesConstantGlobal, Linkage.PrivateLinkage); var superTypesGlobal = LLVM.ConstInBoundsGEP(superTypesConstantGlobal, new[] { zero, zero }); // Interface map global var interfacesConstantGlobal = LLVM.AddGlobal(module, LLVM.ArrayType(intPtrLLVM, (uint)@class.Interfaces.Count), @class.Type.TypeReferenceCecil.MangledName() + ".interfaces"); LLVM.SetLinkage(interfacesConstantGlobal, Linkage.PrivateLinkage); var interfacesGlobal = LLVM.ConstInBoundsGEP(interfacesConstantGlobal, new[] { zero, zero }); // Build VTable var vtableConstant = LLVM.ConstNamedStruct(@class.VTableTypeLLVM, @class.VirtualTable.Select(x => GetVirtualMethod(x)).ToArray()); var staticFieldsInitializer = LLVM.ConstNamedStruct(runtimeTypeInfoTypeElements[(int)RuntimeTypeInfoFields.StaticFields], @class.StaticFields.Select(field => { var fieldType = field.Value.Type; if ((field.Key.Attributes & FieldAttributes.HasFieldRVA) != 0) { var initialValue = field.Key.InitialValue; // Seems like if type size is 8, it uses int64 as backing type // Maybe at some point it might be better to encode static fields in a big byte array and use casts instead? if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) == TypeKind.IntegerTypeKind) { unsafe { fixed(byte *initalValueStart = initialValue) { if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 64) { return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(ulong *)initalValueStart, false)); } if (LLVM.GetIntTypeWidth(fieldType.DefaultTypeLLVM) == 32) { return(LLVM.ConstInt(fieldType.DefaultTypeLLVM, *(uint *)initalValueStart, false)); } } } } // Otherwise, for now we assume that if there was a RVA, it was a type with custom layout (default type is i8[]), // as currently generated by compiler in <PrivateImplementationDetails> class. if (LLVM.GetTypeKind(fieldType.DefaultTypeLLVM) != TypeKind.ArrayTypeKind) { throw new NotSupportedException(); } var arrayElementType = LLVM.Int8TypeInContext(context); return(LLVM.ConstArray(arrayElementType, initialValue.Select(x => LLVM.ConstInt(arrayElementType, x, false)).ToArray())); } return(LLVM.ConstNull(fieldType.DefaultTypeLLVM)); }).ToArray()); runtimeTypeFields.AddRange(new[] { superTypeCount, interfacesCount, superTypesGlobal, interfacesGlobal, LLVM.ConstInt(LLVM.Int8TypeInContext(context), 0, false), // Class initialized? LLVM.ConstIntCast(LLVM.SizeOf(@class.Type.ObjectTypeLLVM), int32LLVM, false), elementTypeSize, interfaceMethodTableConstant, LLVM.ConstInt(int32LLVM, (ulong)@class.VirtualTable.Count, false), vtableConstant, staticFieldsInitializer, }); var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray()); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); // Build super type list (after RTTI since we need pointer to RTTI) var superTypesConstant = LLVM.ConstArray(intPtrLLVM, superTypes.Select(superType => LLVM.ConstPointerCast(superType.GeneratedEETypeTokenLLVM, intPtrLLVM)) .ToArray()); LLVM.SetInitializer(superTypesConstantGlobal, superTypesConstant); // Build interface map var interfacesConstant = LLVM.ConstArray(intPtrLLVM, @class.Interfaces.Select( @interface => LLVM.ConstPointerCast(@interface.GeneratedEETypeTokenLLVM, intPtrLLVM)).ToArray()); LLVM.SetInitializer(interfacesConstantGlobal, interfacesConstant); } else { // Non-concrete type, create type with current fields var runtimeTypeInfoConstant = LLVM.ConstNamedStruct(runtimeTypeInfoType, runtimeTypeFields.ToArray()); LLVM.SetInitializer(runtimeTypeInfoGlobal, runtimeTypeInfoConstant); } // Mark RTTI as external LLVM.SetLinkage(runtimeTypeInfoGlobal, Linkage.ExternalLinkage); }
// TODO: This method is quite incomplete. It would be good to synchronize it with MarshalCodeGenerator.Process() (with Native/Managed swapped) public static MethodDefinition GetOrCreateGenerateDelegateWrapper(AssemblyDefinition currentAssembly, TypeReference multicastDelegateType) { var multicastDelegateTypeResolved = multicastDelegateType.Resolve(); // Try to find method in assembly where delegate is defined var delegateWrapper = GetDelegateWrapper(multicastDelegateTypeResolved.Module, multicastDelegateType); if (delegateWrapper != null) { return(delegateWrapper); } // Try to find method in current assembly delegateWrapper = GetDelegateWrapper(currentAssembly.MainModule, multicastDelegateType); if (delegateWrapper != null) { return(delegateWrapper); } // Not found, let's create it in current assembly // First, get or create DelegateWrappers static class var delegateWrappersType = currentAssembly.MainModule.GetType("DelegateWrappers"); if (delegateWrappersType == null) { delegateWrappersType = new TypeDefinition(string.Empty, "DelegateWrappers", TypeAttributes.Class | TypeAttributes.Sealed | TypeAttributes.Abstract); currentAssembly.MainModule.Types.Add(delegateWrappersType); } var invokeMethod = multicastDelegateTypeResolved.Methods.First(x => x.Name == "Invoke"); //var returnMarshaller = FindMarshallerForType(context.SourceEmitters.Peek().Type, field.MarshalInfo); delegateWrapper = new MethodDefinition(multicastDelegateType.MangledName(), MethodAttributes.Static, currentAssembly.MainModule.Import(ResolveGenericsVisitor.Process(multicastDelegateType, invokeMethod.ReturnType))); // Determine calling convention var callingConvention = CallingConvention.Winapi; // Set default // If there is a UnmanagedFunctionPointerAttribute, get its value var unmanagedFunctionPointerAttribute = multicastDelegateTypeResolved.HasCustomAttributes ? multicastDelegateTypeResolved.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(UnmanagedFunctionPointerAttribute).FullName) : null; if (unmanagedFunctionPointerAttribute != null) { callingConvention = (CallingConvention)unmanagedFunctionPointerAttribute.ConstructorArguments[0].Value; } // On Windows, Winapi (default) means StdCall; TODO: Other platforms if (callingConvention == CallingConvention.Winapi) { callingConvention = CallingConvention.StdCall; } delegateWrapper.CallingConvention = callingConvention.ToCecil(); foreach (var parameter in invokeMethod.Parameters) { delegateWrapper.Parameters.Add(new ParameterDefinition(parameter.Name, parameter.Attributes, currentAssembly.MainModule.Import(parameter.ParameterType))); } // Extract delegate from thunk table var corlib = currentAssembly.MainModule.Import(typeof(void)).Resolve().Module.Assembly; var marshalHelper = corlib.MainModule.GetType("SharpLang.Marshalling.MarshalHelper"); var getDelegate = currentAssembly.MainModule.Import(marshalHelper.Methods.First(x => x.Name == "GetDelegate")); // Get delegate var ilProcessor = delegateWrapper.Body.GetILProcessor(); ilProcessor.Emit(OpCodes.Call, getDelegate); // TODO: Convert parameters foreach (var parameter in invokeMethod.Parameters) { ilProcessor.Emit(OpCodes.Ldarg, parameter); } // Delegate.Invoke ilProcessor.Emit(OpCodes.Call, currentAssembly.MainModule.Import(invokeMethod)); ilProcessor.Emit(OpCodes.Ret); delegateWrapper.Body.UpdateInstructionOffsets(); // TODO: Convert out parameters // Add MarshalFunctionAttribute var typeType = currentAssembly.MainModule.Import(typeof(System.Type)); var marshalFunctionAttribute = corlib.MainModule.GetType("SharpLang.Marshalling.MarshalFunctionAttribute"); var marshalFunctionAttributeCtor = currentAssembly.MainModule.Import(marshalFunctionAttribute.Methods.Single(x => x.IsConstructor)); delegateWrapper.CustomAttributes.Add(new CustomAttribute(marshalFunctionAttributeCtor) { ConstructorArguments = { new CustomAttributeArgument(typeType, multicastDelegateType) } }); delegateWrappersType.Methods.Add(delegateWrapper); return(delegateWrapper); }
private void EnsureGenerateNativeType(MarshalCodeContext context) { // Already done? if (fields != null) { return; } var corlib = context.Assembly.MainModule.Import(typeof(void)).Resolve().Module.Assembly; var voidType = context.Assembly.MainModule.Import(typeof(void)); fields = new List <StructField>(); var marshalledTypeDefinition = marshalledType.Resolve(); // Create native type, with same fields (but using native types) var typeAttributes = marshalledTypeDefinition.Attributes; typeAttributes &= ~TypeAttributes.NestedPublic; nativeType = new TypeDefinition(marshalledType.Namespace, marshalledType.Name + "_Native", typeAttributes, marshalledTypeDefinition.BaseType); context.Assembly.MainModule.Types.Add(nativeType); // Add non-static fields foreach (var fieldDefinition in marshalledTypeDefinition.Fields) { if (fieldDefinition.IsStatic) { continue; } var field = context.Assembly.MainModule.Import(fieldDefinition); var fieldType = ResolveGenericsVisitor.Process(marshalledType, field.FieldType); context.ManagedEmitters.Push(new FieldMarshalledObjectEmitter(field)); var marshaller = FindMarshallerForType(fieldType, fieldDefinition.MarshalInfo); var nativeFieldType = context.Assembly.MainModule.Import(marshaller.GetNativeType(context)); var nativeField = new FieldDefinition(field.Name, fieldDefinition.Attributes, nativeFieldType); nativeType.Fields.Add(nativeField); fields.Add(new StructField(field, fieldType, nativeField, nativeFieldType, marshaller)); context.ManagedEmitters.Pop(); } for (int i = 0; i < 2; ++i) { // Create Managed to Unmanaged wrapper (and opposite) var method = new MethodDefinition(i == 0 ? "MarshalManagedToNative" : "MarshalNativeToManaged", MethodAttributes.Static | MethodAttributes.Public, voidType); // Add method to type nativeType.Methods.Add(method); if (i == 0) { managedToNativeMethod = method; } else { nativeToManagedMethod = method; } var managedParameter = new ParameterDefinition("managedObj", ParameterAttributes.None, context.Assembly.MainModule.Import(new ByReferenceType(marshalledType))); var nativeParameter = new ParameterDefinition("nativeObj", ParameterAttributes.None, context.Assembly.MainModule.Import(new ByReferenceType(nativeType))); method.Parameters.Add(i == 0 ? managedParameter : nativeParameter); method.Parameters.Add(i == 0 ? nativeParameter : managedParameter); method.Parameters[1].Attributes |= ParameterAttributes.Out; var alternateContext = new MarshalCodeContext(context.Assembly, method, false); alternateContext.ManagedEmitters.Push(new ParameterMarshalledObjectEmitter(managedParameter)); alternateContext.ManagedEmitters.Push(new ByReferenceMarshalledObjectEmitter()); alternateContext.NativeEmitters.Push(new ParameterMarshalledObjectEmitter(nativeParameter)); alternateContext.NativeEmitters.Push(new ByReferenceMarshalledObjectEmitter()); EmitStructConvertCore(alternateContext, i == 0); alternateContext.ILProcessor.Emit(OpCodes.Ret); method.Body.UpdateInstructionOffsets(); } }