Extract arguments of method calls with Mono.Cecil - c#

I am trying to extract all arguments passed to a specific method with Mono.Cecil.
In a post-processing script that runs right after the project being build, I am able to find all method calls, and even extract the type of the argument passed to the function. However, I am not able to get the actual value... So is this even possible with Mono.Cecil, and if yes, what value do I need to look at?
Here is my current code :
List<MethodDefinition> methods = new List<MethodDefinition> ();
foreach (ModuleDefinition _module in assembly.Modules) {
foreach (TypeDefinition _type in _module.Types) {
methods.AddRange (_type.Methods);
}
}
var uiExtensionMethod = methods
.Where(m => m.DeclaringType.Name == "SetCustomText").FirstOrDefault();
var instructions = uiExtensionMethod.Body.Instructions;
var count = instructions.Count;
var instructionArray = instructions.ToArray();
for (int i = 0; i < count; i++) {
if (instructionArray [i] != null) {
if (instructionArray [i].Operand != null) {
var paramType = instructionArray [i].Operand.ToString ();
// So here I have the type of the parameter, but I cannot get the value...
}
}
}

Okay, so I found the solution to this.
The problem was, that Mono.Cecil did indeed find my method calls, but it processed them inside the file, where the argument was already written into a variable, and therefor unable to be converted to a string.
So my solution is, parsing ALL methods that have a string as an Operand, and then detecting their NEXT operation. If the next operation is my function of choice, THEN I found the string I am looking for :)

Related

How to check method exists in C# with certain particular signature?

I want to check if the method for certain parameters is implemented. In this scenario, I have for example these method overloads:
public class Transformations
{
public string TransformFrom(string s) { return s;}
public string TransformFrom(int i) { return "00" + i.ToString();}
public string TransformFrom(DateTime dt) { return
DateTime.ToString("MM/dd/yyyy HH:mm:ss.fff");}
}
Suppose I have decimal like:
object obj = 1M;
If I now call TransformFrom(obj), I get an exception.
Other part of my program returns unfortunately only objects, which really are concrete types like int or datetime. And I want to transform some of these objects to string so that those can be written to the log. So this is a runtime problem. Also I want that my Transformations-class is general enough to be used in other programs too, so there can be objects of any type.
Is there fast way to find out that this method overload does not exist?
Either way you are currently going to get a compile time error, even if there was an overload that accepted a decimal. You can approach this one of 2 ways depending on your needs:
To resolve this at compile time you have to cast to the correct type.
object obj = 1;
var objAsInt = (int) obj;
var result = transformationInstance.TransformFrom(objAsInt);
If there is no proper overload you will get a compile time error and you can resolve this at design time.
To resolve this at runtime use reflection to figure out if there is an overload of the type and if there is pass the instance.
object obj = 1;
var underlyingType = obj.GetType();
var method = typeof(Transformations).GetMethod("TransformFrom", new Type[] { underlyingType });
if(method != null)
{
var result = method.Invoke(transformationInstance, new []{obj});
}
It is possible to find out whether a given overload exists using reflection:
public static bool HasOverloadForArgument(Type targetType, string methodName, object arg)
{
var methodInfo = targetType.GetMethod(name: methodName, types: new[] { arg.GetType() });
return methodInfo != null;
}
Live sample
I'm not sure what is the whole idea here as there won't be much you can do when the transformation is not available during runtime.
Maybe, you'd just be best off using dynamic to avoid all the reflection stuff and caching required otherwise, e.g.
var boxedInt = (object)1;
var boxedFloat = (object)1f;
dynamic dynamicInt = boxedInt;
dynamic dynamicFloat = boxedFloat;
var intResult = new Transformations().TransformFrom(dynamicInt); // works
var floatResult = new Transformations().TransformFrom(dynamicFloat); // throws binder exception

How to get arguments of method to make completion with Roslyn?

