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;
}
};
}