Skip to content

Commit 8db7b38

Browse files
authored
Fix handling generic type in several IL (#3172)
***NO_CI***
1 parent 25db936 commit 8db7b38

File tree

7 files changed

+745
-144
lines changed

7 files changed

+745
-144
lines changed

src/CLR/Core/CLR_RT_HeapBlock.cpp

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,137 @@ HRESULT CLR_RT_HeapBlock::Reassign(const CLR_RT_HeapBlock &value)
774774
NANOCLR_NOCLEANUP();
775775
}
776776

777+
HRESULT CLR_RT_HeapBlock::Reassign(CLR_RT_HeapBlock &rhs, const CLR_RT_TypeDef_Instance &expectedType)
778+
{
779+
NATIVE_PROFILE_CLR_CORE();
780+
NANOCLR_HEADER();
781+
782+
// Build a TypeDescriptor for the *expected* type (the IL TypeSpec/TypeDef)
783+
CLR_RT_TypeDescriptor descExpected;
784+
NANOCLR_CHECK_HRESULT(descExpected.InitializeFromTypeDef(expectedType));
785+
786+
// Build a TypeDescriptor for the *actual* runtime object in rhs
787+
CLR_RT_TypeDescriptor descActual;
788+
NANOCLR_CHECK_HRESULT(descActual.InitializeFromObject(rhs));
789+
790+
// Compare them (including generics, arrays, value-types, etc.)
791+
if (!TypeDescriptorsMatch(descExpected, descActual))
792+
{
793+
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
794+
}
795+
796+
// They match: now do the actual copy
797+
// - reference types & arrays: copy the object reference
798+
// - value-types & primitives: copy the raw data
799+
switch (descActual.GetDataType())
800+
{
801+
case DATATYPE_CLASS:
802+
case DATATYPE_SZARRAY:
803+
{
804+
// object reference or single-dim array
805+
this->Assign(rhs);
806+
break;
807+
}
808+
809+
default:
810+
{
811+
// value-type, primitive, struct, etc.
812+
// this->CopyFrom(rhs);
813+
break;
814+
}
815+
}
816+
817+
NANOCLR_NOCLEANUP();
818+
}
819+
820+
bool CLR_RT_HeapBlock::TypeDescriptorsMatch(
821+
const CLR_RT_TypeDescriptor &expectedType,
822+
const CLR_RT_TypeDescriptor &actualType)
823+
{
824+
// Figure out logical DataTypes, promoting ACTUAL CLASS ---> GENERICINST
825+
NanoCLRDataType expectedDataType = expectedType.GetDataType();
826+
NanoCLRDataType actualDataType = actualType.GetDataType();
827+
828+
// If the *actual* object is a closed-generic (even though boxed as CLASS),
829+
// it will have m_handlerGenericType set. Promote it to GENERICINST.
830+
if (actualDataType == DATATYPE_CLASS && actualType.m_handlerGenericType.data != CLR_EmptyToken)
831+
{
832+
actualDataType = DATATYPE_GENERICINST;
833+
}
834+
835+
// If either side is GENERICINST, we do generic-inst matching
836+
if (expectedDataType == DATATYPE_GENERICINST || actualDataType == DATATYPE_GENERICINST)
837+
{
838+
auto &eSpec = expectedType.m_handlerGenericType;
839+
auto &aSpec = actualType.m_handlerGenericType;
840+
841+
return eSpec.Assembly() == aSpec.Assembly() && eSpec.typeDefIndex == aSpec.typeDefIndex;
842+
}
843+
844+
if (actualDataType <= DATATYPE_LAST_PRIMITIVE_TO_PRESERVE)
845+
{
846+
// If they declared a true valuetype, match directly:
847+
if (expectedDataType == DATATYPE_VALUETYPE)
848+
{
849+
const auto &dtl = c_CLR_RT_DataTypeLookup[actualDataType];
850+
if (dtl.m_cls && dtl.m_cls->data == expectedType.m_handlerCls.data)
851+
{
852+
return true;
853+
}
854+
}
855+
// if they declared a boxed struct (CLASS whose TypeDef is a struct),
856+
// need to match that too:
857+
else if (expectedDataType == DATATYPE_CLASS && expectedType.m_handlerGenericType.data == 0)
858+
{
859+
// Look up the TypeDef record flags to see if it's a VALUE-TYPE.
860+
CLR_RT_TypeDef_Index clsIdx = expectedType.m_handlerCls;
861+
862+
// fetch the owning assembly
863+
CLR_RT_Assembly *ownerAsm = g_CLR_RT_TypeSystem.m_assemblies[clsIdx.Assembly() - 1];
864+
const CLR_RECORD_TYPEDEF *rec = ownerAsm->GetTypeDef(clsIdx.Type());
865+
866+
if (rec &&
867+
((rec->flags & CLR_RECORD_TYPEDEF::TD_Semantics_Mask) == CLR_RECORD_TYPEDEF::TD_Semantics_ValueType))
868+
{
869+
const auto &dtl = c_CLR_RT_DataTypeLookup[actualDataType];
870+
if (dtl.m_cls && dtl.m_cls->data == clsIdx.data)
871+
{
872+
return true;
873+
}
874+
}
875+
}
876+
}
877+
878+
// For everything else, DataTypes must line up exactly
879+
if (expectedDataType != actualDataType)
880+
{
881+
return false;
882+
}
883+
884+
// Dispatch on the remaining kinds
885+
switch (expectedDataType)
886+
{
887+
case DATATYPE_CLASS:
888+
case DATATYPE_VALUETYPE:
889+
{
890+
// compare TypeDef indices
891+
auto &eCls = expectedType.m_handlerCls;
892+
auto &aCls = actualType.m_handlerCls;
893+
return eCls.data == aCls.data;
894+
}
895+
896+
case DATATYPE_SZARRAY:
897+
{
898+
// compare outer dims (always 1) then element types
899+
return TypeDescriptorsMatch(expectedType, actualType);
900+
}
901+
902+
// primitives and other leaf types match on the DataType alone
903+
default:
904+
return true;
905+
}
906+
}
907+
777908
void CLR_RT_HeapBlock::AssignAndPinReferencedObject(const CLR_RT_HeapBlock &value)
778909
{
779910
// This is very special case that we have local variable with pinned attribute in metadata.
@@ -787,7 +918,7 @@ void CLR_RT_HeapBlock::AssignAndPinReferencedObject(const CLR_RT_HeapBlock &valu
787918
m_data.objectReference.ptr->Unpin();
788919
}
789920

790-
// Move the data.
921+
// Move the data
791922
m_data = value.m_data;
792923

793924
// Leave the same logic as in AssignAndPreserveType

src/CLR/Core/Execution.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1979,6 +1979,13 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
19791979
parser.Initialize_MethodLocals(assembly, methodDef);
19801980
CLR_RT_SignatureParser::Element element;
19811981

1982+
// ensure we don’t walk past the available generic parameters
1983+
const int maxParams = parser.GenParamCount;
1984+
if (genericParamPosition < 0 || genericParamPosition > maxParams)
1985+
{
1986+
NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE);
1987+
}
1988+
19821989
// advance into the VAR entry
19831990
parser.Advance(element);
19841991

