hazzik / DelegateDecompiler

A library which is able to decompile a delegate or a method body to its lambda representation
MIT License
528 stars 62 forks source link

Seems it doesn't support "out" parameter. A "Object reference not set to an instance of an object." exception happend when decompiling such method #108

Closed jxw00100 closed 3 years ago

jxw00100 commented 7 years ago

Example code:

[TestClass]
public class TestOutArg
    {
        [TestMethod]
        public void TestMethod()
        {
            var mi = typeof (TestOutArg).GetMethod("GetInt");
            var ex = MethodBodyDecompiler.Decompile(mi);
            var i = ex.Compile().DynamicInvoke(this);

        }

        public int GetInt()
        {
            int i;
            if (int.TryParse("123", out i))
            {
                return i;
            }
            return -1;
        }
    }
hazzik commented 7 years ago

It's, probably, not possible. The expression does not support declaring the variable, so following expression is not possible:

Expression<Func<int>> exp = () => int.TryParse("123", out var i) ? i : -1;

I could try to make this possible:

int i; // outer variable closure
Expression<Func<int>> exp = () => int.TryParse("123", out i) ? i : -1;

But I don't think that this would be helpful at all.

For this particular case, try TryParsers library

jxw00100 commented 7 years ago

I tried add the variable in a block expression (along with fixing of some other bugs in the AdjustType method).

In the MethodBodyDecompiler.Decompile() method, I temporarily modified it as:

var args = method.GetParameters()
                .Select(p => (Address)Expression.Parameter(p.ParameterType, p.Name))
                .ToList();

            if (!method.IsStatic)
            {
                args.Insert(0, Expression.Parameter(method.DeclaringType, "this"));
            }

            var body = method.GetMethodBody();
            var addresses = new VariableInfo[body.LocalVariables.Count];
            for (int i = 0; i < addresses.Length; i++)
            {
                addresses[i] = new VariableInfo(body.LocalVariables[i].LocalType);
                addresses[i].Address = Expression.Variable(body.LocalVariables[i].LocalType, "var" + i);
            }
            var locals = addresses.ToArray();

            var instructions = method.GetInstructions();
            var ex = Processor.Process(locals, args, instructions.First(), method.ReturnType);
            var optimizedEx = new OptimizeExpressionVisitor().Visit(ex);

            var localParams = locals.Select(l => l.Address.Expression as ParameterExpression)
                                    .Where(l => l != null).ToArray();
            var block = Expression.Block(localParams, optimizedEx);

            return Expression.Lambda(block, args.Select(x => (ParameterExpression)x.Expression));

After that, the test method passed. Not sure if such way meets all cases but guess it should be a clue for resolving such problem.

hazzik commented 3 years ago

After fixing #171/#172 it does not throw NullReferenceException. However, the produced expression is not correct.

hazzik commented 2 years ago

Released in 0.30.0