using System; using System.CodeDom; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Reflection.Emit; using miew.Lambda; using miew.String; namespace miew.Reflection { using String = System.String; static public class Extensions { public struct Retrolabel { public Label l; public int il_offset; public Retrolabel(ILGenerator il) { l = il.DefineLabel(); il_offset = il.ILOffset; il.MarkLabel(l); } }; public static void EmitLdcI4(this ILGenerator il, int i) { switch (i) { case -1: il.Emit(OpCodes.Ldc_I4_M1); break; case 0: il.Emit(OpCodes.Ldc_I4_0); break; case 1: il.Emit(OpCodes.Ldc_I4_1); break; case 2: il.Emit(OpCodes.Ldc_I4_2); break; case 3: il.Emit(OpCodes.Ldc_I4_3); break; case 4: il.Emit(OpCodes.Ldc_I4_4); break; case 5: il.Emit(OpCodes.Ldc_I4_5); break; case 6: il.Emit(OpCodes.Ldc_I4_6); break; case 7: il.Emit(OpCodes.Ldc_I4_7); break; case 8: il.Emit(OpCodes.Ldc_I4_8); break; default: { if (-128 <= i && i <= 127) il.Emit(OpCodes.Ldc_I4_S, (byte)i); else il.Emit(OpCodes.Ldc_I4, i); } break; } } public static Retrolabel MarkRetrolabel(this ILGenerator il) { return new Retrolabel(il); } public static void Emit(this ILGenerator il, OpCode op, Retrolabel retro) { if (il.ILOffset < retro.il_offset) throw new InvalidOperationException(); bool f_s = (il.ILOffset + 7) - retro.il_offset <= 127; if (op == OpCodes.Br || op == OpCodes.Br_S) il.Emit(f_s ? OpCodes.Br_S : OpCodes.Br, retro.l); else if (op == OpCodes.Brtrue || op == OpCodes.Brtrue_S) il.Emit(f_s ? OpCodes.Brtrue_S : OpCodes.Brtrue, retro.l); else if (op == OpCodes.Brfalse || op == OpCodes.Brfalse_S) il.Emit(f_s ? OpCodes.Brfalse_S : OpCodes.Brfalse, retro.l); else throw new NotImplementedException(); } public static IEnumerable<Object> PromoteAnonymous(this IEnumerable seq, String s_typename) { IEnumerator ie = seq.GetEnumerator(); if (!ie.MoveNext()) yield break; Object o = ie.Current; var e = AnonymousTypePromoter.GetPromotionInfo(s_typename, o, null, null, null); yield return e.Promote(o); while (ie.MoveNext()) yield return e.Promote(ie.Current); } public static bool HasInterface(this Type t, Type T_int) { return t.GetInterface(T_int.Name) != null; } } public class AnonymousTypePromoter : CodeCompileUnit { public struct TypePromotionInfo { internal TypePromotionInfo(Assembly asm, Type type) { this.asm = asm; this.type = type; } readonly Assembly asm; readonly Type type; public Assembly Assembly { get { return asm; } } public Type Type { get { return type; } } public Object Promote(Object a) { return asm.CreateInstance(type.Name, false, BindingFlags.Public | BindingFlags.Instance, null, new Object[] { a }, null, null); } public IEnumerable<Object> Promote(IEnumerable seq) { foreach (Object o in seq) yield return asm.CreateInstance(type.Name, false, BindingFlags.Public | BindingFlags.Instance, null, new Object[] { o }, null, null); } }; static Dictionary<Type, TypePromotionInfo> dict = new Dictionary<Type, TypePromotionInfo>(); CodeTypeDeclaration target_class; String[] rgra = null; /// <summary> /// Create a class based on the fields in the specified prototypical instance of an anonymous type. /// The returned structure indicates the created type and assembly, and provides a method for converting /// other instances of the anonymous type to the newly created type. The specified name for the newly /// created class type is only used if this anonymous type has not been converted before. /// </summary> public static TypePromotionInfo GetPromotionInfo( String s_typename, Object prototype, String[] usings, String[] referenced_assemblies, Type[] base_types) { TypePromotionInfo e; Type T = prototype.GetType(); if (!dict.TryGetValue(T, out e)) { var atp = new AnonymousTypePromoter(s_typename, prototype, usings, referenced_assemblies, base_types); #if false Debug.Print(atp.ToString()); #endif Assembly asm = atp.Compile(); e = new TypePromotionInfo(asm, asm.GetType(s_typename, true, false)); dict.Add(T, e); } return e; } AnonymousTypePromoter(String s_typename, Object prototype, String[] usings, String[] rgra, Type[] base_types) { this.rgra = rgra; CodeNamespace _namespace = new CodeNamespace(); _namespace.Imports.Add(new CodeNamespaceImport("System")); if (usings != null) foreach (String u in usings) _namespace.Imports.Add(new CodeNamespaceImport(u)); target_class = new CodeTypeDeclaration(s_typename); target_class.IsClass = true; target_class.TypeAttributes = TypeAttributes.Public; if (base_types != null) foreach (Type bt in base_types) target_class.BaseTypes.Add(new CodeTypeReference(bt)); _namespace.Types.Add(target_class); this.Namespaces.Add(_namespace); AddConstructor(prototype); } void AddFieldAndAccessors(String s_field, String s_prop, Type t) { CodeMemberField fld = new CodeMemberField(); fld.Attributes = MemberAttributes.Private; fld.Name = s_field; fld.Type = new CodeTypeReference(t); target_class.Members.Add(fld); CodeMemberProperty prop = new CodeMemberProperty(); prop.Attributes = MemberAttributes.Public | MemberAttributes.Final; prop.Name = s_prop; prop.HasGet = true; prop.HasSet = true; prop.Type = new CodeTypeReference(t); prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), s_field))); prop.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), s_field), new CodeArgumentReferenceExpression("value"))); target_class.Members.Add(prop); } void AddConstructor(Object prototype) { CodeConstructor constructor = new CodeConstructor(); constructor.Attributes = MemberAttributes.Public | MemberAttributes.Final; target_class.Members.Add(constructor); constructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(Object), "obj")); CodeArgumentReferenceExpression o_arg = new CodeArgumentReferenceExpression("obj"); constructor.Statements.Add(Anon<CodeVariableDeclarationStatement>.New(w => { w.Type = new CodeTypeReference(typeof(FieldInfo[])); w.Name = "rgfi"; w.InitExpression = Anon<CodeMethodInvokeExpression>.New(y => { y.Method = new CodeMethodReferenceExpression( Anon<CodeMethodInvokeExpression>.New(x => { x.Method = new CodeMethodReferenceExpression(o_arg, "GetType"); }), "GetFields"); y.Parameters.Add(new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "NonPublic"), CodeBinaryOperatorType.BitwiseOr, new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(BindingFlags)), "Instance"))); }); })); CodeVariableReferenceExpression o_rgfi = new CodeVariableReferenceExpression("rgfi"); int q = 0; foreach (var mi in prototype.GetType().GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) { String s_prop = mi.Name.ExtractAngleTagged(); if (String.IsNullOrEmpty(s_prop)) throw new Exception(); String s_field = "__" + s_prop; Type T_prop = mi.FieldType; CodeFieldReferenceExpression fld_target = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), s_field); CodeCastExpression fld_source = new CodeCastExpression(new CodeTypeReference(T_prop), new CodeMethodInvokeExpression(Anon<CodeMethodReferenceExpression>.New(x => { x.TargetObject = Anon<CodeArrayIndexerExpression>.New(v => { v.Indices.Add(new CodePrimitiveExpression(q)); v.TargetObject = o_rgfi; }); x.MethodName = "GetValue"; }), o_arg)); constructor.Statements.Add(new CodeAssignStatement(fld_target, fld_source)); AddFieldAndAccessors(s_field, s_prop, T_prop); q++; } } CodeDomProvider Provider { get { return CodeDomProvider.CreateProvider("csharp", new Dictionary<String, String> { { "CompilerVersion", "v4.0" } }); } } public override string ToString() { CodeGeneratorOptions opt = new CodeGeneratorOptions(); opt.BracingStyle = "C"; using (StringWriter sw = new StringWriter()) { Provider.GenerateCodeFromCompileUnit(this, sw, opt); return sw.ToString(); } } public Assembly Compile() { CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp"); var cp = new CompilerParameters(); cp.GenerateInMemory = true; if (rgra != null) foreach (String ra in rgra) cp.ReferencedAssemblies.Add(ra); var cr = Provider.CompileAssemblyFromDom(cp, new CodeCompileUnit[] { this }); #if false if (cr.Errors.Count > 0) Debugger.Break(); #endif return cr.CompiledAssembly; } }; }