I try to make code completion with Roslyn and AvalonEdit.
For example, user have code:
public void Completion(int i,int j) { }
And he types:
Completion(
So, i want to get arguments of method (int i, int j) and make code completion.
I write simple code, wich works with '.' and may be this code will work for '(' letter?
public List<ICompletionData> GetCompletionData(String code,int offset,CompletionType completionType)
{
var syntaxTree = SyntaxFactory.ParseSyntaxTree(code);
var compilation = CSharpCompilation.Create("foo")
.AddReferences(Mscorlib)
.AddSyntaxTrees(syntaxTree);
var semanticModel = compilation.GetSemanticModel(syntaxTree);
var textSpan = GetTextSpan(offset,1);// '.' or '(' coordinates
ITypeSymbol lhsType = null;
if (completionType == CompletionType.DotCompletion)
{
var memberAccessNode = (MemberAccessExpressionSyntax)syntaxTree.GetRoot()
.DescendantNodes(textSpan).Last();
lhsType = semanticModel.GetTypeInfo(memberAccessNode.Expression).Type;
}
else if(completionType==CompletionType.ArgumentListCompletion)
{
var arr = syntaxTree.GetRoot().DescendantNodes(textSpan).Last();
var argumentListMode = (ArgumentListSyntax)syntaxTree.GetRoot().DescendantNodes(textSpan).Last();
var directive = argumentListMode.GetFirstDirective();
var arrgs=argumentListMode.Arguments;
//lhsType = semanticModel.GetTypeInfo(directive).Type;
//how to get lhsType?
}
if (lhsType == null)
return new List<ICompletionData>();
List<ICompletionData> completionDataColl = new List<ICompletionData>();
// and here i make completion data
foreach (var symbol in lhsType.GetMembers())
{
if (!symbol.CanBeReferencedByName
|| symbol.DeclaredAccessibility != Accessibility.Public
|| symbol.IsStatic)
continue;
}
}
The problem is, that i can not get ITypeSymbol lhsType. It is null.
How to get lhsType?
Or, maybe i should use another way?
I don't know the code completion (I couldn't find this class called CompletionType) itself, but here is a way based on Roslyn only: semantic model and the method invocation, which I believe you have available (make the method call string a InvocationExpressionSyntax)
To obtain the arguments of a method you can get its SymbolInfo from the semantic model. Then you get its symbol. A symbol contains the list of parameters(arguments).
You can call SemanticModel.GetSymbolInfo()
The result will provide you a symbol or candidate symbols (if it is an overloaded method).
A method symbol will provide the list of parameters, which is the arguments of that method.

Roslyn: Check if method parameter can not be null

Using Roslyn, my aim is to check if a method parameter is checked for not being null before the parameter is dereferenced. This check can be in a submethod of course.
My approach is to get the first dereferencing of the parameter and search the syntax tree between that and the method start for null checks. How can I do some kind of control flow analysis to determine if the first dereferencing of the parameter can be reached with the parameter being null?
This is way too broad question, with a little explanation what is your final goal. Are you trying to detect null-pointer exceptions before they even happen, 100%? (Pretty much impossible)
I have written static analysis myself few months ago, I didn't use roslyn, but this doesn't matter.
Check this out to get you possibly started - it's reporting warnings when there are unused variables:
internal class UnUsedVariableWarningDefinition : ICodeIssue
{
public IEnumerable<IssueReport> Analyze()
{
var usageMap = new Dictionary<string, int>(StringComparer.InvariantCultureIgnoreCase);
var variableMap = new Dictionary<string, IdentifierNode>(StringComparer.InvariantCultureIgnoreCase);
foreach (var node in NodeAnalyzerHelper.FindNodesDfs(Root))
{
var assignmentNode = node as AssignmentNode;
if (assignmentNode != null)
{
var variableNode = assignmentNode.Identifier;
int usages;
if (!usageMap.TryGetValue(variableNode.Identifier, out usages))
{
usageMap[variableNode.Identifier] = 0;
variableMap[variableNode.Identifier] = variableNode;
}
}
else
{
// not really an assignmentNode,
// let's see if we have detected the usage of IdentifierNode somewhere.
var variableNode = node as IdentifierNode;
if (variableNode != null)
{
if (usageMap.ContainsKey(variableNode.Identifier))
usageMap[variableNode.Identifier]++;
}
}
}
foreach (var node in usageMap.Where(x => x.Value == 0).Select(x => variableMap[x.Key]))
{
yield return node.ConstructWarning("No usages of this variable found. Are you sure this is needed?");
}
}
}
Notice that FindNodesDfs() is basically a syntax tree walker, which walks syntax nodes depth-first style. What it does is just scans AssigfnmentNodes and puts them to Dictionary, as soon as it identifies IdentifierNode, it checks the dictionary if it has previously encountered assignment, or not. It's a bit similiar what you're trying to do, I guess.

