diff --git a/src/CLR/Core/CLR_RT_HeapBlock.cpp b/src/CLR/Core/CLR_RT_HeapBlock.cpp
index 0e6acf0b8c..05ac599a91 100644
--- a/src/CLR/Core/CLR_RT_HeapBlock.cpp
+++ b/src/CLR/Core/CLR_RT_HeapBlock.cpp
@@ -774,6 +774,137 @@ HRESULT CLR_RT_HeapBlock::Reassign(const CLR_RT_HeapBlock &value)
NANOCLR_NOCLEANUP();
}
+HRESULT CLR_RT_HeapBlock::Reassign(CLR_RT_HeapBlock &rhs, const CLR_RT_TypeDef_Instance &expectedType)
+{
+ NATIVE_PROFILE_CLR_CORE();
+ NANOCLR_HEADER();
+
+ // Build a TypeDescriptor for the *expected* type (the IL TypeSpec/TypeDef)
+ CLR_RT_TypeDescriptor descExpected;
+ NANOCLR_CHECK_HRESULT(descExpected.InitializeFromTypeDef(expectedType));
+
+ // Build a TypeDescriptor for the *actual* runtime object in rhs
+ CLR_RT_TypeDescriptor descActual;
+ NANOCLR_CHECK_HRESULT(descActual.InitializeFromObject(rhs));
+
+ // Compare them (including generics, arrays, value-types, etc.)
+ if (!TypeDescriptorsMatch(descExpected, descActual))
+ {
+ NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+ }
+
+ // They match: now do the actual copy
+ // - reference types & arrays: copy the object reference
+ // - value-types & primitives: copy the raw data
+ switch (descActual.GetDataType())
+ {
+ case DATATYPE_CLASS:
+ case DATATYPE_SZARRAY:
+ {
+ // object reference or single-dim array
+ this->Assign(rhs);
+ break;
+ }
+
+ default:
+ {
+ // value-type, primitive, struct, etc.
+ // this->CopyFrom(rhs);
+ break;
+ }
+ }
+
+ NANOCLR_NOCLEANUP();
+}
+
+bool CLR_RT_HeapBlock::TypeDescriptorsMatch(
+ const CLR_RT_TypeDescriptor &expectedType,
+ const CLR_RT_TypeDescriptor &actualType)
+{
+ // Figure out logical DataTypes, promoting ACTUAL CLASS ---> GENERICINST
+ NanoCLRDataType expectedDataType = expectedType.GetDataType();
+ NanoCLRDataType actualDataType = actualType.GetDataType();
+
+ // If the *actual* object is a closed-generic (even though boxed as CLASS),
+ // it will have m_handlerGenericType set. Promote it to GENERICINST.
+ if (actualDataType == DATATYPE_CLASS && actualType.m_handlerGenericType.data != CLR_EmptyToken)
+ {
+ actualDataType = DATATYPE_GENERICINST;
+ }
+
+ // If either side is GENERICINST, we do generic-inst matching
+ if (expectedDataType == DATATYPE_GENERICINST || actualDataType == DATATYPE_GENERICINST)
+ {
+ auto &eSpec = expectedType.m_handlerGenericType;
+ auto &aSpec = actualType.m_handlerGenericType;
+
+ return eSpec.Assembly() == aSpec.Assembly() && eSpec.typeDefIndex == aSpec.typeDefIndex;
+ }
+
+ if (actualDataType <= DATATYPE_LAST_PRIMITIVE_TO_PRESERVE)
+ {
+ // If they declared a true valuetype, match directly:
+ if (expectedDataType == DATATYPE_VALUETYPE)
+ {
+ const auto &dtl = c_CLR_RT_DataTypeLookup[actualDataType];
+ if (dtl.m_cls && dtl.m_cls->data == expectedType.m_handlerCls.data)
+ {
+ return true;
+ }
+ }
+ // if they declared a boxed struct (CLASS whose TypeDef is a struct),
+ // need to match that too:
+ else if (expectedDataType == DATATYPE_CLASS && expectedType.m_handlerGenericType.data == 0)
+ {
+ // Look up the TypeDef record flags to see if it's a VALUE-TYPE.
+ CLR_RT_TypeDef_Index clsIdx = expectedType.m_handlerCls;
+
+ // fetch the owning assembly
+ CLR_RT_Assembly *ownerAsm = g_CLR_RT_TypeSystem.m_assemblies[clsIdx.Assembly() - 1];
+ const CLR_RECORD_TYPEDEF *rec = ownerAsm->GetTypeDef(clsIdx.Type());
+
+ if (rec &&
+ ((rec->flags & CLR_RECORD_TYPEDEF::TD_Semantics_Mask) == CLR_RECORD_TYPEDEF::TD_Semantics_ValueType))
+ {
+ const auto &dtl = c_CLR_RT_DataTypeLookup[actualDataType];
+ if (dtl.m_cls && dtl.m_cls->data == clsIdx.data)
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ // For everything else, DataTypes must line up exactly
+ if (expectedDataType != actualDataType)
+ {
+ return false;
+ }
+
+ // Dispatch on the remaining kinds
+ switch (expectedDataType)
+ {
+ case DATATYPE_CLASS:
+ case DATATYPE_VALUETYPE:
+ {
+ // compare TypeDef indices
+ auto &eCls = expectedType.m_handlerCls;
+ auto &aCls = actualType.m_handlerCls;
+ return eCls.data == aCls.data;
+ }
+
+ case DATATYPE_SZARRAY:
+ {
+ // compare outer dims (always 1) then element types
+ return TypeDescriptorsMatch(expectedType, actualType);
+ }
+
+ // primitives and other leaf types match on the DataType alone
+ default:
+ return true;
+ }
+}
+
void CLR_RT_HeapBlock::AssignAndPinReferencedObject(const CLR_RT_HeapBlock &value)
{
// 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
m_data.objectReference.ptr->Unpin();
}
- // Move the data.
+ // Move the data
m_data = value.m_data;
// Leave the same logic as in AssignAndPreserveType
diff --git a/src/CLR/Core/Execution.cpp b/src/CLR/Core/Execution.cpp
index b6b8fa4ec4..590b2f6fb6 100644
--- a/src/CLR/Core/Execution.cpp
+++ b/src/CLR/Core/Execution.cpp
@@ -1979,6 +1979,13 @@ HRESULT CLR_RT_ExecutionEngine::InitializeLocals(
parser.Initialize_MethodLocals(assembly, methodDef);
CLR_RT_SignatureParser::Element element;
+ // ensure we don’t walk past the available generic parameters
+ const int maxParams = parser.GenParamCount;
+ if (genericParamPosition < 0 || genericParamPosition > maxParams)
+ {
+ NANOCLR_SET_AND_LEAVE(CLR_E_OUT_OF_RANGE);
+ }
+
// advance into the VAR entry
parser.Advance(element);
@@ -3217,6 +3224,38 @@ bool CLR_RT_ExecutionEngine::IsInstanceOf(
return IsInstanceOf(desc, descTarget, isInstInstruction);
}
+///
+/// Checks whether the heap-object 'obj' satisfies exactly the type encoded by
+/// the compressed token 'token' in the IL stream, under the current generic
+/// instantiation in 'caller'. Supports DATATYPE_VAR slots and full GENERICINST.
+///
+bool CLR_RT_ExecutionEngine::IsInstanceOfToken(
+ CLR_UINT32 token,
+ CLR_RT_HeapBlock &obj,
+ const CLR_RT_MethodDef_Instance &caller)
+{
+ // Resolve the *expected* signature into a TypeDescriptor
+ CLR_RT_TypeDescriptor expectedDesc;
+ HRESULT hr = expectedDesc.InitializeFromSignatureToken(caller.assembly, token, &caller);
+
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ // Extract the *actual* runtime type of the object
+ CLR_RT_TypeDescriptor actualDesc;
+ hr = actualDesc.InitializeFromObject(obj);
+
+ if (FAILED(hr))
+ {
+ return false;
+ }
+
+ // Delegate to the CLR built-in type-compatibility test
+ return CLR_RT_HeapBlock::TypeDescriptorsMatch(expectedDesc, actualDesc);
+}
+
HRESULT CLR_RT_ExecutionEngine::CastToType(
CLR_RT_HeapBlock &ref,
CLR_UINT32 tk,
diff --git a/src/CLR/Core/Interpreter.cpp b/src/CLR/Core/Interpreter.cpp
index c2eb8fc67c..d846f6caf4 100644
--- a/src/CLR/Core/Interpreter.cpp
+++ b/src/CLR/Core/Interpreter.cpp
@@ -2644,9 +2644,9 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
switch (dt)
{
+ case DATATYPE_GENERICINST:
case DATATYPE_CLASS:
case DATATYPE_VALUETYPE:
- case DATATYPE_GENERICINST:
obj[fieldInst.CrossReference().offset].AssignAndPreserveType(evalPos[2]);
break;
case DATATYPE_DATETIME: // Special case.
@@ -2969,7 +2969,7 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
NANOCLR_CHECK_HRESULT(CLR_RT_TypeDescriptor::ExtractTypeIndexFromObject(evalPos[0], cls));
// Check this is an object of the requested type.
- if (type.data != cls.data)
+ if (!g_CLR_RT_ExecutionEngine.IsInstanceOfToken(arg, evalPos[0], stack->m_call))
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}
@@ -3125,18 +3125,45 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
OPDEF(CEE_STELEM, "stelem", PopRef + PopI + Pop1, Push0, InlineType, IObjModel, 1, 0xFF, 0xA4, NEXT)
// Stack: ... ... -> ...
{
- // Treat STELEM like ldelema + stobj
- ip += 2; // Skip type argument, not used...
+ FETCH_ARG_COMPRESSED_TYPETOKEN(arg, ip);
- evalPos -= 3; // "pop" args from evaluation stack
+ evalPos -= 3;
CHECKSTACK(stack, evalPos);
+ // Build a by-ref to the array slot at [index]
NANOCLR_CHECK_HRESULT(
evalPos[1].InitializeArrayReference(evalPos[1], evalPos[2].NumericByRef().s4));
evalPos[1].FixArrayReferenceForValueTypes();
- // Reassign will make sure these are objects of the same type.
- NANOCLR_CHECK_HRESULT(evalPos[1].Reassign(evalPos[3]));
+ // Resolve the IL's element type in the context of any generics
+ CLR_RT_TypeDef_Instance expectedType;
+ if (!expectedType.ResolveToken(arg, assm, &stack->m_call))
+ {
+ NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+ }
+
+ NanoCLRDataType elemDT = (NanoCLRDataType)expectedType.target->dataType;
+
+ // Promote the value if it's a reference or boxed struct
+ evalPos[3].Promote();
+
+ // Compute the element‐size: 0 for refs (incl. genericinst), sizeInBytes for primitives
+ size_t size = 0;
+ if (elemDT <= DATATYPE_LAST_PRIMITIVE_TO_PRESERVE)
+ {
+ size = c_CLR_RT_DataTypeLookup[elemDT].m_sizeInBytes;
+ }
+ else if (
+ (expectedType.target->flags & CLR_RECORD_TYPEDEF::TD_Semantics_Mask) ==
+ CLR_RECORD_TYPEDEF::TD_Semantics_ValueType)
+ {
+ size =
+ (CLR_RT_HeapBlock::HB_Object_Fields_Offset + +expectedType.CrossReference().totalFields) *
+ sizeof(CLR_RT_HeapBlock);
+ }
+
+ // Store the value into the actual array buffer
+ NANOCLR_CHECK_HRESULT(evalPos[3].StoreToReference(evalPos[1], size));
break;
}
diff --git a/src/CLR/Core/TypeSystem.cpp b/src/CLR/Core/TypeSystem.cpp
index 9ec3dbdb82..0d7a155a85 100644
--- a/src/CLR/Core/TypeSystem.cpp
+++ b/src/CLR/Core/TypeSystem.cpp
@@ -968,98 +968,94 @@ bool CLR_RT_TypeDef_Instance::ResolveToken(
case TBL_TypeSpec:
{
+ // Grab the raw signature for the IL token (e.g. !T[], List`1, etc.)
const CLR_RECORD_TYPESPEC *ts = assm->GetTypeSpec(index);
-
CLR_RT_SignatureParser parser;
parser.Initialize_TypeSpec(assm, ts);
- CLR_RT_SignatureParser::Element element;
-
- // advance signature: get parameter
- parser.Advance(element);
+ CLR_RT_SignatureParser::Element elem;
- // store parameter position
- auto genericParamPosition = (CLR_INT8)element.GenericParamPosition;
-
- switch (element.DataType)
+ // Skip any leading SZARRAY or BYREF
+ do
{
- case DATATYPE_VAR:
+ if (FAILED(parser.Advance(elem)))
{
- CLR_RT_TypeDef_Index typeDef;
- CLR_RT_SignatureParser::Element genericElement;
-
- CLR_RT_SignatureParser varParser;
- varParser.Initialize_TypeSpec(assm, ts);
-
- // advance once to consume the GENERICINST or VAR entry
- varParser.Advance(genericElement);
+ return false;
+ }
+ } while (elem.DataType == DATATYPE_SZARRAY || elem.DataType == DATATYPE_BYREF);
- // now walk forward genericParameterPosition steps to land on the actual
- // argument in the signature stream.
- for (CLR_INT8 i = 0; i <= genericParamPosition; i++)
- {
- if (FAILED(varParser.Advance(genericElement)))
- {
- return false;
- }
- }
+ // If this is a closed‐generic instantiation header, peel off the wrapper
+ if (elem.DataType == DATATYPE_GENERICINST)
+ {
+ // consume the CLASS/VALUETYPE marker
+ if (FAILED(parser.Advance(elem)))
+ {
+ return false;
+ }
+ // consume the generic‐definition token itself
+ if (FAILED(parser.Advance(elem)))
+ {
+ return false;
+ }
+ // consume the count of generic arguments
+ if (FAILED(parser.Advance(elem)))
+ {
+ return false;
+ }
+ }
- // genericElement.Class now holds the TypeDef_Index of the T or U, etc.
- typeDef = genericElement.Class;
+ // walk forward until a VAR (type‐generic) or MVAR (method‐generic) is hit
+ while (elem.DataType != DATATYPE_VAR && elem.DataType != DATATYPE_MVAR)
+ {
+ if (FAILED(parser.Advance(elem)))
+ {
+ return false;
+ }
+ }
- // populate this instance from the resolved TypeDef
- data = typeDef.data;
- assembly = g_CLR_RT_TypeSystem.m_assemblies[typeDef.Assembly() - 1];
- target = assembly->GetTypeDef(typeDef.Type());
+ // If it's a type‐generic slot (!T), resolve against the caller's closed generic
+ if (elem.DataType == DATATYPE_VAR)
+ {
+ int pos = elem.GenericParamPosition;
-#if defined(NANOCLR_INSTANCE_NAMES)
- name = assembly->GetString(target->name);
-#endif
- return true;
+ // Use the *caller's* bound genericType (Stack, etc.)
+ if (caller == nullptr || caller->genericType == nullptr)
+ {
+ return false;
}
- break;
- case DATATYPE_MVAR:
- {
- CLR_RT_GenericParam_Index gpIndex;
+ auto &tsi = *caller->genericType;
+ CLR_UINT32 closedTsRow = tsi.TypeSpec();
- caller->assembly->FindGenericParamAtMethodDef(*caller, genericParamPosition, gpIndex);
+ CLR_RT_TypeDef_Index realTypeDef;
+ NanoCLRDataType realDataType;
- CLR_RT_GenericParam_CrossReference gp =
- caller->assembly->crossReferenceGenericParam[gpIndex.GenericParam()];
+ // Only call this once to map (e.g. !T→Int32)
+ caller->assembly
+ ->FindGenericParamAtTypeSpec(closedTsRow, (CLR_UINT32)pos, realTypeDef, realDataType);
- // get TypeDef instance from generic parameter index
- data = gp.classTypeDef.data;
- assembly = g_CLR_RT_TypeSystem.m_assemblies[gp.classTypeDef.Assembly() - 1];
- target = assembly->GetTypeDef(gp.classTypeDef.Type());
- }
- break;
+ // populate this instance
+ data = realTypeDef.data;
+ assembly = g_CLR_RT_TypeSystem.m_assemblies[realTypeDef.Assembly() - 1];
+ target = assembly->GetTypeDef(realTypeDef.Type());
- default:
- return false;
+ return true;
+ }
+ else
+ {
+ // elem.DataType == DATATYPE_MVAR
+ CLR_RT_GenericParam_Index gpIdx;
+ caller->assembly->FindGenericParamAtMethodDef(*caller, elem.GenericParamPosition, gpIdx);
+ auto &gp = caller->assembly->crossReferenceGenericParam[gpIdx.GenericParam()];
+
+ data = gp.classTypeDef.data;
+ assembly = g_CLR_RT_TypeSystem.m_assemblies[gp.classTypeDef.Assembly() - 1];
+ target = assembly->GetTypeDef(gp.classTypeDef.Type());
+ return true;
}
-
-#if defined(NANOCLR_INSTANCE_NAMES)
- name = assembly->GetString(target->name);
-#endif
-
- return true;
}
default:
- //// handle generic type from provided data
- // if (sampleData != nullptr)
- //{
- // CLR_RT_TypeDescriptor::ExtractTypeIndexFromObject(*sampleData, *this);
- // m_assm = g_CLR_RT_TypeSystem.m_assemblies[Assembly() - 1];
- // m_target = m_assm->GetTypeDef(Type());
- // }
- // else
- //{
- // m_data = g_CLR_RT_WellKnownTypes.Object.m_data;
- // m_assm = g_CLR_RT_TypeSystem.m_assemblies[g_CLR_RT_WellKnownTypes.Object.Assembly() - 1];
- // m_target = m_assm->GetTypeDef(g_CLR_RT_WellKnownTypes.Object.Type());
- // }
#if defined(NANOCLR_INSTANCE_NAMES)
name = assembly->GetString(target->name);
@@ -1159,81 +1155,81 @@ void CLR_RT_FieldDef_Instance::ClearInstance()
bool CLR_RT_FieldDef_Instance::ResolveToken(CLR_UINT32 tk, CLR_RT_Assembly *assm)
{
NATIVE_PROFILE_CLR_CORE();
- if (assm)
+
+ if (!assm)
{
- CLR_UINT32 index = CLR_DataFromTk(tk);
+ return false;
+ }
- switch (CLR_TypeFromTk(tk))
+ CLR_UINT32 index = CLR_DataFromTk(tk);
+
+ switch (CLR_TypeFromTk(tk))
+ {
+ case TBL_FieldRef:
{
- case TBL_FieldRef:
+ // Find the raw FIELDREF record
+ const CLR_RECORD_FIELDREF *fr = assm->GetFieldRef(index);
+
+ switch (fr->Owner())
{
- const CLR_RECORD_FIELDREF *fr = assm->GetFieldRef(index);
+ case TBL_TypeRef:
+ // Simple (non‐generic) field on a TypeRef
+ data = assm->crossReferenceFieldRef[index].target.data;
+ assembly = g_CLR_RT_TypeSystem.m_assemblies[Assembly() - 1];
+ target = assembly->GetFieldDef(Field());
+ genericType = nullptr; // no TypeSpec
+ break;
- switch (fr->Owner())
+ case TBL_TypeSpec:
{
- case TBL_TypeRef:
- data = assm->crossReferenceFieldRef[index].target.data;
- assembly = g_CLR_RT_TypeSystem.m_assemblies[Assembly() - 1];
- target = assembly->GetFieldDef(Field());
-
- // invalidate generic type
- genericType = nullptr;
+ // Field on a generic‐instantiated type.
+ // Use the MDP TypeSpec (which is already the closed generic),
+ genericType = &assm->crossReferenceFieldRef[index].genericType;
- break;
+ // Retrieve that closed‐generic TypeSpec blob
+ const CLR_RECORD_TYPESPEC *ts = assm->GetTypeSpec(genericType->TypeSpec());
- case TBL_TypeSpec:
+ // Look up the actual FieldDef within that closed type
+ CLR_RT_FieldDef_Index resolvedField;
+ if (!assm->FindFieldDef(ts, assm->GetString(fr->name), assm, fr->signature, resolvedField))
{
- genericType = &assm->crossReferenceFieldRef[index].genericType;
-
- const CLR_RECORD_TYPESPEC *ts = assm->GetTypeSpec(genericType->TypeSpec());
-
- CLR_RT_FieldDef_Index field;
-
- if (!assm->FindFieldDef(ts, assm->GetString(fr->name), assm, fr->signature, field))
- {
- return false;
- }
-
- Set(assm->assemblyIndex, field.Field());
-
- assembly = assm;
- target = assembly->GetFieldDef(Field());
-
- break;
- }
- default:
- // shouldn't be here
return false;
+ }
+
+ // Bind to that real FieldDef
+ Set(assm->assemblyIndex, resolvedField.Field());
+ assembly = assm;
+ target = assembly->GetFieldDef(Field());
+ break;
}
-#if defined(NANOCLR_INSTANCE_NAMES)
- name = assembly->GetString(target->name);
-#endif
- return true;
+ default:
+ // should not happen
+ return false;
}
- case TBL_FieldDef:
- Set(assm->assemblyIndex, index);
- assembly = assm;
- target = assembly->GetFieldDef(index);
+#if defined(NANOCLR_INSTANCE_NAMES)
+ name = assembly->GetString(target->name);
+#endif
+ return true;
+ }
- // invalidate generic type
- genericType = nullptr;
+ case TBL_FieldDef:
+ // Direct FieldDef token
+ Set(assm->assemblyIndex, CLR_DataFromTk(tk));
+ assembly = assm;
+ target = assembly->GetFieldDef(Field());
+ genericType = nullptr;
#if defined(NANOCLR_INSTANCE_NAMES)
- name = assembly->GetString(target->name);
+ name = assembly->GetString(target->name);
#endif
- return true;
+ return true;
- default:
- // the remaining data types aren't to be handled
- break;
- }
+ default:
+ // Not a field token
+ return false;
}
-
- ClearInstance();
-
- return false;
}
//////////////////////////////
@@ -1680,6 +1676,25 @@ HRESULT CLR_RT_TypeDescriptor::InitializeFromType(const CLR_RT_TypeDef_Index &cl
NANOCLR_NOCLEANUP();
}
+HRESULT CLR_RT_TypeDescriptor::InitializeFromTypeDef(const CLR_RT_TypeDef_Index &cls)
+{
+ NATIVE_PROFILE_CLR_CORE();
+ NANOCLR_HEADER();
+ if (m_handlerCls.InitializeFromIndex(cls) == false)
+ {
+ NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+ }
+ else
+ {
+ m_flags = CLR_RT_DataTypeLookup::c_ManagedType;
+ m_reflex.kind = REFLECTION_TYPE;
+ m_reflex.levels = 0;
+ m_reflex.data.type = m_handlerCls;
+ }
+ m_handlerGenericType.ClearInstance();
+ NANOCLR_NOCLEANUP();
+}
+
HRESULT CLR_RT_TypeDescriptor::InitializeFromGenericType(const CLR_RT_TypeSpec_Index &genericType)
{
NATIVE_PROFILE_CLR_CORE();
@@ -1748,6 +1763,89 @@ HRESULT CLR_RT_TypeDescriptor::InitializeFromSignatureParser(CLR_RT_SignaturePar
NANOCLR_NOCLEANUP();
}
+HRESULT CLR_RT_TypeDescriptor::InitializeFromSignatureToken(
+ CLR_RT_Assembly *assm,
+ CLR_UINT32 token,
+ const CLR_RT_MethodDef_Instance *caller)
+{
+ NANOCLR_HEADER();
+
+ // Peel the compressed token to find out which table and index we're in
+ NanoCLRTable dataTable = CLR_TypeFromTk(token);
+ CLR_UINT32 idx = CLR_DataFromTk(token);
+
+ switch (dataTable)
+ {
+ case TBL_TypeRef:
+ case TBL_TypeDef:
+ {
+ CLR_RT_TypeDef_Instance tdInst;
+ if (!tdInst.ResolveToken(token, assm, caller))
+ {
+ NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
+ }
+ this->InitializeFromTypeDef(tdInst);
+ break;
+ }
+
+ case TBL_TypeSpec:
+ {
+ // could be SZARRAY, VAR, MVAR or full GENERICINST
+ const CLR_RECORD_TYPESPEC *ts = assm->GetTypeSpec(idx);
+ CLR_RT_SignatureParser parser;
+ parser.Initialize_TypeSpec(assm, ts);
+
+ CLR_RT_SignatureParser::Element elem;
+ parser.Advance(elem);
+
+ if (elem.DataType == DATATYPE_VAR)
+ {
+ // !T: ask the CLR to map that slot into the *actual* argument
+ CLR_RT_TypeDef_Index td;
+ NanoCLRDataType dt;
+ assm->FindGenericParamAtTypeSpec(caller->genericType->TypeSpec(), elem.GenericParamPosition, td, dt);
+ this->InitializeFromTypeDef(td);
+ }
+ else if (elem.DataType == DATATYPE_MVAR)
+ {
+ // !!U: method-generic
+ CLR_RT_GenericParam_Index gp;
+ assm->FindGenericParamAtMethodDef(*caller, elem.GenericParamPosition, gp);
+ auto &info = assm->crossReferenceGenericParam[gp.GenericParam()];
+ this->InitializeFromTypeDef(info.classTypeDef);
+ }
+ else if (elem.DataType == DATATYPE_GENERICINST)
+ {
+ // full generic instantiation: read it out
+ // CLASS/VALUETYPE
+ parser.Advance(elem);
+ // generic-definition token
+ parser.Advance(elem);
+
+ CLR_RT_TypeSpec_Index tsInst{};
+ tsInst.Set(elem.Class.Assembly(), elem.Class.Type());
+
+ // argument count
+ parser.Advance(elem);
+
+ // now read each argument and record in tsInst.m_data.GenericArguments
+ this->InitializeFromTypeSpec(tsInst);
+ }
+ else
+ {
+ // e.g. SZARRAY, VALUETYPE, CLASS
+ this->InitializeFromSignatureParser(parser);
+ }
+ break;
+ }
+
+ default:
+ NANOCLR_SET_AND_LEAVE(CLR_E_NOTIMPL);
+ }
+
+ NANOCLR_NOCLEANUP();
+}
+
HRESULT CLR_RT_TypeDescriptor::InitializeFromObject(const CLR_RT_HeapBlock &ref)
{
NATIVE_PROFILE_CLR_CORE();
@@ -4476,9 +4574,50 @@ bool CLR_RT_Assembly::FindGenericParam(CLR_INDEX typeSpecIndex, CLR_RT_GenericPa
return false;
}
+bool CLR_RT_Assembly::FindGenericParamAtTypeSpec(
+ CLR_UINT32 typeSpecIndex,
+ CLR_INT32 genericParameterPosition,
+ CLR_RT_TypeDef_Index &typeDef,
+ NanoCLRDataType &dataType)
+{
+ NATIVE_PROFILE_CLR_CORE();
+
+ CLR_RT_SignatureParser parser;
+ parser.Initialize_TypeSpec(this, GetTypeSpec(typeSpecIndex));
+
+ CLR_RT_SignatureParser::Element element;
+
+ // get into the GENERICINST
+ if (FAILED(parser.Advance(element)))
+ {
+ return false;
+ }
+
+ // sanity check for invalid parameter position
+ if (genericParameterPosition > parser.GenParamCount)
+ {
+ // not enough parameters!!
+ return false;
+ }
+
+ // walk to the requested parameter position
+ for (int32_t i = 0; i <= genericParameterPosition; i++)
+ {
+ if (FAILED(parser.Advance(element)))
+ {
+ return false;
+ }
+ }
+
+ // element.Class was filled from the VAR position
+ typeDef = element.Class;
+ dataType = element.DataType;
+ return true;
+}
+
bool CLR_RT_Assembly::FindGenericParamAtMethodDef(
CLR_RT_MethodDef_Instance md,
- CLR_UINT32 genericParameterPosition,
+ CLR_INT32 genericParameterPosition,
CLR_RT_GenericParam_Index &index)
{
NATIVE_PROFILE_CLR_CORE();
@@ -6118,7 +6257,31 @@ HRESULT CLR_RT_TypeSystem::BuildMethodRefName(const CLR_RT_MethodRef_Index &meth
NANOCLR_NOCLEANUP();
}
+HRESULT CLR_RT_TypeSystem::BuildMethodRefName(
+ const CLR_RT_MethodRef_Index &mri,
+ const CLR_RT_TypeSpec_Index *callerGeneric, // may be nullptr if none
+ char *&szBuffer,
+ size_t &iBuffer)
+{
+ NATIVE_PROFILE_CLR_CORE();
+ NANOCLR_HEADER();
+
+ // 1) Grab the assembly that owns this MethodRef
+ CLR_RT_Assembly *assembly = g_CLR_RT_TypeSystem.m_assemblies[mri.Assembly() - 1];
+
+ // 2) Pull the raw CLR_RECORD_METHODREF
+ const CLR_RECORD_METHODREF *mr = assembly->GetMethodRef(mri.Method());
+
+ // 3) Build the corresponding MethodDef_Index
+ // (MethodRef.method is the metadata token for the definition)
+ CLR_RT_MethodDef_Index md;
+ md.Set(mri.Assembly(), mr->OwnerIndex());
+
+ // 4) Delegate to your existing BuildMethodName, passing the *declaring type’s* TypeSpec
+ NANOCLR_CHECK_HRESULT(BuildMethodName(md, callerGeneric, szBuffer, iBuffer));
+ NANOCLR_NOCLEANUP();
+}
HRESULT CLR_RT_TypeSystem::BuildMethodSpecName(const CLR_RT_MethodSpec_Index &ms, char *&szBuffer, size_t &iBuffer)
{
NATIVE_PROFILE_CLR_CORE();
@@ -6206,7 +6369,31 @@ HRESULT CLR_RT_TypeSystem::BuildMethodSpecName(const CLR_RT_MethodSpec_Index &ms
NANOCLR_NOCLEANUP();
}
+HRESULT CLR_RT_TypeSystem::BuildMethodSpecName(
+ const CLR_RT_MethodSpec_Index &msi,
+ const CLR_RT_TypeSpec_Index *callerGeneric, // may be nullptr if none
+ char *&szBuffer,
+ size_t &iBuffer)
+{
+ NATIVE_PROFILE_CLR_CORE();
+ NANOCLR_HEADER();
+ // 1) Grab the assembly that owns this MethodSpec
+ CLR_RT_Assembly *assembly = g_CLR_RT_TypeSystem.m_assemblies[msi.Assembly() - 1];
+
+ // 2) Pull the raw CLR_RECORD_METHODSPEC
+ const CLR_RECORD_METHODSPEC *ms = assembly->GetMethodSpec(msi.Method());
+
+ // 3) Build the corresponding MethodDef_Index
+ // (MethodSpec.method is the metadata token for the definition)
+ CLR_RT_MethodDef_Index md;
+ md.Set(msi.Assembly(), ms->MethodIndex());
+
+ // 4) Delegate to BuildMethodName, again passing the closed declaring‐type
+ NANOCLR_CHECK_HRESULT(BuildMethodName(md, callerGeneric, szBuffer, iBuffer));
+
+ NANOCLR_NOCLEANUP();
+}
//--//
bool CLR_RT_TypeSystem::FindVirtualMethodDef(
diff --git a/src/CLR/Diagnostics/Info.cpp b/src/CLR/Diagnostics/Info.cpp
index af3f1686fd..a0c3c1e452 100644
--- a/src/CLR/Diagnostics/Info.cpp
+++ b/src/CLR/Diagnostics/Info.cpp
@@ -469,16 +469,177 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_TypeSpec_Index *g
}
case TBL_MethodRef:
{
- LOOKUP_ELEMENT_IDX(index, MethodRef, METHODREF);
- CLR_RT_DUMP::METHODREF(s);
+ CLR_UINT32 idx = CLR_DataFromTk(token);
+ auto &xref = crossReferenceMethodRef[idx];
+
+ // Build a MethodRef_Index so we can format the reference
+ CLR_RT_MethodRef_Index mri;
+ mri.Set(assemblyIndex, idx);
+
+ // Pass the *target* method’s closed genericType, not "callGeneric"!
+ char buf[256], *p = buf;
+ size_t cb = sizeof(buf);
+ g_CLR_RT_TypeSystem.BuildMethodRefName(
+ mri,
+ &xref.genericType, // THIS is the TypeSpec for SimpleList
+ p,
+ cb);
+ CLR_Debug::Printf("%s", buf);
break;
}
case TBL_TypeDef:
{
+ // A genuine TypeDef token—print its (possibly non‐generic) name.
LOOKUP_ELEMENT_IDX(index, TypeDef, TYPEDEF);
CLR_RT_DUMP::TYPE(s);
break;
}
+ case TBL_TypeSpec:
+ {
+ //
+ // The TypeSpec token can represent:
+ // - a generic parameter (DATATYPE_VAR/ MVAR), e.g. !0
+ // - a primitive or class (DATATYPE_CLASS / VALUETYPE), e.g. Int32
+ // - an n-dimensional array (DATATYPE_ARRAY) or a single‐dim SZARRAY inside the signature
+ // - a closed generic instantiation (DATATYPE_GENERICINST) like List`1
+ //
+ // When DumpToken is called for “newarr”, often the token is the element‐type alone,
+ // so if that token is exactly “VAR0” (or “MVAR0”), we must substitute “!0→I4” right here.
+ // Only if it is not a plain VAR/MVAR do we then check for arrays or else fall
+ // back to BuildTypeName for the full concrete name.
+ //
+
+ CLR_RT_TypeSpec_Index tsIdx;
+ tsIdx.Set(assemblyIndex, index);
+
+ // bind to get the signature blob
+ CLR_RT_TypeSpec_Instance tsInst{};
+ if (!tsInst.InitializeFromIndex(tsIdx))
+ {
+ // unable to bind; dump raw RID
+ CLR_Debug::Printf("[TYPESPEC:%08x]", token);
+ break;
+ }
+
+ // start parsing the signature
+ CLR_RT_SignatureParser parser;
+ parser.Initialize_TypeSpec(tsInst.assembly, tsInst.target);
+
+ // read first element
+ CLR_RT_SignatureParser::Element elem;
+ if (FAILED(parser.Advance(elem)))
+ {
+ // corrupt signature: just fallback to full type name
+ char bufCorrupt[256];
+ char *pCorrupt = bufCorrupt;
+ size_t cbCorrupt = sizeof(bufCorrupt);
+ g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pCorrupt, cbCorrupt);
+ CLR_Debug::Printf("%s", bufCorrupt);
+ break;
+ }
+
+ if (elem.DataType == DATATYPE_VAR || elem.DataType == DATATYPE_MVAR)
+ {
+ int gpIndex = elem.GenericParamPosition;
+
+ // if the caller's genericType is non‐null, ask the CLR to map !n→actual argument:
+ if (genericType != nullptr && NANOCLR_INDEX_IS_VALID(*genericType))
+ {
+ CLR_RT_TypeDef_Index tdArg{};
+ NanoCLRDataType dtArg;
+
+ bool ok =
+ tsInst.assembly->FindGenericParamAtTypeSpec(genericType->TypeSpec(), gpIndex, tdArg, dtArg);
+ if (ok)
+ {
+ // Print that bound argument (e.g. "I4" or full class name)
+ char bufArg[256];
+ char *pArg = bufArg;
+ size_t cbArg = sizeof(bufArg);
+ g_CLR_RT_TypeSystem.BuildTypeName(tdArg, pArg, cbArg);
+ CLR_Debug::Printf("%s", bufArg);
+ break;
+ }
+ }
+
+ // Couldn't resolve or caller was not generic: print "!n" or "!!n" literally
+ if (elem.DataType == DATATYPE_VAR)
+ {
+ CLR_Debug::Printf("!%d", gpIndex);
+ }
+ else
+ {
+ CLR_Debug::Printf("!!%d", gpIndex);
+ }
+ break;
+ }
+
+ if (elem.DataType == DATATYPE_SZARRAY)
+ {
+ // advance to see what’s inside the array
+ if (FAILED(parser.Advance(elem)))
+ {
+ // seems to be malformed: print SZARRAY and stop
+ CLR_Debug::Printf("SZARRAY");
+ break;
+ }
+
+ // inner element is a VAR/MVAR, it describes “!n[]”
+ if (elem.DataType == DATATYPE_VAR || elem.DataType == DATATYPE_MVAR)
+ {
+ int gpIndex = elem.GenericParamPosition;
+
+ if (genericType != nullptr && NANOCLR_INDEX_IS_VALID(*genericType))
+ {
+ CLR_RT_TypeDef_Index tdArg{};
+ NanoCLRDataType dtArg;
+
+ bool genericParamFound =
+ tsInst.assembly->FindGenericParamAtTypeSpec(genericType->TypeSpec(), gpIndex, tdArg, dtArg);
+ if (genericParamFound)
+ {
+ // print "I4[]" or the bound argument plus []
+ char bufArg[256];
+ char *pArg = bufArg;
+ size_t cbArg = sizeof(bufArg);
+ g_CLR_RT_TypeSystem.BuildTypeName(tdArg, pArg, cbArg);
+ CLR_Debug::Printf("%s[]", bufArg);
+ break;
+ }
+ }
+
+ // Fallback if we couldn’t resolve: "!n[]" or "!!n[]"
+ if (elem.DataType == DATATYPE_VAR)
+ {
+ CLR_Debug::Printf("!%d[]", gpIndex);
+ }
+ else
+ {
+ CLR_Debug::Printf("!!%d[]", gpIndex);
+ }
+ break;
+ }
+
+ // If it's SZARRAY of a primitive or class (e.g. "Int32[]"), just print full name:
+ {
+ char bufArr[256];
+ char *pArr = bufArr;
+ size_t cbArr = sizeof(bufArr);
+ g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pArr, cbArr);
+ CLR_Debug::Printf("%s", bufArr);
+ break;
+ }
+ }
+
+ // now all the rest: just print the full type name
+ char bufGeneric[256];
+ char *pGeneric = bufGeneric;
+ size_t cbGeneric = sizeof(bufGeneric);
+ g_CLR_RT_TypeSystem.BuildTypeName(tsIdx, pGeneric, cbGeneric);
+ CLR_Debug::Printf("%s", bufGeneric);
+ break;
+ }
+
case TBL_FieldDef:
{
LOOKUP_ELEMENT_IDX(index, FieldDef, FIELDDEF);
@@ -493,8 +654,20 @@ void CLR_RT_Assembly::DumpToken(CLR_UINT32 token, const CLR_RT_TypeSpec_Index *g
}
case TBL_MethodSpec:
{
- LOOKUP_ELEMENT_IDX(index, MethodSpec, METHODSPEC);
- CLR_RT_DUMP::METHODSPEC(s);
+ CLR_UINT32 idx = CLR_DataFromTk(token);
+ auto &xref = crossReferenceMethodSpec[idx];
+
+ CLR_RT_MethodSpec_Index msi;
+ msi.Set(assemblyIndex, idx);
+
+ char buf[256], *p = buf;
+ size_t cb = sizeof(buf);
+ g_CLR_RT_TypeSystem.BuildMethodSpecName(
+ msi,
+ &xref.genericType, // again: the closed declaring type
+ p,
+ cb);
+ CLR_Debug::Printf("%s", buf);
break;
}
case TBL_Strings:
@@ -675,12 +848,34 @@ void CLR_RT_Assembly::DumpOpcodeDirect(
if (IsOpParamToken(opParam))
{
- DumpToken(CLR_ReadTokenCompressed(ip, op), call.genericType);
+ // Read the raw 32-bit “token” out of the IL stream:
+ CLR_UINT32 token = CLR_ReadTokenCompressed(ip, op);
+
+ // If this is a CALL or CALLVIRT, force a MethodDef_Instance resolve and
+ // use CLR_RT_DUMP::METHOD to print “TypeName::MethodName”. Otherwise fall
+ // back to the existing DumpToken logic (fields, types, strings, etc.).
+ if (op == CEE_CALL || op == CEE_CALLVIRT)
+ {
+ CLR_RT_MethodDef_Instance mdInst{};
+ if (mdInst.ResolveToken(token, call.assembly))
+ {
+ // mdInst now holds the target MethodDef (or MethodSpec) plus any genericType.
+ CLR_RT_DUMP::METHOD(mdInst, mdInst.genericType);
+ }
+ else
+ {
+ // In the unlikely case ResolveToken fails, fall back to raw DumpToken:
+ DumpToken(token, call.genericType);
+ }
+ }
+ else
+ {
+ DumpToken(token, call.genericType);
+ }
}
else
{
- CLR_UINT32 argLo;
- CLR_UINT32 argHi;
+ CLR_UINT32 argLo, argHi;
switch (c_CLR_opParamSizeCompressed[opParam])
{
diff --git a/src/CLR/Include/nanoCLR_Runtime.h b/src/CLR/Include/nanoCLR_Runtime.h
index 1c8c7f961f..65e857ea5e 100644
--- a/src/CLR/Include/nanoCLR_Runtime.h
+++ b/src/CLR/Include/nanoCLR_Runtime.h
@@ -1422,10 +1422,14 @@ struct CLR_RT_Assembly : public CLR_RT_HeapBlock_Node // EVENT HEAP - NO RELOCAT
bool FindTypeDef(CLR_UINT32 hash, CLR_RT_TypeDef_Index &index);
bool FindTypeSpec(const CLR_PMETADATA sig, CLR_RT_TypeSpec_Index &index);
-
+ bool FindGenericParamAtTypeSpec(
+ CLR_UINT32 typeSpecIndex,
+ CLR_INT32 genericParameterPosition,
+ CLR_RT_TypeDef_Index &typeDef,
+ NanoCLRDataType &dataType);
bool FindGenericParamAtMethodDef(
CLR_RT_MethodDef_Instance md,
- CLR_UINT32 genericParameterPosition,
+ CLR_INT32 genericParameterPosition,
CLR_RT_GenericParam_Index &index);
bool FindGenericParam(CLR_INDEX typeSpecIndex, CLR_RT_GenericParam_Index &index);
bool FindMethodSpecFromTypeSpec(CLR_INDEX typeSpecIndex, CLR_RT_MethodSpec_Index &index);
@@ -2009,7 +2013,17 @@ struct CLR_RT_TypeSystem // EVENT HEAP - NO RELOCATION -
size_t &size);
HRESULT BuildFieldName(const CLR_RT_FieldDef_Index &fd, char *&szBuffer, size_t &size);
HRESULT BuildMethodRefName(const CLR_RT_MethodRef_Index &method, char *&szBuffer, size_t &iBuffer);
+ HRESULT BuildMethodRefName(
+ const CLR_RT_MethodRef_Index &mri,
+ const CLR_RT_TypeSpec_Index *callerGeneric, // may be nullptr if none
+ char *&szBuffer,
+ size_t &iBuffer);
HRESULT BuildMethodSpecName(const CLR_RT_MethodSpec_Index &ms, char *&szBuffer, size_t &iBuffer);
+ HRESULT BuildMethodSpecName(
+ const CLR_RT_MethodSpec_Index &msi,
+ const CLR_RT_TypeSpec_Index *callerGeneric, // may be nullptr if none
+ char *&szBuffer,
+ size_t &iBuffer);
HRESULT QueueStringToBuffer(char *&szBuffer, size_t &size, const char *szText);
@@ -2329,9 +2343,14 @@ struct CLR_RT_TypeDescriptor
HRESULT InitializeFromReflection(const CLR_RT_ReflectionDef_Index &reflex);
HRESULT InitializeFromTypeSpec(const CLR_RT_TypeSpec_Index &sig);
HRESULT InitializeFromType(const CLR_RT_TypeDef_Index &cls);
+ HRESULT InitializeFromTypeDef(const CLR_RT_TypeDef_Index &cls);
HRESULT InitializeFromGenericType(const CLR_RT_TypeSpec_Index &genericType);
HRESULT InitializeFromFieldDefinition(const CLR_RT_FieldDef_Instance &fd);
HRESULT InitializeFromSignatureParser(CLR_RT_SignatureParser &parser);
+ HRESULT InitializeFromSignatureToken(
+ CLR_RT_Assembly *assm,
+ CLR_UINT32 token,
+ const CLR_RT_MethodDef_Instance *caller);
HRESULT InitializeFromObject(const CLR_RT_HeapBlock &ref);
void ConvertToArray();
@@ -4062,6 +4081,7 @@ struct CLR_RT_ExecutionEngine
static bool IsInstanceOf(const CLR_RT_TypeDef_Index &cls, const CLR_RT_TypeDef_Index &clsTarget);
static bool IsInstanceOf(CLR_RT_HeapBlock &obj, const CLR_RT_TypeDef_Index &clsTarget);
static bool IsInstanceOf(CLR_RT_HeapBlock &obj, CLR_RT_Assembly *assm, CLR_UINT32 token, bool isInstInstruction);
+ bool IsInstanceOfToken(CLR_UINT32 token, CLR_RT_HeapBlock &obj, const CLR_RT_MethodDef_Instance &caller);
static HRESULT CastToType(CLR_RT_HeapBlock &ref, CLR_UINT32 tk, CLR_RT_Assembly *assm, bool isInstInstruction);
diff --git a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
index 778ab87287..e7b33e8434 100644
--- a/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
+++ b/src/CLR/Include/nanoCLR_Runtime__HeapBlock.h
@@ -1360,6 +1360,7 @@ struct CLR_RT_HeapBlock
HRESULT LoadFromReference(CLR_RT_HeapBlock &ref);
HRESULT StoreToReference(CLR_RT_HeapBlock &ref, int size);
HRESULT Reassign(const CLR_RT_HeapBlock &value);
+ HRESULT Reassign(CLR_RT_HeapBlock &rhs, const CLR_RT_TypeDef_Instance &expectedType);
HRESULT PerformBoxingIfNeeded();
HRESULT PerformBoxing(const CLR_RT_TypeDef_Instance &cls);
HRESULT PerformUnboxing(const CLR_RT_TypeDef_Instance &cls);
@@ -1371,6 +1372,7 @@ struct CLR_RT_HeapBlock
bool IsZero() const;
void Promote();
+ static bool TypeDescriptorsMatch(const CLR_RT_TypeDescriptor &exp, const CLR_RT_TypeDescriptor &act);
static CLR_UINT32 GetHashCode(CLR_RT_HeapBlock *ptr, bool fRecurse, CLR_UINT32 crc);
static bool ObjectsEqual(const CLR_RT_HeapBlock &left, const CLR_RT_HeapBlock &right, bool fSameReference);