How can I improve the recursion capabilities of my ECMAScript implementation? - c#

After some resent tests I have found my implementation cannot handle very much recursion. Although after I ran a few tests in Firefox I found that this may be more common than I originally thought. I believe the basic problem is that my implementation requires 3 calls to make a function call. The first call is made to a method named Call that makes sure the call is being made to a callable object and gets the value of any arguments that are references. The second call is made to a method named Call which is defined in the ICallable interface. This method creates the new execution context and builds the lambda expression if it has not been created. The final call is made to the lambda that the function object encapsulates. Clearly making a function call is quite heavy but I am sure that with a little bit of tweaking I can make recursion a viable tool when using this implementation.
public static object Call(ExecutionContext context, object value, object[] args)
{
var func = Reference.GetValue(value) as ICallable;
if (func == null)
{
throw new TypeException();
}
if (args != null && args.Length > 0)
{
for (int i = 0; i < args.Length; i++)
{
args[i] = Reference.GetValue(args[i]);
}
}
var reference = value as Reference;
if (reference != null)
{
if (reference.IsProperty)
{
return func.Call(reference.Value, args);
}
else
{
return func.Call(((EnviromentRecord)reference.Value).ImplicitThisValue(), args);
}
}
return func.Call(Undefined.Value, args);
}
public object Call(object thisObject, object[] arguments)
{
var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
var variableEnviroment = Scope.NewDeclarativeEnviroment();
var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
Engine.EnterContext(newContext);
var result = Function.Value(newContext, arguments);
Engine.LeaveContext();
return result;
}

I cannot believe how easy this was to get working. Basically in my compiler I check to see if the function is returning the result of calling itself. If so I instead return the arguments that are being passed. Then I simply grab any reference values and re-invoke the backing lambda. With this in place I was able to make millions of recursive calls.
I would like to thank DrJokepu for inspiring this solution.
public object Call(object thisObject, object[] arguments)
{
var lexicalEnviroment = Scope.NewDeclarativeEnviroment();
var variableEnviroment = Scope.NewDeclarativeEnviroment();
var thisBinding = thisObject ?? Engine.GlobalEnviroment.GlobalObject;
var newContext = new ExecutionContext(Engine, lexicalEnviroment, variableEnviroment, thisBinding);
var result = default(object);
var callArgs = default(object[]);
Engine.EnterContext(newContext);
while (true)
{
result = Function.Value(newContext, arguments);
callArgs = result as object[];
if (callArgs == null)
{
break;
}
for (int i = 0; i < callArgs.Length; i++)
{
callArgs[i] = Reference.GetValue(callArgs[i]);
}
arguments = callArgs;
}
Engine.LeaveContext();
return result;
}

Related

C# - pass on variable amount of parameters to other method within a method

