Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/coreclr/vm/clsload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4623,6 +4623,22 @@ BOOL ClassLoader::CanAccessFamily(

#endif // #ifndef DACCESS_COMPILE

bool ClassLoader::EligibleForSpecialMarkerTypeUsage(Instantiation inst, MethodTable* pOwnerMT)
{
LIMITED_METHOD_DAC_CONTRACT;

if (pOwnerMT == NULL)
return false;

if (inst.GetNumArgs() > MethodTable::MaxGenericParametersForSpecialMarkerType)
return false;

if (!inst.ContainsAllOneType(pOwnerMT->GetSpecialInstantiationType()))
return false;

return true;
}

#ifdef DACCESS_COMPILE

void
Expand Down
2 changes: 2 additions & 0 deletions src/coreclr/vm/clsload.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,8 @@ class ClassLoader
TypeHandle typeHnd,
ClassLoadLevel targetLevel);
#endif //!DACCESS_COMPILE
public:
static bool EligibleForSpecialMarkerTypeUsage(Instantiation inst, MethodTable* pOwnerMT);

}; // class ClassLoader

Expand Down
113 changes: 100 additions & 13 deletions src/coreclr/vm/methodtablebuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9805,8 +9805,6 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT)
break;
}

bool uninstGenericCase = !retryWithExactInterfaces && pNewIntfMT->IsSpecialMarkerTypeForGenericCasting();

duplicates |= InsertMethodTable(pNewIntfMT, pExactMTs, nInterfacesCount, &nAssigned);

