weixin_39972151
weixin_39972151
2021-01-05 03:48

add a convenient factory to generate transpilers

I find quite of my code is duplicate when using harmony transpiler to patch. So i made a TranspilerFactory because i didn't find something similar to this in Tools. It can parse input string with wildcards to generate transpilers to replace,insert and delete. Here is the usage:

csharp
class Student
{
    public Student(string name)
    {
        Name = name;
    }
    private string Name;
    public void Speak(string something)
    {
        Console.WriteLine($"{Name} says : {something}");
    }
}
[HarmonyPatch(typeof(Student), "Speak")]
static class StudentSpeakPatch
{
    static IEnumerable<codeinstruction> Transpiler(ILGenerator generator, IEnumerable<codeinstruction> instr)
    => new TranspilerFactory().Delete("ldstr  says : ").Insert("ldstr  said nothing")
    .Replace("ldarg.1;call *", "call string::Concat(string, string)").Transpiler(generator, instr);
}
</codeinstruction></codeinstruction>

Part of my idea comes from /pull/9

该提问来源于开源项目:pardeike/Harmony

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

9条回答

  • weixin_39953244 weixin_39953244 4月前

    That’s a lot of code and no clear description on how to use it. Your example is not sufficient because your API is string based and thus need a full specification of the specific domain language you used/invented.

    As it is right now I cannot incorporate your code.

    点赞 评论 复制链接分享
  • weixin_39972151 weixin_39972151 4月前

    Now it is definitely more clear and convenient! Cant wait to be added😝

    点赞 评论 复制链接分享
  • weixin_39972151 weixin_39972151 4月前

    Usage of this version: - targetClass:

    cs
    class Student
    {
        public Student(string name) => Name = name;
        private string Name;
        public void Speak(string something) => Console.WriteLine($"{Name} says : {something}");
    }
    
    • patch1:
    cs
    var harmony = new Harmony("zzz");
    new TranspilerFactory()
        .Search("ldfld Student::Name")
        .Insert("localvar string;stloc.s 0;br 0")
        .Replace("ldarg.1", "label 0;ldarg.1;ldstr  is said by ;ldloc.s 0")
        .PatchFor(harmony, "Student::Speak(string)");
    
    • patch2:
    cs
    [HarmonyPatch(typeof(Student), "Speak")]
    class MyPatch : TranspilerPatch
    {
        static void Prepare() => factory.Search("ldfld Student::Name")
            .Insert("localvar string;stloc.s 0;br 0")
            .Replace("ldarg.1", "label 0;ldarg.1;ldstr  is said by ;ldloc.s 0");
    }
    

    Both of them works!

    点赞 评论 复制链接分享
  • weixin_39953244 weixin_39953244 4月前

    Maybe you misunderstood me, so let me rephrase it. I need full documentation on the methods and the arguments of all methods. As it is now, it is not clear to me what I can pass to your methods - especially the string arguments.

    Some examples (but there are many more questions):

    • what happens if I call .Search() twice in a row?
    • what does PatchFor do?
    • are there more methods than the ones you listed in the example?
    • can I pass ”Andreas” in any argument and if not, what will happen?
    • what are the search arguments?
    • how do they work?
    • how do the methods work together?
    • what are the limits?

    If your pull request would contain a simple self-explaining method or just a bug fix, I could pull it right away. Here, you give me a whole new way to run stuff but no explanation. I could put a lot of time reading through your code and figuring it out myself but just expecting me to accept this or understand your design from just the code is asking too much.

    点赞 评论 复制链接分享
  • weixin_39972151 weixin_39972151 4月前

    Sorry I do have a bad habit that write very few comments. A full documentation is little bit hard to me. But we programmers communicate with code, aren't we😅 Some explaintion: - My main idea is to treat the CodeInstuctions more as a stream instead of an array. It is only processed once from beginning to end. - Once you add a Search task, the position of the stream is moved forward. This is the only thing that it does. - So you can't Search backwards, the position never goes back. It will search the rest of the stream - PatchFor just calls Harmony.Patch(), the generated transpiler will be applied to specific method - If you pass ”Andreas” in Parse.Parse(), it trys to get a coresponding InlineNone OpCode and, of course, throw a Exception - I believe strings are the shortest way to represent a bunch of CodeInstructions. So i tried to make a Parser which parse like logged ILCode's style - However, the limitation is obvious. The above two simple ways can't handle multithreading situation - It sacrifice rigor for convenience. It may have some strange problems in extreme cases

    点赞 评论 复制链接分享
  • weixin_39953244 weixin_39953244 4月前

    But as a minimum you have to explain the string syntax. Completely, on all calls and for all cases. I understand that you find it difficult to document this but I won’t pull it otherwise.

    点赞 评论 复制链接分享
  • weixin_39972151 weixin_39972151 4月前

    It's hard to explained in the comments. But i can do here - Format for single instruction "opcode operand(optional)" both opcode and operand can be * , meaning arbitrary arbitrary opcode implies arbitrary operand. If opcode is * , the following is ignored, which is same to "*" - Format for instructions "[instructions seperated by semicolon]"

    opcode type | format for operand -------------- | ------------------- InlineMethod(call,newobj) | [type name][one or two colon][method name](optional)([parameter types seperated by comma])(optional) InlineField(ldfld,stfld) | [type name][one or two colon][field name] DeclareVar(localvar) | string for localvar type InlineVar(ldloc,stloc_s) | number for localvar index MarkLabel(label) | number for label index InlineBrTarget(br,brtrue) | number for label index InlineString(ldstr) | just the string itself, no need to add quotation marks InlineI etc. | will be converted to certain value type

    Examples - call string::Concat(string, string) - newobj Student(string) - br 0 - label 0 - localvar int - ldloc_s 0

    It really parse like logged ILCodes, as you can see😥

    点赞 评论 复制链接分享
  • weixin_39680121 weixin_39680121 4月前

    My 2 cents: I don't think this belongs in Harmony proper - it should be its own library.

    It's a rather opinionated take on transpiler abstraction. I appreciate the overall functionality, but I'm not a fan of the string-based API. I'd much prefer a more type-safe API (for ex, something like the internal ILGen API). And some functionality like the searching could be generified to work on any list rather than a list of CodeInstructions.

    点赞 评论 复制链接分享
  • weixin_39953244 weixin_39953244 4月前

    I totally agree with and will reject this pull.

    点赞 评论 复制链接分享

相关推荐