Example #1
0
        private void EnsureInitialized(AssemblyProcessorContext context)
        {
            if (isInitialized)
            {
                return;
            }

            var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly);

            xenkoCoreModule   = context.Assembly.GetXenkoCoreModule();
            pooledClosureType = context.Assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Threading.IPooledClosure"));

            // Func type and it's contructor
            var funcType = mscorlibAssembly.MainModule.GetTypeResolved("System.Func`1");

            funcConstructor = context.Assembly.MainModule.ImportReference(funcType.Methods.FirstOrDefault(x => x.Name == ".ctor"));

            // Pool type and it's constructor
            var poolTypeDefinition = xenkoCoreModule.GetType("Xenko.Core.Threading.ConcurrentPool`1");

            poolType          = context.Assembly.MainModule.ImportReference(poolTypeDefinition);
            poolConstructor   = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == ".ctor"));
            poolAcquireMethod = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == "Acquire"));
            poolReleaseMethod = context.Assembly.MainModule.ImportReference(poolTypeDefinition.Methods.FirstOrDefault(x => x.Name == "Release"));

            // Interlocked (either from mscorlib.dll or System.Threading.dll)
            var interlockedTypeDefinition = mscorlibAssembly.MainModule.GetTypeResolved("System.Threading.Interlocked");

            if (interlockedTypeDefinition == null)
            {
                var threadingAssembly = context.AssemblyResolver.Resolve(new AssemblyNameReference("System.Threading", null));
                interlockedTypeDefinition = threadingAssembly.MainModule.GetTypeResolved("System.Threading.Interlocked");
            }
            var interlockedType = context.Assembly.MainModule.ImportReference(interlockedTypeDefinition);

            // NOTE: We create method references manually, because ImportReference imports a different version of the runtime, causing
            interlockedIncrementMethod = new MethodReference("Increment", context.Assembly.MainModule.TypeSystem.Int32, interlockedType);
            interlockedIncrementMethod.Parameters.Add(new ParameterDefinition("", ParameterAttributes.None, context.Assembly.MainModule.TypeSystem.Int32.MakeByReferenceType()));
            //interlockedIncrementMethod = context.Assembly.MainModule.ImportReference(interlockedType.Methods.FirstOrDefault(x => x.Name == "Increment" && x.ReturnType.MetadataType == MetadataType.Int32));

            interlockedDecrementMethod = new MethodReference("Decrement", context.Assembly.MainModule.TypeSystem.Int32, interlockedType);
            interlockedDecrementMethod.Parameters.Add(new ParameterDefinition("", ParameterAttributes.None, context.Assembly.MainModule.TypeSystem.Int32.MakeByReferenceType()));
            //interlockedDecrementMethod = context.Assembly.MainModule.ImportReference(interlockedType.Methods.FirstOrDefault(x => x.Name == "Decrement" && x.ReturnType.MetadataType == MetadataType.Int32));

            isInitialized = true;
        }