// We have a special algorithm for interface maps in CoreLib, which doesn't expand interfaces, and assumes no ambiguous
Expand All @@ -9816,23 +9814,112 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT)
MethodTable::InterfaceMapIterator intIt = pNewIntfMT->IterateInterfaceMap();
while (intIt.Next())
{
MethodTable *pItfPossiblyApprox = intIt.GetInterfaceApprox();
if (uninstGenericCase && pItfPossiblyApprox->HasInstantiation() && pItfPossiblyApprox->ContainsGenericVariables())
if (retryWithExactInterfaces)
{
// We allow a limited set of interface generic shapes with type variables. In particular, we require the
// instantiations to be exactly simple type variables, and to have a relatively small number of generic arguments
// so that the fallback instantiating logic works efficiently
if (InstantiationIsAllTypeVariables(pItfPossiblyApprox->GetInstantiation()) && pItfPossiblyApprox->GetInstantiation().GetNumArgs() <= MethodTable::MaxGenericParametersForSpecialMarkerType)
duplicates |= InsertMethodTable(intIt.GetInterface(pNewIntfMT, CLASS_LOAD_EXACTPARENTS), pExactMTs, nInterfacesCount, &nAssigned);
}
else
{
MethodTable *pItfPossiblyApprox = intIt.GetInterfaceApprox();

// pItfPossiblyApprox can be in 4 situations
// 1. It has no instantiation
// 2. It is a special marker type, AND pNewItfMT is a special marker type. Compute the exact instantiation as containing entirely a list of types corresponding to calling GetSpecialInstantiationType on pMT (This rule works based on the current behavior of GetSpecialInstantiationType where it treats all interfaces the same)
// 3. It is a special marker type, but pNewItfMT is NOT a special marker type. Compute the exact instantiation as containing entirely a list of types corresponding to calling GetSpecialInstantiationType on pNewItfMT
// 4. It is an exact instantiation
//
// NOTE: pItfPossiblyApprox must not be considered a special marker type if pNewItfMT has the MayHaveOpenInterfacesInInterfaceMap flag set
//
// Then determine if all of the following conditions hold true.
// 1. All generic arguments are the same (always true for cases 2 and 3 above)
// 2. The first generic argument in the instantiation is exactly the value of calling GetSpecialInstantiationType on pMT (always true for case 2)
//
// If so, then we should insert the special marker type
// Otherwise, we should insert the exact instantiation of the interface
// HOWEVER: If the exact instantiation IS a special marker interface, we need to retry with exact interfaces to avoid ambiguity situations
//
// NOTE: This is also part of the logic which determines if we need to call SetMayHaveOpenInterfacesInInterfaceMap. The CLR type system has a bug in its structure
// such that if you attempt to instantiate a type over its own type parameter from the open type, we will load the GenericTypeDefinition instead of loading
// a type explicitly instantiated over those type parameters. We re-use the GenericTypeDefinition as the special marker type, which leads to a conflict
// when something like that happens. So, we need to detect when something like that happens, and set the MayHaveOpenInterfacesInInterfaceMap flag,
// and avoid using the special marker type in those situations.
MethodTable *pItfToInsert = NULL;
bool intendedExactMatch = false;

if (!pItfPossiblyApprox->HasInstantiation())
{
// case 1
pItfToInsert = pItfPossiblyApprox;
intendedExactMatch = true;
}
else if (pItfPossiblyApprox->IsSpecialMarkerTypeForGenericCasting() && !pNewIntfMT->GetAuxiliaryData()->MayHaveOpenInterfacesInInterfaceMap())
{
pItfPossiblyApprox = ClassLoader::LoadTypeDefThrowing(pItfPossiblyApprox->GetModule(), pItfPossiblyApprox->GetCl(), ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_EXACTPARENTS).AsMethodTable();
// We are in case 2 or 3 above
bool pNewIntfMTIsSpecialMarkerType = pNewIntfMT->IsSpecialMarkerTypeForGenericCasting() && !pMT->GetAuxiliaryData()->MayHaveOpenInterfacesInInterfaceMap();
if (pNewIntfMTIsSpecialMarkerType)
{
// case 2
pItfToInsert = pItfPossiblyApprox; // We have the special marker type already, so this is what we insert
}
else
{
// case 3
bool mustUseSpecialMarkerType = pNewIntfMT->GetSpecialInstantiationType() == pMT->GetSpecialInstantiationType();
if (mustUseSpecialMarkerType)
{
pItfToInsert = pItfPossiblyApprox; // We have the special marker type already, so this is what we insert
}
else
{
pItfToInsert = intIt.GetInterface(pNewIntfMT, CLASS_LOAD_EXACTPARENTS);
intendedExactMatch = true;
}
}
}
else
{
retry = true;
break;
// case 4 (We have an exact interface)
if (ClassLoader::EligibleForSpecialMarkerTypeUsage(pItfPossiblyApprox->GetInstantiation(), pMT))
{
// Validated that all generic arguments are the same, and that the first generic argument in the instantiation is exactly the value of calling GetSpecialInstantiationType on pMT
// Then use the special marker type here
pItfToInsert = ClassLoader::LoadTypeDefThrowing(pItfPossiblyApprox->GetModule(), pItfPossiblyApprox->GetCl(), ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, CLASS_LOAD_EXACTPARENTS).AsMethodTable();
}
else
{
pItfToInsert = pItfPossiblyApprox;
intendedExactMatch = true;
}
}

if (pItfToInsert->IsSpecialMarkerTypeForGenericCasting())
{
if (intendedExactMatch)
{
// We are trying to insert a special marker type into the interface list, but it is exactly the same as the exact instantiation we should actually want, we need to set the
// MayHaveOpenInterfacesInInterfaceMap flag, so trigger the retry logic.
retry = true;
break;
}
else if (pMT->GetSpecialInstantiationType() == pItfToInsert->GetInstantiation()[0])
{
// We are trying to insert a special marker type into the interface list, but the first generic argument
// in the instantiation is exactly the value of calling GetSpecialInstantiationType on pMT.
// This implies that the special marker type is actually the exact instantiation that we should be using, which
// will cause the same ambiguity situation as above. Trigger a retry, which will set MayHaveOpenInterfacesInInterfaceMap
// and disable the special marker type behavior for this type.
retry = true;
break;
}
}

if (!intendedExactMatch)
{
_ASSERTE(pItfToInsert->IsSpecialMarkerTypeForGenericCasting());
}

duplicates |= InsertMethodTable(pItfToInsert, pExactMTs, nInterfacesCount, &nAssigned);
}
duplicates |= InsertMethodTable(intIt.GetInterface(pNewIntfMT, CLASS_LOAD_EXACTPARENTS), pExactMTs, nInterfacesCount, &nAssigned);
}
}

