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);