Example #2
0
        private static void RegisterDefaultSerializationProfile(IAssemblyResolver assemblyResolver, AssemblyDefinition assembly, ComplexSerializerRegistry registry, System.IO.TextWriter log)
        {
            var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);

            if (mscorlibAssembly == null)
            {
                log.WriteLine("Missing mscorlib.dll from assembly {0}", assembly.FullName);
                throw new InvalidOperationException("Missing mscorlib.dll from assembly");
            }

            var coreSerializationAssembly = assemblyResolver.Resolve(new AssemblyNameReference("Xenko.Core", null));

            // Register serializer factories (determine which type requires which serializer)
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IList <>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.ListInterfaceSerializer`1")));
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(List <>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.ListSerializer`1")));
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(KeyValuePair <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.KeyValuePairSerializer`2")));
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(IDictionary <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2")));
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Dictionary <,>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.DictionarySerializer`2")));
            registry.SerializerFactories.Add(new CecilGenericSerializerFactory(typeof(Nullable <>), coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.NullableSerializer`1")));
            registry.SerializerFactories.Add(new CecilEnumSerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.EnumSerializer`1")));
            registry.SerializerFactories.Add(new CecilArraySerializerFactory(coreSerializationAssembly.MainModule.GetTypeResolved("Xenko.Core.Serialization.Serializers.ArraySerializer`1")));

            // Iterate over tuple size
            for (int i = 1; i <= 4; ++i)
            {
                registry.SerializerDependencies.Add(new CecilSerializerDependency(
                                                        string.Format("System.Tuple`{0}", i),
                                                        coreSerializationAssembly.MainModule.GetTypeResolved(string.Format("Xenko.Core.Serialization.Serializers.TupleSerializer`{0}", i))));

                registry.SerializerDependencies.Add(new CecilSerializerDependency(string.Format("Xenko.Core.Serialization.Serializers.TupleSerializer`{0}", i)));
            }

            // Register serializer dependencies (determine which serializer serializes which sub-type)
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.ArraySerializer`1"));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.KeyValuePairSerializer`2"));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.ListSerializer`1"));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.ListInterfaceSerializer`1"));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.NullableSerializer`1"));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.DictionarySerializer`2",
                                                                              mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair <,>).FullName)));
            registry.SerializerDependencies.Add(new CecilSerializerDependency("Xenko.Core.Serialization.Serializers.DictionaryInterfaceSerializer`2",
                                                                              mscorlibAssembly.MainModule.GetTypeResolved(typeof(KeyValuePair <,>).FullName)));
        }
        public bool Process(AssemblyProcessorContext context)
        {
            var assembly         = context.Assembly;
            var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);

            if (mscorlibAssembly == null)
            {
                throw new InvalidOperationException("Missing mscorlib.dll from assembly");
            }

            // For now, use import, but this can cause mixed framework versions when processing an assembly with a different framework version.
            voidType   = assembly.MainModule.TypeSystem.Void;
            stringType = assembly.MainModule.TypeSystem.String;
            objectType = assembly.MainModule.TypeSystem.Object;
            var propertyInfoType = assembly.MainModule.ImportReference(mscorlibAssembly.MainModule.GetTypeResolved(typeof(PropertyInfo).FullName));
            var typeType         = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);


            TypeDefinition propertyChangedExtendedEventArgsType;

            ModuleDefinition xenkoCoreModule;

            try
            {
                xenkoCoreModule = assembly.GetXenkoCoreModule();
            }
            catch (Exception)
            {
                return(true);
            }

            propertyChangedExtendedEventArgsType = xenkoCoreModule.GetTypes().First(x => x.Name == "PropertyChangedExtendedEventArgs").Resolve();

            var typeTokenInfoEx         = mscorlibAssembly.MainModule.GetTypeResolved("System.Reflection.TypeInfo").Resolve();
            var getPropertyMethod       = typeTokenInfoEx.Methods.First(x => x.Name == "GetDeclaredProperty" && x.Parameters.Count == 1);
            var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == "GetTypeFromHandle");
            var getTokenInfoExMethod    = CecilExtensions.FindReflectionAssembly(assembly).MainModule.GetTypeResolved("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == "GetTypeInfo");

            var propertyChangedExtendedEventArgsConstructor = assembly.MainModule.ImportReference(propertyChangedExtendedEventArgsType.Methods.First(x => x.IsConstructor));

            bool modified = false;

            foreach (var type in assembly.MainModule.GetTypes())
            {
                MethodReference getPropertyChangedMethod;

                getPropertyChangedMethod = GetGetPropertyChangedMethod(assembly, type);
                //var propertyChangedField = GetPropertyChangedField(type);
                //if (propertyChangedField == null)
                //    continue;

                var propertyChangedField = GetPropertyChangedField(type);

                if (getPropertyChangedMethod == null && propertyChangedField == null)
                {
                    continue;
                }

                TypeReference propertyChangedFieldType;

                if (getPropertyChangedMethod == null)
                {
                    modified = true;
                    getPropertyChangedMethod = GetOrCreateGetPropertyChangedMethod(assembly, type, propertyChangedField);
                }

                if (propertyChangedField != null)
                {
                    propertyChangedField     = assembly.MainModule.ImportReference(propertyChangedField);
                    propertyChangedFieldType = propertyChangedField.FieldType;
                }
                else
                {
                    propertyChangedFieldType = getPropertyChangedMethod.ReturnType;
                }

                // Add generic to declaring type
                if (getPropertyChangedMethod.DeclaringType.HasGenericParameters)
                {
                    getPropertyChangedMethod = getPropertyChangedMethod.MakeGeneric(getPropertyChangedMethod.DeclaringType.GenericParameters.ToArray());
                }

                var propertyChangedInvoke = assembly.MainModule.ImportReference(propertyChangedFieldType.Resolve().Methods.First(x => x.Name == "Invoke"));

                foreach (var property in type.Properties)
                {
                    if (property.SetMethod == null || !property.HasThis)
                    {
                        continue;
                    }

                    MethodReference propertyGetMethod = property.GetMethod;

                    // Only patch properties that have a public Getter
                    var methodDefinition = propertyGetMethod.Resolve();
                    if ((methodDefinition.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
                    {
                        continue;
                    }

                    // Add generic to declaring type
                    if (propertyGetMethod.DeclaringType.HasGenericParameters)
                    {
                        propertyGetMethod = propertyGetMethod.MakeGeneric(propertyGetMethod.DeclaringType.GenericParameters.ToArray());
                    }

                    //var versionableAttribute = property.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(VersionableAttribute).FullName);
                    //if (versionableAttribute == null)
                    //    continue;

                    modified = true;

                    FieldReference staticField = new FieldDefinition(property.Name + "PropertyInfo", FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly, propertyInfoType);
                    type.Fields.Add((FieldDefinition)staticField);

                    // Add generic to declaring type
                    if (staticField.DeclaringType.HasGenericParameters)
                    {
                        staticField = staticField.MakeGeneric(staticField.DeclaringType.GenericParameters.ToArray());
                    }

                    // In static constructor, find PropertyInfo and store it in static field
                    {
                        var staticConstructor = type.GetStaticConstructor();
                        if (staticConstructor == null)
                        {
                            staticConstructor = new MethodDefinition(".cctor",
                                                                     MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                                                                     voidType);
                            staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));

                            type.Methods.Add(staticConstructor);
                        }


                        VariableReference localTokenEx = null;
                        int localTokenExIndex          = 0;
                        for (int i = 0; i < staticConstructor.Body.Variables.Count; i++)
                        {
                            var localVar = staticConstructor.Body.Variables[i];
                            if (localVar.VariableType.FullName == typeTokenInfoEx.FullName)
                            {
                                localTokenEx      = localVar;
                                localTokenExIndex = i;
                                break;
                            }
                        }

                        if (localTokenEx == null)
                        {
                            localTokenEx = new VariableDefinition(assembly.MainModule.ImportReference(typeTokenInfoEx));
                            staticConstructor.Body.Variables.Add((VariableDefinition)localTokenEx);
                            localTokenExIndex = staticConstructor.Body.Variables.Count - 1;
                        }

                        var ilProcessor       = staticConstructor.Body.GetILProcessor();
                        var returnInstruction = staticConstructor.Body.Instructions.Last();

                        var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
                        newReturnInstruction.Operand = returnInstruction.Operand;

                        returnInstruction.OpCode  = OpCodes.Nop;
                        returnInstruction.Operand = null;

                        // Find PropertyInfo and store it in static field
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldtoken, type));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)));
                        ilProcessor.Append(LocationToStloc(ilProcessor, localTokenExIndex));
                        ilProcessor.Append(ilProcessor.Create(OpCodes.Ldloca_S, (byte)localTokenExIndex));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldstr, property.Name));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getPropertyMethod)));
                        ilProcessor.Append(Instruction.Create(OpCodes.Stsfld, staticField));

                        ilProcessor.Append(newReturnInstruction);
                    }

                    {
                        var ilProcessor       = property.SetMethod.Body.GetILProcessor();
                        var returnInstruction = property.SetMethod.Body.Instructions.Last();

                        var firstInstruction = property.SetMethod.Body.Instructions[0];

                        if (property.SetMethod.Body.Instructions[0].OpCode != OpCodes.Nop)
                        {
                            ilProcessor.InsertBefore(property.SetMethod.Body.Instructions[0], Instruction.Create(OpCodes.Nop));
                        }

                        var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
                        newReturnInstruction.Operand = returnInstruction.Operand;

                        returnInstruction.OpCode  = OpCodes.Nop;
                        returnInstruction.Operand = null;

                        var propertyChangedVariable = new VariableDefinition(assembly.MainModule.ImportReference(propertyChangedFieldType));
                        property.SetMethod.Body.Variables.Add(propertyChangedVariable);

                        var oldValueVariable = new VariableDefinition(objectType);
                        property.SetMethod.Body.Variables.Add(oldValueVariable);

                        Instruction jump1, jump2;

                        // Prepend:
                        // var propertyChanged = GetPropertyChanged();
                        // var oldValue = propertyChanged != null ? (object)Property : null;
                        property.SetMethod.Body.SimplifyMacros();
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, getPropertyChangedMethod));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, propertyChangedVariable));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.InsertBefore(firstInstruction, jump1 = Instruction.Create(OpCodes.Brtrue, Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldnull));
                        ilProcessor.InsertBefore(firstInstruction, jump2 = Instruction.Create(OpCodes.Br, Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump1.Operand = Instruction.Create(OpCodes.Ldarg_0)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, propertyGetMethod));
                        if (property.PropertyType.IsValueType)
                        {
                            ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Box, property.PropertyType));
                        }
                        ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump2.Operand = Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, oldValueVariable));

                        // Append:
                        // if (propertyChanged != null)
                        //     propertyChanged(this, new PropertyChangedExtendedEventArgs("Property", oldValue, Property));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldnull));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ceq));
                        ilProcessor.Append(Instruction.Create(OpCodes.Brtrue, newReturnInstruction));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldsfld, staticField));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, oldValueVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, propertyGetMethod));
                        if (property.PropertyType.IsValueType)
                        {
                            ilProcessor.Append(Instruction.Create(OpCodes.Box, property.PropertyType));
                        }
                        ilProcessor.Append(Instruction.Create(OpCodes.Newobj, propertyChangedExtendedEventArgsConstructor));
                        ilProcessor.Append(Instruction.Create(OpCodes.Callvirt, propertyChangedInvoke));
                        ilProcessor.Append(Instruction.Create(OpCodes.Nop));
                        ilProcessor.Append(newReturnInstruction);
                        property.SetMethod.Body.OptimizeMacros();
                    }
                }
            }

            return(modified);
        }
Example #4
0
        public bool Run(ref AssemblyDefinition assemblyDefinition, ref bool readWriteSymbols, out bool modified)
        {
            modified = false;

            try
            {
                var assemblyResolver = (CustomAssemblyResolver)assemblyDefinition.MainModule.AssemblyResolver;

                // Register self
                assemblyResolver.Register(assemblyDefinition);

                var processors = new List <IAssemblyDefinitionProcessor>();

                // We are no longer using it so we are deactivating it for now to avoid processing
                //if (AutoNotifyProperty)
                //{
                //    processors.Add(new NotifyPropertyProcessor());
                //}

                processors.Add(new AddReferenceProcessor(ReferencesToAdd));

                if (ParameterKey)
                {
                    processors.Add(new ParameterKeyProcessor());
                }

                if (NewAssemblyName != null)
                {
                    processors.Add(new RenameAssemblyProcessor(NewAssemblyName));
                }

                //processors.Add(new AsyncBridgeProcessor());

                // Always applies the interop processor
                processors.Add(new InteropProcessor());
                processors.Add(new MonoFixedProcessor());

                processors.Add(new AssemblyVersionProcessor());

                if (DocumentationFile != null)
                {
                    processors.Add(new GenerateUserDocumentationProcessor(DocumentationFile));
                }

                if (SerializationAssembly)
                {
                    processors.Add(new AssemblyScanProcessor());
                    processors.Add(new SerializationProcessor());
                }

                if (ModuleInitializer)
                {
                    processors.Add(new ModuleInitializerProcessor());
                }

                processors.Add(new InitLocalsProcessor());
                processors.Add(new DispatcherProcessor());
                processors.Add(new OpenSourceSignProcessor());

                // Check if there is already a AssemblyProcessedAttribute (in which case we can skip processing, it has already been done).
                // Note that we should probably also match the command line as well so that we throw an error if processing is different (need to rebuild).
                if (
                    assemblyDefinition.CustomAttributes.Any(
                        x => x.AttributeType.FullName == "Xenko.Core.AssemblyProcessedAttribute"))
                {
                    OnInfoAction($"Assembly [{assemblyDefinition.Name}] has already been processed, skip it.");
                    return(true);
                }

                // Register references so that our assembly resolver can use them
                foreach (var reference in References)
                {
                    assemblyResolver.RegisterReference(reference);
                }

                var assemblyProcessorContext = new AssemblyProcessorContext(assemblyResolver, assemblyDefinition,
                                                                            Platform, log);

                foreach (var processor in processors)
                {
                    modified = processor.Process(assemblyProcessorContext) || modified;
                }

                // Assembly might have been recreated (i.e. il-repack), so let's use it from now on
                assemblyDefinition = assemblyProcessorContext.Assembly;

                if (modified)
                {
                    // In case assembly has been modified,
                    // add AssemblyProcessedAttribute to assembly so that it doesn't get processed again
                    var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition);
                    if (mscorlibAssembly == null)
                    {
                        OnErrorAction("Missing reference to mscorlib.dll or System.Runtime.dll in assembly!");
                        return(false);
                    }

                    var attributeType    = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Attribute).FullName);
                    var attributeTypeRef = assemblyDefinition.MainModule.ImportReference(attributeType);
                    var attributeCtorRef =
                        assemblyDefinition.MainModule.ImportReference(
                            attributeType.GetConstructors().Single(x => x.Parameters.Count == 0));
                    var voidType = assemblyDefinition.MainModule.TypeSystem.Void;

                    // Create custom attribute
                    var assemblyProcessedAttributeType = new TypeDefinition("Xenko.Core",
                                                                            "AssemblyProcessedAttribute",
                                                                            TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass |
                                                                            TypeAttributes.Public, attributeTypeRef);

                    // Add constructor (call parent constructor)
                    var assemblyProcessedAttributeConstructor = new MethodDefinition(".ctor",
                                                                                     MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig |
                                                                                     MethodAttributes.Public, voidType);
                    assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                    assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Call,
                                                                                                   attributeCtorRef));
                    assemblyProcessedAttributeConstructor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                    assemblyProcessedAttributeType.Methods.Add(assemblyProcessedAttributeConstructor);

                    // Add AssemblyProcessedAttribute to assembly
                    assemblyDefinition.MainModule.Types.Add(assemblyProcessedAttributeType);
                    assemblyDefinition.CustomAttributes.Add(new CustomAttribute(assemblyProcessedAttributeConstructor));
                }
            }
            catch (Exception e)
            {
                OnErrorAction(null, e);
                return(false);
            }
            finally
            {
            }

            return(true);
        }
