/// <summary> /// Constructs a new instance of <see cref="DelegateCreationInfo"/> set up to construct a delegate of type /// '<paramref name="delegateType"/>' pointing to '<paramref name="targetMethod"/>'. /// </summary> public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, NodeFactory factory, bool followVirtualDispatch) { CompilerTypeSystemContext context = factory.TypeSystemContext; DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; int paramCountTargetMethod = targetMethod.Signature.Length; if (!targetMethod.Signature.IsStatic) { paramCountTargetMethod++; } DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; bool closed = false; if (paramCountDelegateClosed == paramCountTargetMethod) { closed = true; } else { Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); } if (targetMethod.Signature.IsStatic) { MethodDesc invokeThunk; MethodDesc initMethod; if (!closed) { initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; } else { // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk", null); } var instantiatedDelegateType = delegateType as InstantiatedType; if (instantiatedDelegateType != null) { invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); } return(new DelegateCreationInfo( factory.MethodEntrypoint(initMethod), targetMethod, TargetKind.ExactCallableAddress, factory.MethodEntrypoint(invokeThunk))); } else { if (!closed) { throw new NotImplementedException("Open instance delegates"); } string initializeMethodName = "InitializeClosedInstance"; MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); TargetKind kind; if (targetMethod.HasInstantiation) { if (followVirtualDispatch && targetMethod.IsVirtual) { initializeMethodName = "InitializeClosedInstanceWithGVMResolution"; kind = TargetKind.MethodHandle; } else { if (targetMethod != targetCanonMethod) { // Closed delegates to generic instance methods need to be constructed through a slow helper that // checks for the fat function pointer case (function pointer + instantiation argument in a single // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of // the invocation if necessary. initializeMethodName = "InitializeClosedInstanceSlow"; } kind = TargetKind.ExactCallableAddress; } } else { if (followVirtualDispatch && targetMethod.IsVirtual) { if (targetMethod.OwningType.IsInterface) { kind = TargetKind.InterfaceDispatch; initializeMethodName = "InitializeClosedInstanceToInterface"; } else { kind = TargetKind.VTableLookup; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } else { kind = TargetKind.CanonicalEntrypoint; targetMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); } } return(new DelegateCreationInfo( factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), targetMethod, kind)); } }
/// <summary> /// Constructs a new instance of <see cref="DelegateCreationInfo"/> set up to construct a delegate of type /// '<paramref name="delegateType"/>' pointing to '<paramref name="targetMethod"/>'. /// </summary> public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, NodeFactory factory) { CompilerTypeSystemContext context = factory.TypeSystemContext; DefType systemDelegate = targetMethod.Context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType; int paramCountTargetMethod = targetMethod.Signature.Length; if (!targetMethod.Signature.IsStatic) { paramCountTargetMethod++; } DelegateInfo delegateInfo = context.GetDelegateInfo(delegateType.GetTypeDefinition()); int paramCountDelegateClosed = delegateInfo.Signature.Length + 1; bool closed = false; if (paramCountDelegateClosed == paramCountTargetMethod) { closed = true; } else { Debug.Assert(paramCountDelegateClosed == paramCountTargetMethod + 1); } if (targetMethod.Signature.IsStatic) { MethodDesc invokeThunk; MethodDesc initMethod; if (!closed) { // Open delegate to a static method if (targetMethod.IsNativeCallable) { // If target method is native callable, create a reverse PInvoke delegate initMethod = systemDelegate.GetKnownMethod("InitializeReversePInvokeThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ReversePinvokeThunk]; // You might hit this when the delegate is generic: you need to make the delegate non-generic. // If the code works on Project N, it's because the delegate is used in connection with // AddrOf intrinsic (please validate that). We don't have the necessary AddrOf expansion in // the codegen to make this work without actually constructing the delegate. You can't construct // the delegate if it's generic, even on Project N. // TODO: Make this throw something like "TypeSystemException.InvalidProgramException"? Debug.Assert(invokeThunk != null, "Delegate with a non-native signature for a NativeCallable method"); } else { initMethod = systemDelegate.GetKnownMethod("InitializeOpenStaticThunk", null); invokeThunk = delegateInfo.Thunks[DelegateThunkKind.OpenStaticThunk]; } } else { // Closed delegate to a static method (i.e. delegate to an extension method that locks the first parameter) invokeThunk = delegateInfo.Thunks[DelegateThunkKind.ClosedStaticThunk]; initMethod = systemDelegate.GetKnownMethod("InitializeClosedStaticThunk", null); } var instantiatedDelegateType = delegateType as InstantiatedType; if (instantiatedDelegateType != null) { invokeThunk = context.GetMethodForInstantiatedType(invokeThunk, instantiatedDelegateType); } return(new DelegateCreationInfo( factory.MethodEntrypoint(initMethod), factory.MethodEntrypoint(targetMethod), factory.MethodEntrypoint(invokeThunk))); } else { if (!closed) { throw new NotImplementedException("Open instance delegates"); } bool useUnboxingStub = targetMethod.OwningType.IsValueType; IMethodNode targetMethodNode; string initializeMethodName = "InitializeClosedInstance"; MethodDesc targetCanonMethod = targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific); if (targetMethod.HasInstantiation) { Debug.Assert(!targetMethod.IsVirtual, "TODO: delegate to generic virtual method"); if (targetMethod != targetCanonMethod) { // Closed delegates to generic instance methods need to be constructed through a slow helper that // checks for the fat function pointer case (function pointer + instantiation argument in a single // pointer) and injects an invocation thunk to unwrap the fat function pointer as part of // the invocation if necessary. initializeMethodName = "InitializeClosedInstanceSlow"; targetMethodNode = factory.FatFunctionPointer(targetMethod, useUnboxingStub); } else { targetMethodNode = factory.MethodEntrypoint(targetMethod, useUnboxingStub); } } else { // If the method can be canonicalized, point to the canon method body, but track the dependencies. if (targetMethod != targetCanonMethod) { targetMethodNode = factory.ShadowConcreteMethod(targetMethod, useUnboxingStub); } else { targetMethodNode = factory.MethodEntrypoint(targetMethod, useUnboxingStub); } } return(new DelegateCreationInfo( factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)), targetMethodNode)); } }