Esempio n. 1
0
        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)));
                }
            }
        }
Esempio n. 2
0
        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);
            }
        }
Esempio n. 3
0
        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()));
                }
            }
        }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
        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);
                }
            }
        }
Esempio n. 6
0
        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);
        }
Esempio n. 7
0
        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));
            }
        }
Esempio n. 8
0
        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);
                }
            }
        }
Esempio n. 9
0
        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));
                    }
                }
            }
        }
Esempio n. 10
0
        /// <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);
        }
Esempio n. 11
0
        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));
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
Esempio n. 12
0
        /// <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);
        }
Esempio n. 13
0
        /// <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);
        }
Esempio n. 14
0
        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);
        }
Esempio n. 15
0
        /// <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);
        }
Esempio n. 16
0
        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);
                }
            }
        }
Esempio n. 17
0
        /// <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);
        }
Esempio n. 18
0
        /// <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);
        }
Esempio n. 19
0
        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);
                }
            }
        }
Esempio n. 20
0
        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);
        }
Esempio n. 21
0
        // 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);
        }
Esempio n. 22
0
        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();
            }
        }