Detecting closures with cecil

I have code to detect when an exception is created inside of a method like so:
foreach (var instr in body.Instructions)
{
if (instr.OpCode.Code == Code.Newobj)
{
var methRef = (Mono.Cecil.MethodReference)instr.Operand;
var type = Type.GetType(methRef.DeclaringType.FullName);
if (typeof(System.Exception).IsAssignableFrom(type))
// do stuff
}
}
full code here.
So far this is working well for straight forward code, but in the case where I'm dealing with a closure it obviously won't work because the exception is inside of a method on the object generated for the closure, not in the method that I am currently testing. The exception in:
private static int DataAccessMethod(int value)
{
int r = 0;
System.Threading.ManualResetEvent evt = new System.Threading.ManualResetEvent(false);
var workItem = System.Threading.ThreadPool.QueueUserWorkItem((_) =>
{
if (r == 0)
{
throw new System.InvalidOperationException("I canr spell.");
}
r = value * value;
evt.Set();
});
evt.WaitOne();
return r;
}
for instance will go undetected. My question is:
Is there anyway to detect that the operand to newobj is referring to a closure type?
As #usr noted in comments closures are just normal classes, they doesn't exist distinctly in IL. Only difference is you've not created it; compiler did it for you.
I believe you should be looking at unique compiler generated names to find them. EricLippert describes naming conventions followed by compiler here, Do note that it is subject to change in future versions of compiler.
Also after coming to the conclusion that the Type is a closure you can verify that using CompilerGenerated attribute.

MethodInfo.Invoke parameter order

I'm trying to invoke a method using reflection.
Something like this:
method.Invoke(instance, propValues.ToArray())
The problem is that there isn't a way to ensure the array of parameters is in the right order. Is there a way to specific which values goes on which parameter by name? Or do I really have to make a custom binder? If so, can anyone guide me in the right direction?
Is there a way to specific which values goes on which parameter by name?
Well, you specify them in parameter order. So if you want to map specific values to specific names, you should fetch the parameter list with method.GetParameters and map them that way. For example, if you had a Dictionary<string, object> with the parameters:
var arguments = method.GetParameters()
.Select(p => dictionary[p.Name])
.ToArray();
method.Invoke(instance, arguments);
EDIT: This answer focuses on parameter types not the parameter names. If the code is obfuscated (or having different param names) then it will be difficult to map the solution that Jon Skeet has provided.
Anyway, I had been playing with this a lot.... This is what works best for me (without knowing param names) :
public object CallMethod(string method, params object[] args)
{
object result = null;
// lines below answers your question, you must determine the types of
// your parameters so that the exact method is invoked. That is a must!
Type[] types = new Type[args.Length];
for (int i = 0; i < types.Length; i++)
{
if (args[i] != null)
types[i] = args[i].GetType();
}
MethodInfo _method = this.GetType().GetMethod(method, types);
if (_method != null)
{
try
{
_method.Invoke(this, args);
}
catch (Exception ex)
{
// instead of throwing exception, you can do some work to return your special return value
throw ex;
}
}
return result;
}
so, you can call the above function:
object o = CallMethod("MyMethodName", 10, "hello", 'a');
The above call will should be able to invoke this method with matching signature:
public int MyMethodName(int a, string b, char c) {
return 1000;
}
Please note that he above example is in the scope of 'this'

Categories

Resources