public static object InstantiatePatch(Type proxyType, List <object> baseCascade) { var(baseTypes, baseMethodSets) = BuildBaseCascadeInfo(baseCascade); var patchTargets = new Dictionary <MethodInfo, MethodInfo>(); foreach (var method in proxyType.GetMethods(InstanceBinding)) { var patchInfo = method.GetCustomAttribute <LogicProxyAttribute>(); if (patchInfo == null) { continue; } var patchTarget = GetPatchTarget(); patchTargets.Add(patchTarget, method); MethodInfo GetPatchTarget() { var t = method.FindMatchingMethod(baseMethodSets, true, (real, general) => { if (real == general) { return(true); } if (real.IsClass && general == typeof(object)) { return(true); } if (real.IsByRef && (general == typeof(IntPtr) || general == typeof(void *))) { return(true); } return(false); }); //Patch real implementation, not V-table stub return(t.GetDeclaredMember()); } } var(fullInterface, proxyServiceBaseType) = GetProxyServiceInfo(proxyType); var proxyServiceInstanceProperty = proxyServiceBaseType.BaseType.GetProperty(nameof(ProxyService <object, object> .Instance)); var redirectedMethods = new Dictionary <MethodInfo, MethodInfo>(); foreach (var(target, redirectionTarget) in patchTargets) { var copy = CreateDuplicate(target); redirectedMethods.Add(target, copy); var args = target.GetEffectiveParameterTypes(); var redirectionStub = new DynamicMethod(target.Name + "_" + proxyType.Name + "_DetourStub", target.ReturnType, args); var IL = redirectionStub.GetILGenerator(); if (redirectionTarget.IsStatic == false) { IL.Emit(OpCodes.Call, proxyServiceInstanceProperty.GetMethod); } var offset = target.IsStatic ? 0 : 1; for (int i = 0; i < args.Length - offset; i++) { IL.Emit(OpCodes.Ldarg, i + offset); } IL.Emit(OpCodes.Call, redirectionTarget); IL.Emit(OpCodes.Ret); RedirectMethod(target, redirectionStub); } object interfaceInstance; if (redirectedMethods.Count == 0 && baseTypes[0].Implements(fullInterface)) { interfaceInstance = baseCascade[0]; } else { var implementationTargets = new List <(MethodInfo Interface, int BaseIndex, MemberInfo ImplementationTarget)>(); foreach (var method in fullInterface.GetAllInterfaceMethods()) { for (var i = 0; i < baseTypes.Length; i++) { var baseType = baseTypes[i]; var implementation = baseType.TryFindImplementation(method, out var isInterfaceImplementation); if (implementation != null) { if (redirectedMethods.TryGetValue(implementation, out var redirection)) { //Direct call of duplicated method implementation = redirection; } else if (implementation.IsAssemblyPublic()) { //Direct call of implementing method is best choice, if accessible //=> No op } else if (isInterfaceImplementation && method.IsAssemblyPublic()) { //Direct interface call is second best choice implementation = method; } else { //Fall-back to delegate proxy call to expose internal method //=> No op } implementationTargets.Add((method, i, implementation)); goto matchFound; } } if (method.IsProperty()) { string dataTargetName = method.Name.Substring("get_".Length); for (var i = 0; i < baseTypes.Length; i++) { var baseType = baseTypes[i]; var dataTarget = TryFindDataTarget(baseType, method, dataTargetName); if (dataTarget != null) { implementationTargets.Add((method, i, dataTarget)); goto matchFound; } } } throw new Exception("Could not find implementation or data target for " + method); matchFound :; } var proxy = DynamicTypeBuilder.Create($"{fullInterface.Name}_" + proxyType.Name + proxyType.GetHashCode() + "_Proxy"); proxy.Type.AddInterfaceImplementation(fullInterface); var baseIndexToField = new Dictionary <int, FieldBuilder>(); foreach (var(_, baseIndex, _) in implementationTargets) { if (baseIndexToField.ContainsKey(baseIndex)) { continue; } var field = proxy.DeclareField(baseCascade[baseIndex].GetType()); baseIndexToField.Add(baseIndex, field); } var proxyDelegates = new List <(Delegate Instance, FieldBuilder Field)>(); foreach (var(interfaceTarget, baseIndex, implementationTarget) in implementationTargets) { var method = proxy.Type.DefineMethod(interfaceTarget.Name, MethodAttributes.Public | MethodAttributes.Virtual); var IL = method.GetILGenerator(); var argTypes = interfaceTarget.GetParameters().Select(x => x.ParameterType).ToArray(); method.SetParameters(argTypes); var returnType = interfaceTarget.ReturnType; method.SetReturnType(returnType); proxy.Type.DefineMethodOverride(method, interfaceTarget); if (implementationTarget is MethodInfo methodTarget) { EmitCallTarget(methodTarget); } else if (implementationTarget is FieldInfo fieldTarget) { var il = IL; var isInstanceField = fieldTarget.IsStatic == false; DynamicMethod proxyStub = null; if (fieldTarget.IsAssemblyPublic()) { if (isInstanceField) { LoadBaseInstance(); } } else { //Note: Nasty hack follows! //Private field, dynamic type visibility rules, hacky solution, read below var declaringType = fieldTarget.DeclaringType; var args = argTypes.Prepend(new[] { declaringType }).ToArray(); var stubName = fieldTarget.Name + '_' + returnType.Name + "_ProxyAccessor"; proxyStub = new DynamicMethod(stubName, returnType, args, declaringType, true); il = proxyStub.GetILGenerator(); if (isInstanceField) { il.Emit(OpCodes.Ldarg_0); } } if (returnType == typeof(void)) { var op = isInstanceField ? OpCodes.Stfld : OpCodes.Stsfld; il.Emit(OpCodes.Ldarg_1); il.Emit(op, fieldTarget); } else { var op = isInstanceField ? OpCodes.Ldfld : OpCodes.Ldsfld; il.Emit(op, fieldTarget); } if (proxyStub != null) { il.Emit(OpCodes.Ret); EmitCallTarget(proxyStub); } } else { throw new Exception("Unexpected implementation target " + implementationTarget.GetType()); } IL.Emit(OpCodes.Ret); void LoadBaseInstance() { IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldfld, baseIndexToField[baseIndex]); } void PassArgs() { for (int i = 1; i <= argTypes.Length; i++) { IL.Emit(OpCodes.Ldarg, i); } } void EmitCallTarget(MethodInfo callTarget) { //TODO: For some reason Mono is super unhappy when calling IL.Emit(Call, DynamicMethod) // => Proxy via delegate which is apparently fine bool forceDelegateProxy = callTarget is DynamicMethod; if (forceDelegateProxy || callTarget.IsAssemblyPublic() == false) { //Note: Nasty hack follows! //Dynamic type has to follow all visibility rules normal assembly would have to so it can't call private methods directly //Runtime delegates can reflect, call and expose private members, but can't instantiate interface //=> Call proxy delegate inside proxy type to expose private method via public interface. We need to go deeper :) var delegateType = DelegateCreator.NewDelegateType(callTarget, unboundInstanceCall: true); //var proxyDelegate = Delegate.CreateDelegate(delegateType, callTarget); var proxyDelegate = callTarget.CreateDelegate(delegateType); var delegateField = proxy.DeclareField(delegateType); IL.Emit(OpCodes.Ldarg_0); IL.Emit(OpCodes.Ldfld, delegateField); proxyDelegates.Add((proxyDelegate, delegateField)); callTarget = delegateType.GetMethod("Invoke"); } LoadBaseInstance(); PassArgs(); var isInterfaceCall = callTarget.DeclaringType?.IsInterface ?? false; IL.Emit(isInterfaceCall ? OpCodes.Callvirt : OpCodes.Call, callTarget); } } proxy.DeclareCtorAndInitFields ( proxyDelegates.Select(x => x.Field).Concat( baseIndexToField.Select(x => x.Value)) ); interfaceInstance = proxy.Instantiate ( proxyDelegates.Select(x => x.Instance).Concat( baseIndexToField.Select(x => baseCascade[x.Key])) .ToArray() ); } var proxyInstance = Activator.CreateInstance(proxyType); proxyType.GetProperty(nameof(ProxyService <object, object> .Vanilla)).SetValue(proxyInstance, interfaceInstance); return(proxyInstance); }