예제 #1
0
        SharedSettings(
            System.Collections.Generic.IEnumerable<Bam.Core.Module> objectFiles,
            System.Type convertExtensionClassType,
            System.Type conversionInterfaceType,
            Bam.Core.TypeArray convertParameterTypes)
        {
            var sharedInterfaces = SharedInterfaces(objectFiles);
            var implementedInterfaces = new Bam.Core.TypeArray(sharedInterfaces);
            implementedInterfaces.Add(conversionInterfaceType);

            // define a new type, that contains just the shared interfaces between all object files
            // (any interface not shared, must be cloned later)
            var typeSignature = "IDESharedSettings";
            var assemblyName = new System.Reflection.AssemblyName(typeSignature);
            var assemblyBuilder = System.AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, System.Reflection.Emit.AssemblyBuilderAccess.Run);
            var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule", true);
            var sharedSettingsTypeDefn = moduleBuilder.DefineType(typeSignature,
                System.Reflection.TypeAttributes.Public |
                System.Reflection.TypeAttributes.Class |
                System.Reflection.TypeAttributes.AutoClass |
                System.Reflection.TypeAttributes.AnsiClass |
                System.Reflection.TypeAttributes.BeforeFieldInit |
                System.Reflection.TypeAttributes.AutoLayout,
                typeof(C.SettingsBase),
                implementedInterfaces.ToArray());

                // TODO: is this necessary?
#if false
            sharedSettingsTypeDefn.DefineDefaultConstructor(
                System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.SpecialName | System.Reflection.MethodAttributes.RTSpecialName);
#endif

            // implement 'automatic property' setter and getters for each property in each interface
            foreach (var i in sharedInterfaces)
            {
                var properties = i.GetProperties();
                foreach (var prop in properties)
                {
                    var dynamicProperty = sharedSettingsTypeDefn.DefineProperty(prop.Name,
                        System.Reflection.PropertyAttributes.None,
                        prop.PropertyType,
                        System.Type.EmptyTypes);
                    var field = sharedSettingsTypeDefn.DefineField("m" + prop.Name,
                        prop.PropertyType,
                        System.Reflection.FieldAttributes.Private);
                    var methodAttrs = System.Reflection.MethodAttributes.Public |
                        System.Reflection.MethodAttributes.HideBySig |
                        System.Reflection.MethodAttributes.Virtual;
                    if (prop.IsSpecialName)
                    {
                        methodAttrs |= System.Reflection.MethodAttributes.SpecialName;
                    }
                    var getter = sharedSettingsTypeDefn.DefineMethod("get_" + prop.Name,
                        methodAttrs,
                        prop.PropertyType,
                        System.Type.EmptyTypes);
                    var setter = sharedSettingsTypeDefn.DefineMethod("set_" + prop.Name,
                        methodAttrs,
                        null,
                        new[] { prop.PropertyType });
                    var getIL = getter.GetILGenerator();
                    getIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
                    getIL.Emit(System.Reflection.Emit.OpCodes.Ldfld, field);
                    getIL.Emit(System.Reflection.Emit.OpCodes.Ret);
                    dynamicProperty.SetGetMethod(getter);
                    var setIL = setter.GetILGenerator();
                    setIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
                    setIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
                    setIL.Emit(System.Reflection.Emit.OpCodes.Stfld, field);
                    setIL.Emit(System.Reflection.Emit.OpCodes.Ret);
                    dynamicProperty.SetSetMethod(setter);
                }
            }

            var projectSettingsConvertMethod = sharedSettingsTypeDefn.DefineMethod("Convert",
                System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Final | System.Reflection.MethodAttributes.HideBySig | System.Reflection.MethodAttributes.NewSlot | System.Reflection.MethodAttributes.Virtual,
                null,
                convertParameterTypes.ToArray());
            var convertIL = projectSettingsConvertMethod.GetILGenerator();
            foreach (var i in sharedInterfaces)
            {
                var extConvertParameterTypes = new Bam.Core.TypeArray(i);
                extConvertParameterTypes.AddRange(convertParameterTypes);
                var methInfo = convertExtensionClassType.GetMethod("Convert", extConvertParameterTypes.ToArray());
                if (null == methInfo)
                {
                        throw new Bam.Core.Exception("Unable to locate the function {0}.{1}(this {2})", convertExtensionClassType.FullName, "Convert", i.Name);
                }
                // TODO: can this be simplified, using the ldarg opcode? a simple loop would suffice
                convertIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
                if (extConvertParameterTypes.Count > 1)
                {
                    convertIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
                }
                if (extConvertParameterTypes.Count > 2)
                {
                    convertIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_2);
                }
                if (extConvertParameterTypes.Count > 3)
                {
                    convertIL.Emit(System.Reflection.Emit.OpCodes.Ldarg_3);
                }
                convertIL.Emit(System.Reflection.Emit.OpCodes.Call, methInfo);
            }
            convertIL.Emit(System.Reflection.Emit.OpCodes.Ret);

            var sharedSettingsType = sharedSettingsTypeDefn.CreateType();
            var attributeType = typeof(Bam.Core.SettingsExtensionsAttribute);

            // now that we have an instance of the shared settings type, calculate the values of the individual settings across all object files
            // for all shared interfaces
            var commonSettings = System.Activator.CreateInstance(sharedSettingsType) as SettingsBase;
            commonSettings.InitializeAllInterfaces(objectFiles.First(), true, false);
            foreach (var i in sharedInterfaces)
            {
                var attributeArray = i.GetCustomAttributes(attributeType, false);
                if (0 == attributeArray.Length)
                {
                    throw new Bam.Core.Exception("Settings interface {0} is missing attribute {1}", i.ToString(), attributeType.ToString());
                }

                var attribute = attributeArray[0] as Bam.Core.SettingsExtensionsAttribute;

                var cloneSettingsMethod = attribute.GetMethod("Clone", new[] { i, i });
                if (null == cloneSettingsMethod)
                {
                    throw new Bam.Core.Exception("Unable to find extension method {0}.SharedSettings(this {1}, {1}, {1}, {1})",
                        attribute.ExtensionsClassName,
                        i.ToString());
                }

                var intersectSettingsMethod = attribute.GetMethod("Intersect", new[] { i, i });
                if (null == intersectSettingsMethod)
                {
                    throw new Bam.Core.Exception("Unable to find extension method {0}.Intersect(this {1}, {1})",
                        attribute.ExtensionsClassName,
                        i.ToString());
                }

                var objectFileCount = objectFiles.Count();
                cloneSettingsMethod.Invoke(null, new[] { commonSettings, objectFiles.First().Settings });
                for (int objIndex = 1; objIndex < objectFileCount; ++objIndex)
                {
                    intersectSettingsMethod.Invoke(null, new[] { commonSettings, objectFiles.ElementAt(objIndex).Settings });
                }
            }
            return commonSettings;
        }