Example #1
0
 private static unsafe IntPtr ResolveCallOnReferenceTypeCacheMiss(IntPtr context, IntPtr callDescIntPtr, object contextObject, out IntPtr auxResult)
 {
     auxResult = IntPtr.Zero;
     NonGenericConstrainedCallDesc* callDesc = (NonGenericConstrainedCallDesc*)callDescIntPtr;
     IntPtr target = RuntimeAugments.ResolveDispatch(contextObject, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot);
     return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target);
 }
            private static unsafe IntPtr ResolveCallOnReferenceType(ref object thisPtr, IntPtr callDescIntPtr)
#endif
            {
                return(RuntimeAugments.RuntimeCacheLookup(thisPtr.GetType().TypeHandle.ToIntPtr(), callDescIntPtr,
                                                          (IntPtr context, IntPtr callDescPtr, object contextObject, ref IntPtr auxResult) =>
                {
                    NonGenericConstrainedCallDesc *callDesc = (NonGenericConstrainedCallDesc *)callDescPtr;
                    IntPtr target = RuntimeAugments.ResolveDispatch(contextObject, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot);
                    return GetThunkThatDereferencesThisPointerAndTailCallsTarget(target);
                }, thisPtr, out _));
            }
Example #3
0
            // Resolve a constrained call in case where the call is an MDIL constrained call directly through a function pointer located in the generic dictionary
            // This can only happen if there is a call from shared generic code to a structure which implements multiple of the same generic interface, and which instantiation
            // is decided by the exact type of the caller. For instance
            //
            // interface IFunc<T>
            // {
            //    void M();
            // }
            //
            // struct UnusualCase : IFunc<object>, IFunc<string>
            // {
            //    void IFunc<object>.M() { Console.WriteLine("In IFunc<object>");}
            //    void IFunc<string>.M() { Console.WriteLine("In IFunc<object>");}
            // }
            // class Caller<T,U> where T : IFunc<U>
            // {
            //    void Call(T c)
            //    {
            //        c.M();
            //    }
            // }
            //
            // If Caller is instantiated as Caller<UnusualCase,object>, or Caller<UnusualCase,string> we will generate code for Caller<UnusualCase,__Canon>.Call(UnusualCase)
            // However, that code will not be able to exactly specify the target of the call. It will need to use the generic dictionary.
            unsafe private static IntPtr ResolveDirectConstrainedCall(IntPtr callerTransitionBlockParam, IntPtr callDescIntPtr)
            {
                NonGenericConstrainedCallDesc* callDesc = (NonGenericConstrainedCallDesc*)callDescIntPtr;
                Debug.Assert(RuntimeAugments.IsInterface(callDesc->_constrainedMethodType));
                IntPtr targetOnTypeVtable = RuntimeAugments.ResolveDispatchOnType(callDesc->_constraintType, callDesc->_constrainedMethodType, callDesc->_constrainedMethodSlot);
                IntPtr exactTarget = RuntimeAugments.GetCodeTarget(targetOnTypeVtable);
                IntPtr underlyingTargetIfUnboxingAndInstantiatingStub;
                if (TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(exactTarget, out underlyingTargetIfUnboxingAndInstantiatingStub))
                {
                    // If this is an unboxing and instantiating stub, get the underlying pointer. The caller of this function is required to have already setup the 
                    // instantiation argument
                    exactTarget = underlyingTargetIfUnboxingAndInstantiatingStub;
                }

                callDesc->_exactTarget = exactTarget;
                return exactTarget;
            }
Example #4
0
            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);
            }
Example #5
0
            public static unsafe IntPtr Get(RuntimeTypeHandle constraintType, RuntimeTypeHandle constrainedMethodType, int constrainedMethodSlot, bool directConstrainedCall = false)
            {
                lock (s_nonGenericConstrainedCallDescs)
                {
                    // Get list of constrained call descs associated with a given type
                    LowLevelList <IntPtr> associatedCallDescs;
                    if (!s_nonGenericConstrainedCallDescs.TryGetValue(constraintType, out associatedCallDescs))
                    {
                        associatedCallDescs = new LowLevelList <IntPtr>();
                        s_nonGenericConstrainedCallDescs.Add(constraintType, associatedCallDescs);
                    }

                    // Perform linear scan of associated call descs to see if one matches
                    for (int i = 0; i < associatedCallDescs.Count; i++)
                    {
                        NonGenericConstrainedCallDesc *callDesc = (NonGenericConstrainedCallDesc *)associatedCallDescs[i];

                        Debug.Assert(constraintType.Equals(callDesc->_constraintType));

                        if (callDesc->_constrainedMethodSlot != constrainedMethodSlot)
                        {
                            continue;
                        }

                        if (!callDesc->_constrainedMethodType.Equals(constrainedMethodType))
                        {
                            continue;
                        }

                        // Found matching entry.
                        return(associatedCallDescs[i]);
                    }

                    // Did not find match, allocate a new one and add it to the lookup list
                    IntPtr newCallDescPtr = MemoryHelpers.AllocateMemory(sizeof(NonGenericConstrainedCallDesc));
                    NonGenericConstrainedCallDesc *newCallDesc = (NonGenericConstrainedCallDesc *)newCallDescPtr;
                    newCallDesc->_exactTarget = IntPtr.Zero;
                    if (directConstrainedCall)
                    {
                        newCallDesc->_lookupFunc = RuntimeAugments.GetUniversalTransitionThunk();
                    }
                    else
                    {
                        if (RuntimeAugments.IsValueType(constraintType))
                        {
                            newCallDesc->_lookupFunc = s_resolveCallOnValueTypeFuncPtr;
                        }
                        else
                        {
                            newCallDesc->_lookupFunc = s_resolveCallOnReferenceTypeFuncPtr;
                        }
                    }


                    newCallDesc->_constraintType        = constraintType;
                    newCallDesc->_constrainedMethodSlot = constrainedMethodSlot;
                    newCallDesc->_constrainedMethodType = constrainedMethodType;

                    associatedCallDescs.Add(newCallDescPtr);

                    return(newCallDescPtr);
                }
            }
Example #6
0
            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);
            }