@@ -3217,6 +3224,38 @@ bool CLR_RT_ExecutionEngine::IsInstanceOf(
32173224
return IsInstanceOf(desc, descTarget, isInstInstruction);
32183225
}
32193226

3227+
/// <summary>
3228+
/// Checks whether the heap-object 'obj' satisfies exactly the type encoded by
3229+
/// the compressed token 'token' in the IL stream, under the current generic
3230+
/// instantiation in 'caller'. Supports DATATYPE_VAR slots and full GENERICINST.
3231+
/// </summary>
3232+
bool CLR_RT_ExecutionEngine::IsInstanceOfToken(
3233+
CLR_UINT32 token,
3234+
CLR_RT_HeapBlock &obj,
3235+
const CLR_RT_MethodDef_Instance &caller)
3236+
{
3237+
// Resolve the *expected* signature into a TypeDescriptor
3238+
CLR_RT_TypeDescriptor expectedDesc;
3239+
HRESULT hr = expectedDesc.InitializeFromSignatureToken(caller.assembly, token, &caller);
3240+
3241+
if (FAILED(hr))
3242+
{
3243+
return false;
3244+
}
3245+
3246+
// Extract the *actual* runtime type of the object
3247+
CLR_RT_TypeDescriptor actualDesc;
3248+
hr = actualDesc.InitializeFromObject(obj);
3249+
3250+
if (FAILED(hr))
3251+
{
3252+
return false;
3253+
}
3254+
3255+
// Delegate to the CLR built-in type-compatibility test
3256+
return CLR_RT_HeapBlock::TypeDescriptorsMatch(expectedDesc, actualDesc);
3257+
}
3258+
32203259
HRESULT CLR_RT_ExecutionEngine::CastToType(
32213260
CLR_RT_HeapBlock &ref,
32223261
CLR_UINT32 tk,

src/CLR/Core/Interpreter.cpp

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,9 +2644,9 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
26442644

26452645
switch (dt)
26462646
{
2647+
case DATATYPE_GENERICINST:
26472648
case DATATYPE_CLASS:
26482649
case DATATYPE_VALUETYPE:
2649-
case DATATYPE_GENERICINST:
26502650
obj[fieldInst.CrossReference().offset].AssignAndPreserveType(evalPos[2]);
26512651
break;
26522652
case DATATYPE_DATETIME: // Special case.
@@ -2969,7 +2969,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
29692969
NANOCLR_CHECK_HRESULT(CLR_RT_TypeDescriptor::ExtractTypeIndexFromObject(evalPos[0], cls));
29702970

29712971
// Check this is an object of the requested type.
2972-
if (type.data != cls.data)
2972+
if (!g_CLR_RT_ExecutionEngine.IsInstanceOfToken(arg, evalPos[0], stack->m_call))
29732973
{
29742974
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
29752975
}
@@ -3125,18 +3125,45 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
31253125
OPDEF(CEE_STELEM, "stelem", PopRef + PopI + Pop1, Push0, InlineType, IObjModel, 1, 0xFF, 0xA4, NEXT)
31263126
// Stack: ... ... <obj> <index> <value> -> ...
31273127
{
3128-
// Treat STELEM like ldelema + stobj
3129-
ip += 2; // Skip type argument, not used...
3128+
FETCH_ARG_COMPRESSED_TYPETOKEN(arg, ip);
31303129

3131-
evalPos -= 3; // "pop" args from evaluation stack
3130+
evalPos -= 3;
31323131
CHECKSTACK(stack, evalPos);
31333132

3133+
// Build a by-ref to the array slot at [index]
31343134
NANOCLR_CHECK_HRESULT(
31353135
evalPos[1].InitializeArrayReference(evalPos[1], evalPos[2].NumericByRef().s4));
31363136
evalPos[1].FixArrayReferenceForValueTypes();
31373137

3138-
// Reassign will make sure these are objects of the same type.
3139-
NANOCLR_CHECK_HRESULT(evalPos[1].Reassign(evalPos[3]));
3138+
// Resolve the IL's element type in the context of any generics
3139+
CLR_RT_TypeDef_Instance expectedType;
3140+
if (!expectedType.ResolveToken(arg, assm, &stack->m_call))
3141+
{
3142+
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
3143+
}
3144+
3145+
NanoCLRDataType elemDT = (NanoCLRDataType)expectedType.target->dataType;
3146+
3147+
// Promote the value if it's a reference or boxed struct
3148+
evalPos[3].Promote();
3149+
3150+
// Compute the element‐size: 0 for refs (incl. genericinst), sizeInBytes for primitives
3151+
size_t size = 0;
3152+
if (elemDT <= DATATYPE_LAST_PRIMITIVE_TO_PRESERVE)
3153+
{
3154+
size = c_CLR_RT_DataTypeLookup[elemDT].m_sizeInBytes;
3155+
}
3156+
else if (
3157+
(expectedType.target->flags & CLR_RECORD_TYPEDEF::TD_Semantics_Mask) ==
3158+
CLR_RECORD_TYPEDEF::TD_Semantics_ValueType)
3159+
{
3160+
size =
3161+
(CLR_RT_HeapBlock::HB_Object_Fields_Offset + +expectedType.CrossReference().totalFields) *
3162+
sizeof(CLR_RT_HeapBlock);
3163+
}
3164+
3165+
// Store the value into the actual array buffer
3166+
NANOCLR_CHECK_HRESULT(evalPos[3].StoreToReference(evalPos[1], size));
31403167

31413168
break;
31423169
}

0 commit comments

Comments
 (0)