Skip to content
Merged
9 changes: 7 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1467,12 +1467,17 @@ jobs:
$fileCount = (Get-ChildItem $dumpPath -File | Measure-Object).Count
$hasFiles = $fileCount -gt 0
}
echo "##vso[task.setvariable variable=hasFiles;isOutput=true]$hasFiles"
echo "##vso[task.setvariable variable=hasFiles]$hasFiles"
condition: succeededOrFailed()
displayName: "Check for dump files"
name: checkFiles

- task: PublishPipelineArtifact@1
condition: eq(variables['hasFiles'], 'true')
condition: >-
and(
succeededOrFailed(),
eq(variables['hasFiles'], 'true')
)
displayName: Publish vstest dump files
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)/vstest_dumps"
Expand Down
146 changes: 120 additions & 26 deletions src/CLR/Core/Execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2074,6 +2074,12 @@ static HRESULT ResolveGenericTypeParameter(
NANOCLR_SET_AND_LEAVE(CLR_E_FAIL);
}

// Fail here so the caller's allowUnresolvedVarFallback path handles it.
if (paramElement.DataType == DATATYPE_VAR || paramElement.DataType == DATATYPE_MVAR)
{
NANOCLR_SET_AND_LEAVE(CLR_E_FAIL);
}

outClass = paramElement.Class;
outDataType = paramElement.DataType;

Expand Down Expand Up @@ -2133,10 +2139,23 @@ HRESULT CLR_RT_ExecutionEngine::InitializeReference(
}
else
{
NANOCLR_CHECK_HRESULT(
ResolveGenericTypeParameter(*genericInstance, res.GenericParamPosition, realTypeDef, dt));

goto process_datatype;
HRESULT hrParam =
ResolveGenericTypeParameter(*genericInstance, res.GenericParamPosition, realTypeDef, dt);
if (FAILED(hrParam))
{
if (allowUnresolvedVarFallback)
{
dt = DATATYPE_OBJECT;
}
else
{
NANOCLR_CHECK_HRESULT(hrParam);
}
}
else
{
goto process_datatype;
}
}
}
else if (dt == DATATYPE_MVAR)
Expand Down Expand Up @@ -2306,51 +2325,85 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
typeSpecSignature--;

CLR_RT_TypeSpec_Index genericTSIndex = {};
bool foundGenericTypeSpec = false;

if (methodDefInstance.genericType && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType) &&
methodDefInstance.genericType->data != CLR_EmptyToken)
{
// method is generic, it can only use class from method's class generic parameters
genericInstance.InitializeFromIndex(*methodDefInstance.genericType);
foundGenericTypeSpec = true;
}
else
{
if (!assembly->FindTypeSpec(typeSpecSignature, genericTSIndex))
if (assembly->FindTypeSpec(typeSpecSignature, genericTSIndex))
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
// copy over to parameter
genericInstance.InitializeFromIndex(genericTSIndex);
foundGenericTypeSpec = true;
}

// copy over to parameter
genericInstance.InitializeFromIndex(genericTSIndex);
}

CLR_RT_SignatureParser parser;
parser.Initialize_TypeSpec(genericInstance);

CLR_RT_SignatureParser::Element element;
NANOCLR_CHECK_HRESULT(parser.Advance(element));

// if this is another generic instance, need to advance to get the type
if (dt == DATATYPE_GENERICINST)
if (foundGenericTypeSpec)
{
CLR_RT_SignatureParser parser;
parser.Initialize_TypeSpec(genericInstance);

CLR_RT_SignatureParser::Element element;
NANOCLR_CHECK_HRESULT(parser.Advance(element));

// if this is another generic instance, need to advance to get the type
if (dt == DATATYPE_GENERICINST)
{
NANOCLR_CHECK_HRESULT(parser.Advance(element));
}

cls = element.Class;
dt = element.DataType;
}
else
{
// Some generic method locals are open instantiations, such as IEnumerator<!!T>.
// A reference-type local only needs a null object slot, so parse the local signature
// directly instead of requiring a closed TypeSpec row.
CLR_RT_SignatureParser ownerParser;
ownerParser.Initialize_LocalVar(assembly, typeSpecSignature);

cls = element.Class;
dt = element.DataType;
CLR_RT_SignatureParser::Element ownerElement;
NANOCLR_CHECK_HRESULT(ownerParser.Advance(ownerElement));

if (ownerElement.DataType != DATATYPE_GENERICINST)
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}

NANOCLR_CHECK_HRESULT(ownerParser.Advance(ownerElement));

cls = ownerElement.Class;
dt = ownerElement.DataType;

