private static void AddGenericPrototypeCalls(MethodDefinition method, MethodDefinition real_method) { //external types Type[] systemFuncs; //TODO: find a better way to check for void if(method.ReturnType.FullName!="System.Void") systemFuncs = new Type[]{ typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), typeof(Func<,,,>) }; else systemFuncs = new Type[] { typeof(Action<>), typeof(Action<,>), typeof(Action<,,>), typeof(Action<,,,>) }; var systemFuncClass = method.Module.Import(systemFuncs[method.GenericParameters.Count-1]); var systemTypeClass = method.Module.Import(typeof(Type)); //external call var TypeFromHandle = method.Module.Import(systemTypeClass.Resolve().Methods.Single(m => m.Name == "GetTypeFromHandle")); //setup Dictionary<Type,object> and it's ContainsKey method var dictType = method.Module.Import(typeof(Dictionary<Type, object>)); var dictContainsKeyMethod = method.Module.Import(dictType.Resolve().Methods.Single(m => m.Name == "ContainsKey")); var dictGetItemMethod = method.Module.Import(dictType.Resolve().Methods.Single(m => m.Name == "get_Item")); dictContainsKeyMethod.DeclaringType = dictType; dictGetItemMethod.DeclaringType = dictType; //setup prototype class and delegate for method var protoClassType = method.DeclaringType.NestedTypes.Single(m => m.Name == "PrototypeClass"); var protoDelegateType = protoClassType.NestedTypes.Single(m => m.Name == "Callback_" + ComposeFullMethodName(method)); //setup fields var protoField = method.DeclaringType.Fields.Single(m => m.Name == method.DeclaringType.Name.Replace('`', '_') + "Prototype"); var staticProtoField = method.DeclaringType.Fields.Single(m => m.Name == "StaticPrototype"); var dictProperty = protoClassType.Properties.Single(m => m.Name == ComposeFullMethodName(method)); //setup delegate proto var protoDelegate = new GenericInstanceType(protoDelegateType); foreach (var param in method.DeclaringType.GenericParameters.Concat(method.GenericParameters)) protoDelegate.GenericArguments.Add(param); //setup invoke method var protoInvoke = protoDelegateType.Methods.Single(m => m.Name == "Invoke"); var il = method.Body.GetILProcessor(); //this is a place to store instructions, which operands needs to be replaced List<Instruction> jmpReplacements = new List<Instruction>(); var allParamsCount = method.Parameters.Count + (method.IsStatic ? 0 : 1); //setup body variables method.Body.InitLocals = true; method.Body.Variables.Add(new VariableDefinition(systemTypeClass)); //key if(method.ReturnType.FullName!="System.Void") method.Body.Variables.Add(new VariableDefinition(method.ReturnType)); //data to return method.Body.Variables.Add(new VariableDefinition(method.Module.Import(typeof(bool)))); //for evaluation of conditions il.Emit(OpCodes.Nop); //making key variable systemFuncClass = new GenericInstanceType(systemFuncClass); foreach (var param in method.GenericParameters) ((GenericInstanceType)systemFuncClass).GenericArguments.Add(param); il.Emit(OpCodes.Ldtoken, systemFuncClass); il.Emit(OpCodes.Call, TypeFromHandle); il.Emit(OpCodes.Stloc_0); if (!method.IsStatic && !method.IsConstructor) { //finding key in dictionary il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, protoField.Instance()); il.Emit(OpCodes.Call, dictProperty.GetMethod.Instance()); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, dictContainsKeyMethod); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brtrue_S, label("KeyNotFound")); //will be replaced //if key is found - call proto function il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, protoField.Instance()); // valuetype Cheese.GenericStorage`1/Test<!0> class Cheese.GenericStorage`1<!T>::testSample il.Emit(OpCodes.Call, dictProperty.GetMethod.Instance()); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, dictGetItemMethod); // instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<class [mscorlib]System.Type[], object>::get_Item(!0) il.Emit(OpCodes.Castclass, protoDelegate); // class Cheese.GenericStorage`1/Test/Maker`1<!T, !!L> for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Callvirt, protoInvoke.Instance(method.DeclaringType.GenericParameters, method.GenericParameters)); // instance !1 class Cheese.GenericStorage`1/Test/Maker`1<!T, !!L>::Invoke(class Cheese.GenericStorage`1<!0>, !1) il.Emit(OpCodes.Ret); il.SetLabel("KeyNotFound"); } //finding key in static prototype dictionary il.Emit(OpCodes.Ldsflda, staticProtoField.Instance()); il.Emit(OpCodes.Call, dictProperty.GetMethod.Instance()); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, dictContainsKeyMethod); il.Emit(OpCodes.Ldc_I4_0); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brtrue_S, label("CallDefault")); //will be replaced //if key is found - call proto function il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ldsflda, staticProtoField.Instance()); // valuetype Cheese.GenericStorage`1/Test<!0> class Cheese.GenericStorage`1<!T>::testSample il.Emit(OpCodes.Call, dictProperty.GetMethod.Instance()); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Callvirt, dictGetItemMethod); // instance !1 class [mscorlib]System.Collections.Generic.Dictionary`2<class [mscorlib]System.Type[], object>::get_Item(!0) il.Emit(OpCodes.Castclass, protoDelegate); // class Cheese.GenericStorage`1/Test/Maker`1<!T, !!L> for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Callvirt, protoInvoke.Instance(method.DeclaringType.GenericParameters, method.GenericParameters)); // instance !1 class Cheese.GenericStorage`1/Test/Maker`1<!T, !!L>::Invoke(class Cheese.GenericStorage`1<!0>, !1) il.Emit(OpCodes.Ret); //call x-method by default il.SetLabel("CallDefault"); for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg,i); il.Emit(OpCodes.Call, real_method.Instance()); il.Emit(OpCodes.Ret); }
private static void AddPrototypeCalls(MethodDefinition method, MethodDefinition real_method) { var staticPrototypeField = method.DeclaringType.Fields.Single(m => m.Name == "StaticPrototype"); var dynamicPrototypeField = method.DeclaringType.Fields.SingleOrDefault(m => m.Name == method.DeclaringType.Name.Replace('`', '_') + "Prototype"); var delegateField = method.DeclaringType.NestedTypes.Single(m => m.Name == "PrototypeClass").Fields.Single(m => m.Name == ComposeFullMethodName(method)); var il = method.Body.GetILProcessor(); TypeDefinition delegateType = delegateField.FieldType.Resolve(); var invokeMethod = delegateType.Methods.Single(m => m.Name == "Invoke"); int allParamsCount = method.Parameters.Count + (method.IsStatic ? 0 : 1); //all params and maybe this if (!method.IsStatic && !method.IsConstructor) { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, dynamicPrototypeField.Instance()); il.Emit(OpCodes.Ldfld, delegateField.Instance()); il.Emit(OpCodes.Brfalse, label("nextCheck")); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldflda, dynamicPrototypeField.Instance()); il.Emit(OpCodes.Ldfld, delegateField.Instance()); for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Callvirt, invokeMethod.Instance()); il.Emit(OpCodes.Ret); } il.SetLabel("nextCheck"); il.Emit(OpCodes.Ldsflda, staticPrototypeField.Instance()); il.Emit(OpCodes.Ldfld, delegateField.Instance()); il.Emit(OpCodes.Brfalse, label("callDefault")); //addres will be replaced il.Emit(OpCodes.Ldsflda, staticPrototypeField.Instance()); il.Emit(OpCodes.Ldfld, delegateField.Instance()); for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Callvirt, invokeMethod.Instance()); il.Emit(OpCodes.Ret); il.SetLabel("callDefault"); for (int i = 0; i < allParamsCount; i++) il.Emit(OpCodes.Ldarg, i); il.Emit(OpCodes.Call, real_method.Instance()); il.Emit(OpCodes.Ret); }