Example #5
0
        /// <summary>
        /// Generates serializer code using Cecil.
        /// Note: we might want something more fluent? (probably lot of work to get the system working, but would make changes easier to do -- not sure if worth it considering it didn't change much recently)
        /// </summary>
        /// <param name="registry"></param>
        private static void GenerateSerializerCode(ComplexSerializerRegistry registry)
        {
            var assembly        = registry.Assembly;
            var xenkoCoreModule = assembly.GetXenkoCoreModule();

            var dataSerializerTypeRef              = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Serialization.DataSerializer`1"));
            var serializerSelectorType             = xenkoCoreModule.GetType("Xenko.Core.Serialization.SerializerSelector");
            var serializerSelectorTypeRef          = assembly.MainModule.ImportReference(serializerSelectorType);
            var serializerSelectorGetSerializerRef = assembly.MainModule.ImportReference(serializerSelectorType.Methods.Single(x => x.Name == "GetSerializer" && x.Parameters.Count == 0 && x.GenericParameters.Count == 1));
            var memberSerializerCreateRef          = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Serialization.MemberSerializer`1").Methods.Single(x => x.Name == "Create"));

            var dataSerializerSerializeMethod    = dataSerializerTypeRef.Resolve().Methods.Single(x => x.Name == "Serialize" && (x.Attributes & MethodAttributes.Abstract) != 0);
            var dataSerializerSerializeMethodRef = assembly.MainModule.ImportReference(dataSerializerSerializeMethod);

            // Generate serializer code for each type (we generate code similar to ComplexClassSerializerGenerator.tt, see this file for reference)
            foreach (var complexType in registry.Context.ComplexTypes)
            {
                var type              = complexType.Key;
                var serializerType    = (TypeDefinition)complexType.Value.SerializerType;
                var genericParameters = serializerType.GenericParameters.ToArray <TypeReference>();
                var typeWithGenerics  = type.MakeGenericType(genericParameters);

                TypeReference   parentType            = null;
                FieldDefinition parentSerializerField = null;
                if (complexType.Value.ComplexSerializerProcessParentType)
                {
                    parentType = ResolveGenericsVisitor.Process(serializerType, type.BaseType);
                    serializerType.Fields.Add(parentSerializerField = new FieldDefinition("parentSerializer", Mono.Cecil.FieldAttributes.Private, dataSerializerTypeRef.MakeGenericType(parentType)));
                }

                var serializableItems     = ComplexSerializerRegistry.GetSerializableItems(type, true).ToArray();
                var serializableItemInfos = new Dictionary <TypeReference, (FieldDefinition SerializerField, TypeReference Type)>(TypeReferenceEqualityComparer.Default);
                var localsByTypes         = new Dictionary <TypeReference, VariableDefinition>(TypeReferenceEqualityComparer.Default);

                ResolveGenericsVisitor genericResolver = null;
                if (type.HasGenericParameters)
                {
                    var genericMapping = new Dictionary <TypeReference, TypeReference>();
                    for (int i = 0; i < type.GenericParameters.Count; i++)
                    {
                        genericMapping[type.GenericParameters[i]] = serializerType.GenericParameters[i];
                    }
                    genericResolver = new ResolveGenericsVisitor(genericMapping);
                }

                foreach (var serializableItem in serializableItems)
                {
                    if (serializableItemInfos.ContainsKey(serializableItem.Type))
                    {
                        continue;
                    }

                    var serializableItemType = serializableItem.Type;
                    if (genericResolver != null)
                    {
                        serializableItemType = genericResolver.VisitDynamic(serializableItemType);
                    }
                    var fieldDefinition = new FieldDefinition($"{Utilities.BuildValidClassName(serializableItemType.FullName)}Serializer", Mono.Cecil.FieldAttributes.Private, dataSerializerTypeRef.MakeGenericType(serializableItemType));
                    serializableItemInfos.Add(serializableItem.Type, (fieldDefinition, serializableItemType));
                    serializerType.Fields.Add(fieldDefinition);
                }

                // Add constructor (call parent constructor)
                var ctor = new MethodDefinition(".ctor",
                                                MethodAttributes.RTSpecialName | MethodAttributes.SpecialName | MethodAttributes.HideBySig |
                                                MethodAttributes.Public, assembly.MainModule.TypeSystem.Void);
                ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(serializerType.BaseType.Resolve().GetEmptyConstructor(true)).MakeGeneric(typeWithGenerics)));
                ctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                serializerType.Methods.Add(ctor);

                // Add Initialize method
                var initialize = new MethodDefinition("Initialize", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, assembly.MainModule.TypeSystem.Void);
                initialize.Parameters.Add(new ParameterDefinition("serializerSelector", ParameterAttributes.None, serializerSelectorTypeRef));
                if (complexType.Value.ComplexSerializerProcessParentType)
                {
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, serializerSelectorGetSerializerRef.MakeGenericMethod(parentType)));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, parentSerializerField.MakeGeneric(genericParameters)));
                }
                foreach (var serializableItem in serializableItemInfos)
                {
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Call, memberSerializerCreateRef.MakeGeneric(serializableItem.Value.Type)));
                    initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, serializableItem.Value.SerializerField.MakeGeneric(genericParameters)));
                }
                initialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                serializerType.Methods.Add(initialize);

                // Add Serialize method
                var serialize = new MethodDefinition("Serialize", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual, assembly.MainModule.TypeSystem.Void);
                serialize.Parameters.Add(new ParameterDefinition("obj", ParameterAttributes.None, typeWithGenerics.MakeByReferenceType()));
                // Copy other parameters from parent method
                for (int i = 1; i < dataSerializerSerializeMethod.Parameters.Count; ++i)
                {
                    var parentParameter = dataSerializerSerializeMethod.Parameters[i];
                    serialize.Parameters.Add(new ParameterDefinition(parentParameter.Name, ParameterAttributes.None, assembly.MainModule.ImportReference(parentParameter.ParameterType)));
                }

                if (complexType.Value.ComplexSerializerProcessParentType)
                {
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, parentSerializerField.MakeGeneric(genericParameters)));
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2));
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_3));
                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, dataSerializerSerializeMethodRef.MakeGeneric(parentType)));
                }

                if (serializableItems.Length > 0)
                {
                    var blockStartInstructions = new[] { Instruction.Create(OpCodes.Nop), Instruction.Create(OpCodes.Nop) };
                    // Iterate over ArchiveMode
                    for (int i = 0; i < 2; ++i)
                    {
                        var archiveMode = i == 0 ? ArchiveMode.Serialize : ArchiveMode.Deserialize;

                        // Check mode
                        if (archiveMode == ArchiveMode.Serialize)
                        {
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2));
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldc_I4, (int)archiveMode));
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ceq));
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Brfalse, blockStartInstructions[0]));
                        }
                        else
                        {
                            serialize.Body.Instructions.Add(blockStartInstructions[0]);
                        }

                        foreach (var serializableItem in serializableItems)
                        {
                            if (serializableItem.HasFixedAttribute)
                            {
                                throw new NotImplementedException("FixedBuffer attribute is not supported.");
                            }

                            var memberAssignBack     = serializableItem.AssignBack;
                            var memberVariableName   = (serializableItem.MemberInfo is PropertyDefinition || !memberAssignBack) ? ComplexSerializerRegistry.CreateMemberVariableName(serializableItem.MemberInfo) : null;
                            var serializableItemInfo = serializableItemInfos[serializableItem.Type];
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_0));
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, serializableItemInfo.SerializerField.MakeGeneric(genericParameters)));

                            var fieldReference = serializableItem.MemberInfo is FieldReference?assembly.MainModule.ImportReference((FieldReference)serializableItem.MemberInfo).MakeGeneric(genericParameters) : null;

                            if (memberVariableName != null)
                            {
                                // Use a temporary variable
                                if (!localsByTypes.TryGetValue(serializableItemInfo.Type, out var tempLocal))
                                {
                                    tempLocal = new VariableDefinition(serializableItemInfo.Type);
                                    localsByTypes.Add(serializableItemInfo.Type, tempLocal);
                                    serialize.Body.Variables.Add(tempLocal);
                                    serialize.Body.InitLocals = true;
                                }

                                if (!(archiveMode == ArchiveMode.Deserialize && memberAssignBack))
                                {
                                    // obj.Member
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                                    if (!type.IsValueType)
                                    {
                                        serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref));
                                    }

                                    if (serializableItem.MemberInfo is PropertyDefinition property)
                                    {
                                        var getMethod = property.Resolve().GetMethod;
                                        serialize.Body.Instructions.Add(Instruction.Create(getMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, assembly.MainModule.ImportReference(getMethod).MakeGeneric(genericParameters)));
                                    }
                                    else if (serializableItem.MemberInfo is FieldDefinition)
                                    {
                                        serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldfld, fieldReference));
                                    }
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stloc, tempLocal));
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloca, tempLocal));
                                }
                                else
                                {
                                    // default(T)
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloca, tempLocal));
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Dup));
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Initobj, serializableItemInfo.Type));
                                }
                            }
                            else
                            {
                                // Use object directly
                                serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                                if (!type.IsValueType)
                                {
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref));
                                }
                                serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldflda, fieldReference));
                            }
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_2));
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_3));

                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Callvirt, dataSerializerSerializeMethodRef.MakeGeneric(serializableItemInfo.Type)));

                            if (archiveMode == ArchiveMode.Deserialize && memberVariableName != null && memberAssignBack)
                            {
                                // Need to copy back to object
                                serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldarg_1));
                                if (!type.IsValueType)
                                {
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldind_Ref));
                                }

                                serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ldloc, localsByTypes[serializableItemInfo.Type]));

                                if (serializableItem.MemberInfo is PropertyDefinition property)
                                {
                                    var setMethod = property.Resolve().SetMethod;
                                    serialize.Body.Instructions.Add(Instruction.Create(setMethod.IsVirtual ? OpCodes.Callvirt : OpCodes.Call, assembly.MainModule.ImportReference(setMethod).MakeGeneric(genericParameters)));
                                }
                                else if (serializableItem.MemberInfo is FieldDefinition)
                                {
                                    serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Stfld, fieldReference));
                                }
                            }
                        }

                        if (archiveMode == ArchiveMode.Serialize)
                        {
                            serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Br, blockStartInstructions[1]));
                        }
                    }

                    serialize.Body.Instructions.Add(blockStartInstructions[1]);
                }
                serialize.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                serializerType.Methods.Add(serialize);

                //assembly.MainModule.Types.Add(serializerType);
            }

            var mscorlibAssembly   = CecilExtensions.FindCorlibAssembly(assembly);
            var reflectionAssembly = CecilExtensions.FindReflectionAssembly(assembly);

            // Type
            var typeType                = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
            var typeTypeRef             = assembly.MainModule.ImportReference(typeType);
            var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == nameof(Type.GetTypeFromHandle));
            var getTokenInfoExMethod    = reflectionAssembly.MainModule.GetTypeResolved("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == nameof(IntrospectionExtensions.GetTypeInfo));
            var typeInfoType            = reflectionAssembly.MainModule.GetTypeResolved(typeof(TypeInfo).FullName);
            // Note: TypeInfo.Assembly/Module could be on the type itself or on its parent MemberInfo depending on runtime
            var getTypeInfoAssembly    = typeInfoType.Properties.Concat(typeInfoType.BaseType.Resolve().Properties).First(x => x.Name == nameof(TypeInfo.Assembly)).GetMethod;
            var getTypeInfoModule      = typeInfoType.Properties.Concat(typeInfoType.BaseType.Resolve().Properties).First(x => x.Name == nameof(TypeInfo.Module)).GetMethod;
            var typeHandleProperty     = typeType.Properties.First(x => x.Name == nameof(Type.TypeHandle));
            var getTypeHandleMethodRef = assembly.MainModule.ImportReference(typeHandleProperty.GetMethod);

            // Generate code
            var serializerFactoryType = new TypeDefinition("Xenko.Core.DataSerializers",
                                                           Utilities.BuildValidClassName(assembly.Name.Name) + "SerializerFactory",
                                                           TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass |
                                                           TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract,
                                                           assembly.MainModule.TypeSystem.Object);

            assembly.MainModule.Types.Add(serializerFactoryType);

            var dataSerializerModeTypeRef = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Serialization.DataSerializerGenericMode"));

            var dataSerializerGlobalAttribute = xenkoCoreModule.GetType("Xenko.Core.Serialization.DataSerializerGlobalAttribute");
            var dataSerializerGlobalCtorRef   = assembly.MainModule.ImportReference(dataSerializerGlobalAttribute.GetConstructors().Single(x => !x.IsStatic && x.Parameters.Count == 5));

            foreach (var profile in registry.Context.SerializableTypesProfiles)
            {
                foreach (var type in profile.Value.SerializableTypes.Where(x => x.Value.Local))
                {
                    // Generating: [DataSerializerGlobalAttribute(<#= type.Value.SerializerType != null ? $"typeof({type.Value.SerializerType.ConvertCSharp(false)})" : "null" #>, typeof(<#= type.Key.ConvertCSharp(false) #>), DataSerializerGenericMode.<#= type.Value.Mode.ToString() #>, <#=type.Value.Inherited ? "true" : "false"#>, <#=type.Value.ComplexSerializer ? "true" : "false"#>, Profile = "<#=profile.Key#>")]
                    serializerFactoryType.CustomAttributes.Add(new CustomAttribute(dataSerializerGlobalCtorRef)
                    {
                        ConstructorArguments =
                        {
                            new CustomAttributeArgument(typeTypeRef,                            type.Value.SerializerType != null ? assembly.MainModule.ImportReference(type.Value.SerializerType) : null),
                            new CustomAttributeArgument(typeTypeRef,                            assembly.MainModule.ImportReference(type.Key)),
                            new CustomAttributeArgument(dataSerializerModeTypeRef,              type.Value.Mode),
                            new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.Inherited),
                            new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.ComplexSerializer),
                        },
                        Properties =
                        {
                            new CustomAttributeNamedArgument("Profile", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, profile.Key))
                        },
                    });
                }
                foreach (var type in profile.Value.GenericSerializableTypes.Where(x => x.Value.Local))
                {
                    // Generating: [DataSerializerGlobalAttribute(<#= type.Value.SerializerType != null ? $"typeof({type.Value.SerializerType.ConvertCSharp(true)})" : "null" #>, typeof(<#= type.Key.ConvertCSharp(true) #>), DataSerializerGenericMode.<#= type.Value.Mode.ToString() #>, <#=type.Value.Inherited ? "true" : "false"#>, <#=type.Value.ComplexSerializer ? "true" : "false"#>, Profile = "<#=profile.Key#>")]
                    serializerFactoryType.CustomAttributes.Add(new CustomAttribute(dataSerializerGlobalCtorRef)
                    {
                        ConstructorArguments =
                        {
                            new CustomAttributeArgument(typeTypeRef,                            type.Value.SerializerType != null ? assembly.MainModule.ImportReference(type.Value.SerializerType) : null),
                            new CustomAttributeArgument(typeTypeRef,                            assembly.MainModule.ImportReference(type.Key)),
                            new CustomAttributeArgument(dataSerializerModeTypeRef,              type.Value.Mode),
                            new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.Inherited),
                            new CustomAttributeArgument(assembly.MainModule.TypeSystem.Boolean, type.Value.ComplexSerializer),
                        },
                        Properties =
                        {
                            new CustomAttributeNamedArgument("Profile", new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, profile.Key))
                        },
                    });
                }
            }

            // Create Initialize method
            var initializeMethod = new MethodDefinition("Initialize",
                                                        MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Static,
                                                        assembly.MainModule.TypeSystem.Void);

            serializerFactoryType.Methods.Add(initializeMethod);

            // Make sure it is called at module startup
            initializeMethod.AddModuleInitializer(-1000);

            var initializeMethodIL = initializeMethod.Body.GetILProcessor();

            // Generating: var assemblySerializers = new AssemblySerializers(typeof(<#=registry.ClassName#>).GetTypeInfo().Assembly);
            var assemblySerializersType = xenkoCoreModule.GetType("Xenko.Core.Serialization.AssemblySerializers");

            var assemblySerializersGetDataContractAliasesRef = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "DataContractAliases").GetMethod);
            var assemblySerializersGetDataContractAliasesAdd = assemblySerializersGetDataContractAliasesRef.ReturnType.Resolve().Methods.First(x => x.Name == "Add");
            var dataContractAliasTypeRef     = ((GenericInstanceType)assemblySerializersGetDataContractAliasesRef.ReturnType).GenericArguments[0];
            var dataContractAliasTypeCtorRef = assembly.MainModule.ImportReference(dataContractAliasTypeRef.Resolve().GetConstructors().Single());
            var assemblySerializersGetDataContractAliasesAddRef = assembly.MainModule.ImportReference(assemblySerializersGetDataContractAliasesAdd).MakeGeneric(dataContractAliasTypeRef);

            initializeMethodIL.Emit(OpCodes.Ldtoken, serializerFactoryType);
            initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
            initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod));
            initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly));
            initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(assemblySerializersType.Methods.Single(x => x.IsConstructor && x.Parameters.Count == 1)));

            foreach (var alias in registry.Context.DataContractAliases)
            {
                initializeMethodIL.Emit(OpCodes.Dup);

                // Generating: assemblySerializers.DataContractAliases.Add(new AssemblySerializers.DataContractAlias(@"<#= alias.Item1 #>", typeof(<#= alias.Item2.ConvertCSharp(true) #>), <#=alias.Item3 ? "true" : "false"#>));
                initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetDataContractAliasesRef);
                initializeMethodIL.Emit(OpCodes.Ldstr, alias.Item1);
                initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(alias.Item2));
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
                initializeMethodIL.Emit(alias.Item3 ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0);
                initializeMethodIL.Emit(OpCodes.Newobj, dataContractAliasTypeCtorRef);
                initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetDataContractAliasesAddRef);
            }

            var assemblySerializersGetModulesRef = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "Modules").GetMethod);
            var assemblySerializersGetModulesAdd = assemblySerializersGetModulesRef.ReturnType.Resolve().Methods.First(x => x.Name == "Add");
            var moduleRef = ((GenericInstanceType)assemblySerializersGetModulesRef.ReturnType).GenericArguments[0];
            var assemblySerializersGetModulesAddRef = assembly.MainModule.ImportReference(assemblySerializersGetModulesAdd).MakeGeneric(moduleRef);

            foreach (var referencedAssemblySerializerFactoryType in registry.ReferencedAssemblySerializerFactoryTypes)
            {
                initializeMethodIL.Emit(OpCodes.Dup);

                // Generating: assemblySerializers.Modules.Add(typeof(<#=referencedAssemblySerializerFactoryType.ConvertCSharp()#>).GetTypeInfo().Module);
                initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetModulesRef);
                initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(referencedAssemblySerializerFactoryType));
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod));
                initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoModule));
                initializeMethodIL.Emit(OpCodes.Call, assemblySerializersGetModulesAddRef);
            }

            var objectIdCtorRef                          = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Storage.ObjectId").GetConstructors().Single(x => x.Parameters.Count == 4));
            var serializerEntryTypeCtorRef               = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Serialization.AssemblySerializerEntry").GetConstructors().Single());
            var assemblySerializersPerProfileType        = xenkoCoreModule.GetType("Xenko.Core.Serialization.AssemblySerializersPerProfile");
            var assemblySerializersPerProfileTypeAddRef  = assembly.MainModule.ImportReference(assemblySerializersPerProfileType.BaseType.Resolve().Methods.First(x => x.Name == "Add")).MakeGeneric(serializerEntryTypeCtorRef.DeclaringType);
            var assemblySerializersPerProfileTypeCtorRef = assembly.MainModule.ImportReference(assemblySerializersPerProfileType.GetEmptyConstructor());
            var assemblySerializersGetProfilesRef        = assembly.MainModule.ImportReference(assemblySerializersType.Properties.First(x => x.Name == "Profiles").GetMethod);
            var assemblySerializersGetProfilesSetItemRef = assembly.MainModule.ImportReference(assemblySerializersGetProfilesRef.ReturnType.Resolve().Methods.First(x => x.Name == "set_Item"))
                                                           .MakeGeneric(((GenericInstanceType)assemblySerializersGetProfilesRef.ReturnType).GenericArguments.ToArray());

            var runtimeHelpersType        = mscorlibAssembly.MainModule.GetTypeResolved(typeof(RuntimeHelpers).FullName);
            var runClassConstructorMethod = assembly.MainModule.ImportReference(runtimeHelpersType.Methods.Single(x => x.IsPublic && x.Name == "RunClassConstructor" && x.Parameters.Count == 1 && x.Parameters[0].ParameterType.FullName == typeof(RuntimeTypeHandle).FullName));

            foreach (var profile in registry.Context.SerializableTypesProfiles)
            {
                initializeMethodIL.Emit(OpCodes.Dup);

                // Generating: var assemblySerializersProfile = new AssemblySerializersPerProfile();
                // Generating: assemblySerializers.Profiles["<#=profile.Key#>"] = assemblySerializersProfile;
                initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersGetProfilesRef);
                initializeMethodIL.Emit(OpCodes.Ldstr, profile.Key);
                initializeMethodIL.Emit(OpCodes.Newobj, assemblySerializersPerProfileTypeCtorRef);

                foreach (var type in profile.Value.SerializableTypes.Where(x => x.Value.Local))
                {
                    // Generating: assemblySerializersProfile.Add(new AssemblySerializerEntry(<#=type.Key.ConvertTypeId()#>, typeof(<#= type.Key.ConvertCSharp() #>), <# if (type.Value.SerializerType != null) { #>typeof(<#= type.Value.SerializerType.ConvertCSharp() #>)<# } else { #>null<# } #>));
                    initializeMethodIL.Emit(OpCodes.Dup);

                    var typeName = type.Key.ConvertCSharp(false);
                    var typeId   = ObjectId.FromBytes(Encoding.UTF8.GetBytes(typeName));

                    unsafe
                    {
                        var typeIdHash = (int *)&typeId;

                        for (int i = 0; i < ObjectId.HashSize / 4; ++i)
                        {
                            initializeMethodIL.Emit(OpCodes.Ldc_I4, typeIdHash[i]);
                        }
                    }

                    initializeMethodIL.Emit(OpCodes.Newobj, objectIdCtorRef);

                    initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(type.Key));
                    initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));

                    if (type.Value.SerializerType != null)
                    {
                        initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(type.Value.SerializerType));
                        initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
                    }
                    else
                    {
                        initializeMethodIL.Emit(OpCodes.Ldnull);
                    }

                    initializeMethodIL.Emit(OpCodes.Newobj, serializerEntryTypeCtorRef);
                    initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersPerProfileTypeAddRef);

                    if (type.Value.SerializerType?.Resolve()?.Methods.Any(x => x.IsConstructor && x.IsStatic) == true)
                    {
                        // Generating: System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(<#=type.Value.SerializerType.ConvertCSharp()#>).TypeHandle);
                        initializeMethodIL.Append(Instruction.Create(OpCodes.Ldtoken, type.Value.SerializerType));
                        initializeMethodIL.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)));
                        initializeMethodIL.Append(Instruction.Create(OpCodes.Callvirt, getTypeHandleMethodRef));
                        initializeMethodIL.Append(Instruction.Create(OpCodes.Call, runClassConstructorMethod));
                    }
                }

                initializeMethodIL.Emit(OpCodes.Callvirt, assemblySerializersGetProfilesSetItemRef);
            }

            // Generating: DataSerializerFactory.RegisterSerializationAssembly(assemblySerializers);
            var dataSerializerFactoryRegisterSerializationAssemblyMethodRef = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Serialization.DataSerializerFactory").Methods.Single(x => x.Name == "RegisterSerializationAssembly" && x.Parameters[0].ParameterType.FullName == assemblySerializersType.FullName));

            initializeMethodIL.Emit(OpCodes.Call, dataSerializerFactoryRegisterSerializationAssemblyMethodRef);

            // Generating: AssemblyRegistry.Register(typeof(<#=registry.ClassName#>).GetTypeInfo().Assembly, AssemblyCommonCategories.Engine);
            initializeMethodIL.Emit(OpCodes.Ldtoken, serializerFactoryType);
            initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
            initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod));
            initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly));

            // create new[] { AssemblyCommonCategories.Engine }
            initializeMethodIL.Emit(OpCodes.Ldc_I4_1);
            initializeMethodIL.Emit(OpCodes.Newarr, assembly.MainModule.TypeSystem.String);
            initializeMethodIL.Emit(OpCodes.Dup);
            initializeMethodIL.Emit(OpCodes.Ldc_I4_0);
            initializeMethodIL.Emit(OpCodes.Ldstr, Core.Reflection.AssemblyCommonCategories.Engine);
            initializeMethodIL.Emit(OpCodes.Stelem_Ref);

            var assemblyRegistryRegisterMethodRef = assembly.MainModule.ImportReference(xenkoCoreModule.GetType("Xenko.Core.Reflection.AssemblyRegistry").Methods.Single(x => x.Name == "Register" && x.Parameters[1].ParameterType.IsArray));

            initializeMethodIL.Emit(OpCodes.Call, assemblyRegistryRegisterMethodRef);

            initializeMethodIL.Emit(OpCodes.Ret);

            // Add AssemblySerializerFactoryAttribute
            var assemblySerializerFactoryAttribute = xenkoCoreModule.GetType("Xenko.Core.Serialization.AssemblySerializerFactoryAttribute");

            assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.ImportReference(assemblySerializerFactoryAttribute.GetEmptyConstructor()))
            {
                Fields =
                {
                    new CustomAttributeNamedArgument("Type", new CustomAttributeArgument(typeTypeRef, serializerFactoryType))
                }
            });
        }
