Skip to content

Commit

Permalink
Strip unused generic type context for VASigCookie
Browse files Browse the repository at this point in the history
  • Loading branch information
jkotas committed Feb 28, 2024
1 parent 79dd9ba commit f4b713e
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 7 deletions.
139 changes: 132 additions & 7 deletions src/coreclr/vm/ceeload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4659,6 +4659,113 @@ PTR_VOID ReflectionModule::GetRvaField(RVA field) // virtual
// VASigCookies
// ===========================================================================

static bool TypeSignatureContainsGenericVariables(SigParser& sp);
static bool MethodSignatureContainsGenericVariables(SigParser& sp);

static bool TypeSignatureContainsGenericVariables(SigParser& sp)
{
STANDARD_VM_CONTRACT;

CorElementType et = ELEMENT_TYPE_END;
IfFailThrow(sp.GetElemType(&et));

if (CorIsPrimitiveType(et))
return false;

switch (et)
{
case ELEMENT_TYPE_OBJECT:
case ELEMENT_TYPE_STRING:
case ELEMENT_TYPE_TYPEDBYREF:
return false;

case ELEMENT_TYPE_BYREF:
case ELEMENT_TYPE_PTR:
case ELEMENT_TYPE_SZARRAY:
return TypeSignatureContainsGenericVariables(sp);

case ELEMENT_TYPE_VALUETYPE:
case ELEMENT_TYPE_CLASS:
IfFailThrow(sp.GetToken(NULL)); // Skip RID
return false;

case ELEMENT_TYPE_FNPTR:
return MethodSignatureContainsGenericVariables(sp);

case ELEMENT_TYPE_ARRAY:
{
if (TypeSignatureContainsGenericVariables(sp))
return true;

uint32_t rank;
IfFailThrow(sp.GetData(&rank)); // Get rank
if (rank)
{
uint32_t nsizes;
IfFailThrow(sp.GetData(&nsizes)); // Get # of sizes
while (nsizes--)
{
IfFailThrow(sp.GetData(NULL)); // Skip size
}

uint32_t nlbounds;
IfFailThrow(sp.GetData(&nlbounds)); // Get # of lower bounds
while (nlbounds--)
{
IfFailThrow(sp.GetData(NULL)); // Skip lower bounds
}
}

}
return false;

case ELEMENT_TYPE_GENERICINST:
{
if (TypeSignatureContainsGenericVariables(sp))
return true;

uint32_t argCnt;
IfFailThrow(sp.GetData(&argCnt)); // Get number of parameters
while (argCnt--)
{
if (TypeSignatureContainsGenericVariables(sp))
return true;
}
}
return false;

default:
// Return conservative answer for unhandled elements
return true;
}
}

static bool MethodSignatureContainsGenericVariables(SigParser& sp)
{
STANDARD_VM_CONTRACT;

uint32_t callConv = 0;
IfFailThrow(sp.GetCallingConvInfo(&callConv));

if (callConv & IMAGE_CEE_CS_CALLCONV_GENERIC)
{
// Generic signatures should never show up here, return conservative answer.
return true;
}

uint32_t numArgs = 0;
IfFailThrow(sp.GetData(&numArgs));

// iterate over the return type and parameters
for (uint32_t i = 0; i <= numArgs; i++)
{
if (TypeSignatureContainsGenericVariables(sp))
return true;
}

return false;
}

//==========================================================================
// Enregisters a VASig.
//==========================================================================
Expand All @@ -4667,15 +4774,35 @@ VASigCookie *Module::GetVASigCookie(Signature vaSignature, const SigTypeContext*
CONTRACT(VASigCookie*)
{
INSTANCE_CHECK;
THROWS;
GC_TRIGGERS;
MODE_ANY;
STANDARD_VM_CHECK;
POSTCONDITION(CheckPointer(RETVAL));
INJECT_FAULT(COMPlusThrowOM());
}
CONTRACT_END;

Module* pLoaderModule = ClassLoader::ComputeLoaderModuleWorker(this, mdTokenNil, typeContext->m_classInst, typeContext->m_methodInst);
SigTypeContext emptyContext;

Module* pLoaderModule = this;
if (!typeContext->IsEmpty())
{
// Strip the generic context if it is not actually used by signature. It is nececessary for both:
// - Performance: allow more sharing of vasig cookies
// - Functionality: built-in runtime marshalling is disallowed for generic signatures
if (MethodSignatureContainsGenericVariables(vaSignature.CreateSigParser()))
{
pLoaderModule = ClassLoader::ComputeLoaderModuleWorker(this, mdTokenNil, typeContext->m_classInst, typeContext->m_methodInst);
}
else
{
typeContext = &emptyContext;
}
}
else
{
// The method signature should not contain any generic variables if the generic context is not provided.
_ASSERTE(!MethodSignatureContainsGenericVariables(vaSignature.CreateSigParser()));
}

VASigCookie *pCookie = GetVASigCookieWorker(this, pLoaderModule, vaSignature, typeContext);

RETURN pCookie;
Expand All @@ -4685,9 +4812,7 @@ VASigCookie *Module::GetVASigCookieWorker(Module* pDefiningModule, Module* pLoad
{
CONTRACT(VASigCookie*)
{
THROWS;
GC_TRIGGERS;
MODE_ANY;
STANDARD_VM_CHECK;
POSTCONDITION(CheckPointer(RETVAL));
INJECT_FAULT(COMPlusThrowOM());
}
Expand Down
12 changes: 12 additions & 0 deletions src/tests/nativeaot/SmokeTests/PInvoke/PInvoke.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@ public static int Main()
TestForwardDelegateWithUnmanagedCallersOnly();
TestDecimal();
TestDifferentModopts();
TestGenericCaller<string>();
TestFunctionPointers();

return 100;
Expand Down Expand Up @@ -1168,6 +1169,17 @@ public static unsafe void TestDifferentModopts()
refStdcallSuppressTransition(ref storage, 56);
}
}

public static unsafe void TestGenericCaller<T>()
{
byte storage;

delegate* unmanaged<byte*, byte, void> unmanagedMethod = &UnmanagedMethod;

var outUnmanagedMethod = (delegate* unmanaged<out byte, byte, void>)unmanagedMethod;
outUnmanagedMethod(out storage, 12);
ThrowIfNotEquals(storage, 12, "Out unmanaged call failed.");
}
}

public class SafeMemoryHandle : SafeHandle //SafeHandle subclass
Expand Down

0 comments on commit f4b713e

Please sign in to comment.