Пример #1
0
        public bool Process(AssemblyProcessorContext context)
        {
            var assembly           = context.Assembly;
            var moduleInitializers = new List <(int Order, MethodReference Method)>();

            // Generate a module initializer for all types, including nested types
            foreach (var type in assembly.MainModule.GetAllTypes())
            {
                foreach (var method in type.Methods)
                {
                    var moduleInitializerAttribute = method.CustomAttributes.FirstOrDefault(
                        attrib => attrib.AttributeType.FullName == "Stride.Core.ModuleInitializerAttribute");

                    if (moduleInitializerAttribute != null)
                    {
                        var order = moduleInitializerAttribute.HasConstructorArguments
                            ? (int)moduleInitializerAttribute.ConstructorArguments[0].Value : 0;

                        moduleInitializers.Add((order, method));
                    }
                }
            }

            if (moduleInitializers.Count == 0)
            {
                return(false);
            }

            // Sort by Order property
            moduleInitializers = moduleInitializers.OrderBy(x => x.Order).ToList();

            // Get or create module static constructor
            var staticConstructor = OpenModuleConstructor(assembly, out Instruction returnInstruction);
            var il = staticConstructor.Body.GetILProcessor();

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

            newReturnInstruction.Operand = returnInstruction.Operand;

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

            staticConstructor.Body.SimplifyMacros();
            foreach (var moduleInitializer in moduleInitializers)
            {
                il.Append(Instruction.Create(OpCodes.Call, moduleInitializer.Method));
            }
            il.Append(newReturnInstruction);
            staticConstructor.Body.OptimizeMacros();

            return(true);
        }
Пример #2
0
        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");
            }


            // Resolve mscorlib types
            var assemblyFileVersionAttributeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(AssemblyFileVersionAttribute).FullName);
            var assemblyMethodConstructor        = assembly.MainModule.ImportReference(assemblyFileVersionAttributeType.Methods.FirstOrDefault(method => method.IsConstructor && method.Parameters.Count == 1));
            var stringType = assembly.MainModule.TypeSystem.String;

            // TODO: Git Commit SHA
            var gitCommitShortId = "0";

            // Use epoch time to get a "unique" build number (different each time)
            var build = (long)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds;

            // Get current AssemblyVersion and clone it
            var version     = assembly.Name.Version;
            var fileVersion = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, build, gitCommitShortId);

            // Copy build/revision to the AssemblyFileVersion
            bool fileVersionUpdated = false;

            for (int i = 0; i < assembly.CustomAttributes.Count; i++)
            {
                var customAttribute = assembly.CustomAttributes[i];
                if (customAttribute.AttributeType.FullName == typeof(AssemblyFileVersionAttribute).FullName)
                {
                    customAttribute.ConstructorArguments.Clear();
                    customAttribute.ConstructorArguments.Add(new CustomAttributeArgument(stringType, fileVersion));
                    fileVersionUpdated = true;
                    break;
                }
            }

            if (!fileVersionUpdated)
            {
                var assemblyFileVersion = new CustomAttribute(assemblyMethodConstructor);
                assemblyFileVersion.ConstructorArguments.Add(new CustomAttributeArgument(stringType, fileVersion));
                assembly.CustomAttributes.Add(assemblyFileVersion);
            }

            return(true);
        }
Пример #3
0
        public bool Process(AssemblyProcessorContext context)
        {
            this.assembly       = context.Assembly;
            asyncBridgeAssembly = context.AssemblyResolver.Resolve(new AssemblyNameReference("AsyncBridge", null));

            assembly.MainModule.AssemblyReferences.Add(asyncBridgeAssembly.Name);

            foreach (var type in assembly.MainModule.Types)
            {
                ProcessType(type);
            }

            return(true);
        }
Пример #4
0
        public bool Process(AssemblyProcessorContext context)
        {
            var registry = new ComplexSerializerRegistry(context.Platform, context.Assembly, context.Log);

            // Register default serialization profile (to help AOT generic instantiation of serializers)
            RegisterDefaultSerializationProfile(context.AssemblyResolver, context.Assembly, registry, context.Log);

            // Generate serializer code
            // Create the serializer code generator
            //var serializerGenerator = new ComplexSerializerCodeGenerator(registry);
            //sourceCodeRegisterAction(serializerGenerator.TransformText(), "DataSerializers");

            GenerateSerializerCode(registry);

            return(true);
        }