I know the title is a little bit tricky to understand, but the following example should clarify what I mean:
Imagine you have a method with 2 overloads:
void Method(int i)
{
Console.WriteLine("Method(int) called");
}
void Method(int i, string s)
{
Console.WriteLine("Method(int, string) called");
}
Then you have another method that takes a variable amount of parameters:
void MethodOverload(params dynamic[] parameters)
{
Method(parameters); // Call one of the overloading methods depending on the parameter amount and their type
}
The method above accepts any amount of parameters with any type. I want to call one of the overloading methods depending on the amount of passed parameters and their type.
For example:
void Run()
{
TestFuncOverload(5); // Output: "testFunc(int) called"
TestFuncOverload(5, "some text"); // Output: "testFunc(int, string) called"
TestFuncOverload(5, 5); //Error
}
How does one achieve this in C#?
You could do it with reflection but I don't recommend it. Reflection is slow and to do this you need to use it a lot. You should try to solve it with a different approach, but in case you really need to do it this way, this should do the trick:
public void MethodOverload(params dynamic[] parameters)
{
//Check if the array is null
if (parameters == null)
throw new ArgumentNullException(nameof(parameters));
//Create a list of the types in the dynamic[]
var inputParameterTypes = new Type[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
inputParameterTypes[i] = parameters[i].GetType();
}
const string NameOfMethod = nameof(Method); //This should be the name of your method which will be called
//Get every method from this class which has the name you are looking for
var methods = this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(x => x.Name == NameOfMethod);
foreach (var method in methods)
{
//Get the parameters of the method
var methodParameters = method.GetParameters();
if (methodParameters.Length != inputParameterTypes.Length)
continue;
//Check if the types match with the input parameters
var match = true;
for (int i = 0; i < methodParameters.Length; i++)
{
//Check if the type matches
if (methodParameters[i].ParameterType == inputParameterTypes[i])
continue;
//Doesn't match
match = false;
}
if (!match)
continue;
//Call the method and return
method.Invoke(this, parameters);
return;
}
//If this is reached no valid methods were found
throw new Exception("No valid methods found!");
}
(This code assumes that all methods are in the same class)
After doing some measurements with this code:
var a = new Foo();
var parameters = new dynamic[][]
{
new dynamic[] { 1, "Test" },
new dynamic[] { 2 }
};
var sw = Stopwatch.StartNew();
for (int i = 0; i < Num; i++)
{
a.MethodOverload(parameters[i % 2]);
}
sw.Stop();
Console.WriteLine($"{Num} iterations took {sw.Elapsed.TotalMilliseconds} milliseconds. Average time: {sw.Elapsed.TotalMilliseconds / Num} milliseconds");
(Removed the Console.WriteLine from the called functions)
here are the results:
10000000 iterations took 5283.9398 milliseconds. Average time: 0.00052839398 milliseconds
100000000 iterations took 51244.9142 milliseconds. Average time: 0.000512449142 milliseconds
I could do that with below code. It costs tons of reflection, you can consider another approach to avoid performance problems.
public class Test
{
private void Method(int i)
{
Console.WriteLine("Method(int) called");
}
private void Method(int i, string s)
{
Console.WriteLine("Method(int, string) called");
}
public void Method(params object[] parameters)
{
var m = typeof(Test).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
.Where(x => x.Name == "Method" &&
!x.GetParameters()
.Any(p => p.IsDefined(typeof(ParamArrayAttribute), false)))
.Where(x => x.GetParameters().Count() == parameters.Count())
.Where(x => x.GetParameters()
.Select(y => Type.GetType("System." + y.ParameterType.Name))
.Zip(parameters.Select(z => z.GetType()), Equals)
.All(q => q))
.FirstOrDefault();
if(m == null) throw new Exception ("method not found");
//null result because of void method.
var result = m.Invoke(this, parameters);
}
}
static void Main(string[] args)
{
Test t = new Test();
//"Method(int) called"
t.Method(0);
//"Method(int, string) called"
t.Method(0, "");
//throws ex
t.Method("", "");
}
You'll need to use Reflection and call the method by Invoke.

dynamically create delegate for ctor