if (dt == DATATYPE_VALUETYPE)
{
// Value-type generic locals need a closed generic instance for allocation.
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}
}

// done, now consume the remaining of the local var signature
CLR_RT_SignatureParser varParser;
varParser.Initialize_LocalVar(assembly, typeSpecSignature);

CLR_RT_SignatureParser::Element varElement;
// consume GENERICINST
varParser.Advance(varElement);
// consume class|valuetype
// consume GENERICINST (first element) - this sets ParamCount to 1 for
// the class/valuetype that follows, plus all nested generic arguments.
varParser.Advance(varElement);

// consume parameters
for (int paramIndex = 0; paramIndex < varElement.GenParamCount; paramIndex++)
// Drain all remaining elements (class/valuetype token, generic argument count, and
// all nested arguments recursively). Using Available() handles nested GENERICINSTs
// such as ICollection<KeyValuePair<TKey,TValue>> where KVP itself has inner bytes
// (VALUETYPE + TypeRef + count + I4 + STRING) that a fixed GenParamCount loop
// would leave unread, corrupting `sig` for subsequent locals.
while (varParser.Available() > 0)
{
NANOCLR_CHECK_HRESULT(varParser.Advance(varElement));
}
Expand All @@ -2369,8 +2422,22 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
// type-level generic parameter in a locals signature (e.g. 'T' inside a generic type)
CLR_INT8 genericParamPosition = *sig++;

// A propagated runtime element type can also bind the first type generic parameter
// on helper objects whose closed generic context is inferred at runtime.
if (NANOCLR_INDEX_IS_VALID(methodDefInstance.arrayElementType) && genericParamPosition == 0)
{
CLR_RT_TypeDef_Instance td;
if (!td.InitializeFromIndex(methodDefInstance.arrayElementType))
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}

cls = methodDefInstance.arrayElementType;
dt = (NanoCLRDataType)td.target->dataType;
}
// Resolve type-level generic parameter (VAR) using the method's enclosing type context
if (methodDefInstance.genericType && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType) &&
else if (
methodDefInstance.genericType && NANOCLR_INDEX_IS_VALID(*methodDefInstance.genericType) &&
methodDefInstance.genericType->data != CLR_EmptyToken)
{
NANOCLR_CHECK_HRESULT(
Expand All @@ -2388,8 +2455,21 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
// Method-level generic parameter (e.g., '!!T' in a generic method like Array.Empty<T>())
CLR_UINT8 genericParamPosition = *sig++;

// Some rebound generic methods receive their method-generic argument from the
// runtime element type rather than from a MethodSpec.
if (NANOCLR_INDEX_IS_VALID(methodDefInstance.arrayElementType) && genericParamPosition == 0)
{
CLR_RT_TypeDef_Instance td;
if (!td.InitializeFromIndex(methodDefInstance.arrayElementType))
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}

cls = methodDefInstance.arrayElementType;
dt = (NanoCLRDataType)td.target->dataType;
}
// For generic methods, use the MethodSpec's signature to get the concrete type
if (NANOCLR_INDEX_IS_VALID(methodDefInstance.methodSpec))
else if (NANOCLR_INDEX_IS_VALID(methodDefInstance.methodSpec))
{
CLR_RT_MethodSpec_Instance methodSpec;
CLR_RT_SignatureParser::Element element;
Expand Down Expand Up @@ -2742,6 +2822,12 @@ HRESULT CLR_RT_ExecutionEngine::NewGenericInstanceObject(

fieldCursor = reinterpret_cast<CLR_RT_HeapBlock *>(giHeader) + totFields;

CLR_Debug::Printf(
"DBG GenericInst: NewGenericInstanceObject type='%s' totFields=%d giHeader=%08X\r\n",
instance.assembly->GetString(instance.target->name),
totFields,
(uintptr_t)giHeader);

while (--totFields > 0)
{
while (clsFields == 0)
Expand All @@ -2765,7 +2851,15 @@ HRESULT CLR_RT_ExecutionEngine::NewGenericInstanceObject(
target--;
clsFields--;

CLR_Debug::Printf(
"DBG GenericInst: InitField field='%s' type='%s' cursor=%08X\r\n",
assm->GetString(target->name),
assm->GetString(target->type),
(uintptr_t)fieldCursor);

NANOCLR_CHECK_HRESULT(InitializeReference(*fieldCursor, target, assm, genericInstance));

CLR_Debug::Printf("DBG GenericInst: InitField done dt=%d\r\n", (int)fieldCursor->DataType());
}

if (instance.HasFinalizer())
Expand Down
Loading