Пример #5
0
        private void EnsureInitialized(AssemblyProcessorContext context)
        {
            if (isInitialized)
            {
                return;
            }

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

            strideCoreModule  = context.Assembly.GetStrideCoreModule();
            pooledClosureType = context.Assembly.MainModule.ImportReference(strideCoreModule.GetType("Stride.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 = strideCoreModule.GetType("Stride.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;
        }
Пример #6
0
        public bool Process(AssemblyProcessorContext context)
        {
            this.assembly = context.Assembly;
            // Import void* and int32 from assembly using mscorlib specific version (2.0 or 4.0 depending on assembly)
            voidPointerType = new PointerType(assembly.MainModule.TypeSystem.Void);
            intType         = assembly.MainModule.TypeSystem.Int32;

            context.Log.WriteLine($"Patch for assembly [{assembly.FullName}]");
            foreach (var type in assembly.MainModule.Types)
            {
                PatchType(type);
            }

            // Remove All Interop classes
            foreach (var type in classToRemoveList)
            {
                assembly.MainModule.Types.Remove(type);
            }

            return(true);
        }
Пример #7
0
        public bool Process(AssemblyProcessorContext context)
        {
            bool changed = false;

            foreach (var type in context.Assembly.MainModule.GetAllTypes())
            {
                foreach (var method in type.Methods)
                {
                    if (method.CustomAttributes.Any(x => x.AttributeType.FullName == "Stride.Core.IL.RemoveInitLocalsAttribute"))
                    {
                        if (method.Body == null)
                        {
                            throw new InvalidOperationException($"Trying to remove initlocals from method {method.FullName} without body.");
                        }

                        method.Body.InitLocals = false;
                        changed = true;
                    }
                }
            }

            return(changed);
        }
Пример #8
0
        private MethodReference ChangeGenericArguments(AssemblyProcessorContext context, MethodReference method, TypeReference relativeType)
        {
            var genericInstance = method as GenericInstanceMethod;

            if (genericInstance == null)
            {
                return(method.Resolve().MakeGeneric(relativeType.Resolve().GenericParameters.ToArray()));
            }

            Debugger.Launch();

            var genericArguments = new List <TypeReference>();

            foreach (var genericArgument in genericInstance.GenericArguments)
            {
                if (genericArgument.IsGenericParameter)
                {
                    var genericParameter = GetGenericParameterForArgument(relativeType, genericArgument);
                    if (genericParameter != null)
                    {
                        genericArguments.Add(genericParameter);
                    }
                }
                else
                {
                    var newGenericArgument = ChangeGenericArguments(context, genericArgument, relativeType);
                    genericArguments.Add(newGenericArgument);
                }
            }

            if (genericArguments.Count != genericInstance.GenericArguments.Count)
            {
                throw new InvalidOperationException("Could not resolve generic arguments");
            }

            return(context.Assembly.MainModule.ImportReference(genericInstance.Resolve()).MakeGeneric(genericArguments.ToArray()));
        }
Пример #9
0
        public bool Process(AssemblyProcessorContext context)
        {
            isInitialized = false;

            bool changed = false;

            foreach (var type in context.Assembly.MainModule.GetAllTypes().ToArray())
            {
                foreach (var method in type.Methods.ToArray())
                {
                    if (method.Body == null)
                    {
                        continue;
                    }

                    foreach (var instruction in method.Body.Instructions.ToArray())
                    {
                        if (instruction.OpCode != OpCodes.Call && instruction.OpCode != OpCodes.Callvirt)
                        {
                            continue;
                        }

                        var calledMethod = ((MethodReference)instruction.Operand).Resolve();
                        if (calledMethod == null)
                        {
                            continue;
                        }

                        for (int parameterIndex = 0; parameterIndex < calledMethod.Parameters.Count; parameterIndex++)
                        {
                            var parameter = calledMethod.Parameters[parameterIndex];

                            // Parameter must be decorated with PooledAttribute
                            if (parameter.CustomAttributes.All(x => x.AttributeType.FullName != "Stride.Core.Threading.PooledAttribute"))
                            {
                                continue;
                            }

                            // Parameter must be a delegate
                            if (parameter.ParameterType.Resolve().BaseType.FullName != typeof(MulticastDelegate).FullName)
                            {
                                continue;
                            }

                            // Find the instruction that pushes the parameter on the stack
                            // Non-trivial control flow is not supported
                            var pushParameterInstruction = WalkStack(instruction, calledMethod.Parameters.Count - parameterIndex);

                            // Try to replace delegate and closure allocations
                            {
                                EnsureInitialized(context);

                                changed |= ProcessDelegateAllocation(context, method, pushParameterInstruction);
                            }
                        }
                    }
                }
            }

            return(changed);
        }
Пример #10
0
        private ClosureInfo ProcessClosure(AssemblyProcessorContext context, TypeDefinition closureType, TypeReference[] genericParameters)
        {
            ClosureInfo closure;

            if (closures.TryGetValue(closureType, out closure))
            {
                return(closure);
            }

            var closureTypeConstructor = closureType.Methods.FirstOrDefault(x => x.Name == ".ctor");
            var closureGenericType     = closureType.MakeGenericType(genericParameters);

            // Create factory method for pool items
            var factoryMethod = new MethodDefinition("<Factory>", MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.Static, closureGenericType);

            closureType.Methods.Add(factoryMethod);
            factoryMethod.Body.InitLocals = true;
            factoryMethod.Body.Variables.Add(new VariableDefinition(closureGenericType));
            var factoryMethodProcessor = factoryMethod.Body.GetILProcessor();

            // Create and store closure
            factoryMethodProcessor.Emit(OpCodes.Newobj, closureTypeConstructor.MakeGeneric(genericParameters));
            factoryMethodProcessor.Emit(OpCodes.Stloc_0);
            //// Return closure
            factoryMethodProcessor.Emit(OpCodes.Ldloc_0);
            factoryMethodProcessor.Emit(OpCodes.Ret);

            // Create pool field
            var poolField = new FieldDefinition("<pool>", FieldAttributes.Public | FieldAttributes.Static, poolType.MakeGenericType(closureGenericType));

            closureType.Fields.Add(poolField);
            var poolFieldReference = poolField.MakeGeneric(genericParameters);

            // Initialize pool
            var cctor          = GetOrCreateClassConstructor(closureType);
            var ilProcessor2   = cctor.Body.GetILProcessor();
            var retInstruction = cctor.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);

            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldnull));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldftn, factoryMethod.MakeGeneric(genericParameters)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, funcConstructor.MakeGeneric(closureGenericType)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, poolConstructor.MakeGeneric(closureGenericType)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Stsfld, poolFieldReference));

            // Implement IPooledClosure
            closureType.Interfaces.Add(new InterfaceImplementation(pooledClosureType));

            // Create reference count field
            var countField = new FieldDefinition("<referenceCount>", FieldAttributes.Public, context.Assembly.MainModule.TypeSystem.Int32);

            closureType.Fields.Add(countField);
            var oountFieldReference = countField.MakeGeneric(genericParameters);

            // Create AddReference method
            var addReferenceMethod = new MethodDefinition("AddReference", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void);
            var ilProcessor4       = addReferenceMethod.Body.GetILProcessor();

            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference);
            ilProcessor4.Emit(OpCodes.Call, interlockedIncrementMethod);
            ilProcessor4.Emit(OpCodes.Pop);
            ilProcessor4.Emit(OpCodes.Ret);
            closureType.Methods.Add(addReferenceMethod);

            // Create Release method
            var releaseMethod = new MethodDefinition("Release", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void);

            ilProcessor4   = releaseMethod.Body.GetILProcessor();
            retInstruction = ilProcessor4.Create(OpCodes.Ret);
            // Check decremented reference count
            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference);
            ilProcessor4.Emit(OpCodes.Call, interlockedDecrementMethod);
            ilProcessor4.Emit(OpCodes.Ldc_I4_0);
            ilProcessor4.Emit(OpCodes.Ceq);
            ilProcessor4.Emit(OpCodes.Brfalse_S, retInstruction);
            // Clear fields
            foreach (var field in closureType.Fields)
            {
                if (field.IsStatic || field.FieldType.IsPrimitive || field == countField)
                {
                    continue;
                }

                ilProcessor4.Emit(OpCodes.Ldarg_0);
                if (field.FieldType.IsValueType)
                {
                    ilProcessor4.Emit(OpCodes.Ldflda, field.MakeGeneric(genericParameters));
                    ilProcessor4.Emit(OpCodes.Initobj, field.FieldType);
                }
                else
                {
                    ilProcessor4.Emit(OpCodes.Ldnull);
                    ilProcessor4.Emit(OpCodes.Stfld, field.MakeGeneric(genericParameters));
                }
            }
            // Release this to pool
            ilProcessor4.Emit(OpCodes.Ldsfld, poolFieldReference);
            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Callvirt, poolReleaseMethod.MakeGeneric(closureGenericType));
            ilProcessor4.Append(retInstruction);
            closureType.Methods.Add(releaseMethod);

            closures.Add(closureType, closure = new ClosureInfo
            {
                FactoryMethod      = factoryMethod,
                AddReferenceMethod = addReferenceMethod,
                ReleaseMethod      = releaseMethod,
                PoolField          = poolField
            });

            return(closure);
        }
