MethodBody (または他のリフレクション手法) を System.Linq.Expressions.Expression ツリーに変換する方法はありますか?
3 に答える
実際に可能です。DelegateDecompiler を参照してください。
https://github.com/hazzik/DelegateDecompiler
注:私はこのプロジェクトと提携していません
編集
プロジェクトの基本的なアプローチは次のとおりです。
- 変換したいメソッドの MethodInfo を取得する
- methodInfo.GetMethodBody を使用して MethodBody オブジェクトを取得します。これには、とりわけ、MSIL と、引数とローカルに関する情報が含まれます。
- 手順を確認し、オペコードを調べて、適切な式を構築します
- すべてを結び付けて、最適化された Expression を返す
メソッド本体を逆コンパイルするプロジェクトのコード スニペットを次に示します。
public class MethodBodyDecompiler
{
readonly IList<Address> args;
readonly VariableInfo[] locals;
readonly MethodInfo method;
public MethodBodyDecompiler(MethodInfo method)
{
this.method = method;
var parameters = method.GetParameters();
if (method.IsStatic)
args = parameters
.Select(p => (Address) Expression.Parameter(p.ParameterType, p.Name))
.ToList();
else
args = new[] {(Address) Expression.Parameter(method.DeclaringType, "this")}
.Union(parameters.Select(p => (Address) Expression.Parameter(p.ParameterType, p.Name)))
.ToList();
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);
}
locals = addresses.ToArray();
}
public LambdaExpression Decompile()
{
var instructions = method.GetInstructions();
var ex = Processor.Process(locals, args, instructions.First(), method.ReturnType);
return Expression.Lambda(new OptimizeExpressionVisitor().Visit(ex), args.Select(x => (ParameterExpression) x.Expression));
}
}
いいえ、ありません。
あなたは基本的にReflectorのやや単純なバージョンを求めています。
はい、可能です...しかし、私の知る限り、まだ実行されていません。
メソッドを式ツリーに逆コンパイルするライブラリを誰かが知っている場合は、私に知らせるか、上記のステートメントを編集してください。
あなたがしなければならないことの中で最も難しい部分は、CIL逆コンパイラを書くことです。つまり、かなり低レベルの CIL 命令 (概念的にはスタック マシンを対象とする) を、より高レベルの式に変換する必要があります。
Redgate のReflectorや Telerik のJustDecompileなどのツールはまさにそれを行いますが、式ツリーを構築する代わりにソース コードを表示します。式ツリーは基本的にまだ言語に依存しないため、さらに一歩進んでいると言えます。
これが特にトリッキーになるいくつかの注目すべきケース:
Expression
定義済みのツリー ノードが存在しない CIL 命令のケースに対処する必要があります。tail.call
、、またはcpblk
(ここでは少し推測しています)としましょう。つまり、カスタムの式ツリー ノード タイプを作成する必要があります。.Compile()
式ツリーコンパイラはカスタムノードを標準ノードに分解しようとするため、式ツリーが問題になる可能性があるときに、それらを実行可能なメソッドにコンパイルし直します。それが不可能な場合は、これ以上式ツリーをコンパイルできず、調べることしかできません。C# ブロックなど、特定の高レベルの構造を認識して
using
、(カスタム) 式ツリー ノードを構築しようとしますか? C#はコンパイル時にusing
相当するものに分解されることに注意してください。メソッド本体の CIL 命令と例外処理句を反映した場合ではなく、これが表示される可能性があります。try…finally { someObj.Dispose(); }
using
したがって、一般に、特定のコード パターンを「認識」し、それらをより高いレベルの概念に要約できる必要があると考えてください。