最近重度使用csharp, 在写egraph中的图匹配, 简单记录一下遇到的一些小问题.

0x00 注意隐式转换与构造函数的冲突问题

因为cshapr每次都要写new,所以添加了一些隐式类型转换语法糖. 但是下面的代码就会出现问题, 就是new WildCardPattern(Name)这里其实并不是调用默认的WildCardPattern(string Name, ExprPattern? Pattern), 而是又被隐式类型转换成了WildCardPattern然后准备调用复制构造函数构造,但是隐式类型转换的时候就陷入递归了.

public sealed record WildCardPattern(string Name, ExprPattern? Pattern) : ExprPattern
{
private static int _globalCardIndex = 0;

public WildCardPattern() : this($"wc_{_globalCardIndex++}", null)
{
}

public static implicit operator WildCardPattern(string Name) => new WildCardPattern(Name);

public override bool MatchLeaf(Expr expr) => (Pattern?.MatchLeaf(expr) ?? true) && MatchCheckedType(expr);
}

0x01 C# delegate的本质

最近在弄一个直接jit生成代码然后用c#动态调用的东西,但是需要动态调用你必须要告诉当前的函数指针一个delegate的定义,不然c#就不知道你要输入什么返回什么. 那么既然是jit,我们的就不能提前写好这个定义, 所以需要动态构造一个delegate.

给定一个类:

public class CustomType
{
public delegate float declf(float x, float y);
}
他的delegate的修饰符并不是一种attr,而是一种表示他继承自MulticastDelegate, 所以你可以发现他的是一个NestedType,并且他的基类是MulticastDelegate.
var t = cls_type.GetNestedType("declf");
Assert.Equal(t.BaseType, typeof(MulticastDelegate));
同时他还具备了4个方法,其中一个是构造方法,以及三个重写的方法.

通过检查他的il我们可以发现declf就是个class,所以事情就简化成了构造这个类的问题.:

.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi beforefieldinit CustomType
extends [mscorlib]System.Object
{
// Nested Types
.class nested public auto ansi sealed declf
extends [mscorlib]System.MulticastDelegate
{
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor (
object 'object',
native int 'method'
) runtime managed
{
} // end of method declf::.ctor

.method public hidebysig newslot virtual
instance float32 Invoke (
float32 x,
float32 y
) runtime managed
{
} // end of method declf::Invoke

.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult BeginInvoke (
float32 x,
float32 y,
class [mscorlib]System.AsyncCallback callback,
object 'object'
) runtime managed
{
} // end of method declf::BeginInvoke

.method public hidebysig newslot virtual
instance float32 EndInvoke (
class [mscorlib]System.IAsyncResult result
) runtime managed
{
} // end of method declf::EndInvoke

} // end of class declf


// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2050
// Code size 7 (0x7)
.maxstack 8

IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method CustomType::.ctor

} // end of class CustomType
接下来就照葫芦画瓢把这个类的定义给生成出来,然后我们再用这个定义的类型拿去binding那个dll里面的函数,然后我们就可以动态的invoke生成的代码了~
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab = AssemblyBuilder.DefineDynamicAssembly(aName, AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
TypeBuilder tb = mb.DefineType("MyDynamicType", TypeAttributes.Public);
TypeBuilder nesttb = tb.DefineNestedType("DynamicDelegate", TypeAttributes.NestedPublic | TypeAttributes.Sealed, typeof(MulticastDelegate));
var ctor = nesttb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard | CallingConventions.HasThis, new[] { typeof(object), typeof(IntPtr) });

ILGenerator ctorIL = ctor.GetILGenerator();
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldarg_1);
ctorIL.Emit(OpCodes.Ret);

var invoke = nesttb.DefineMethod("Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(float), new[] { typeof(float), typeof(float) });
var beginInvoke = nesttb.DefineMethod("BeginInvoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(IAsyncResult), new[] { typeof(float), typeof(float), typeof(IAsyncResult), typeof(object) });

var endInvoke = nesttb.DefineMethod("EndInvoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.NewSlot, CallingConventions.Standard | CallingConventions.HasThis, typeof(float), new[] { typeof(IAsyncResult) });
var created_class = tb.CreateType();
return created_class;