#region License // Copyright (c) 2010, Jasper Yeh. // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // * Neither the name of ClearCanvas Inc. nor the names of its contributors // may be used to endorse or promote products derived from this software without // specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE // GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY // OF SUCH DAMAGE. #endregion using System; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; namespace Nullstack.ClearCanvasEx.DesktopEx.View.WpfAdapter.Utilities { internal abstract class DynamicAdapterBuilder { private readonly AssemblyName _assemblyName; private readonly AssemblyBuilder _assemblyBuilder; private readonly ModuleBuilder _moduleBuilder; public DynamicAdapterBuilder(string assemblyName) { if (string.IsNullOrEmpty(assemblyName)) throw new ArgumentException("Assembly name must not be null or empty.", "assemblyName"); _assemblyName = new AssemblyName(assemblyName); _assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(_assemblyName, AssemblyBuilderAccess.RunAndSave); _moduleBuilder = _assemblyBuilder.DefineDynamicModule("DynamicAdapterModule", string.Format("{0}.dll", assemblyName)); } protected DynamicAdapterBuilder(AssemblyBuilder assemblyBuilder) { _assemblyName = assemblyBuilder.GetName(); _assemblyBuilder = assemblyBuilder; _moduleBuilder = _assemblyBuilder.DefineDynamicModule("DynamicAdapterModule", string.Format("{0}.dll", _assemblyName)); } public Assembly Assembly { get { return _assemblyBuilder; } } protected AssemblyBuilder AssemblyBuilder { get { return _assemblyBuilder; } } protected Type DefineAdapter(string adapterName, Type sourceType, Type adapterBaseType, IEnumerable adapterInterfaceTypes, IEnumerable adapterAttributes) { if (string.IsNullOrEmpty(adapterName)) throw new ArgumentException("Adapter type name must not be null or empty.", "adapterName"); if (sourceType == null) throw new ArgumentException("Source type must not be null.", "sourceType"); if (adapterBaseType == null) throw new ArgumentException("Adapter base type must not be null.", "adapterBaseType"); var baseDefaultCtor = adapterBaseType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); if (baseDefaultCtor == null) throw new ArgumentException("Adapter base type must have a default constructor.", "adapterBaseType"); if (FindOne(adapterInterfaceTypes, t => !t.IsInterface) != null) throw new ArgumentException("Adapter interface types must be interfaces.", "adapterInterfaceTypes"); // create the type (public class that derives from base) var typeBuilder = _moduleBuilder.DefineType(adapterName, TypeAttributes.Public | TypeAttributes.Class, adapterBaseType); // add interfaces to the type if (adapterInterfaceTypes != null) { foreach (var interfaceType in adapterInterfaceTypes) typeBuilder.AddInterfaceImplementation(interfaceType); } // add attributes to the type if (adapterAttributes != null) { foreach (var customAttribute in adapterAttributes) typeBuilder.SetCustomAttribute(customAttribute); } // create the field for the source instance (private readonly instance field) var instanceField = typeBuilder.DefineField("_instance", sourceType, FieldAttributes.Private | FieldAttributes.InitOnly); // create a constructor for each public constructor on the source type foreach (var sourceCtor in sourceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)) { var paramTypes = new List(Convert(sourceCtor.GetParameters(), p => p.ParameterType)); var ctorBuilder = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, paramTypes.ToArray()); { var ig = ctorBuilder.GetILGenerator(); ig.Emit(OpCodes.Ldarg_0); ig.Emit(OpCodes.Call, baseDefaultCtor); // call adapter base constructor ig.Emit(OpCodes.Nop); ig.Emit(OpCodes.Nop); ig.Emit(OpCodes.Ldarg_0); if (paramTypes.Count >= 1) ig.Emit(OpCodes.Ldarg_1); if (paramTypes.Count >= 2) ig.Emit(OpCodes.Ldarg_2); if (paramTypes.Count >= 3) ig.Emit(OpCodes.Ldarg_3); for (byte i = 4; i <= paramTypes.Count; i++) ig.Emit(OpCodes.Ldarg_S, i); ig.Emit(OpCodes.Newobj, sourceCtor); ig.Emit(OpCodes.Stfld, instanceField); // instantiate source instance with provided arguments ig.Emit(OpCodes.Nop); ig.Emit(OpCodes.Ret); } } //foreach (MethodInfo method in toImplement.GetMethods()) //{ // ParameterInfo[] parameters = method.GetParameters(); // Type[] parameterTypes = new Type[parameters.Length]; // for (int i = 0; i < parameters.Length; i++) // { // parameterTypes[i] = parameters[i].ParameterType; // } // MethodBuilder methodBuilder = typeBuilder.DefineMethod(method.Name, MethodAttributes.Public, method.ReturnType, parameterTypes); // ILGenerator ilGenerator = methodBuilder.GetILGenerator(); // ilGenerator.Emit(OpCodes.Ldarg_0); // ilGenerator.Emit(OpCodes.Callvirt, AdapterBase.AdaptedGetter); // //push parameters on the stack // for (int i = 1; i < parameters.Length + 1; i++) // { // ilGenerator.Emit(OpCodes.Ldarg, i); // } // MethodInfo srcMethod = implementer.GetMethod(method.Name, parameterTypes); // if (srcMethod == null) // { // throw new MissingMethodException("The source type " + implementer.FullName + " does not contain the " + method.Name + " method " + // "required to implement the " + toImplement.FullName + " interface!"); // } // if (method.IsVirtual) // { // ilGenerator.Emit(OpCodes.Callvirt, srcMethod); // } // else // { // ilGenerator.Emit(OpCodes.Call, srcMethod); // } // //return // ilGenerator.Emit(OpCodes.Ret); //} //return adapter; return typeBuilder.CreateType(); } private static bool CheckFlag(MethodAttributes value, MethodAttributes flag) { return ((value & flag) == flag); } private static IEnumerable Convert(IEnumerable collection, Converter converter) { if (collection != null) foreach (var item in collection) yield return converter(item); } private static IEnumerable Find(IEnumerable collection, Predicate predicate) { if (collection != null) foreach (var item in collection) if (predicate(item)) yield return item; } private static T FindOne(IEnumerable collection, Predicate predicate) { if (collection != null) foreach (var item in collection) if (predicate(item)) return item; return default(T); } } }