Example #6
0
        public bool Process(AssemblyProcessorContext context)
        {
            var registry = new AssemblyScanRegistry();

            foreach (var type in context.Assembly.MainModule.GetAllTypes())
            {
                // Ignore interface types as well as types with generics
                // Note: we could support generic types at some point but we probably need
                //       to get static generic instantiation type list from serializer code generator
                if (type.IsInterface || type.HasGenericParameters)
                {
                    continue;
                }

                var currentType = type;
                // Scan type and parent types
                while (currentType != null)
                {
                    // Scan interfaces
                    foreach (var @interface in currentType.Interfaces)
                    {
                        ScanAttributes(context.Log, registry, @interface.InterfaceType, type);
                    }

                    ScanAttributes(context.Log, registry, currentType, type);
                    currentType = currentType.BaseType?.Resolve();
                }
            }

            if (registry.HasScanTypes)
            {
                // This code should mirror what AssemblyScanCodeGenerator.tt generates
                var assembly = context.Assembly;

                var xenkoCoreModule      = assembly.GetXenkoCoreModule();
                var assemblyRegistryType = xenkoCoreModule.GetType("Xenko.Core.Reflection.AssemblyRegistry");

                // Generate code
                var assemblyScanType = new TypeDefinition("Xenko.Core.Serialization.AssemblyScan",
                                                          Utilities.BuildValidClassName(assembly.Name.Name) + "AssemblyScan",
                                                          TypeAttributes.BeforeFieldInit | TypeAttributes.AnsiClass | TypeAttributes.AutoClass |
                                                          TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.Abstract,
                                                          assembly.MainModule.TypeSystem.Object);
                assembly.MainModule.Types.Add(assemblyScanType);

                // Create Initialize method
                var initializeMethod = new MethodDefinition("Initialize",
                                                            MethodAttributes.Assembly | MethodAttributes.HideBySig | MethodAttributes.Static,
                                                            assembly.MainModule.TypeSystem.Void);
                assemblyScanType.Methods.Add(initializeMethod);

                // Make sure it is called at module startup
                initializeMethod.AddModuleInitializer(-2000);

                var mscorlibAssembly   = CecilExtensions.FindCorlibAssembly(assembly);
                var collectionAssembly = CecilExtensions.FindCollectionsAssembly(assembly);
                var reflectionAssembly = CecilExtensions.FindReflectionAssembly(assembly);

                // Type
                var typeType                = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
                var typeTypeRef             = assembly.MainModule.ImportReference(typeType);
                var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == nameof(Type.GetTypeFromHandle));
                var getTokenInfoExMethod    = reflectionAssembly.MainModule.GetTypeResolved("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == nameof(IntrospectionExtensions.GetTypeInfo));
                var typeInfoType            = reflectionAssembly.MainModule.GetTypeResolved(typeof(TypeInfo).FullName);
                // Note: TypeInfo.Assembly/Module could be on the type itself or on its parent MemberInfo depending on runtime
                var getTypeInfoAssembly = typeInfoType.Properties.Concat(typeInfoType.BaseType.Resolve().Properties).First(x => x.Name == nameof(TypeInfo.Assembly)).GetMethod;

                // List<Type>
                var listType        = collectionAssembly.MainModule.GetTypeResolved(typeof(List <>).FullName);
                var listTypeTypeRef = assembly.MainModule.ImportReference(listType).MakeGenericType(typeTypeRef);
                // Dictionary<Type, List<Type>>
                var dictionaryType = collectionAssembly.MainModule.GetType(typeof(Dictionary <,>).FullName);

                var initializeMethodIL = initializeMethod.Body.GetILProcessor();

                // AssemblyRegistry.RegisterScanTypes(typeof(AssemblyScanType).GetTypeInfo().Assembly, dictionary);
                initializeMethodIL.Emit(OpCodes.Ldtoken, assemblyScanType);
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod));
                initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(getTypeInfoAssembly));

                // dictionary = new Dictionary<Type, List<Type>>();
                initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(dictionaryType.GetEmptyConstructor()).MakeGeneric(typeTypeRef, listTypeTypeRef));
                foreach (var scanTypeEntry in registry.ScanTypes)
                {
                    initializeMethodIL.Emit(OpCodes.Dup);

                    // typeof(X)
                    initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(scanTypeEntry.Key.Resolve()));
                    initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));

                    // new List<Type>();
                    initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(listType.GetEmptyConstructor()).MakeGeneric(typeTypeRef));

                    foreach (var scanType in scanTypeEntry.Value)
                    {
                        initializeMethodIL.Emit(OpCodes.Dup);
                        initializeMethodIL.Emit(OpCodes.Ldtoken, assembly.MainModule.ImportReference(scanType));
                        initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod));
                        initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(listType.Methods.First(x => x.Name == "Add")).MakeGeneric(typeTypeRef));
                    }

                    initializeMethodIL.Emit(OpCodes.Callvirt, assembly.MainModule.ImportReference(dictionaryType.Methods.First(x => x.Name == "Add")).MakeGeneric(typeTypeRef, listTypeTypeRef));
                }

                initializeMethodIL.Emit(OpCodes.Newobj, assembly.MainModule.ImportReference(assemblyRegistryType.NestedTypes.First(x => x.Name == "ScanTypes").Methods.Single(x => x.IsConstructor && x.Parameters.Count == 1)));
                initializeMethodIL.Emit(OpCodes.Call, assembly.MainModule.ImportReference(assemblyRegistryType.Methods.First(x => x.Name == "RegisterScanTypes")));

                initializeMethodIL.Emit(OpCodes.Ret);

                //var assemblyScanCodeGenerator = new AssemblyScanCodeGenerator(assembly, registry);
                //sourceCodeRegisterAction(assemblyScanCodeGenerator.TransformText(), "AssemblyScan");
            }

            return(registry.HasScanTypes);
        }
