未验证 提交 370f1604 编写于 作者: M Michal Strehovský 提交者: GitHub

Add support for creating delegates to static virtual methods (#66936)

Requires a JitInterface change because we need to be able to pass information about constraints to `getReadyToRunDelegateCtorHelper`
上级 71ad9f53
......@@ -2441,6 +2441,7 @@ public:
virtual void getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN * pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP * pLookup
) = 0;
......
......@@ -283,6 +283,7 @@ bool getReadyToRunHelper(
void getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup) override;
......
......@@ -43,11 +43,11 @@ typedef const GUID *LPCGUID;
#define GUID_DEFINED
#endif // !GUID_DEFINED
constexpr GUID JITEEVersionIdentifier = { /* bcc99ca6-5291-4cc0-a5d9-2758456053f3 */
0xbcc99ca6,
0x5291,
0x4cc0,
{ 0xa5, 0xd9, 0x27, 0x58, 0x45, 0x60, 0x53, 0xf3 }
constexpr GUID JITEEVersionIdentifier = { /* 398270b8-d474-428f-8113-3834281853cf */
0x398270b8,
0xd474,
0x428f,
{0x81, 0x13, 0x38, 0x34, 0x28, 0x18, 0x53, 0xcf}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////
......
......@@ -656,11 +656,12 @@ bool WrapICorJitInfo::getReadyToRunHelper(
void WrapICorJitInfo::getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
API_ENTER(getReadyToRunDelegateCtorHelper);
wrapHnd->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
wrapHnd->getReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
API_LEAVE(getReadyToRunDelegateCtorHelper);
}
......
......@@ -142,6 +142,21 @@ inline ti_types JITtype2tiType(CorInfoType type)
return g_ti_types_map[type];
};
/*****************************************************************************
* Captures information about a method pointer
*
* m_token is the CORINFO_RESOLVED_TOKEN from the IL, potentially with a more
* precise method handle from getCallInfo
* m_tokenConstraint is the constraint if this was a constrained ldftn.
*
*/
class methodPointerInfo
{
public:
CORINFO_RESOLVED_TOKEN m_token;
mdToken m_tokenConstraint;
};
/*****************************************************************************
* Declares the typeInfo class, which represents the type of an entity on the
* stack, in a local variable or an argument.
......@@ -221,9 +236,6 @@ inline ti_types JITtype2tiType(CorInfoType type)
// since conversions between them are not verifiable.
#define TI_FLAG_NATIVE_INT 0x00000200
// This item contains resolved token. It is used for ctor delegate optimization.
#define TI_FLAG_TOKEN 0x00000400
// This item contains the 'this' pointer (used for tracking)
#define TI_FLAG_THIS_PTR 0x00001000
......@@ -270,7 +282,7 @@ inline ti_types JITtype2tiType(CorInfoType type)
* - A type (ref, array, value type) (m_cls describes the type)
* - An array (m_cls describes the array type)
* - A byref (byref flag set, otherwise the same as the above),
* - A Function Pointer (m_method)
* - A Function Pointer (m_methodPointerInfo)
* - A byref local variable (byref and byref local flags set), can be
* uninitialized
*
......@@ -291,7 +303,7 @@ private:
unsigned byref : 1; // used
unsigned byref_readonly : 1; // used
unsigned nativeInt : 1; // used
unsigned token : 1; // used
unsigned : 1; // unused
unsigned : 1; // unused
unsigned thisPtr : 1; // used
unsigned thisPermHome : 1; // used
......@@ -303,10 +315,8 @@ private:
union {
CORINFO_CLASS_HANDLE m_cls;
// Valid only for type TI_METHOD without IsToken
CORINFO_METHOD_HANDLE m_method;
// Valid only for TI_TOKEN with IsToken
CORINFO_RESOLVED_TOKEN* m_token;
// Valid only for type TI_METHOD
methodPointerInfo* m_methodPointerInfo;
};
template <typename T>
......@@ -362,21 +372,13 @@ public:
m_cls = cls;
}
typeInfo(CORINFO_METHOD_HANDLE method)
{
assert(method != nullptr && !isInvalidHandle(method));
m_flags = TI_METHOD;
m_method = method;
}
typeInfo(CORINFO_RESOLVED_TOKEN* token)
typeInfo(methodPointerInfo* methodPointerInfo)
{
assert(token != nullptr);
assert(token->hMethod != nullptr);
assert(!isInvalidHandle(token->hMethod));
m_flags = TI_METHOD;
SetIsToken();
m_token = token;
assert(methodPointerInfo != nullptr);
assert(methodPointerInfo->m_token.hMethod != nullptr);
assert(!isInvalidHandle(methodPointerInfo->m_token.hMethod));
m_flags = TI_METHOD;
m_methodPointerInfo = methodPointerInfo;
}
#ifdef DEBUG
......@@ -458,12 +460,6 @@ public:
// Operations
/////////////////////////////////////////////////////////////////////////
void SetIsToken()
{
m_flags |= TI_FLAG_TOKEN;
assert(m_bits.token);
}
void SetIsThisPtr()
{
m_flags |= TI_FLAG_THIS_PTR;
......@@ -573,17 +569,13 @@ public:
CORINFO_METHOD_HANDLE GetMethod() const
{
assert(GetType() == TI_METHOD);
if (IsToken())
{
return m_token->hMethod;
}
return m_method;
return m_methodPointerInfo->m_token.hMethod;
}
CORINFO_RESOLVED_TOKEN* GetToken() const
methodPointerInfo* GetMethodPointerInfo() const
{
assert(IsToken());
return m_token;
assert(GetType() == TI_METHOD);
return m_methodPointerInfo;
}
// Get this item's type
......@@ -750,11 +742,6 @@ public:
return (m_flags & TI_FLAG_UNINIT_OBJREF);
}
bool IsToken() const
{
return IsMethod() && ((m_flags & TI_FLAG_TOKEN) != 0);
}
private:
// used to make functions that return typeinfo efficient.
typeInfo(DWORD flags, CORINFO_CLASS_HANDLE cls)
......
......@@ -4988,7 +4988,7 @@ private:
bool impIsClassExact(CORINFO_CLASS_HANDLE classHnd);
bool impCanSkipCovariantStoreCheck(GenTree* value, GenTree* array);
CORINFO_RESOLVED_TOKEN* impAllocateToken(const CORINFO_RESOLVED_TOKEN& token);
methodPointerInfo* impAllocateMethodPointerInfo(const CORINFO_RESOLVED_TOKEN& token, mdToken tokenConstrained);
/*
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
......@@ -6487,7 +6487,7 @@ private:
#endif
GenTree* fgOptimizeDelegateConstructor(GenTreeCall* call,
CORINFO_CONTEXT_HANDLE* ExactContextHnd,
CORINFO_RESOLVED_TOKEN* ldftnToken);
methodPointerInfo* ldftnToken);
GenTree* fgMorphLeaf(GenTree* tree);
void fgAssignSetVarDef(GenTree* tree);
GenTree* fgMorphOneAsgBlockOp(GenTree* tree);
......
......@@ -1047,7 +1047,7 @@ bool Compiler::fgAddrCouldBeNull(GenTree* addr)
GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
CORINFO_CONTEXT_HANDLE* ExactContextHnd,
CORINFO_RESOLVED_TOKEN* ldftnToken)
methodPointerInfo* ldftnToken)
{
JITDUMP("\nfgOptimizeDelegateConstructor: ");
noway_assert(call->gtCallType == CT_USER_FUNC);
......@@ -1115,14 +1115,14 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
// via the above pattern match, and more...
if (ldftnToken != nullptr)
{
assert(ldftnToken->hMethod != nullptr);
assert(ldftnToken->m_token.hMethod != nullptr);
if (targetMethodHnd != nullptr)
{
assert(targetMethodHnd == ldftnToken->hMethod);
assert(targetMethodHnd == ldftnToken->m_token.hMethod);
}
targetMethodHnd = ldftnToken->hMethod;
targetMethodHnd = ldftnToken->m_token.hMethod;
}
else
{
......@@ -1143,7 +1143,8 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
GenTreeCall::Use* helperArgs = nullptr;
CORINFO_LOOKUP pLookup;
CORINFO_CONST_LOOKUP entryPoint;
info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &pLookup);
info.compCompHnd->getReadyToRunDelegateCtorHelper(&ldftnToken->m_token, ldftnToken->m_tokenConstraint,
clsHnd, &pLookup);
if (!pLookup.lookupKind.needsRuntimeLookup)
{
helperArgs = gtNewCallArgs(thisPointer, targetObjPointers);
......@@ -1153,7 +1154,7 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
{
assert(oper != GT_FTN_ADDR);
CORINFO_CONST_LOOKUP genericLookup;
info.compCompHnd->getReadyToRunHelper(ldftnToken, &pLookup.lookupKind,
info.compCompHnd->getReadyToRunHelper(&ldftnToken->m_token, &pLookup.lookupKind,
CORINFO_HELP_READYTORUN_GENERIC_HANDLE, &genericLookup);
GenTree* ctxTree = getRuntimeContextTree(pLookup.lookupKind.runtimeLookupKind);
helperArgs = gtNewCallArgs(thisPointer, targetObjPointers, ctxTree);
......@@ -1179,7 +1180,8 @@ GenTree* Compiler::fgOptimizeDelegateConstructor(GenTreeCall* call,
call = gtNewHelperCallNode(CORINFO_HELP_READYTORUN_DELEGATE_CTOR, TYP_VOID, helperArgs);
CORINFO_LOOKUP entryPoint;
info.compCompHnd->getReadyToRunDelegateCtorHelper(ldftnToken, clsHnd, &entryPoint);
info.compCompHnd->getReadyToRunDelegateCtorHelper(&ldftnToken->m_token, ldftnToken->m_tokenConstraint,
clsHnd, &entryPoint);
assert(!entryPoint.lookupKind.needsRuntimeLookup);
call->setEntryPoint(entryPoint.constLookup);
}
......
......@@ -8716,7 +8716,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
const int tailCallFlags = (prefixFlags & PREFIX_TAILCALL);
const bool isReadonlyCall = (prefixFlags & PREFIX_READONLY) != 0;
CORINFO_RESOLVED_TOKEN* ldftnToken = nullptr;
methodPointerInfo* ldftnInfo = nullptr;
// Synchronized methods need to call CORINFO_HELP_MON_EXIT at the end. We could
// do that before tailcalls, but that is probably not the intended
......@@ -9530,9 +9530,9 @@ var_types Compiler::impImportCall(OPCODE opcode,
if (impStackHeight() > 0)
{
typeInfo delegateTypeInfo = impStackTop().seTypeInfo;
if (delegateTypeInfo.IsToken())
if (delegateTypeInfo.IsMethod())
{
ldftnToken = delegateTypeInfo.GetToken();
ldftnInfo = delegateTypeInfo.GetMethodPointerInfo();
}
}
}
......@@ -9626,7 +9626,7 @@ var_types Compiler::impImportCall(OPCODE opcode,
{
// New inliner morph it in impImportCall.
// This will allow us to inline the call to the delegate constructor.
call = fgOptimizeDelegateConstructor(call->AsCall(), &exactContextHnd, ldftnToken);
call = fgOptimizeDelegateConstructor(call->AsCall(), &exactContextHnd, ldftnInfo);
}
if (!bIntrinsicImported)
......@@ -12156,7 +12156,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
bool usingReadyToRunHelper = false;
#endif
CORINFO_RESOLVED_TOKEN resolvedToken;
CORINFO_RESOLVED_TOKEN constrainedResolvedToken;
CORINFO_RESOLVED_TOKEN constrainedResolvedToken = {};
CORINFO_CALL_INFO callInfo;
CORINFO_FIELD_INFO fieldInfo;
......@@ -14558,9 +14558,10 @@ void Compiler::impImportBlockCode(BasicBlock* block)
// Call info may have more precise information about the function than
// the resolved token.
CORINFO_RESOLVED_TOKEN* heapToken = impAllocateToken(resolvedToken);
mdToken constrainedToken = prefixFlags & PREFIX_CONSTRAINED ? constrainedResolvedToken.token : 0;
methodPointerInfo* heapToken = impAllocateMethodPointerInfo(resolvedToken, constrainedToken);
assert(callInfo.hMethod != nullptr);
heapToken->hMethod = callInfo.hMethod;
heapToken->m_token.hMethod = callInfo.hMethod;
impPushOnStack(op1, typeInfo(heapToken));
break;
......@@ -14632,13 +14633,13 @@ void Compiler::impImportBlockCode(BasicBlock* block)
return;
}
CORINFO_RESOLVED_TOKEN* heapToken = impAllocateToken(resolvedToken);
methodPointerInfo* heapToken = impAllocateMethodPointerInfo(resolvedToken, 0);
assert(heapToken->tokenType == CORINFO_TOKENKIND_Method);
assert(heapToken->m_token.tokenType == CORINFO_TOKENKIND_Method);
assert(callInfo.hMethod != nullptr);
heapToken->tokenType = CORINFO_TOKENKIND_Ldvirtftn;
heapToken->hMethod = callInfo.hMethod;
heapToken->m_token.tokenType = CORINFO_TOKENKIND_Ldvirtftn;
heapToken->m_token.hMethod = callInfo.hMethod;
impPushOnStack(fptr, typeInfo(heapToken));
break;
......@@ -21662,17 +21663,19 @@ CORINFO_CLASS_HANDLE Compiler::impGetSpecialIntrinsicExactReturnType(CORINFO_MET
}
//------------------------------------------------------------------------
// impAllocateToken: create CORINFO_RESOLVED_TOKEN into jit-allocated memory and init it.
// impAllocateMethodPointerInfo: create methodPointerInfo into jit-allocated memory and init it.
//
// Arguments:
// token - init value for the allocated token.
// tokenConstrained - init value for the constraint associated with the token
//
// Return Value:
// pointer to token into jit-allocated memory.
CORINFO_RESOLVED_TOKEN* Compiler::impAllocateToken(const CORINFO_RESOLVED_TOKEN& token)
methodPointerInfo* Compiler::impAllocateMethodPointerInfo(const CORINFO_RESOLVED_TOKEN& token, mdToken tokenConstrained)
{
CORINFO_RESOLVED_TOKEN* memory = getAllocator(CMK_Unknown).allocate<CORINFO_RESOLVED_TOKEN>(1);
*memory = token;
methodPointerInfo* memory = getAllocator(CMK_Unknown).allocate<methodPointerInfo>(1);
memory->m_token = token;
memory->m_tokenConstraint = tokenConstrained;
return memory;
}
......
......@@ -989,12 +989,12 @@ static byte _getReadyToRunHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO
}
[UnmanagedCallersOnly]
static void _getReadyToRunDelegateCtorHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_STRUCT_* delegateType, CORINFO_LOOKUP* pLookup)
static void _getReadyToRunDelegateCtorHelper(IntPtr thisHandle, IntPtr* ppException, CORINFO_RESOLVED_TOKEN* pTargetMethod, mdToken targetConstraint, CORINFO_CLASS_STRUCT_* delegateType, CORINFO_LOOKUP* pLookup)
{
var _this = GetThis(thisHandle);
try
{
_this.getReadyToRunDelegateCtorHelper(ref *pTargetMethod, delegateType, ref *pLookup);
_this.getReadyToRunDelegateCtorHelper(ref *pTargetMethod, targetConstraint, delegateType, ref *pLookup);
}
catch (Exception ex)
{
......@@ -2635,7 +2635,7 @@ static IntPtr GetUnmanagedCallbacks()
callbacks[63] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getBoxHelper;
callbacks[64] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, CorInfoHelpFunc>)&_getUnBoxHelper;
callbacks[65] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_LOOKUP_KIND*, CorInfoHelpFunc, CORINFO_CONST_LOOKUP*, byte>)&_getReadyToRunHelper;
callbacks[66] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, CORINFO_CLASS_STRUCT_*, CORINFO_LOOKUP*, void>)&_getReadyToRunDelegateCtorHelper;
callbacks[66] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_RESOLVED_TOKEN*, mdToken, CORINFO_CLASS_STRUCT_*, CORINFO_LOOKUP*, void>)&_getReadyToRunDelegateCtorHelper;
callbacks[67] = (delegate* unmanaged<IntPtr, IntPtr*, CorInfoHelpFunc, byte*>)&_getHelperName;
callbacks[68] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_FIELD_STRUCT_*, CORINFO_METHOD_STRUCT_*, CORINFO_CONTEXT_STRUCT*, CorInfoInitClassResult>)&_initClass;
callbacks[69] = (delegate* unmanaged<IntPtr, IntPtr*, CORINFO_CLASS_STRUCT_*, void>)&_classMustBeLoadedBeforeCodeIsRun;
......
......@@ -1672,14 +1672,23 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe
var typeOrMethodContext = (pResolvedToken.tokenContext == contextFromMethodBeingCompiled()) ?
MethodBeingCompiled : HandleToObject((IntPtr)pResolvedToken.tokenContext);
object result = ResolveTokenInScope(methodIL, typeOrMethodContext, pResolvedToken.token);
object result = GetRuntimeDeterminedObjectForToken(methodIL, typeOrMethodContext, pResolvedToken.token);
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr)
result = ((TypeDesc)result).MakeArrayType();
return result;
}
private object GetRuntimeDeterminedObjectForToken(MethodILScope methodIL, object typeOrMethodContext, mdToken token)
{
object result = ResolveTokenInScope(methodIL, typeOrMethodContext, token);
if (result is MethodDesc method)
{
if (method.IsSharedByGenericInstantiations)
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, pResolvedToken.token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((MethodDesc)result).IsRuntimeDeterminedExactMethod);
}
}
......@@ -1688,7 +1697,7 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe
if (field.OwningType.IsCanonicalSubtype(CanonicalFormKind.Any))
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, pResolvedToken.token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((FieldDesc)result).OwningType.IsRuntimeDeterminedSubtype);
}
}
......@@ -1698,16 +1707,13 @@ private object GetRuntimeDeterminedObjectForToken(ref CORINFO_RESOLVED_TOKEN pRe
if (type.IsCanonicalSubtype(CanonicalFormKind.Any))
{
MethodDesc sharedMethod = methodIL.OwningMethod.GetSharedRuntimeFormMethodTarget();
result = ResolveTokenWithSubstitution(methodIL, pResolvedToken.token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
result = ResolveTokenWithSubstitution(methodIL, token, sharedMethod.OwningType.Instantiation, sharedMethod.Instantiation);
Debug.Assert(((TypeDesc)result).IsRuntimeDeterminedSubtype ||
/* If the resolved type is not runtime determined there's a chance we went down this path
because there was a literal typeof(__Canon) in the compiled IL - check for that
by resolving the token in the definition. */
((TypeDesc)methodIL.GetMethodILScopeDefinition().GetObject((int)pResolvedToken.token)).IsCanonicalDefinitionType(CanonicalFormKind.Any));
((TypeDesc)methodIL.GetMethodILScopeDefinition().GetObject((int)token)).IsCanonicalDefinitionType(CanonicalFormKind.Any));
}
if (pResolvedToken.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Newarr)
result = ((TypeDesc)result).MakeArrayType();
}
return result;
......
......@@ -219,7 +219,7 @@ FUNCTIONS
CorInfoHelpFunc getBoxHelper(CORINFO_CLASS_HANDLE cls)
CorInfoHelpFunc getUnBoxHelper(CORINFO_CLASS_HANDLE cls)
bool getReadyToRunHelper(CORINFO_RESOLVED_TOKEN * pResolvedToken, CORINFO_LOOKUP_KIND * pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP *pLookup)
void getReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN * pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_LOOKUP *pLookup)
void getReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN * pTargetMethod, mdToken targetConstraint, CORINFO_CLASS_HANDLE delegateType, CORINFO_LOOKUP *pLookup)
const char* getHelperName(CorInfoHelpFunc helpFunc)
CorInfoInitClassResult initClass(CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context)
void classMustBeLoadedBeforeCodeIsRun(CORINFO_CLASS_HANDLE cls)
......
......@@ -112,7 +112,7 @@ public bool CanConstructType(TypeDesc type)
return _devirtualizationManager.CanConstructType(type);
}
public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, bool followVirtualDispatch)
public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc target, TypeDesc constrainedType, bool followVirtualDispatch)
{
// If we're creating a delegate to a virtual method that cannot be overriden, devirtualize.
// This is not just an optimization - it's required for correctness in the presence of sealed
......@@ -123,7 +123,7 @@ public DelegateCreationInfo GetDelegateCtor(TypeDesc delegateType, MethodDesc ta
if (followVirtualDispatch)
target = MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(target);
return DelegateCreationInfo.Create(delegateType, target, NodeFactory, followVirtualDispatch);
return DelegateCreationInfo.Create(delegateType, target, constrainedType, NodeFactory, followVirtualDispatch);
}
/// <summary>
......
......@@ -25,10 +25,14 @@ private enum TargetKind
InterfaceDispatch,
VTableLookup,
MethodHandle,
ConstrainedMethod,
}
private TargetKind _targetKind;
private TypeDesc _constrainedType;
private MethodDesc _targetMethod;
/// <summary>
/// Gets the node corresponding to the method that initializes the delegate.
/// </summary>
......@@ -39,7 +43,11 @@ public IMethodNode Constructor
public MethodDesc TargetMethod
{
get;
get
{
Debug.Assert(_constrainedType == null);
return _targetMethod;
}
}
private bool TargetMethodIsUnboxingThunk
......@@ -75,6 +83,10 @@ public bool NeedsRuntimeLookup
case TargetKind.MethodHandle:
return TargetMethod.IsRuntimeDeterminedExactMethod;
case TargetKind.ConstrainedMethod:
Debug.Assert(_targetMethod.IsRuntimeDeterminedExactMethod || _constrainedType.IsRuntimeDeterminedSubtype);
return true;
default:
Debug.Assert(false);
return false;
......@@ -100,6 +112,9 @@ public GenericLookupResult GetLookupKind(NodeFactory factory)
case TargetKind.MethodHandle:
return factory.GenericLookup.MethodHandle(TargetMethod);
case TargetKind.ConstrainedMethod:
return factory.GenericLookup.ConstrainedMethodUse(_targetMethod, _constrainedType, directCall: !_targetMethod.HasInstantiation);
default:
Debug.Assert(false);
return null;
......@@ -145,12 +160,13 @@ public IMethodNode Thunk
get;
}
private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, TargetKind targetKind, IMethodNode thunk = null)
private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, TypeDesc constrainedType, TargetKind targetKind, IMethodNode thunk = null)
{
Debug.Assert(targetKind != TargetKind.VTableLookup
|| MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod) == targetMethod);
Constructor = constructor;
TargetMethod = targetMethod;
_targetMethod = targetMethod;
_constrainedType = constrainedType;
_targetKind = targetKind;
Thunk = thunk;
}
......@@ -159,7 +175,7 @@ private DelegateCreationInfo(IMethodNode constructor, MethodDesc targetMethod, T
/// 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)
public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targetMethod, TypeDesc constrainedType, NodeFactory factory, bool followVirtualDispatch)
{
CompilerTypeSystemContext context = factory.TypeSystemContext;
DefType systemDelegate = context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType;
......@@ -206,7 +222,8 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ
return new DelegateCreationInfo(
factory.MethodEntrypoint(initMethod),
targetMethod,
TargetKind.ExactCallableAddress,
constrainedType,
constrainedType == null ? TargetKind.ExactCallableAddress : TargetKind.ConstrainedMethod,
factory.MethodEntrypoint(invokeThunk));
}
else
......@@ -260,9 +277,11 @@ public static DelegateCreationInfo Create(TypeDesc delegateType, MethodDesc targ
}
}
Debug.Assert(constrainedType == null);
return new DelegateCreationInfo(
factory.MethodEntrypoint(systemDelegate.GetKnownMethod(initializeMethodName, null)),
targetMethod,
constrainedType,
kind);
}
}
......@@ -274,7 +293,12 @@ public void AppendMangledName(NameMangler nameMangler, Utf8StringBuilder sb)
sb.Append("FromVtbl_");
Constructor.AppendMangledName(nameMangler, sb);
sb.Append("__");
sb.Append(nameMangler.GetMangledMethodName(TargetMethod));
sb.Append(nameMangler.GetMangledMethodName(_targetMethod));
if (_constrainedType != null)
{
sb.Append("__");
nameMangler.GetMangledTypeName(_constrainedType);
}
if (Thunk != null)
{
sb.Append("__");
......@@ -287,14 +311,15 @@ public override bool Equals(object obj)
var other = obj as DelegateCreationInfo;
return other != null
&& Constructor == other.Constructor
&& TargetMethod == other.TargetMethod
&& _targetMethod == other._targetMethod
&& _constrainedType == other._constrainedType
&& _targetKind == other._targetKind
&& Thunk == other.Thunk;
}
public override int GetHashCode()
{
return Constructor.GetHashCode() ^ TargetMethod.GetHashCode();
return Constructor.GetHashCode() ^ _targetMethod.GetHashCode();
}
#if !SUPPORT_JIT
......@@ -312,6 +337,20 @@ internal int CompareTo(DelegateCreationInfo other, TypeSystemComparer comparer)
if (compare != 0)
return compare;
if (_constrainedType != null && other._constrainedType != null)
{
compare = comparer.Compare(_constrainedType, other._constrainedType);
if (compare != 0)
return compare;
}
else
{
if (_constrainedType != null)
return 1;
if (other._constrainedType != null)
return -1;
}
if (Thunk == other.Thunk)
return 0;
......
......@@ -1388,6 +1388,10 @@ private Status TryScanMethod(MethodIL methodIL, Value[] parameters, Stack<Method
}
break;
case ILOpcode.constrained:
// Fallthrough. If this is ever implemented, make sure delegates to static virtual methods
// are also handled. We currently assume the frozen delegate will not be to a static
// virtual interface method.
default:
return Status.Fail(methodIL.OwningMethod, opcode);
}
......@@ -1991,7 +1995,12 @@ public virtual void WriteContent(ref ObjectDataBuilder builder, ISymbolNode this
{
Debug.Assert(_methodPointed.Signature.IsStatic == (_firstParameter == null));
var creationInfo = DelegateCreationInfo.Create(Type.ConvertToCanonForm(CanonicalFormKind.Specific), _methodPointed, factory, followVirtualDispatch: false);
var creationInfo = DelegateCreationInfo.Create(
Type.ConvertToCanonForm(CanonicalFormKind.Specific),
_methodPointed,
constrainedType: null,
factory,
followVirtualDispatch: false);
Debug.Assert(!creationInfo.TargetNeedsVTableLookup);
......
......@@ -318,35 +318,6 @@ private void ImportCall(ILOpcode opcode, int token)
_dependencies.Add(GetHelperEntrypoint(ReadyToRunHelper.NewObject), reason);
}
}
if (owningType.IsDelegate)
{
// If this is a verifiable delegate construction sequence, the previous instruction is a ldftn/ldvirtftn
if (_previousInstructionOffset >= 0 && _ilBytes[_previousInstructionOffset] == (byte)ILOpcode.prefix1)
{
// TODO: for ldvirtftn we need to also check for the `dup` instruction, otherwise this is a normal newobj.
ILOpcode previousOpcode = (ILOpcode)(0x100 + _ilBytes[_previousInstructionOffset + 1]);
if (previousOpcode == ILOpcode.ldvirtftn || previousOpcode == ILOpcode.ldftn)
{
int delTargetToken = ReadILTokenAt(_previousInstructionOffset + 2);
var delTargetMethod = (MethodDesc)_methodIL.GetObject(delTargetToken);
TypeDesc canonDelegateType = method.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, delTargetMethod, previousOpcode == ILOpcode.ldvirtftn);
if (info.NeedsRuntimeLookup)
{
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason);
}
else
{
_dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason);
}
return;
}
}
}
}
if (method.OwningType.IsDelegate && method.Name == "Invoke" &&
......@@ -518,23 +489,13 @@ private void ImportCall(ILOpcode opcode, int token)
ThrowHelper.ThrowBadImageFormatException();
}
bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn;
MethodDesc targetForDelegate = !resolvedConstraint || forceUseRuntimeLookup ? runtimeDeterminedMethod : targetMethod;
TypeDesc constraintForDelegate = !resolvedConstraint || forceUseRuntimeLookup ? _constrained : null;
int numDependenciesBeforeTargetDetermination = _dependencies.Count;
if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg())
{
// Needs a single address to call this method but the method needs a hidden argument.
// We need a fat function pointer for this that captures both things.
bool allowInstParam = opcode != ILOpcode.ldvirtftn && opcode != ILOpcode.ldftn;
if (exactContextNeedsRuntimeLookup)
{
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason);
}
else
{
_dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason);
}
}
else if (directCall && resolvedConstraint && exactContextNeedsRuntimeLookup)
if (directCall && resolvedConstraint && exactContextNeedsRuntimeLookup)
{
// We want to do a direct call to a shared method on a valuetype. We need to provide
// a generic context, but the JitInterface doesn't provide a way for us to do it from here.
......@@ -556,6 +517,22 @@ private void ImportCall(ILOpcode opcode, int token)
}
Debug.Assert(targetOfLookup.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific));
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, targetOfLookup), reason);
targetForDelegate = targetOfLookup;
}
else if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg())
{
// Needs a single address to call this method but the method needs a hidden argument.
// We need a fat function pointer for this that captures both things.
if (exactContextNeedsRuntimeLookup)
{
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.MethodEntry, runtimeDeterminedMethod), reason);
}
else
{
_dependencies.Add(_factory.FatFunctionPointer(runtimeDeterminedMethod), reason);
}
}
else if (directCall)
{
......@@ -756,19 +733,37 @@ private void ImportCall(ILOpcode opcode, int token)
targetMethod : MetadataVirtualMethodAlgorithm.FindSlotDefiningMethodForVirtualMethod(targetMethod);
_dependencies.Add(_factory.VirtualMethodUse(slotDefiningMethod), reason);
}
}
private void ImportLdFtn(int token, ILOpcode opCode)
{
// Is this a verifiable delegate creation? If so, we will handle it when we reach the newobj
if (_ilBytes[_currentOffset] == (byte)ILOpcode.newobj)
// Is this a verifiable delegate creation sequence (load function pointer followed by newobj)?
if ((opcode == ILOpcode.ldftn || opcode == ILOpcode.ldvirtftn)
&& _currentOffset + 5 < _ilBytes.Length
&& _basicBlocks[_currentOffset] == null
&& _ilBytes[_currentOffset] == (byte)ILOpcode.newobj)
{
int delegateToken = ReadILTokenAt(_currentOffset + 1);
var delegateType = ((MethodDesc)_methodIL.GetObject(delegateToken)).OwningType;
if (delegateType.IsDelegate)
return;
// TODO: for ldvirtftn we need to also check for the `dup` instruction
int ctorToken = ReadILTokenAt(_currentOffset + 1);
var ctorMethod = (MethodDesc)_methodIL.GetObject(ctorToken);
if (ctorMethod.OwningType.IsDelegate)
{
// Yep, verifiable delegate creation
// Drop any dependencies we inserted so far - the delegate construction helper is the only dependency
while (_dependencies.Count > numDependenciesBeforeTargetDetermination)
_dependencies.RemoveAt(_dependencies.Count - 1);
TypeDesc canonDelegateType = ctorMethod.OwningType.ConvertToCanonForm(CanonicalFormKind.Specific);
DelegateCreationInfo info = _compilation.GetDelegateCtor(canonDelegateType, targetForDelegate, constraintForDelegate, opcode == ILOpcode.ldvirtftn);
if (info.NeedsRuntimeLookup)
_dependencies.Add(GetGenericLookupHelper(ReadyToRunHelperId.DelegateCtor, info), reason);
else
_dependencies.Add(_factory.ReadyToRunHelper(ReadyToRunHelperId.DelegateCtor, info), reason);
}
}
}
private void ImportLdFtn(int token, ILOpcode opCode)
{
ImportCall(opCode, token);
}
......
......@@ -590,7 +590,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref
return true;
}
private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup)
private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, mdToken targetConstraint, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup)
{
#if DEBUG
// In debug, write some bogus data to the struct to ensure we have filled everything
......
......@@ -352,7 +352,7 @@ private bool getReadyToRunHelper(ref CORINFO_RESOLVED_TOKEN pResolvedToken, ref
return true;
}
private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup)
private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetMethod, mdToken targetConstraint, CORINFO_CLASS_STRUCT_* delegateType, ref CORINFO_LOOKUP pLookup)
{
#if DEBUG
// In debug, write some bogus data to the struct to ensure we have filled everything
......@@ -361,17 +361,58 @@ private void getReadyToRunDelegateCtorHelper(ref CORINFO_RESOLVED_TOKEN pTargetM
MemoryHelper.FillMemory((byte*)tmp, 0xcc, sizeof(CORINFO_LOOKUP));
#endif
MethodDesc targetMethod = HandleToObject(pTargetMethod.hMethod);
MethodDesc expectedTargetMethod = HandleToObject(pTargetMethod.hMethod);
TypeDesc delegateTypeDesc = HandleToObject(delegateType);
if (targetMethod.IsSharedByGenericInstantiations)
MethodDesc targetMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pTargetMethod);
// If this was a constrained+ldftn sequence, we need to resolve the constraint
TypeDesc constrainedType = null;
if (targetConstraint != 0)
{
// If the method is not exact, fetch it as a runtime determined method.
targetMethod = (MethodDesc)GetRuntimeDeterminedObjectForToken(ref pTargetMethod);
// Should really only be here for static virtuals since constrained ldftn is not allowed elsewhere.
Debug.Assert(targetMethod.IsVirtual && targetMethod.Signature.IsStatic
&& pTargetMethod.tokenType != CorInfoTokenKind.CORINFO_TOKENKIND_Ldvirtftn);
var methodIL = HandleToObject(pTargetMethod.tokenScope);
var typeOrMethodContext = (pTargetMethod.tokenContext == contextFromMethodBeingCompiled()) ?
MethodBeingCompiled : HandleToObject((IntPtr)pTargetMethod.tokenContext);
var canonConstrainedType = (TypeDesc)ResolveTokenInScope(methodIL, typeOrMethodContext, targetConstraint);
TypeDesc interfaceType = HandleToObject(pTargetMethod.hClass);
var interfaceMethod = (MethodDesc)ResolveTokenInScope(methodIL, typeOrMethodContext, pTargetMethod.token);
constrainedType = (TypeDesc)GetRuntimeDeterminedObjectForToken(methodIL, typeOrMethodContext, targetConstraint);
MethodDesc directMethod = canonConstrainedType.GetClosestDefType().TryResolveConstraintMethodApprox(interfaceType, interfaceMethod, out bool forceRuntimeLookup);
if (directMethod != null)
{
// We resolved on a canonical form of the valuetype. Now find the method on the runtime determined form.
Debug.Assert(directMethod.OwningType.IsValueType);
Debug.Assert(!forceRuntimeLookup);
MethodDesc targetOfLookup;
if (constrainedType.IsRuntimeDeterminedType)
targetOfLookup = _compilation.TypeSystemContext.GetMethodForRuntimeDeterminedType(directMethod.GetTypicalMethodDefinition(), (RuntimeDeterminedType)constrainedType);
else if (constrainedType.HasInstantiation)
targetOfLookup = _compilation.TypeSystemContext.GetMethodForInstantiatedType(directMethod.GetTypicalMethodDefinition(), (InstantiatedType)constrainedType);
else
targetOfLookup = directMethod.GetMethodDefinition();
if (targetOfLookup.HasInstantiation)
targetOfLookup = targetOfLookup.MakeInstantiatedMethod(targetMethod.Instantiation);
targetMethod = targetOfLookup;
// We resolved the constraint
constrainedType = null;
}
}
// We better come up with the same method that getCallInfo came up with, with the only difference being
// that our targetMethod is RuntimeDetermined.
Debug.Assert(expectedTargetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific) == targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific));
bool isLdvirtftn = pTargetMethod.tokenType == CorInfoTokenKind.CORINFO_TOKENKIND_Ldvirtftn;
DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(delegateTypeDesc, targetMethod, isLdvirtftn);
DelegateCreationInfo delegateInfo = _compilation.GetDelegateCtor(delegateTypeDesc, targetMethod, constrainedType, isLdvirtftn);
if (delegateInfo.NeedsRuntimeLookup)
{
......@@ -1262,44 +1303,7 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO
ThrowHelper.ThrowBadImageFormatException();
}
if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg())
{
// JIT needs a single address to call this method but the method needs a hidden argument.
// We need a fat function pointer for this that captures both things.
targetIsFatFunctionPointer = true;
// JIT won't expect fat function pointers unless this is e.g. delegate creation
Debug.Assert((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0);
pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER;
if (pResult->exactContextNeedsRuntimeLookup)
{
pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = true;
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = 0;
pResult->codePointerOrStubLookup.runtimeLookup.indirections = CORINFO.USEHELPER;
// Do not bother computing the runtime lookup if we are inlining. The JIT is going
// to abort the inlining attempt anyway.
if (pResolvedToken.tokenContext == contextFromMethodBeingCompiled())
{
MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext);
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod);
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry;
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(GetRuntimeDeterminedObjectForToken(ref pResolvedToken));
}
else
{
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_NOT_SUPPORTED;
}
}
else
{
pResult->codePointerOrStubLookup.constLookup =
CreateConstLookupToSymbol(_compilation.NodeFactory.FatFunctionPointer(targetMethod));
}
}
else if (directCall && resolvedConstraint && pResult->exactContextNeedsRuntimeLookup)
if (directCall && resolvedConstraint && pResult->exactContextNeedsRuntimeLookup)
{
// We want to do a direct call to a shared method on a valuetype. We need to provide
// a generic context, but the JitInterface doesn't provide a way for us to do it from here.
......@@ -1347,6 +1351,43 @@ private void getCallInfo(ref CORINFO_RESOLVED_TOKEN pResolvedToken, CORINFO_RESO
targetIsFatFunctionPointer = true;
useFatCallTransform = true;
}
else if (directCall && !allowInstParam && targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific).RequiresInstArg())
{
// JIT needs a single address to call this method but the method needs a hidden argument.
// We need a fat function pointer for this that captures both things.
targetIsFatFunctionPointer = true;
// JIT won't expect fat function pointers unless this is e.g. delegate creation
Debug.Assert((flags & CORINFO_CALLINFO_FLAGS.CORINFO_CALLINFO_LDFTN) != 0);
pResult->kind = CORINFO_CALL_KIND.CORINFO_CALL_CODE_POINTER;
if (pResult->exactContextNeedsRuntimeLookup)
{
pResult->codePointerOrStubLookup.lookupKind.needsRuntimeLookup = true;
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = 0;
pResult->codePointerOrStubLookup.runtimeLookup.indirections = CORINFO.USEHELPER;
// Do not bother computing the runtime lookup if we are inlining. The JIT is going
// to abort the inlining attempt anyway.
if (pResolvedToken.tokenContext == contextFromMethodBeingCompiled())
{
MethodDesc contextMethod = methodFromContext(pResolvedToken.tokenContext);
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = GetGenericRuntimeLookupKind(contextMethod);
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupFlags = (ushort)ReadyToRunHelperId.MethodEntry;
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupArgs = (void*)ObjectToHandle(GetRuntimeDeterminedObjectForToken(ref pResolvedToken));
}
else
{
pResult->codePointerOrStubLookup.lookupKind.runtimeLookupKind = CORINFO_RUNTIME_LOOKUP_KIND.CORINFO_LOOKUP_NOT_SUPPORTED;
}
}
else
{
pResult->codePointerOrStubLookup.constLookup =
CreateConstLookupToSymbol(_compilation.NodeFactory.FatFunctionPointer(targetMethod));
}
}
else if (directCall)
{
bool referencingArrayAddressMethod = false;
......
......@@ -77,7 +77,7 @@ struct JitInterfaceCallbacks
CorInfoHelpFunc (* getBoxHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
CorInfoHelpFunc (* getUnBoxHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
bool (* getReadyToRunHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pResolvedToken, CORINFO_LOOKUP_KIND* pGenericLookupKind, CorInfoHelpFunc id, CORINFO_CONST_LOOKUP* pLookup);
void (* getReadyToRunDelegateCtorHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pTargetMethod, CORINFO_CLASS_HANDLE delegateType, CORINFO_LOOKUP* pLookup);
void (* getReadyToRunDelegateCtorHelper)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_RESOLVED_TOKEN* pTargetMethod, unsigned int targetConstraint, CORINFO_CLASS_HANDLE delegateType, CORINFO_LOOKUP* pLookup);
const char* (* getHelperName)(void * thisHandle, CorInfoExceptionClass** ppException, CorInfoHelpFunc helpFunc);
CorInfoInitClassResult (* initClass)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_FIELD_HANDLE field, CORINFO_METHOD_HANDLE method, CORINFO_CONTEXT_HANDLE context);
void (* classMustBeLoadedBeforeCodeIsRun)(void * thisHandle, CorInfoExceptionClass** ppException, CORINFO_CLASS_HANDLE cls);
......@@ -843,11 +843,12 @@ public:
virtual void getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN* pTargetMethod,
unsigned int targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
CorInfoExceptionClass* pException = nullptr;
_callbacks->getReadyToRunDelegateCtorHelper(_thisHandle, &pException, pTargetMethod, delegateType, pLookup);
_callbacks->getReadyToRunDelegateCtorHelper(_thisHandle, &pException, pTargetMethod, targetConstraint, delegateType, pLookup);
if (pException != nullptr) throw pException;
}
......
......@@ -637,6 +637,7 @@ struct GetReadyToRunHelper_TOKENout
struct GetReadyToRunDelegateCtorHelper_TOKENIn
{
Agnostic_CORINFO_RESOLVED_TOKEN TargetMethod;
mdToken targetConstraint;
DWORDLONG delegateType;
};
......
......@@ -2233,6 +2233,7 @@ bool MethodContext::repGetReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToke
}
void MethodContext::recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
......@@ -2244,6 +2245,7 @@ void MethodContext::recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* p
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.TargetMethod =
SpmiRecordsHelper::StoreAgnostic_CORINFO_RESOLVED_TOKEN(pTargetMethod, GetReadyToRunDelegateCtorHelper);
key.targetConstraint = targetConstraint;
key.delegateType = CastHandle(delegateType);
Agnostic_CORINFO_LOOKUP value = SpmiRecordsHelper::StoreAgnostic_CORINFO_LOOKUP(pLookup);
GetReadyToRunDelegateCtorHelper->Add(key, value);
......@@ -2253,12 +2255,13 @@ void MethodContext::recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* p
void MethodContext::dmpGetReadyToRunDelegateCtorHelper(GetReadyToRunDelegateCtorHelper_TOKENIn key,
Agnostic_CORINFO_LOOKUP value)
{
printf("GetReadyToRunDelegateCtorHelper key: method tk{%s} type-%016llX",
SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.TargetMethod).c_str(), key.delegateType);
printf("GetReadyToRunDelegateCtorHelper key: method tk{%s} type-%016llX constraint-%08X",
SpmiDumpHelper::DumpAgnostic_CORINFO_RESOLVED_TOKEN(key.TargetMethod).c_str(), key.delegateType, key.targetConstraint);
printf(", value: %s", SpmiDumpHelper::DumpAgnostic_CORINFO_LOOKUP(value).c_str());
}
void MethodContext::repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
......@@ -2268,7 +2271,8 @@ void MethodContext::repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* p
ZeroMemory(&key, sizeof(key)); // Zero key including any struct padding
key.TargetMethod =
SpmiRecordsHelper::RestoreAgnostic_CORINFO_RESOLVED_TOKEN(pTargetMethod, GetReadyToRunDelegateCtorHelper);
key.delegateType = CastHandle(delegateType);
key.targetConstraint = targetConstraint;
key.delegateType = CastHandle(delegateType);
AssertKeyExistsNoMessage(GetReadyToRunDelegateCtorHelper, key);
......
......@@ -315,11 +315,13 @@ public:
CORINFO_CONST_LOOKUP* pLookup);
void recGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup);
void dmpGetReadyToRunDelegateCtorHelper(GetReadyToRunDelegateCtorHelper_TOKENIn key,
Agnostic_CORINFO_LOOKUP pLookup);
void repGetReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup);
......
......@@ -760,12 +760,13 @@ bool interceptor_ICJI::getReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToke
}
void interceptor_ICJI::getReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
mc->recGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
mc->recGetReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
}
const char* interceptor_ICJI::getHelperName(CorInfoHelpFunc funcNum)
......
......@@ -538,11 +538,12 @@ bool interceptor_ICJI::getReadyToRunHelper(
void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
mcs->AddCall("getReadyToRunDelegateCtorHelper");
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
}
const char* interceptor_ICJI::getHelperName(
......
......@@ -472,10 +472,11 @@ bool interceptor_ICJI::getReadyToRunHelper(
void interceptor_ICJI::getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
original_ICorJitInfo->getReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
}
const char* interceptor_ICJI::getHelperName(
......
......@@ -657,11 +657,12 @@ bool MyICJI::getReadyToRunHelper(CORINFO_RESOLVED_TOKEN* pResolvedToken,
}
void MyICJI::getReadyToRunDelegateCtorHelper(CORINFO_RESOLVED_TOKEN* pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP* pLookup)
{
jitInstance->mc->cr->AddCall("getReadyToRunDelegateCtorHelper");
jitInstance->mc->repGetReadyToRunDelegateCtorHelper(pTargetMethod, delegateType, pLookup);
jitInstance->mc->repGetReadyToRunDelegateCtorHelper(pTargetMethod, targetConstraint, delegateType, pLookup);
}
const char* MyICJI::getHelperName(CorInfoHelpFunc funcNum)
......
......@@ -5959,6 +5959,7 @@ bool CEEInfo::getReadyToRunHelper(
/***********************************************************************/
void CEEInfo::getReadyToRunDelegateCtorHelper(
CORINFO_RESOLVED_TOKEN * pTargetMethod,
mdToken targetConstraint,
CORINFO_CLASS_HANDLE delegateType,
CORINFO_LOOKUP * pLookup
)
......
......@@ -930,6 +930,16 @@ class Derived : Mid { }
{
throw new Exception($"{actual} != {expected}");
}
// Uncomment after we pick up fix for https://github.com/dotnet/roslyn/issues/60069
#if false
Func<string> del = T.GetCookie;
actual = del();
if (actual != expected)
{
throw new Exception($"{actual} != {expected}");
}
#endif
}
static void TestSimpleInterfaceWithGenericMethod<T, U>(string expected) where T : ISimple
......@@ -939,6 +949,13 @@ class Derived : Mid { }
{
throw new Exception($"{actual} != {expected}");
}
Func<string> del = T.GetCookieGeneric<U>;
actual = del();
if (actual != expected)
{
throw new Exception($"{actual} != {expected}");
}
}
static void TestVariantInterface<T, U>(string expected) where T : IVariant<U>
......@@ -948,6 +965,13 @@ class Derived : Mid { }
{
throw new Exception($"{actual} != {expected}");
}
Func<U, string> del = T.WhichMethod;
actual = del(default);
if (actual != expected)
{
throw new Exception($"{actual} != {expected}");
}
}
public static void Run()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册