Пример #11
0
        private bool ProcessDelegateAllocation(AssemblyProcessorContext context, MethodDefinition method, Instruction delegateAllocationInstruction)
        {
            // The instruction must be a delegate allocation
            // If not, this might be a static delegate, or some unsupported construct
            if (delegateAllocationInstruction.OpCode != OpCodes.Newobj)
            {
                return(false);
            }

            var delegateInstanceConstructor = (MethodReference)delegateAllocationInstruction.Operand;
            var delegateInstanceType        = delegateInstanceConstructor.DeclaringType;

            // The previous instruction pushes the delegate method onto the stack
            var functionPointerInstruction = delegateAllocationInstruction.Previous;

            if (functionPointerInstruction.OpCode != OpCodes.Ldftn)
            {
                return(false);
            }

            var delegateMethod = (MethodReference)functionPointerInstruction.Operand;

            // The previous instruction pushes the target onto the stack
            // If it's the this-parameter, we can create an instance field, and reuse the same delegate
            var loadClosureInstruction = functionPointerInstruction.Previous;

            if ((loadClosureInstruction.OpCode == OpCodes.Ldarg_0 || loadClosureInstruction.OpCode == OpCodes.Ldnull) && !method.IsStatic)
            {
                // TODO: Handle generic methods/delegates
                // TODO: Handle multiple constructors propertly
                var constructor     = method.DeclaringType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters);
                var retInstruction3 = constructor?.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);
                if (retInstruction3 == null)
                {
                    return(false);
                }

                // Create an instance field for the shared delegate
                var sharedDelegateField = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Private, delegateInstanceType);
                method.DeclaringType.Fields.Add(sharedDelegateField);

                // Create and store the delegate in constructor
                var ilProcessor5 = constructor.Body.GetILProcessor();
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldarg_0));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(loadClosureInstruction.OpCode));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldftn, delegateMethod));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Newobj, delegateInstanceConstructor));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Stfld, sharedDelegateField));

                // Load from field instead of allocating
                var ilProcessor4 = method.Body.GetILProcessor();
                ilProcessor4.Remove(functionPointerInstruction);
                loadClosureInstruction.OpCode = OpCodes.Ldarg_0; // In case it was a ldnull
                ilProcessor4.Replace(delegateAllocationInstruction, ilProcessor4.Create(OpCodes.Ldfld, sharedDelegateField));

                return(true);
            }

            // If the target is a compiler generated closure, we only handle local variable load instructions
            int    variableIndex;
            OpCode storeOpCode;

            if (!TryGetStoreOpcode(loadClosureInstruction, out storeOpCode, out variableIndex))
            {
                return(false);
            }

            // Find the instruction that stores the closure variable
            var storeClosureInstruction      = loadClosureInstruction.Previous;
            VariableReference closureVarible = null;

            while (storeClosureInstruction != null)
            {
                closureVarible = storeClosureInstruction.Operand as VariableReference;
                if (storeClosureInstruction.OpCode == storeOpCode && (closureVarible == null || variableIndex == closureVarible.Index))
                {
                    break;
                }

                storeClosureInstruction = storeClosureInstruction.Previous;
            }
            if (storeClosureInstruction == null)
            {
                return(false);
            }

            var closureInstanceType = method.Body.Variables[variableIndex].VariableType;
            var closureType         = closureInstanceType.Resolve();
            var genericParameters   = closureType.GenericParameters.Cast <TypeReference>().ToArray();

            // Patch closure
            var closure = ProcessClosure(context, closureType, genericParameters);

            // Create delegate field
            var delegateFieldType = ChangeGenericArguments(context, delegateInstanceType, closureInstanceType);
            var delegateField     = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Public, delegateFieldType);

            closureType.Fields.Add(delegateField);
            var localDelegateFieldInstance = delegateField.MakeGeneric(genericParameters);

            // Initialize delegate field (the closure instance (local 0) is already on the stack)
            var delegateConstructorInstance = (MethodReference)delegateAllocationInstruction.Operand;
            var delegateGenericArguments    = (delegateFieldType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0];
            var genericDelegateConstructor  = context.Assembly.MainModule.ImportReference(delegateConstructorInstance.Resolve()).MakeGeneric(delegateGenericArguments);

            var methodInstance = (MethodReference)functionPointerInstruction.Operand;
            var genericMethod  = methodInstance.Resolve().MakeGeneric(closureType.GenericParameters.ToArray());

            if (methodInstance is GenericInstanceMethod)
            {
                throw new NotImplementedException();
            }

            var ilProcessor3      = closure.FactoryMethod.Body.GetILProcessor();
            var returnInstruction = ilProcessor3.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);

            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldftn, genericMethod));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Newobj, genericDelegateConstructor));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Stfld, localDelegateFieldInstance));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0));

            var ilProcessor = method.Body.GetILProcessor();

            Func <Instruction> generateClosureVariable = () => closureVarible == null?ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve());

            // Retrieve from pool
            var closureGenericArguments = (closureInstanceType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0];
            var closureAllocation       = storeClosureInstruction.Previous;

            if (closureAllocation.OpCode == OpCodes.Newobj)
            {
                // Retrieve closure from pool, instead of allocating
                ilProcessor.Replace(storeClosureInstruction, ilProcessor.Create(OpCodes.Nop));

                var acquireClosure      = ilProcessor.Create(OpCodes.Callvirt, poolAcquireMethod.MakeGeneric(closureInstanceType));
                var methodPreviousStart = ilProcessor.Body.Instructions.First();
                ilProcessor.InsertBefore(methodPreviousStart, ilProcessor.Create(OpCodes.Nop));
                ilProcessor.InsertBefore(methodPreviousStart, ilProcessor.Create(OpCodes.Ldsfld, closure.PoolField.MakeGeneric(closureGenericArguments)));
                ilProcessor.InsertBefore(methodPreviousStart, acquireClosure);
                ilProcessor.InsertBefore(methodPreviousStart, storeClosureInstruction);
                closureAllocation.OpCode  = OpCodes.Nop; // Change to Nop instead of removing it, as this instruction might be reference somewhere?
                closureAllocation.Operand = null;

                // Add a reference
                ilProcessor.InsertBefore(methodPreviousStart, generateClosureVariable());
                ilProcessor.InsertBefore(methodPreviousStart, ilProcessor.Create(OpCodes.Callvirt, closure.AddReferenceMethod.MakeGeneric(closureGenericArguments)));

                // TODO: Multiple returns + try/finally
                // Release reference
                var retInstructions = method.Body.Instructions.Where(x => x.OpCode == OpCodes.Ret).ToArray();

                Instruction beforeReturn         = generateClosureVariable();
                Instruction newReturnInstruction = ilProcessor.Create(OpCodes.Ret);
                ilProcessor.Append(beforeReturn);
                ilProcessor.Append(ilProcessor.Create(OpCodes.Ldnull));
                ilProcessor.Append(ilProcessor.Create(OpCodes.Beq, newReturnInstruction));
                ilProcessor.Append(generateClosureVariable());
                ilProcessor.Append(ilProcessor.Create(OpCodes.Callvirt, closure.ReleaseMethod.MakeGeneric(closureGenericArguments)));
                ilProcessor.Append(newReturnInstruction);

                foreach (var retInstruction2 in retInstructions)
                {
                    retInstruction2.OpCode  = OpCodes.Br;
                    retInstruction2.Operand = beforeReturn;
                }
            }

            // Get delegate from closure, instead of allocating
            ilProcessor.Remove(functionPointerInstruction);
            ilProcessor.Replace(delegateAllocationInstruction, ilProcessor.Create(OpCodes.Ldfld, delegateField.MakeGeneric(closureGenericArguments))); // Closure object is already on the stack

            return(true);
        }