Expand Down Expand Up @@ -9869,7 +9956,7 @@ MethodTableBuilder::LoadExactInterfaceMap(MethodTable *pMT)
_ASSERTE(!duplicates || !(pMT->GetModule()->IsSystem() && (pMT->IsValueType() || pMT->IsInterface())));

CONSISTENCY_CHECK(duplicates || (nAssigned == pMT->GetNumInterfaces()));
if (duplicates)
if (duplicates || (nAssigned != pMT->GetNumInterfaces()))
{
//#LoadExactInterfaceMap_Algorithm2
// Exact interface instantiation loading TECHNIQUE 2 - The exact instantiation has caused some duplicates to
Expand Down
12 changes: 10 additions & 2 deletions src/coreclr/vm/siginfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1618,9 +1618,17 @@ TypeHandle SigPointer::GetTypeHandleThrowing(

Instantiation genericLoadInst(thisinst, ntypars);

if (pMTInterfaceMapOwner != NULL && genericLoadInst.ContainsAllOneType(pMTInterfaceMapOwner->GetSpecialInstantiationType()))
if (ClassLoader::EligibleForSpecialMarkerTypeUsage(genericLoadInst, pMTInterfaceMapOwner))
{
thRet = ClassLoader::LoadTypeDefThrowing(pGenericTypeModule, tkGenericType, ClassLoader::ThrowIfNotFound, ClassLoader::PermitUninstDefOrRef, 0, level);
if (thRet.AsMethodTable()->GetInstantiation()[0] == pMTInterfaceMapOwner->GetSpecialInstantiationType())
{
// We loaded the special marker type, but it is ALSO the exact expected type which isn't a valid combination
// In this case return something else (object) to indicate that
// we found an invalid situation and this function should be retried without the special marker type logic enabled.
thRet = TypeHandle(g_pObjectClass);
break;
}
}
else
{
Expand All @@ -1645,7 +1653,7 @@ TypeHandle SigPointer::GetTypeHandleThrowing(
// the loaded type is not the expected type we should be looking for to return a special marker type, but the normal load has
// found a type which claims to be a special marker type. In this case return something else (object) to indicate that
// we found an invalid situation and this function should be retried without the special marker type logic enabled.
thRet = TypeHandle(CoreLibBinder::GetElementType(ELEMENT_TYPE_OBJECT));
thRet = TypeHandle(g_pObjectClass);
break;
}
else if (!handlingRecursiveGenericFieldScenario)
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/vm/typehandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ class Instantiation

bool ContainsAllOneType(TypeHandle th)
{
LIMITED_METHOD_DAC_CONTRACT;
for (DWORD i = GetNumArgs(); i > 0;)
{
if ((*this)[--i] != th)
Expand Down
5 changes: 5 additions & 0 deletions src/tests/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@
<DefineConstants>$(DefineConstants);DEBUG;TRACE;XUNIT_PERF</DefineConstants>
</PropertyGroup>

<PropertyGroup>
<!-- The SDK for VB.NET requires the DefineConstants variable to be , delimited instead of ; -->
<DefineConstants Condition="'$(MSBuildProjectExtension)' == '.vbproj'">$(DefineConstants.Replace(';',','))</DefineConstants>
</PropertyGroup>

<!-- Setup the default output and intermediate paths -->
<PropertyGroup>
<SkipXunitDependencyCopying>true</SkipXunitDependencyCopying>
Expand Down
Loading