/// <summary> /// Resolve a MethodDesc to a callable method address and unboxing stub address by searching /// by searching in the InvokeMaps. This function is a wrapper around TryGetMethodInvokeDataFromInvokeMap /// that produces output in the format which matches the code table system. /// </summary> /// <param name="method">Native metadata method description object</param> /// <param name="methodAddress">Resolved method address</param> /// <param name="unboxingStubAddress">Resolved unboxing stub address</param> /// <param name="foundAddressType">Output - The type of method address match found. A canonical address may require extra parameters to call.</param> /// <returns>true when the resolution succeeded, false when not</returns> private static bool TryGetMethodAddressFromTypeSystemMethodViaInvokeMap( MethodDesc method, out IntPtr methodAddress, out IntPtr unboxingStubAddress, out MethodAddressType foundAddressType) { methodAddress = IntPtr.Zero; unboxingStubAddress = IntPtr.Zero; foundAddressType = MethodAddressType.None; #if SUPPORTS_NATIVE_METADATA_TYPE_LOADING NativeFormatMethod nativeFormatMethod = method.GetTypicalMethodDefinition() as NativeFormatMethod; if (nativeFormatMethod == null) { return(false); } MethodSignatureComparer methodSignatureComparer = new MethodSignatureComparer( nativeFormatMethod.MetadataReader, nativeFormatMethod.Handle); // Try to find a specific canonical match, or if that fails, a universal match if (TryGetMethodInvokeDataFromInvokeMap( nativeFormatMethod, method, ref methodSignatureComparer, CanonicalFormKind.Specific, out methodAddress, out foundAddressType) || TryGetMethodInvokeDataFromInvokeMap( nativeFormatMethod, method, ref methodSignatureComparer, CanonicalFormKind.Universal, out methodAddress, out foundAddressType)) { if (method.OwningType.IsValueType && !method.Signature.IsStatic) { // In this case the invoke map found an unboxing stub, and we should pull the method address out as well unboxingStubAddress = methodAddress; methodAddress = RuntimeAugments.GetCodeTarget(unboxingStubAddress); if (!method.HasInstantiation && ((foundAddressType != MethodAddressType.Exact) || method.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))) { IntPtr underlyingTarget; // unboxing and instantiating stub handling if (!TypeLoaderEnvironment.TryGetTargetOfUnboxingAndInstantiatingStub(methodAddress, out underlyingTarget)) { Environment.FailFast("Expected this to be an unboxing and instantiating stub."); } methodAddress = underlyingTarget; } } return(true); } #endif return(false); }
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); }
// 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; }
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); }