Пример #12
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 strideCoreModule     = assembly.GetStrideCoreModule();
                var assemblyRegistryType = strideCoreModule.GetType("Stride.Core.Reflection.AssemblyRegistry");

                // Generate code
                var assemblyScanType = new TypeDefinition("Stride.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);
        }
Пример #13
0
        public bool Process(AssemblyProcessorContext context)
        {
            if (context.Platform != Core.PlatformType.Android && context.Platform != Core.PlatformType.iOS)
            {
                return(false);
            }

            bool changed = false;

            foreach (var type in context.Assembly.MainModule.GetAllTypes())
            {
                foreach (var method in type.Methods)
                {
                    if (method.Body == null)
                    {
                        continue;
                    }

                    for (var index = 0; index < method.Body.Instructions.Count; index++)
                    {
                        var instruction = method.Body.Instructions[index];
                        if (instruction.OpCode == OpCodes.Conv_U)
                        {
                            VariableDefinition variable = null;

                            // Check if next store is in a pointer variable
                            switch (instruction.Next.OpCode.Code)
                            {
                            case Code.Stloc_0:
                                variable = method.Body.Variables[0];
                                break;

                            case Code.Stloc_1:
                                variable = method.Body.Variables[1];
                                break;

                            case Code.Stloc_2:
                                variable = method.Body.Variables[2];
                                break;

                            case Code.Stloc_3:
                                variable = method.Body.Variables[3];
                                break;

                            case Code.Stloc:
                                variable = (VariableDefinition)instruction.Operand;
                                break;
                            }

                            if (variable != null && variable.VariableType.IsPointer)
                            {
                                // We are in a fixed instruction, let's fix it
                                instruction.OpCode = OpCodes.Conv_I;
                                changed            = true;
                            }
                        }
                    }
                }
            }

            return(changed);
        }
