protected virtual object DynamicInvokeImpl(object[] args) { if (IsDynamicDelegate()) { // DynamicDelegate case object result = ((Func <object[], object>)m_helperObject)(args); DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return(result); } else { IntPtr invokeThunk = this.GetThunk(DelegateInvokeThunk); #if PROJECTN object result = InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, this, invokeThunk, IntPtr.Zero, this, args, binderBundle: null, wrapInTargetInvocationException: true); #else IntPtr genericDictionary = IntPtr.Zero; if (FunctionPointerOps.IsGenericMethodPointer(invokeThunk)) { unsafe { GenericMethodDescriptor *descriptor = FunctionPointerOps.ConvertToGenericDescriptor(invokeThunk); genericDictionary = descriptor->InstantiationArgument; invokeThunk = descriptor->MethodFunctionPointer; } } object result = InvokeUtils.CallDynamicInvokeMethod(this.m_firstParameter, this.m_functionPointer, null, invokeThunk, genericDictionary, this, args, binderBundle: null, wrapInTargetInvocationException: true, invokeMethodHelperIsThisCall: false); #endif DebugAnnotations.PreviousCallContainsDebuggerStepInCode(); return(result); } }
// This function is known to the compiler. protected void InitializeClosedInstanceWithGVMResolution(object firstParameter, RuntimeMethodHandle tokenOfGenericVirtualMethod) { if (firstParameter == null) { throw new ArgumentException(SR.Arg_DlgtNullInst); } IntPtr functionResolution = TypeLoaderExports.GVMLookupForSlot(firstParameter, tokenOfGenericVirtualMethod); if (functionResolution == IntPtr.Zero) { // TODO! What to do when GVM resolution fails. Should never happen throw new InvalidOperationException(); } if (!FunctionPointerOps.IsGenericMethodPointer(functionResolution)) { m_functionPointer = functionResolution; m_firstParameter = firstParameter; } else { m_firstParameter = this; m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); m_extraFunctionPointerOrData = functionResolution; m_helperObject = firstParameter; } return; }
public static IntPtr ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(IntPtr unboxingFunctionPointer, RuntimeTypeHandle declaringType) { if (FunctionPointerOps.IsGenericMethodPointer(unboxingFunctionPointer)) { // Handle shared generic methods unsafe { GenericMethodDescriptor *functionPointerDescriptor = FunctionPointerOps.ConvertToGenericDescriptor(unboxingFunctionPointer); IntPtr nonUnboxingTarget = RuntimeAugments.GetCodeTarget(functionPointerDescriptor->MethodFunctionPointer); Debug.Assert(nonUnboxingTarget != functionPointerDescriptor->MethodFunctionPointer); Debug.Assert(nonUnboxingTarget == RuntimeAugments.GetCodeTarget(nonUnboxingTarget)); return(FunctionPointerOps.GetGenericMethodFunctionPointer(nonUnboxingTarget, functionPointerDescriptor->InstantiationArgument)); } } // GetCodeTarget will look through simple unboxing stubs (ones that consist of adjusting the this pointer and then // jumping to the target. IntPtr exactTarget = RuntimeAugments.GetCodeTarget(unboxingFunctionPointer); if (RuntimeAugments.IsGenericType(declaringType)) { IntPtr fatFunctionPointerTarget; // This check looks for unboxing and instantiating stubs generated via the compiler backend if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(exactTarget, out fatFunctionPointerTarget)) { // If this is an unboxing and instantiating stub, use separate table, find target, and create fat function pointer exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(fatFunctionPointerTarget, declaringType.ToIntPtr()); } #if FEATURE_UNIVERSAL_GENERICS else { IntPtr newExactTarget; // This check looks for unboxing and instantiating stubs generated dynamically as thunks in the calling convention converter if (CallConverterThunk.TryGetNonUnboxingFunctionPointerFromUnboxingAndInstantiatingStub(exactTarget, declaringType, out newExactTarget)) { // CallingConventionConverter determined non-unboxing stub exactTarget = newExactTarget; } else { // Target method was a method on a generic, but it wasn't a shared generic, and thus none of the above // complex unboxing stub digging logic was necessary. Do nothing, and use exactTarget as discovered // from GetCodeTarget } } #endif } return(exactTarget); }
public sealed override bool Equals([NotNullWhen(true)] object?obj) { if (obj == null) { return(false); } if (object.ReferenceEquals(this, obj)) { return(true); } if (!InternalEqualTypes(this, obj)) { return(false); } // Since this is a MulticastDelegate and we know // the types are the same, obj should also be a // MulticastDelegate Debug.Assert(obj is MulticastDelegate, "Shouldn't have failed here since we already checked the types are the same!"); var d = Unsafe.As <MulticastDelegate>(obj); // there are 2 kind of delegate kinds for comparison // 1- Multicast (m_helperObject is Delegate[]) // 2- Single-cast delegate, which can be compared with a structural comparison IntPtr multicastThunk = GetThunk(MulticastThunk); if (m_functionPointer == multicastThunk) { return(d.m_functionPointer == multicastThunk && InvocationListEquals(d)); } else { if (!object.ReferenceEquals(m_helperObject, d.m_helperObject) || (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) || (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer))) { return(false); } // Those delegate kinds with thunks put themselves into the m_firstParameter, so we can't // blindly compare the m_firstParameter fields for equality. if (object.ReferenceEquals(m_firstParameter, this)) { return(object.ReferenceEquals(d.m_firstParameter, d)); } return(object.ReferenceEquals(m_firstParameter, d.m_firstParameter)); } }
// This is used to implement MethodInfo.CreateDelegate() in a desktop-compatible way. Yes, the desktop really // let you use that api to invoke an instance method with a null 'this'. private void InitializeClosedInstanceWithoutNullCheck(object firstParameter, IntPtr functionPointer) { if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { m_functionPointer = functionPointer; m_firstParameter = firstParameter; } else { m_firstParameter = this; m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); m_extraFunctionPointerOrData = functionPointer; m_helperObject = firstParameter; } }
private bool UpdateCalleeFunctionPointer(IntPtr newFunctionPointer) { if (FunctionPointerOps.IsGenericMethodPointer(newFunctionPointer)) { GenericMethodDescriptor *genericTarget = FunctionPointerOps.ConvertToGenericDescriptor(newFunctionPointer); _instantiatingStubArgument = genericTarget->InstantiationArgument; _functionPointerToCall = genericTarget->MethodFunctionPointer; return(true); } else { _functionPointerToCall = newFunctionPointer; return(false); } }
private unsafe static IntPtr ResolveCallOnReferenceTypeCacheMiss(IntPtr context, IntPtr callDescIntPtr, object contextObject, out IntPtr auxResult) { auxResult = IntPtr.Zero; // Perform a normal GVM dispatch, then change the function pointer to dereference the this pointer. GenericConstrainedCallDesc *callDesc = (GenericConstrainedCallDesc *)callDescIntPtr; IntPtr target = RuntimeAugments.GVMLookupForSlot(contextObject.GetType().TypeHandle, callDesc->_constrainedMethod); if (FunctionPointerOps.IsGenericMethodPointer(target)) { GenericMethodDescriptor *genMethodDesc = FunctionPointerOps.ConvertToGenericDescriptor(target); IntPtr actualCodeTarget = GetThunkThatDereferencesThisPointerAndTailCallsTarget(genMethodDesc->MethodFunctionPointer); return(FunctionPointerOps.GetGenericMethodFunctionPointer(actualCodeTarget, genMethodDesc->InstantiationArgument)); } else { return(GetThunkThatDereferencesThisPointerAndTailCallsTarget(target)); } }
private unsafe static IntPtr ResolveCallOnValueType(IntPtr unused, IntPtr callDescIntPtr) #endif { GenericConstrainedCallDesc *callDesc = (GenericConstrainedCallDesc *)callDescIntPtr; IntPtr targetAsVirtualCall = RuntimeAugments.GVMLookupForSlot(callDesc->_constraintType, callDesc->_constrainedMethod); IntPtr exactTarget = IntPtr.Zero; if (FunctionPointerOps.IsGenericMethodPointer(targetAsVirtualCall)) { GenericMethodDescriptor *genMethodDesc = FunctionPointerOps.ConvertToGenericDescriptor(targetAsVirtualCall); IntPtr actualCodeTarget = RuntimeAugments.GetCodeTarget(genMethodDesc->MethodFunctionPointer); exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(actualCodeTarget, genMethodDesc->InstantiationArgument); } else { IntPtr actualCodeTarget = RuntimeAugments.GetCodeTarget(targetAsVirtualCall); IntPtr callConverterThunk; if (CallConverterThunk.TryGetNonUnboxingFunctionPointerFromUnboxingAndInstantiatingStub(actualCodeTarget, callDesc->_constraintType, out callConverterThunk)) { actualCodeTarget = callConverterThunk; } exactTarget = actualCodeTarget; } // Ensure that all threads will have their function pointers completely published before updating callDesc. // as the ExactTarget is read from callDesc by binder generated code without a barrier, we need a barrier here // to ensure that the new function pointer data is valid on all threads Interlocked.MemoryBarrier(); // Its possible for multiple threads to race to set exact target. Check to see we always set the same value if (callDesc->_exactTarget != IntPtr.Zero) { Debug.Assert(callDesc->_exactTarget == exactTarget); } callDesc->_exactTarget = exactTarget; return(exactTarget); }
public override sealed bool Equals(Object obj) { if (obj == null || !InternalEqualTypes(this, obj)) { return(false); } MulticastDelegate d = obj as MulticastDelegate; if (d == null) { return(false); } // there are 2 kind of delegate kinds for comparision // 1- Multicast (m_helperObject is Delegate[]) // 2- Single-cast delegate, which can be compared with a structural comparision if (m_functionPointer == GetThunk(MulticastThunk)) { return(InvocationListEquals(d)); } else { if (!Object.ReferenceEquals(m_helperObject, d.m_helperObject) || (!FunctionPointerOps.Compare(m_extraFunctionPointerOrData, d.m_extraFunctionPointerOrData)) || (!FunctionPointerOps.Compare(m_functionPointer, d.m_functionPointer))) { return(false); } // Those delegate kinds with thunks put themselves into the m_firstParamter, so we can't // blindly compare the m_firstParameter fields for equality. if (Object.ReferenceEquals(m_firstParameter, this)) { return(Object.ReferenceEquals(d.m_firstParameter, d)); } return(Object.ReferenceEquals(m_firstParameter, d.m_firstParameter)); } }
// This function is known to the IL Transformer. protected void InitializeClosedInstanceSlow(object firstParameter, IntPtr functionPointer) { // This method is like InitializeClosedInstance, but it handles ALL cases. In particular, it handles generic method with fun function pointers. if (firstParameter == null) { throw new ArgumentException(SR.Arg_DlgtNullInst); } if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { m_functionPointer = functionPointer; m_firstParameter = firstParameter; } else { m_firstParameter = this; m_functionPointer = GetThunk(ClosedInstanceThunkOverGenericMethod); m_extraFunctionPointerOrData = functionPointer; m_helperObject = firstParameter; } }
private string GetTargetMethodsDescriptionForDebugger() { if (m_functionPointer == GetThunk(MulticastThunk)) { // Multi-cast delegates return the Target of the last delegate in the list Delegate[] invocationList = (Delegate[])m_helperObject; int invocationCount = (int)m_extraFunctionPointerOrData; StringBuilder builder = new StringBuilder(); for (int c = 0; c < invocationCount; c++) { if (c != 0) { builder.Append(", "); } builder.Append(invocationList[c].GetTargetMethodsDescriptionForDebugger()); } return(builder.ToString()); } else { RuntimeTypeHandle typeOfFirstParameterIfInstanceDelegate; bool isOpenThunk; IntPtr functionPointer = GetFunctionPointer(out typeOfFirstParameterIfInstanceDelegate, out isOpenThunk); if (!FunctionPointerOps.IsGenericMethodPointer(functionPointer)) { return(DebuggerFunctionPointerFormattingHook(functionPointer, typeOfFirstParameterIfInstanceDelegate)); } else { unsafe { GenericMethodDescriptor *pointerDef = FunctionPointerOps.ConvertToGenericDescriptor(functionPointer); return(DebuggerFunctionPointerFormattingHook(pointerDef->InstantiationArgument, typeOfFirstParameterIfInstanceDelegate)); } } } }
private unsafe static IntPtr ResolveCallOnValueType(IntPtr unused, IntPtr callDescIntPtr) #endif { NonGenericConstrainedCallDesc *callDesc = (NonGenericConstrainedCallDesc *)callDescIntPtr; IntPtr exactTarget = IntPtr.Zero; IntPtr targetOnTypeVtable = RuntimeAugments.ResolveDispatchOnType(callDesc->_constraintType, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot); bool decodeUnboxing = true; if (!RuntimeAugments.IsInterface(callDesc->_constrainedMethodType)) { // Non-interface constrained call on a valuetype to a method that isn't GetHashCode/Equals/ToString?!?! if (callDesc->_constrainedMethodSlot > s_MaxObjectVTableSlot) { throw new NotSupportedException(); } RuntimeTypeHandle baseTypeHandle; bool gotBaseType = RuntimeAugments.TryGetBaseType(callDesc->_constraintType, out baseTypeHandle); Debug.Assert(gotBaseType); if (targetOnTypeVtable == RuntimeAugments.ResolveDispatchOnType(baseTypeHandle, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot)) { // In this case, the valuetype does not override the base types implementation of ToString(), GetHashCode(), or Equals(object) decodeUnboxing = false; } } if (decodeUnboxing) { exactTarget = RuntimeAugments.GetCodeTarget(targetOnTypeVtable); if (RuntimeAugments.IsGenericType(callDesc->_constraintType)) { IntPtr fatFunctionPointerTarget; if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(exactTarget, out fatFunctionPointerTarget)) { // If this is an unboxing and instantiating stub, use seperate table, find target, and create fat function pointer exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(fatFunctionPointerTarget, callDesc->_constraintType.ToIntPtr()); } else { IntPtr newExactTarget; if (CallConverterThunk.TryGetNonUnboxingFunctionPointerFromUnboxingAndInstantiatingStub(exactTarget, callDesc->_constraintType, out newExactTarget)) { // CallingConventionConverter determined non-unboxing stub exactTarget = newExactTarget; } else { // Target method was a method on a generic, but it wasn't a shared generic, and thus none of the above // complex unboxing stub digging logic was necessary. Do nothing, and use exactTarget as discovered // from GetCodeTarget } } } } else { // Create a fat function pointer, where the instantiation argument is ConstraintType, and the target is BoxAndToString, BoxAndGetHashCode, or BoxAndEquals IntPtr realTarget; switch (callDesc->_constrainedMethodSlot) { case s_ToStringSlot: realTarget = s_boxAndToStringFuncPtr; break; case s_GetHashCodeSlot: realTarget = s_boxAndGetHashCodeFuncPtr; break; case s_EqualsSlot: realTarget = s_boxAndEqualsFuncPtr; break; default: throw new NotSupportedException(); } exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(realTarget, callDesc->_constraintType.ToIntPtr()); } // Ensure that all threads will have their function pointers completely published before updating callDesc. // as the ExactTarget is read from callDesc by binder generated code without a barrier, we need a barrier here // to ensure that the new function pointer data is valid on all threads Interlocked.MemoryBarrier(); // Its possible for multiple threads to race to set exact target. Check to see we always set the same value if (callDesc->_exactTarget != IntPtr.Zero) { Debug.Assert(callDesc->_exactTarget == exactTarget); } callDesc->_exactTarget = exactTarget; return(exactTarget); }
private static unsafe IntPtr ResolveCallOnValueType(IntPtr unused, IntPtr callDescIntPtr) #endif { NonGenericConstrainedCallDesc *callDesc = (NonGenericConstrainedCallDesc *)callDescIntPtr; IntPtr exactTarget = IntPtr.Zero; IntPtr targetOnTypeVtable = RuntimeAugments.ResolveDispatchOnType(callDesc->_constraintType, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot); bool decodeUnboxing = true; if (!RuntimeAugments.IsInterface(callDesc->_constrainedMethodType)) { // Non-interface constrained call on a valuetype to a method that isn't GetHashCode/Equals/ToString?!?! if (callDesc->_constrainedMethodSlot > s_MaxObjectVTableSlot) { throw new NotSupportedException(); } RuntimeTypeHandle baseTypeHandle; bool gotBaseType = RuntimeAugments.TryGetBaseType(callDesc->_constraintType, out baseTypeHandle); Debug.Assert(gotBaseType); if (targetOnTypeVtable == RuntimeAugments.ResolveDispatchOnType(baseTypeHandle, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot)) { // In this case, the valuetype does not override the base types implementation of ToString(), GetHashCode(), or Equals(object) decodeUnboxing = false; } } if (decodeUnboxing) { exactTarget = TypeLoaderEnvironment.ConvertUnboxingFunctionPointerToUnderlyingNonUnboxingPointer(targetOnTypeVtable, callDesc->_constraintType); } else { // Create a fat function pointer, where the instantiation argument is ConstraintType, and the target is BoxAndToString, BoxAndGetHashCode, or BoxAndEquals IntPtr realTarget; switch (callDesc->_constrainedMethodSlot) { case s_ToStringSlot: realTarget = s_boxAndToStringFuncPtr; break; case s_GetHashCodeSlot: realTarget = s_boxAndGetHashCodeFuncPtr; break; case s_EqualsSlot: realTarget = s_boxAndEqualsFuncPtr; break; default: throw new NotSupportedException(); } exactTarget = FunctionPointerOps.GetGenericMethodFunctionPointer(realTarget, callDesc->_constraintType.ToIntPtr()); } // Ensure that all threads will have their function pointers completely published before updating callDesc. // as the ExactTarget is read from callDesc by binder generated code without a barrier, we need a barrier here // to ensure that the new function pointer data is valid on all threads Interlocked.MemoryBarrier(); // Its possible for multiple threads to race to set exact target. Check to see we always set the same value if (callDesc->_exactTarget != IntPtr.Zero) { Debug.Assert(callDesc->_exactTarget == exactTarget); } callDesc->_exactTarget = exactTarget; return(exactTarget); }