Example #7
0
        public void ProcessSerializers(CecilSerializerContext context)
        {
            var references = new HashSet <AssemblyDefinition>();

            EnumerateReferences(references, context.Assembly);

            var coreAssembly = CecilExtensions.FindCorlibAssembly(context.Assembly);

            // Only process assemblies depending on Xenko.Engine
            if (!references.Any(x => x.Name.Name == "Xenko.Engine"))
            {
                // Make sure Xenko.Engine.Serializers can access everything internally
                var internalsVisibleToAttribute = coreAssembly.MainModule.GetTypeResolved(typeof(InternalsVisibleToAttribute).FullName);
                var serializationAssemblyName   = "Xenko.Engine.Serializers";

                // Add [InteralsVisibleTo] attribute
                var internalsVisibleToAttributeCtor = context.Assembly.MainModule.ImportReference(internalsVisibleToAttribute.GetConstructors().Single());
                var internalsVisibleAttribute       = new CustomAttribute(internalsVisibleToAttributeCtor)
                {
                    ConstructorArguments =
                    {
                        new CustomAttributeArgument(context.Assembly.MainModule.ImportReference(context.Assembly.MainModule.TypeSystem.String), serializationAssemblyName)
                    }
                };
                context.Assembly.CustomAttributes.Add(internalsVisibleAttribute);

                return;
            }



            var xenkoEngineAssembly = context.Assembly.Name.Name == "Xenko.Engine"
                    ? context.Assembly
                    : context.Assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Xenko.Engine", null));
            var xenkoEngineModule = xenkoEngineAssembly.MainModule;

            // Generate IL for Xenko.Core
            if (context.Assembly.Name.Name == "Xenko.Engine")
            {
                ProcessXenkoEngineAssembly(context);
            }

            var updatableFieldGenericType = xenkoEngineModule.GetType("Xenko.Updater.UpdatableField`1");

            updatableFieldGenericCtor = updatableFieldGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic);

            updatablePropertyGenericType = xenkoEngineModule.GetType("Xenko.Updater.UpdatableProperty`1");
            updatablePropertyGenericCtor = updatablePropertyGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic);

            var updatablePropertyObjectGenericType = xenkoEngineModule.GetType("Xenko.Updater.UpdatablePropertyObject`1");

            updatablePropertyObjectGenericCtor = updatablePropertyObjectGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic);

            var updatableListUpdateResolverGenericType = xenkoEngineModule.GetType("Xenko.Updater.ListUpdateResolver`1");

            updatableListUpdateResolverGenericCtor = updatableListUpdateResolverGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic);

            var updatableArrayUpdateResolverGenericType = xenkoEngineModule.GetType("Xenko.Updater.ArrayUpdateResolver`1");

            updatableArrayUpdateResolverGenericCtor = updatableArrayUpdateResolverGenericType.Methods.First(x => x.IsConstructor && !x.IsStatic);

            var parameterCollectionResolver = xenkoEngineModule.GetType("Xenko.Engine.Design.ParameterCollectionResolver");

            parameterCollectionResolverInstantiateValueAccessor = parameterCollectionResolver.Methods.First(x => x.Name == "InstantiateValueAccessor");

            var registerMemberMethod = xenkoEngineModule.GetType("Xenko.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMember");

            updateEngineRegisterMemberMethod = context.Assembly.MainModule.ImportReference(registerMemberMethod);

            var registerMemberResolverMethod = xenkoEngineModule.GetType("Xenko.Updater.UpdateEngine").Methods.First(x => x.Name == "RegisterMemberResolver");

            //pclVisitor.VisitMethod(registerMemberResolverMethod);
            updateEngineRegisterMemberResolverMethod = context.Assembly.MainModule.ImportReference(registerMemberResolverMethod);

            var typeType = coreAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);

            getTypeFromHandleMethod = context.Assembly.MainModule.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle"));

            var mainPrepareMethod = CreateUpdateMethod(context.Assembly);

            // Emit serialization code for all the types we care about
            var processedTypes = new HashSet <TypeDefinition>(TypeReferenceEqualityComparer.Default);

            foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes))
            {
                // Special case: when processing Xenko.Engine assembly, we automatically add dependent assemblies types too
                if (!serializableType.Value.Local && xenkoEngineAssembly != context.Assembly)
                {
                    continue;
                }

                var typeDefinition = serializableType.Key as TypeDefinition;
                if (typeDefinition == null)
                {
                    continue;
                }

                // Ignore already processed types
                if (!processedTypes.Add(typeDefinition))
                {
                    continue;
                }

                try
                {
                    ProcessType(context, context.Assembly.MainModule.ImportReference(typeDefinition), mainPrepareMethod);
                }
                catch (Exception e)
                {
                    throw new InvalidOperationException(string.Format("Error when generating update engine code for {0}", typeDefinition), e);
                }
            }

            // Force generic instantiations
            var il = mainPrepareMethod.Body.GetILProcessor();

            foreach (var serializableType in context.SerializableTypesProfiles.SelectMany(x => x.Value.SerializableTypes).ToArray())
            {
                // Special case: when processing Xenko.Engine assembly, we automatically add dependent assemblies types too
                if (!serializableType.Value.Local && xenkoEngineAssembly != context.Assembly)
                {
                    continue;
                }

                // Try to find if original method definition was generated
                var typeDefinition = serializableType.Key.Resolve();

                // If using List<T>, register this type in UpdateEngine
                var parentTypeDefinition = typeDefinition;
                while (parentTypeDefinition != null)
                {
                    var listInterfaceType = parentTypeDefinition.Interfaces.Select(x => x.InterfaceType).OfType <GenericInstanceType>().FirstOrDefault(x => x.ElementType.FullName == typeof(IList <>).FullName);
                    if (listInterfaceType != null)
                    {
                        //call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ListUpdateResolver<T>());
                        var elementType = ResolveGenericsVisitor.Process(serializableType.Key, listInterfaceType.GenericArguments[0]);
                        il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableListUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType)));
                        il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
                    }

                    parentTypeDefinition = parentTypeDefinition.BaseType?.Resolve();
                }

                // Same for arrays
                var arrayType = serializableType.Key as ArrayType;
                if (arrayType != null)
                {
                    //call Updater.UpdateEngine.RegisterMemberResolver(new Updater.ArrayUpdateResolver<T>());
                    var elementType = ResolveGenericsVisitor.Process(serializableType.Key, arrayType.ElementType);
                    il.Emit(OpCodes.Newobj, context.Assembly.MainModule.ImportReference(updatableArrayUpdateResolverGenericCtor).MakeGeneric(context.Assembly.MainModule.ImportReference(elementType)));
                    il.Emit(OpCodes.Call, updateEngineRegisterMemberResolverMethod);
                }

                // Generic instantiation for AOT platforms
                if (context.Platform == Core.PlatformType.iOS && serializableType.Key.Name == "ValueParameterKey`1")
                {
                    var keyType = ((GenericInstanceType)serializableType.Key).GenericArguments[0];
                    il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(parameterCollectionResolverInstantiateValueAccessor).MakeGenericMethod(context.Assembly.MainModule.ImportReference(keyType)));
                }

                var genericInstanceType = serializableType.Key as GenericInstanceType;
                if (genericInstanceType != null)
                {
                    var expectedUpdateMethodName = ComputeUpdateMethodName(typeDefinition);
                    var updateMethod             = GetOrCreateUpdateType(typeDefinition.Module.Assembly, false)?.Methods.FirstOrDefault(x => x.Name == expectedUpdateMethodName && x.HasGenericParameters && x.GenericParameters.Count == genericInstanceType.GenericParameters.Count);

                    // If nothing was found in main assembly, also look in Xenko.Engine assembly, just in case (it might defines some shared/corlib types -- currently not the case)
                    if (updateMethod == null)
                    {
                        updateMethod = GetOrCreateUpdateType(xenkoEngineAssembly, false)?.Methods.FirstOrDefault(x => x.Name == expectedUpdateMethodName && x.HasGenericParameters && x.GenericParameters.Count == genericInstanceType.GenericParameters.Count);
                    }

                    if (updateMethod != null)
                    {
                        // Emit call to update engine setup method with generic arguments of current type
                        il.Emit(OpCodes.Call, context.Assembly.MainModule.ImportReference(updateMethod)
                                .MakeGenericMethod(genericInstanceType.GenericArguments
                                                   .Select(context.Assembly.MainModule.ImportReference)
                                                   .ToArray()));
                    }
                }
            }

            il.Emit(OpCodes.Ret);
        }