Пример #14
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 == "Stride.Rendering.ParameterKey")
                        {
                            break;
                        }

                        // TODO: Get PropertyKey.PropertyType instead
                        var genericInstance = fieldBaseType as GenericInstanceType;
                        if (genericInstance != null && genericInstance.ElementType.FullName == "Stride.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 Stride methods/attributes
                if (parameterKeysMergeMethod == null)
                {
                    AssemblyDefinition strideEngineAssembly;
                    try
                    {
                        strideEngineAssembly = assembly.Name.Name == "Stride"
                            ? assembly
                            : context.AssemblyResolver.Resolve(new AssemblyNameReference("Stride", null));
                    }
                    catch (Exception)
                    {
                        context.Log.WriteLine("Error, cannot find [Stride] assembly for processing ParameterKeyProcessor");
                        // We can't generate an exception, so we are just returning. It means that Stride has not been generated so far.
                        return(true);
                    }

                    var parameterKeysType = strideEngineAssembly.MainModule.GetTypes().First(x => x.Name == "ParameterKeys");
                    parameterKeysMergeMethod        = parameterKeysType.Methods.First(x => x.Name == "Merge");
                    assemblyEffectKeysAttributeType = strideEngineAssembly.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 strideAssembly                = assembly.Name.Name == "Stride" ? assembly : assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Stride", null));
                    var parameterCollectionType       = strideAssembly.MainModule.GetTypeResolved("Stride.Rendering.ParameterCollection");
                    var internalValueArrayType        = parameterCollectionType.NestedTypes.First(x => x.Name == "InternalValueArray`1");
                    var constructor                   = internalValueArrayType.GetConstructors().First();
                    var internalValueArrayConstructor = strideAssembly.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);
        }
        public bool Process(AssemblyProcessorContext context)
        {
            var basePath   = Path.Combine(Path.GetDirectoryName(inputFile) ?? "", Path.GetFileNameWithoutExtension(inputFile) ?? "");
            var xmlFile    = basePath + ".xml";
            var targetFile = basePath + ".usrdoc";

            // No xml documentation file available, stop here.
            if (!File.Exists(xmlFile))
            {
                return(false);
            }

            var result = new Dictionary <string, string>();

            var document = XElement.Load(xmlFile);

            foreach (var member in document.Descendants("member"))
            {
                var nameAttribute = member.Attribute("name");
                if (nameAttribute == null)
                {
                    continue;
                }

                foreach (var userdocElement in member.Descendants("userdoc"))
                {
                    string userdoc = null;
                    var    key     = nameAttribute.Value;

                    var docOverride = userdocElement.Attribute("override");
                    if (docOverride != null && key.StartsWith("T"))             // if on top of the class we have some overrides we must process them now
                    {
                        key = "P" + key.Substring(1) + "." + docOverride.Value; //replace T with M
                    }

                    if (result.ContainsKey(key))
                    {
                        context.Log.WriteLine($"Warning: the member {key} has multiple userdoc, only the first one will be used.");
                        continue;
                    }
                    if (userdocElement.Descendants().Any())
                    {
                        context.Log.WriteLine($"Warning: the userdoc of member {key} has descendant nodes, which is not supported.");
                        continue;
                    }

                    userdoc = userdocElement.Value;
                    userdoc = userdoc.Replace('\t', ' ').Replace('\r', ' ').Replace('\n', ' ').Trim();
                    // Removes double space.
                    var regex = new Regex(@"[ ]{2,}", RegexOptions.None);
                    userdoc = regex.Replace(userdoc, @" ");

                    result.Add(key, userdoc);
                }
            }

            using (var writer = new StreamWriter(targetFile))
            {
                foreach (var entry in result)
                {
                    writer.WriteLine("{0}={1}", entry.Key, entry.Value);
                }
            }

            return(true);
        }
