Skip to content

Commit 1c96e58

Browse files
committed
Fix handling generic type in several IL
- Rework handlers for ldfld, ldflda, stfld, ldsflda, ldelem, stelem and ldtoken to perperly deal with opened and closed generic types and instances. - Add several new helper APIs to HeapBlock, CLR_RT_TypeDef_Instance, CLR_RT_FieldDef_Instance, CLR_RT_TypeDescriptor and CLR_RT_Assembly to support the latter. - Fix crawling signatures for generic parameters in several places.
1 parent 77d7b2d commit 1c96e58

File tree

7 files changed

+493
-140
lines changed

7 files changed

+493
-140
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: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1983,7 +1983,7 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
19831983
parser.Advance(element);
19841984

19851985
// walk forward to the Nth generic-parameter
1986-
for (int i = 0; i < genericParamPosition; i++)
1986+
for (int i = 0; i <= genericParamPosition; i++)
19871987
{
19881988
parser.Advance(element);
19891989
}
@@ -3217,6 +3217,85 @@ bool CLR_RT_ExecutionEngine::IsInstanceOf(
32173217
return IsInstanceOf(desc, descTarget, isInstInstruction);
32183218
}
32193219

3220+
/// <summary>
3221+
/// Checks whether the heap-object 'obj' satisfies exactly the type encoded by
3222+
/// the compressed token 'token' in the IL stream, under the current generic
3223+
/// instantiation in 'caller'. Supports DATATYPE_VAR slots and full GENERICINST.
3224+
/// </summary>
3225+
bool CLR_RT_ExecutionEngine::IsInstanceOfToken(
3226+
CLR_UINT32 token,
3227+
CLR_RT_HeapBlock &obj,
3228+
const CLR_RT_MethodDef_Instance &caller)
3229+
{
3230+
// Resolve the *expected* signature into a TypeDescriptor
3231+
CLR_RT_TypeDescriptor expectedDesc;
3232+
HRESULT hr = expectedDesc.InitializeFromSignatureToken(caller.assembly, token, &caller);
3233+
3234+
if (FAILED(hr))
3235+
{
3236+
return false;
3237+
}
3238+
3239+
// Extract the *actual* runtime type of the object
3240+
CLR_RT_TypeDescriptor actualDesc;
3241+
hr = actualDesc.InitializeFromObject(obj);
3242+
3243+
if (FAILED(hr))
3244+
{
3245+
return false;
3246+
}
3247+
3248+
// Delegate to the CLR built-in type-compatibility test
3249+
return TypeDescriptorsMatch(expectedDesc, actualDesc);
3250+
}
3251+
3252+
bool CLR_RT_ExecutionEngine::TypeDescriptorsMatch(const CLR_RT_TypeDescriptor &exp, const CLR_RT_TypeDescriptor &act)
3253+
{
3254+
// Quick check on the raw element kind
3255+
if (exp.GetDataType() != act.GetDataType())
3256+
{
3257+
return false;
3258+
}
3259+
3260+
switch (exp.GetDataType())
3261+
{
3262+
// Closed‐generic instantiation: compare the TypeSpec head
3263+
case DATATYPE_GENERICINST:
3264+
case DATATYPE_VAR:
3265+
{
3266+
auto &eSpec = exp.m_handlerGenericType;
3267+
auto &aSpec = act.m_handlerGenericType;
3268+
return eSpec.Assembly() == aSpec.Assembly() && eSpec.typeDefIndex == aSpec.typeDefIndex;
3269+
}
3270+
3271+
// Plain object or value‐type: compare the TypeDef_Index
3272+
case DATATYPE_CLASS:
3273+
case DATATYPE_VALUETYPE:
3274+
case DATATYPE_SZARRAY:
3275+
{
3276+
auto &eCls = exp.m_handlerCls;
3277+
auto &aCls = act.m_handlerCls;
3278+
3279+
if (eCls.data != aCls.data)
3280+
{
3281+
return false;
3282+
}
3283+
3284+
// for array we may need to compare element‐types
3285+
if (exp.GetDataType() == DATATYPE_SZARRAY)
3286+
{
3287+
// recurse into element descriptors
3288+
return TypeDescriptorsMatch(exp, act);
3289+
}
3290+
return true;
3291+
}
3292+
3293+
// All the primitives (I4, I8, R4, etc.) don't carry extra metadata
3294+
default:
3295+
return true;
3296+
}
3297+
}
3298+
32203299
HRESULT CLR_RT_ExecutionEngine::CastToType(
32213300
CLR_RT_HeapBlock &ref,
32223301
CLR_UINT32 tk,

src/CLR/Core/Interpreter.cpp

Lines changed: 26 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,37 @@ 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+
3157+
// Store the value into the actual array buffer
3158+
NANOCLR_CHECK_HRESULT(evalPos[3].StoreToReference(evalPos[1], size));
31403159

31413160
break;
31423161
}

0 commit comments

Comments
 (0)