I'm trying to create generic factory class. Since Activator.CreateInstance is pretty slow, I decided to use delegates. The goal was call constructor any public constructor, regardless of parameters count. So I was going like this:
public void Register<VType>(TKey key, params object[] args) where VType : TType
{
ConstructorInfo ci = typeof(VType).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, args.Select(a => a.GetType()).ToArray(), new ParameterModifier[] { });
if (ci == null)
throw new InvalidOperationException(string.Format("Constructor for type '{0}' was not found.", typeof(VType)));
var pExp = Expression.Parameter(args.GetType());
var ctorParams = ci.GetParameters();
var expArr = new Expression[ctorParams.Length];
var p = new ParameterExpression[ctorParams.Length];
for (var i = 0; i < ctorParams.Length; i++)
{
var ctorType = ctorParams[i].ParameterType;
var pName = ctorParams[i].Name;
var argExp = Expression.ArrayIndex(pExp, Expression.Constant(i));
var argExpConverted = Expression.Convert(argExp, ctorType);
expArr[i] = argExpConverted;
p[i] = Expression.Parameter(args[i].GetType(), pName);
}
var foo = Expression.Lambda(Expression.New(ci, expArr), p);
Delegate constructorDelegate = foo.Compile();
FactoryMap.Add(key, constructorDelegate);
}
And then - call delegate in Create method. With no parameters all goes well, but when I'm adding some - I'm getting InvalidOperationException - "variable '' of type 'System.Object[]' referenced from scope '', but it is not defined", after foo.Compile() call.
Why? How can I resolve this issue?
Below is a class that exposes an extention method that gives you a delegate for creating an instance of type T by calling the constructor that binds to specified paramArguments types.
public static class ConstructorCallExcentions
{
private static Dictionary<ConstructorInfo, Func<Object[], Object>> _constructors = new Dictionary<ConstructorInfo,Func<object[],object>> ();
private static object syncObject = new object();
public static Func<Object[], Object> CreateConstructor<T>(this T #this, params Type[] paramArguments)
{
ConstructorInfo cInfo = typeof(T).GetConstructor(paramArguments);
if (cInfo == null)
throw new NotSupportedException("Could not detect constructor having the coresponding parameter types");
Func<Object[], Object> ctor;
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
lock (_constructors)
{
if (false == _constructors.TryGetValue (cInfo, out ctor))
{
// compile the call
var parameterExpression = Expression.Parameter(typeof(object[]), "arguments");
List<Expression> argumentsExpressions = new List<Expression>();
for (var i = 0; i < paramArguments.Length; i++)
{
var indexedAcccess = Expression.ArrayIndex(parameterExpression, Expression.Constant(i));
// it is NOT a reference type!
if (paramArguments [i].IsClass == false && paramArguments [i].IsInterface == false)
{
// it might be the case when I receive null and must convert to a structure. In this case I must put default (ThatStructure).
var localVariable = Expression.Variable(paramArguments[i], "localVariable");
var block = Expression.Block (new [] {localVariable},
Expression.IfThenElse (Expression.Equal (indexedAcccess, Expression.Constant (null)),
Expression.Assign (localVariable, Expression.Default (paramArguments [i])),
Expression.Assign (localVariable, Expression.Convert(indexedAcccess, paramArguments[i]))
),
localVariable
);
argumentsExpressions.Add(block);
}
else
argumentsExpressions.Add(Expression.Convert(indexedAcccess, paramArguments[i])); // do a convert to that reference type. If null, the convert is FINE.
}
// check if parameters length maches the length of constructor parameters!
var lengthProperty = typeof (Object[]).GetProperty ("Length");
var len = Expression.Property (parameterExpression, lengthProperty);
var invalidParameterExpression = typeof(ArgumentException).GetConstructor(new Type[] { typeof(string) });
var checkLengthExpression = Expression.IfThen (Expression.NotEqual (len, Expression.Constant (paramArguments.Length)),
Expression.Throw(Expression.New(invalidParameterExpression, Expression.Constant ("The length does not match parameters number")))
);
var newExpr = Expression.New(cInfo, argumentsExpressions);
var finalBlock = Expression.Block(checkLengthExpression, Expression.Convert(newExpr, typeof(Object)));
_constructors[cInfo] = ctor = Expression.Lambda(finalBlock, new[] { parameterExpression }).Compile() as Func<Object[], Object>;
}
}
}
return ctor;
}
}
To use it, for example supose you have this class:
public class Test
{
public Test(string s, int h)
{
Console.Write("aaa");
}
}
Then write this code:
var ctor = default(Test).CreateConstructor(typeof(string), typeof(int));
var newlyObject = ctor(new object[] { "john", 22 });
From your example, I saw that your intentions are to use the Delegate to invoke later any constructor. Instead of using Delegate and the DynamicInvoke API, use my
Func <Object[], Object>.
Why? Here is a couple of advantages that I have in mind right now:
1) DynamicInvoke is much slower than calling a direct typed delegate.
2) DynamicInvoke will break any stack trace in case of an exception. What I mean is that whenever an exception is thrown in the constructor, you will receive a TargetInvocationException instead of the real exception that happened. You can inspect the InnerException of that TargetInvocationException but ... clear is more work to do. Calling directly the typed delegate Func will save you from this issue.
Happy coding!

Generic extension method for retrieving 'default' value?