Пример #16
0
        public bool Run(ref AssemblyDefinition assemblyDefinition, out bool modified, out ObjectId?serializationHash)
        {
            modified          = false;
            serializationHash = null;

            try
            {
                // Check if there is a AssemblyProcessedAttribute (already processed, we can skip).
                // 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.HasCustomAttribute("Stride.Core.AssemblyProcessedAttribute"))
                {
                    OnInfoAction($"Assembly [{assemblyDefinition.Name}] has already been processed. Skipping it.");
                    return(true);
                }

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

                // Register self
                assemblyResolver.Register(assemblyDefinition);

                var processors = new List <IAssemblyDefinitionProcessor>();

                processors.Add(new AddReferenceProcessor(ReferencesToAdd));

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

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

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

                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());

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

                var assemblyProcessorContext = new AssemblyProcessorContext(assemblyResolver, assemblyDefinition, 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;
                serializationHash  = assemblyProcessorContext.SerializationHash;

                if (modified)
                {
                    // Add AssemblyProcessedAttribute to assembly so that it doesn't get processed again
                    var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assemblyDefinition);
                    if (mscorlibAssembly is 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(
                        "Stride.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 ex)
            {
                OnErrorAction(errorMessage: null, ex);
                return(false);
            }

            return(true);
        }
Пример #17
0
        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 strideCoreModule;

            try
            {
                strideCoreModule = assembly.GetStrideCoreModule();
            }
            catch (Exception)
            {
                return(true);
            }

            propertyChangedExtendedEventArgsType = strideCoreModule.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);
        }