Example #8
0
        public bool Process(AssemblyProcessorContext context)
        {
            var assembly = context.Assembly;
            var fields   = new List <FieldDefinition>();

            var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);

            if (mscorlibAssembly == null)
            {
                throw new InvalidOperationException("Missing mscorlib.dll from assembly");
            }

            MethodDefinition parameterKeysMergeMethod        = null;
            TypeDefinition   assemblyEffectKeysAttributeType = null;
            var getTypeFromHandleMethod = new Lazy <MethodReference>(() =>
            {
                // Find Type.GetTypeFromHandle
                var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
                return(assembly.MainModule.ImportReference(typeType.Methods.First(x => x.Name == "GetTypeFromHandle")));
            });

            var effectKeysStaticConstructors  = new List <MethodReference>();
            var effectKeysArrayElemementTypes = new HashSet <TypeReference>();

            foreach (var type in assembly.MainModule.GetTypes())
            {
                fields.Clear();

                foreach (var field in type.Fields.Where(x => x.IsStatic))
                {
                    var fieldBaseType = field.FieldType;
                    while (fieldBaseType != null)
                    {
                        if (fieldBaseType.FullName == "Xenko.Rendering.ParameterKey")
                        {
                            break;
                        }

                        // TODO: Get PropertyKey.PropertyType instead
                        var genericInstance = fieldBaseType as GenericInstanceType;
                        if (genericInstance != null && genericInstance.ElementType.FullName == "Xenko.Rendering.ParameterKey`1")
                        {
                            var genericArgument = genericInstance.GenericArguments.First();
                            if (genericArgument.IsArray)
                            {
                                effectKeysArrayElemementTypes.Add(genericArgument.GetElementType());
                            }
                        }

                        var resolvedFieldBaseType = fieldBaseType.Resolve();
                        if (resolvedFieldBaseType == null)
                        {
                            fieldBaseType = null;
                            break;
                        }

                        fieldBaseType = resolvedFieldBaseType.BaseType;
                    }

                    if (fieldBaseType == null)
                    {
                        continue;
                    }

                    fields.Add(field);
                }

                if (fields.Count == 0)
                {
                    continue;
                }

                // ParameterKey present means we should have a static cctor.
                var cctor = type.GetStaticConstructor();
                if (cctor == null)
                {
                    continue;
                }

                // Load necessary Xenko methods/attributes
                if (parameterKeysMergeMethod == null)
                {
                    AssemblyDefinition xenkoEngineAssembly;
                    try
                    {
                        xenkoEngineAssembly = assembly.Name.Name == "Xenko"
                            ? assembly
                            : context.AssemblyResolver.Resolve(new AssemblyNameReference("Xenko", null));
                    }
                    catch (Exception)
                    {
                        context.Log.WriteLine("Error, cannot find [Xenko] assembly for processing ParameterKeyProcessor");
                        // We can't generate an exception, so we are just returning. It means that Xenko has not been generated so far.
                        return(true);
                    }

                    var parameterKeysType = xenkoEngineAssembly.MainModule.GetTypes().First(x => x.Name == "ParameterKeys");
                    parameterKeysMergeMethod        = parameterKeysType.Methods.First(x => x.Name == "Merge");
                    assemblyEffectKeysAttributeType = xenkoEngineAssembly.MainModule.GetTypes().First(x => x.Name == "AssemblyEffectKeysAttribute");
                }

                var cctorIL           = cctor.Body.GetILProcessor();
                var cctorInstructions = cctor.Body.Instructions;

                var keyClassName = type.Name;
                if (keyClassName.EndsWith("Keys"))
                {
                    keyClassName = keyClassName.Substring(0, keyClassName.Length - 4);
                }

                keyClassName += '.';

                bool cctorModified = false;

                // Find field store instruction
                for (int i = 0; i < cctorInstructions.Count; ++i)
                {
                    var fieldInstruction = cctorInstructions[i];

                    if (fieldInstruction.OpCode == OpCodes.Stsfld &&
                        fields.Contains(fieldInstruction.Operand))
                    {
                        var activeField = (FieldReference)fieldInstruction.Operand;

                        var nextInstruction = cctorInstructions[i + 1];
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldsfld, activeField));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldtoken, type));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Ldstr, keyClassName + activeField.Name));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(parameterKeysMergeMethod)));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Castclass, activeField.FieldType));
                        cctorIL.InsertBefore(nextInstruction, Instruction.Create(OpCodes.Stsfld, activeField));
                        i             = cctorInstructions.IndexOf(nextInstruction);
                        cctorModified = true;
                    }
                }

                if (cctorModified)
                {
                    effectKeysStaticConstructors.Add(cctor);
                }
            }

            if (effectKeysStaticConstructors.Count > 0)
            {
                // Add [AssemblyEffectKeysAttribute] to the assembly
                assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.ImportReference(assemblyEffectKeysAttributeType.GetConstructors().First(x => !x.HasParameters))));

                // Get or create module static constructor
                var voidType          = assembly.MainModule.TypeSystem.Void;
                var moduleClass       = assembly.MainModule.Types.First(t => t.Name == "<Module>");
                var staticConstructor = moduleClass.GetStaticConstructor();
                if (staticConstructor == null)
                {
                    staticConstructor = new MethodDefinition(".cctor",
                                                             MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                                                             voidType);
                    staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));

                    moduleClass.Methods.Add(staticConstructor);
                }

                var il = staticConstructor.Body.GetILProcessor();

                var returnInstruction    = staticConstructor.Body.Instructions.Last();
                var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
                newReturnInstruction.Operand = returnInstruction.Operand;

                returnInstruction.OpCode  = OpCodes.Nop;
                returnInstruction.Operand = null;

                var typeType            = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);
                var typeHandleProperty  = typeType.Properties.First(x => x.Name == "TypeHandle");
                var getTypeHandleMethod = assembly.MainModule.ImportReference(typeHandleProperty.GetMethod);

                var runtimeHelpersType        = mscorlibAssembly.MainModule.GetTypeResolved(typeof(RuntimeHelpers).FullName);
                var runClassConstructorMethod = assembly.MainModule.ImportReference(runtimeHelpersType.Methods.Single(x => x.IsPublic && x.Name == "RunClassConstructor" && x.Parameters.Count == 1 && x.Parameters[0].ParameterType.FullName == typeof(RuntimeTypeHandle).FullName));

                // Call every key class static constructor from the module static constructor so that they are properly constructed (because accessing through reflection might cause problems)
                staticConstructor.Body.SimplifyMacros();
                foreach (var effectKeysStaticConstructor in effectKeysStaticConstructors)
                {
                    il.Append(Instruction.Create(OpCodes.Ldtoken, effectKeysStaticConstructor.DeclaringType));
                    il.Append(Instruction.Create(OpCodes.Call, getTypeFromHandleMethod.Value));
                    il.Append(Instruction.Create(OpCodes.Callvirt, getTypeHandleMethod));
                    il.Append(Instruction.Create(OpCodes.Call, runClassConstructorMethod));
                }

                if (effectKeysArrayElemementTypes.Count > 0)
                {
                    var methodImplAttributeType  = mscorlibAssembly.MainModule.GetTypeResolved(typeof(MethodImplAttribute).FullName);
                    var methodImplAttributesType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(MethodImplOptions).FullName);

                    var attribute = new CustomAttribute(methodImplAttributeType.GetConstructors().First(x => x.HasParameters && x.Parameters[0].ParameterType.FullName == methodImplAttributesType.FullName));
                    attribute.ConstructorArguments.Add(new CustomAttributeArgument(methodImplAttributesType, MethodImplOptions.NoOptimization));

                    staticConstructor.CustomAttributes.Add(attribute);
                }

                // Create instances of InternalValueArray<T>. Required for LLVM AOT
                foreach (var elementType in effectKeysArrayElemementTypes)
                {
                    var xenkoAssembly                 = assembly.Name.Name == "Xenko" ? assembly : assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Xenko", null));
                    var parameterCollectionType       = xenkoAssembly.MainModule.GetTypeResolved("Xenko.Rendering.ParameterCollection");
                    var internalValueArrayType        = parameterCollectionType.NestedTypes.First(x => x.Name == "InternalValueArray`1");
                    var constructor                   = internalValueArrayType.GetConstructors().First();
                    var internalValueArrayConstructor = xenkoAssembly.MainModule.ImportReference(constructor).MakeGeneric(elementType);

                    il.Append(Instruction.Create(OpCodes.Ldc_I4_0));
                    il.Append(Instruction.Create(OpCodes.Newobj, internalValueArrayConstructor));
                    il.Append(Instruction.Create(OpCodes.Pop));
                }

                il.Append(newReturnInstruction);
                staticConstructor.Body.OptimizeMacros();
            }

            return(true);
        }