I'm playing around a bit with determining the default values of objects, based on the example here:
https://stackoverflow.com/a/3195792/1293496
This particular extension method was created for System.Type. What I was trying to accomplish was to make this even more generic, where I could do something like this:
int i = 3;
bool amIaDefaultValue = i.IsDefaultValue();
I would expect this to return true if i == 0 (the default value for an int), and false for all other instances.
Here is my initial attempt:
public static bool IsDefaultValue<T>(this T value)
{
var t = typeof(T); // always comes back as object..?
if (t.IsValueType && Nullable.GetUnderlyingType(t) == null)
{
return value.Equals(Activator.CreateInstance<T>());
}
else
{
var defaultValue = default(T);
if (value == null)
return defaultValue == null;
else
return value.Equals(defaultValue);
}
}
On the plus side, I'm able to attach .IsDefaultValue() to any object. Unfortunately, the type of T always comes back as System.Object. I can get the correct type if I set it up this way:
var t = typeof(value);
But if the value happens to be null, I'll get an error straight away. Is there a good workaround for implementing an extension method like this? Or should I stick to the tried and tested route from the example?
Edit
As pointed out by comments, it seems I oversimplified this a bit and missed the root of the problem. Here's what was actually calling my IsDefaultValue():
foreach (var imprintProperty in deltas.GetType().GetProperties())
{
var value = imprintProperty.GetValue(deltas);
if (!value.IsDefaultValue())
{
// get corresponding prop in programmable area
var programmableProp = progarea.GetType().GetProperty(imprintProperty.Name);
if (programmableProp != null)
programmableProp.SetValue(progarea, value);
}
}
And now it becomes obvious that .GetValue is always returning as System.Object. Uff.
Is it still possible to treat the object as its underlying type in the extension method? Sorry for the confusion with this.
Take a look at this:
static class Program
{
static void Main()
{
int a = 1;
Console.WriteLine("a.IsDefaultValue() : " + a.IsDefaultValue());
a = 0;
Console.WriteLine("a.IsDefaultValue() : " + a.IsDefaultValue());
object obj = new object();
Console.WriteLine("obj.IsDefaultValue() : " + obj.IsDefaultValue());
obj = null;
Console.WriteLine("obj.IsDefaultValue() : " + obj.IsDefaultValue());
int? b = 1;
Console.WriteLine("b.IsDefaultValue() : " + b.IsDefaultValue());
b = null;
Console.WriteLine("b.IsDefaultValue() : " + b.IsDefaultValue());
Console.ReadKey(true);
}
static bool IsDefaultValue<T>(this T value)
{
if (ReferenceEquals(value, null))
{
return true;
}
var t = value.GetType();
if (t.IsValueType)
{
return value.Equals(Activator.CreateInstance(value.GetType()));
}
return false;
}
}
Apparently works (I've to say that I was convinced that the other way should have worked but not)

Handle null parameters while calling a method using Reflection

I'm trying to write code that will infer types from a parameter list and then call the method that matches those parameters. This works very well, except when the parameter list has a null value in it.
I am wondering how I might cause the Type.GetMethod call to match a function/overload, even with a null parameter in the parameters list.
object CallMethodReflection(object o, string nameMethod, params object[] args)
{
try
{
var types = TypesFromObjects(args);
var theMethod = o.GetType().GetMethod(nameMethod, types);
return (theMethod == null) ? null : theMethod.Invoke(o, args);
}
catch (Exception ex)
{
return null;
}
}
Type[] TypesFromObjects(params object[] pParams)
{
var types = new List<Type>();
foreach (var param in pParams)
{
types.Add((param == null) ? null : param.GetType());
}
return types.ToArray();
}
The main problem line is the types.Add((param == null) ? null : param.GetType());, which will cause the GetMethod call to fail with a null value in the types array.
void Function1(string arg1){ }
void Function1(string arg1, string arg2){ }
void Function1(string arg1, string arg2, string arg3){ }
void Function2(string arg1){ }
void Function2(string arg1, int arg2){ }
void Function2(string arg1, string arg2){ }
/*1*/ CallMethodReflection(obj, "Function1", "String", "String"); // This works
/*2*/ CallMethodReflection(obj, "Function1", "String", null); // This doesn't work, but still only matches one overload
/*3*/ CallMethodReflection(obj, "Function2", "String", "String"); // This works
/*4*/ CallMethodReflection(obj, "Function2", "String", null); // This doesn't work, and I can see why this would cause problems
Mainly, I'm trying to determine how to change my code so that line /*2*/ works as well.
There are overrides to the GetMethod call which take an object derived from the Binder class. This allows you to override the default method binding and return the method you want to use, based on the actual parameters passed. This is essentially what the two other answers are doing as well. There is some sample code here:
http://msdn.microsoft.com/en-us/library/system.reflection.binder.aspx
An option that has not been mentioned is to use Fasterflect, a library designed to make reflection tasks easier and faster (through IL generation).
To invoke a method given a dictionary of named parameters (or an object with properties that should be used as parameters), you can invoke the best match like this:
obj.TryCallMethod( "SomeMethod", argsDictionary );
obj.TryCallMethod( "AnotherMethod", new { Foo = "Bar" } );
If all you have are the parameter values and their ordering, you can use another overload:
obj.TryCallMethodWithValues( "MyMethod", 42, "foo", "bar", null, 2.0 );
PS: You'll need to obtain the latest bits from source control to take advantage of the TryCallMethodWithValues extension.
Disclaimer: I am a contributor to the Fasterflect project.
For any parameter that is null you could just match to any reference type. The following very simple/naive code will work for your methods as shown, but it doesn't handle things like exceptions on ambiguities or more complex cases using ref/out parameters or being able to pass a derived type to the method or generic methods.
If you are using 4.0 then simply using dynamic might be a better choice.
object CallMethodReflection(object o, string nameMethod, params object[] args)
{
try
{
var types = TypesFromObjects(args);
var oType = o.GetType();
MethodInfo theMethod = null;
// If any types are null have to perform custom resolution logic
if (types.Any(type => type == null))
{
foreach (var method in oType.GetMethods().Where(method => method.Name == nameMethod))
{
var parameters = method.GetParameters();
if (parameters.Length != types.Length)
continue;
//check to see if all the parameters match close enough to use
bool methodMatches = true;
for (int paramIndex = 0; paramIndex < parameters.Length; paramIndex++)
{
//if arg is null, then match on any non value type
if (args[paramIndex] == null)
{
if (parameters[paramIndex].ParameterType.IsValueType)
{
methodMatches = false;
break;
}
}
else //otherwise match on exact type, !!! this wont handle things passing a type derived from the parameter type !!!
{
if (parameters[paramIndex].ParameterType != args[paramIndex].GetType())
{
methodMatches = false;
break;
}
}
}
if (methodMatches)
{
theMethod = method;
break;
}
}
}
else
{
theMethod = oType.GetMethod(nameMethod, types);
}
Console.WriteLine("Calling {0}", theMethod);
return theMethod.Invoke(o, args);
}
catch (Exception ex)
{
Console.WriteLine("Could not call method: {0}, error: {1}", nameMethod, ex.ToString());
return null;
}
}
I think you would have to do:
var methods = o.GetType().GetMethods().Where(m => m.Name == methodName);
Then essentially do your own overload resolution. You could try your existing method first, catch the exception and then try the above.
Thanks to the MSDN link as well as some additional SO discussion and an outside forum discussion involving a prominent SO member, I have tried to implement my own solution, which is working for me so far.
I created a class which inherited the Binder class and put my logic to handle the potentially null arguments/types in there.
object CallMethodReflection(object o, string nameMethod, params object[] args)
{
try
{
var types = TypesFromObjects(args);
var theMethod = o.GetType().GetMethod(nameMethod, CustomBinder.Flags, new CustomBinder(), types, null);
return (theMethod == null) ? null : theMethod.Invoke(o, args);
}
catch (Exception ex)
{
return null;
}
}
Type[] TypesFromObjects(params object[] pParams)
{
var types = new List<Type>();
foreach (var param in pParams)
{
types.Add((param == null) ? typeof(void) : param.GetType()); // GetMethod above doesn't like a simply null value for the type
}
return types.ToArray();
}
private class CustomBinder : Binder
{
public const BindingFlags Flags = BindingFlags.Public | BindingFlags.Instance;
public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] matches, Type[] types, ParameterModifier[] modifiers)
{
if (matches == null)
throw new ArgumentNullException("matches");
foreach (var match in matches)
{
if (MethodMatches(match.GetParameters(), types, modifiers))
return match;
}
return Type.DefaultBinder.SelectMethod(bindingAttr, matches, types, modifiers); // No matches. Fall back to default
}
private static bool MethodMatches(ParameterInfo[] parameters, Type[] types, ParameterModifier[] modifiers)
{
if (types.Length != parameters.Length)
return false;
for (int i = types.Length - 1; i >= 0; i--)
{
if ((types[i] == null) || (types[i] == typeof(void)))
{
if (parameters[i].ParameterType.IsValueType)
return false; // We don't want to chance it with a wonky value
}
else if (!parameters[i].ParameterType.IsAssignableFrom(types[i]))
{
return false; // If any parameter doesn't match, then the method doesn't match
}
}
return true;
}
}
Since the Binder class is an abstract class, you have to override a few other members to actually use this code, but most of my overrides just front the Type.DefaultBinder object.
public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] matches, object value, CultureInfo culture)
{
return Type.DefaultBinder.BindToField(bindingAttr, matches, value, culture);
}
I didn't test it and i think the other answers are much better, but i'm wondering why this wouldn't work:
foreach (var param in pParams.Where(p => p != null)
{
types.Add(param.GetType());
}
You could approach the problem by implementing your own GetMethod that iterates through all the method in the object and determine which one is the best match, I hope this helps.
I tested the following method with the example you provided and it worked
MethodInfo SmarterGetMethod(object o, string nameMethod, params object[] args)
{
var methods = o.GetType().GetMethods();
var min = args.Length;
var values = new int[methods.Length];
values.Initialize();
//Iterates through all methods in o
for (var i = 0; i < methods.Length; i += 1)
{
if (methods[i].Name == nameMethod)
{
var parameters = methods[i].GetParameters();
if (parameters.Length == min)
{
//Iterates through parameters
for (var j = 0; j < min; j += 1)
{
if (args[j] == null)
{
if (parameters[j].ParameterType.IsValueType)
{
values[i] = 0;
break;
}
else
{
values[i] += 1;
}
}
else
{
if (parameters[j].ParameterType != args[j].GetType())
{
values[i] = 0;
break;
}
else
{
values[i] += 2;
}
}
}
if (values[i] == min * 2) //Exact match
return methods[i];
}
}
}
var best = values.Max();
if (best < min) //There is no match
return null;
//Iterates through value until it finds first best match
for (var i = 0; i < values.Length; i += 1)
{
if (values[i] == best)
return methods[i];
}
return null; //Should never happen
}
If none of parameters is NULL you perform usual method call, if one is null however
else if at least one is null you take different approach:
build parameter type list from parameters : like "int, char, null, int"
get functions overloads with same number of parameters for your function name
see whether there is just one matching function, cause if there are 2 you cannot determine which to call (hardest part but fairly straightforward I think)
call the function you figured out with your parameters and nulls

Get types used inside a C# method body

Is there a way to get all types used inside C# method?
For example,
public int foo(string str)
{
Bar bar = new Bar();
string x = "test";
TEST t = bar.GetTEST();
}
would return: Bar, string and TEST.
All I can get now is the method body text using EnvDTE.CodeFunction. Maybe there is a better way to achieve it than trying to parse this code.
I'm going to take this opportunity to post up a proof of concept I did because somebody told me it couldn't be done - with a bit of tweaking here and there, it'd be relatively trivial to extend this to extract out all referenced Types in a method - apologies for the size of it and the lack of a preface, but it's somewhat commented:
void Main()
{
Func<int,int> addOne = i => i + 1;
Console.WriteLine(DumpMethod(addOne));
Func<int,string> stuff = i =>
{
var m = 10312;
var j = i + m;
var k = j * j + i;
var foo = "Bar";
var asStr = k.ToString();
return foo + asStr;
};
Console.WriteLine(DumpMethod(stuff));
Console.WriteLine(DumpMethod((Func<string>)Foo.GetFooName));
Console.WriteLine(DumpMethod((Action)Console.Beep));
}
public class Foo
{
public const string FooName = "Foo";
public static string GetFooName() { return typeof(Foo).Name + ":" + FooName; }
}
public static string DumpMethod(Delegate method)
{
// For aggregating our response
StringBuilder sb = new StringBuilder();
// First we need to extract out the raw IL
var mb = method.Method.GetMethodBody();
var il = mb.GetILAsByteArray();
// We'll also need a full set of the IL opcodes so we
// can remap them over our method body
var opCodes = typeof(System.Reflection.Emit.OpCodes)
.GetFields()
.Select(fi => (System.Reflection.Emit.OpCode)fi.GetValue(null));
//opCodes.Dump();
// For each byte in our method body, try to match it to an opcode
var mappedIL = il.Select(op =>
opCodes.FirstOrDefault(opCode => opCode.Value == op));
// OpCode/Operand parsing:
// Some opcodes have no operands, some use ints, etc.
// let's try to cover all cases
var ilWalker = mappedIL.GetEnumerator();
while(ilWalker.MoveNext())
{
var mappedOp = ilWalker.Current;
if(mappedOp.OperandType != OperandType.InlineNone)
{
// For operand inference:
// MOST operands are 32 bit,
// so we'll start there
var byteCount = 4;
long operand = 0;
string token = string.Empty;
// For metadata token resolution
var module = method.Method.Module;
Func<int, string> tokenResolver = tkn => string.Empty;
switch(mappedOp.OperandType)
{
// These are all 32bit metadata tokens
case OperandType.InlineMethod:
tokenResolver = tkn =>
{
var resMethod = module.SafeResolveMethod((int)tkn);
return string.Format("({0}())", resMethod == null ? "unknown" : resMethod.Name);
};
break;
case OperandType.InlineField:
tokenResolver = tkn =>
{
var field = module.SafeResolveField((int)tkn);
return string.Format("({0})", field == null ? "unknown" : field.Name);
};
break;
case OperandType.InlineSig:
tokenResolver = tkn =>
{
var sigBytes = module.SafeResolveSignature((int)tkn);
var catSig = string
.Join(",", sigBytes);
return string.Format("(SIG:{0})", catSig == null ? "unknown" : catSig);
};
break;
case OperandType.InlineString:
tokenResolver = tkn =>
{
var str = module.SafeResolveString((int)tkn);
return string.Format("('{0}')", str == null ? "unknown" : str);
};
break;
case OperandType.InlineType:
tokenResolver = tkn =>
{
var type = module.SafeResolveType((int)tkn);
return string.Format("(typeof({0}))", type == null ? "unknown" : type.Name);
};
break;
// These are plain old 32bit operands
case OperandType.InlineI:
case OperandType.InlineBrTarget:
case OperandType.InlineSwitch:
case OperandType.ShortInlineR:
break;
// These are 64bit operands
case OperandType.InlineI8:
case OperandType.InlineR:
byteCount = 8;
break;
// These are all 8bit values
case OperandType.ShortInlineBrTarget:
case OperandType.ShortInlineI:
case OperandType.ShortInlineVar:
byteCount = 1;
break;
}
// Based on byte count, pull out the full operand
for(int i=0; i < byteCount; i++)
{
ilWalker.MoveNext();
operand |= ((long)ilWalker.Current.Value) << (8 * i);
}
var resolved = tokenResolver((int)operand);
resolved = string.IsNullOrEmpty(resolved) ? operand.ToString() : resolved;
sb.AppendFormat("{0} {1}",
mappedOp.Name,
resolved)
.AppendLine();
}
else
{
sb.AppendLine(mappedOp.Name);
}
}
return sb.ToString();
}
public static class Ext
{
public static FieldInfo SafeResolveField(this Module m, int token)
{
FieldInfo fi;
m.TryResolveField(token, out fi);
return fi;
}
public static bool TryResolveField(this Module m, int token, out FieldInfo fi)
{
var ok = false;
try { fi = m.ResolveField(token); ok = true; }
catch { fi = null; }
return ok;
}
public static MethodBase SafeResolveMethod(this Module m, int token)
{
MethodBase fi;
m.TryResolveMethod(token, out fi);
return fi;
}
public static bool TryResolveMethod(this Module m, int token, out MethodBase fi)
{
var ok = false;
try { fi = m.ResolveMethod(token); ok = true; }
catch { fi = null; }
return ok;
}
public static string SafeResolveString(this Module m, int token)
{
string fi;
m.TryResolveString(token, out fi);
return fi;
}
public static bool TryResolveString(this Module m, int token, out string fi)
{
var ok = false;
try { fi = m.ResolveString(token); ok = true; }
catch { fi = null; }
return ok;
}
public static byte[] SafeResolveSignature(this Module m, int token)
{
byte[] fi;
m.TryResolveSignature(token, out fi);
return fi;
}
public static bool TryResolveSignature(this Module m, int token, out byte[] fi)
{
var ok = false;
try { fi = m.ResolveSignature(token); ok = true; }
catch { fi = null; }
return ok;
}
public static Type SafeResolveType(this Module m, int token)
{
Type fi;
m.TryResolveType(token, out fi);
return fi;
}
public static bool TryResolveType(this Module m, int token, out Type fi)
{
var ok = false;
try { fi = m.ResolveType(token); ok = true; }
catch { fi = null; }
return ok;
}
}
If you can access the IL for this method, you might be able to do something suitable. Perhaps look at the open source project ILSpy and see whether you can leverage any of their work.
As others have mentioned, if you had the DLL you could use something similar to what ILSpy does in its Analyze feature (iterating over all the IL instructions in the assembly to find references to a specific type).
Otherwise, there is no way to do it without parsing the text into a C# Abstract Syntax Tree, AND employing a Resolver - something that can understand the semantics of the code well enough to know if "Bar" in your example is indeed a name of a type that is accessible from that method (in its "using" scope), or perhaps the name of a method, member field, etc... SharpDevelop contains a C# parser (called "NRefactory") and also contains such a Resolver, you can look into pursuing that option by looking at this thread, but beware that it is a fair amount of work to set it up to work right.
I just posted an extensive example of how to use Mono.Cecil to do static code analysis like this.
I also show a CallTreeSearch enumerator class that can statically analyze call trees, looking for certain interesting things and generating results using a custom supplied selector function, so you can plug it with your 'payload' logic, e.g.
static IEnumerable<TypeUsage> SearchMessages(TypeDefinition uiType, bool onlyConstructions)
{
return uiType.SearchCallTree(IsBusinessCall,
(instruction, stack) => DetectTypeUsage(instruction, stack, onlyConstructions));
}
internal class TypeUsage : IEquatable<TypeUsage>
{
public TypeReference Type;
public Stack<MethodReference> Stack;
#region equality
// ... omitted for brevity ...
#endregion
}
private static TypeUsage DetectTypeUsage(
Instruction instruction, IEnumerable<MethodReference> stack, bool onlyConstructions)
{
TypeDefinition resolve = null;
{
TypeReference tr = null;
var methodReference = instruction.Operand as MethodReference;
if (methodReference != null)
tr = methodReference.DeclaringType;
tr = tr ?? instruction.Operand as TypeReference;
if ((tr == null) || !IsInterestingType(tr))
return null;
resolve = tr.GetOriginalType().TryResolve();
}
if (resolve == null)
throw new ApplicationException("Required assembly not loaded.");
if (resolve.IsSerializable)
if (!onlyConstructions || IsConstructorCall(instruction))
return new TypeUsage {Stack = new Stack<MethodReference>(stack.Reverse()), Type = resolve};
return null;
}
This leaves out a few details
implementation of IsBusinessCall, IsConstructorCall and TryResolve as these are trivial and serve as illustrative only
Hope that helps
The closest thing to that that I can think of are expression trees. Take a look at the documentation from Microsoft.
They are very limited however and only work on simple expressions and not full methods with statement bodies.
Edit: Since the intention of the poster was to find class couplings and used types, I would suggest using a commercial tool like NDepend to do the code analysis as an easy solution.
This definitely cannot be done from reflection (GetMethod(), Expression Trees, etc.). As you mentioned, using EnvDTE's CodeModel is an option since you get line-by-line C# there, but using it outside Visual Studio (that is, processing an already existing function, not in your editor window) is nigh-impossible, IMHO.
But I can recommend Mono.Cecil, which can process CIL code line-by-line (inside a method), and you can use it on any method from any assembly you have reference to. Then, you can check every line if it is a variable declaration (like string x = "test", or a methodCall, and you can get the types involved in those lines.
With reflection you can get the method. This returns a MethodInfo object, and with this object you cannot get the types which are used in the method. So I think the answer is that you cannot get this native in C#.

Categories