Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
022586f
Fix box/unbox Nullable<T> types
josesimoes Jul 7, 2025
6fb89fa
Merge branch 'develop' of https://github.com/nanoframework/nf-interpr…
josesimoes Jul 18, 2025
f5694b8
Replace SetGenericInstanceObject with SetGenericInstanceType
josesimoes Aug 5, 2025
aff8379
Fix creation of generic objects
josesimoes Aug 5, 2025
4803a77
Fix locals initialization
josesimoes Aug 7, 2025
5c84183
Move debug info about fields name to correct location
josesimoes Aug 7, 2025
534a980
Remove commented code
josesimoes Aug 7, 2025
e0c3b31
Fix FieldDef_Instance::ResolveToken
josesimoes Aug 7, 2025
cff66f0
Code style fixes
josesimoes Aug 7, 2025
d4f1beb
Code style fixes (#160)
nfbot Aug 7, 2025
d151656
Merge branch 'develop' of https://github.com/nanoframework/nf-interpr…
josesimoes Aug 12, 2025
bb1c2e1
Fix resolving type descriptor from TypeSpec token
josesimoes Aug 13, 2025
f3a70b7
Fix finding FieldDef for a generic instance
josesimoes Aug 13, 2025
677a8fa
TypeSpec instance now has a CLR_RT_TypeDef_Index
josesimoes Aug 13, 2025
1f52015
Fix DumpToken in info
josesimoes Aug 13, 2025
65dc497
Revert wrong trace config
josesimoes Aug 13, 2025
903e3db
Fix locals initialization for generic types
josesimoes Aug 18, 2025
cfab319
Fix initialization of TypeSpec from index
josesimoes Aug 18, 2025
024d976
Fix token resolution for TypeDef
josesimoes Aug 18, 2025
4c9f1dd
Fix field token resolution
josesimoes Aug 18, 2025
e93e8bb
Fix finding FieldDef
josesimoes Aug 18, 2025
a008b14
Remove unecessary code from MethodDef finder
josesimoes Aug 18, 2025
bfdb13e
Fix unboxing operation
josesimoes Aug 20, 2025
4c6ad00
Fix reflection for TypeSpec
josesimoes Aug 20, 2025
4701c18
Add new Initialize_TypeSpec to initialize from TypeSpec instance
josesimoes Aug 21, 2025
39944b7
Add IsAGenericInstance to Heapblock
josesimoes Aug 21, 2025
5babab3
Fix TypeDescriptor from generic type
josesimoes Aug 21, 2025
92f1eb1
Fix TypeDescriptor initialization from object
josesimoes Aug 21, 2025
beb14da
Fix extracting type index in TypeDescriptor
josesimoes Aug 21, 2025
036f260
ObjectGenericType in heap block now returns reflection data
josesimoes Aug 21, 2025
0a75279
Update declaration of mscorlib
josesimoes Aug 21, 2025
a7e957c
Add implementation for new methods in Type class
josesimoes Aug 21, 2025
10c46d8
Fix get_IsArray to deal with TypeSpecs
josesimoes Aug 21, 2025
7151fd6
Fix compiler errors
josesimoes Aug 21, 2025
b3d6213
Fix build error
josesimoes Aug 21, 2025
51889c0
Remove unused var
josesimoes Aug 21, 2025
b136778
Fix dangling pointer
josesimoes Aug 21, 2025
2996ca4
Fix pointer
josesimoes Aug 21, 2025
dba8b3d
Fix compiler errors
josesimoes Aug 21, 2025
5e5d3cf
Add Span_1 to project and class stub
josesimoes Aug 21, 2025
fd820e7
Replaced SpanByte with Span_1
josesimoes Aug 21, 2025
df4b419
Code style fixes (#161)
nfbot Aug 21, 2025
385670f
Add missing ref to Span<> source code
josesimoes Aug 21, 2025
09ba158
Fix InitializeReference for nested generic types
josesimoes Sep 1, 2025
de6bd51
Merge branch 'develop' of https://github.com/nanoframework/nf-interpr…
josesimoes Sep 4, 2025
1900db4
Fix mscorlib checksum
josesimoes Sep 4, 2025
4b03921
Add implementation for Span<T> CopyTo
josesimoes Sep 4, 2025
ed9126c
Fix token resolution for MethodDef when using empty generic types
josesimoes Sep 4, 2025
e2d8d6c
Fix local initialization for method in generic type
josesimoes Sep 4, 2025
ba315fc
Update mscorlib declaration with Span<T> and ReadOnlySpan<T>
josesimoes Sep 4, 2025
44c1c70
Fix Span<T> CopyTo
josesimoes Sep 4, 2025
9d8af32
Span<T> CopyTo now uses Array::Copy
josesimoes Sep 4, 2025
5bc67db
Fix ResolveToken for TypeDef when token is a TypeSpec
josesimoes Sep 4, 2025
e7651b6
Fix locals initialization for VAR signature with generic caller
josesimoes Sep 4, 2025
fdd237b
Fix DumpToken for MethodRef in generic types
josesimoes Sep 5, 2025
0e4891d
Fix token resolution for MedthoDef with generic instances
josesimoes Sep 5, 2025
642546b
Add missing levels processing in TypeDef instance from TypeSpec
josesimoes Sep 5, 2025
17167dc
Code style fixes (#163)
nfbot Sep 5, 2025
9ca1814
Fix compiler errors
josesimoes Sep 5, 2025
8c140de
Code style fixes (#164)
nfbot Sep 5, 2025
197503c
Fix compiler warning
josesimoes Sep 9, 2025
b99fad9
Fix resolving token from TypeSpec when creating an array
josesimoes Sep 9, 2025
f9b3f21
Code style fixes (#165)
nfbot Sep 9, 2025
f46021b
Missed previous commit
josesimoes Sep 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/CLR/CorLib/corlib_native.h
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,14 @@ struct Library_corlib_native_System_MulticastDelegate
//--//
};

struct Library_corlib_native_System_Nullable_1
{
static const int FIELD__hasValue = 1;
static const int FIELD__value = 2;

//--//
};

struct Library_corlib_native_System_Number
{
NANOCLR_NATIVE_DECLARE(FormatNative___STATIC__STRING__OBJECT__BOOLEAN__STRING__STRING__STRING__STRING__SZARRAY_I4);
Expand Down
96 changes: 94 additions & 2 deletions src/CLR/Core/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
//
#include "Core.h"

typedef Library_corlib_native_System_Nullable_1 Sys_Nullable;

////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined(NANOCLR_TRACE_EXCEPTIONS) && defined(VIRTUAL_DEVICE)
Expand Down Expand Up @@ -2802,13 +2804,101 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)

UPDATESTACK(stack, evalPos);

// check if value is a nullable type
bool hasValue = false;
CLR_RT_HeapBlock *nullableValue = nullptr;
bool valueIsNullableType = false;
bool tokenIsNullableType = false;
CLR_RT_TypeDef_Instance destinationType;

valueIsNullableType =
CLR_RT_ExecutionEngine::IsInstanceOf(evalPos[0], g_CLR_RT_WellKnownTypes.Nullable);
tokenIsNullableType =
CLR_RT_ExecutionEngine::IsInstanceOf(typeInst, g_CLR_RT_WellKnownTypes.Nullable);

// resolve the T to box to / unbox from
if (tokenIsNullableType && destinationType.ResolveNullableType(arg, assm, &stack->m_call) == false)
{
NANOCLR_SET_AND_LEAVE(CLR_E_WRONG_TYPE);
}

if (valueIsNullableType)
{
// check HasValue property
nullableValue = evalPos[0].Dereference();
hasValue = nullableValue[Library_corlib_native_System_Nullable_1::FIELD__hasValue]
.NumericByRefConst()
.u1;
}

if (op == CEE_BOX)
{
NANOCLR_CHECK_HRESULT(evalPos[0].PerformBoxing(typeInst));
if (tokenIsNullableType)
{
if (!hasValue)
{
// box a null reference
evalPos[0].SetObjectReference(nullptr);
}
else
{
// reach the value to box
CLR_RT_HeapBlock &value =
nullableValue[Library_corlib_native_System_Nullable_1::FIELD__value];

// box the value
NANOCLR_CHECK_HRESULT(value.PerformBoxing(destinationType));

// assign the boxed result back to the evaluation stack
evalPos[0].Assign(value);
}
}
else
{
NANOCLR_CHECK_HRESULT(evalPos[0].PerformBoxing(typeInst));
}
}
else
{
NANOCLR_CHECK_HRESULT(evalPos[0].PerformUnboxing(typeInst));
if (tokenIsNullableType)
{
// create a Nullable<T>...
CLR_RT_HeapBlock nullableObject;
CLR_RT_TypeSpec_Index destinationTypeSpec;
destinationTypeSpec.data = arg;

NANOCLR_CHECK_HRESULT(g_CLR_RT_ExecutionEngine.NewGenericInstanceObject(
nullableObject,
typeInst,
destinationTypeSpec));

CLR_RT_ProtectFromGC gc(nullableObject);

if (evalPos[0].Dereference() == nullptr)
{
// assign a null reference (already carried out by NewObjectFromIndex)
// set HasValue to false
nullableObject.Dereference()[Sys_Nullable::FIELD__hasValue].SetBoolean(false);
}
else
{
// unbox the T value...
NANOCLR_CHECK_HRESULT(evalPos[0].PerformUnboxing(destinationType));

// assign the copied unboxed value
nullableObject.Dereference()[Sys_Nullable::FIELD__value].Assign(evalPos[0]);

// set HasValue to true
nullableObject.Dereference()[Sys_Nullable::FIELD__hasValue].SetBoolean(true);
}

// assign the Nullable<T> object to the evaluation stack
evalPos[0].SetObjectReference(nullableObject.Dereference());
}
else
{
NANOCLR_CHECK_HRESULT(evalPos[0].PerformUnboxing(typeInst));
}
}
break;
}
Expand All @@ -2825,6 +2915,8 @@ HRESULT CLR_RT_Thread::Execute_IL(CLR_RT_StackFrame &stackArg)
// ldobj.) When applied to a reference type, the unbox.any instruction has the same effect as
// castclass typeTok.

// TODO: still not handling Nullable<T> types here

CLR_RT_TypeDef_Instance typeInst{};
if (typeInst.ResolveToken(arg, assm, &stack->m_call) == false)
{
Expand Down
146 changes: 119 additions & 27 deletions src/CLR/Core/TypeSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -976,34 +976,23 @@ bool CLR_RT_TypeDef_Instance::ResolveToken(
parser.Initialize_TypeSpec(assm, ts);

CLR_RT_SignatureParser::Element elem;

// Skip any leading SZARRAY or BYREF
do
if (FAILED(parser.Advance(elem)))
{
if (FAILED(parser.Advance(elem)))
{
return false;
}
} while (elem.DataType == DATATYPE_SZARRAY || elem.DataType == DATATYPE_BYREF);
return false;
}

// If this is a closed‐generic instantiation header, peel off the wrapper
if (elem.DataType == DATATYPE_GENERICINST)
if (elem.DataType == DATATYPE_CLASS || elem.DataType == DATATYPE_VALUETYPE)
{
// 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;
}
// If it's a class or value type, resolve the type
data = elem.Class.data;
assembly = g_CLR_RT_TypeSystem.m_assemblies[elem.Class.Assembly() - 1];
target = assembly->GetTypeDef(elem.Class.Type());

#if defined(NANOCLR_INSTANCE_NAMES)
name = assembly->GetString(target->name);
#endif

return true;
}

// walk forward until a VAR (type‐generic) or MVAR (method‐generic) is hit
Expand Down Expand Up @@ -1045,8 +1034,6 @@ bool CLR_RT_TypeDef_Instance::ResolveToken(
}
else
{
// elem.DataType == DATATYPE_MVAR

// Use the *caller's* bound genericType (Stack<Int32>, etc.)
if (caller == nullptr || caller->genericType == nullptr)
{
Expand All @@ -1060,6 +1047,7 @@ bool CLR_RT_TypeDef_Instance::ResolveToken(
data = gp.classTypeDef.data;
assembly = g_CLR_RT_TypeSystem.m_assemblies[gp.classTypeDef.Assembly() - 1];
target = assembly->GetTypeDef(gp.classTypeDef.Type());

return true;
}
}
Expand All @@ -1081,6 +1069,109 @@ bool CLR_RT_TypeDef_Instance::ResolveToken(
return false;
}

bool CLR_RT_TypeDef_Instance::ResolveNullableType(
CLR_UINT32 tk,
CLR_RT_Assembly *assm,
const CLR_RT_MethodDef_Instance *caller)
{
NATIVE_PROFILE_CLR_CORE();

if (assm)
{
CLR_UINT32 index = CLR_DataFromTk(tk);

if (CLR_TypeFromTk(tk) != TBL_TypeSpec)
{
// not a TypeSpec, so return false
ClearInstance();
return false;
}

// Grab the raw signature for the IL token (e.g. !T[], List`1<T>, etc.)
const CLR_RECORD_TYPESPEC *ts = assm->GetTypeSpec(index);
CLR_RT_SignatureParser parser;
parser.Initialize_TypeSpec(assm, ts);

CLR_RT_SignatureParser::Element elem;
if (FAILED(parser.Advance(elem)))
{
return false;
}

if (elem.DataType != DATATYPE_VALUETYPE)
{
// If it's not a value type, we can't resolve it as a nullable type
ClearInstance();
return false;
}

// move to the next element in the signature
if (FAILED(parser.Advance(elem)))
{
return false;
}

// If it's a type‐generic slot (!T), resolve against the caller's closed generic
if (elem.DataType == DATATYPE_VAR)
{
int pos = elem.GenericParamPosition;

// Use the *caller's* bound genericType (Stack<Int32>, etc.)
if (caller == nullptr || caller->genericType == nullptr)
{
return false;
}

auto &tsi = *caller->genericType;
CLR_UINT32 closedTsRow = tsi.TypeSpec();

CLR_RT_TypeDef_Index realTypeDef;
NanoCLRDataType realDataType;

// Only call this once to map (e.g. !T→Int32)
caller->assembly->FindGenericParamAtTypeSpec(closedTsRow, (CLR_UINT32)pos, realTypeDef, realDataType);

// populate this instance
data = realTypeDef.data;
assembly = g_CLR_RT_TypeSystem.m_assemblies[realTypeDef.Assembly() - 1];
target = assembly->GetTypeDef(realTypeDef.Type());

return true;
}
else if (elem.DataType == DATATYPE_MVAR)
{
// Use the *caller's* bound genericType (Stack<Int32>, etc.)
if (caller == nullptr || caller->genericType == nullptr)
{
return false;
}

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;
}
else
{
// If it's a class or value type, resolve the type
data = elem.Class.data;
assembly = g_CLR_RT_TypeSystem.m_assemblies[elem.Class.Assembly() - 1];
target = assembly->GetTypeDef(elem.Class.Type());

return true;
}
}

ClearInstance();

return false;
}

//--//

bool CLR_RT_TypeDef_Instance::SwitchToParent()
Expand Down Expand Up @@ -4293,6 +4384,7 @@ static const TypeIndexLookup c_TypeIndexLookup[] = {
TIL("System", "Object", Object),
TIL("System", "ValueType", ValueType),
TIL("System", "Enum", Enum),
TIL("System", "Nullable`1", Nullable),

TIL("System", "AppDomainUnloadedException", AppDomainUnloadedException),
TIL("System", "ArgumentNullException", ArgumentNullException),
Expand Down
2 changes: 2 additions & 0 deletions src/CLR/Include/nanoCLR_Runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -1691,6 +1691,7 @@ struct CLR_RT_WellKnownTypes
CLR_RT_TypeDef_Index Object;
CLR_RT_TypeDef_Index ValueType;
CLR_RT_TypeDef_Index Enum;
CLR_RT_TypeDef_Index Nullable;

CLR_RT_TypeDef_Index AppDomainUnloadedException;
CLR_RT_TypeDef_Index ArgumentNullException;
Expand Down Expand Up @@ -2114,6 +2115,7 @@ struct CLR_RT_TypeDef_Instance : public CLR_RT_TypeDef_Index
void ClearInstance();

bool ResolveToken(CLR_UINT32 tk, CLR_RT_Assembly *assm, const CLR_RT_MethodDef_Instance *caller = nullptr);
bool ResolveNullableType(CLR_UINT32 tk, CLR_RT_Assembly *assm, const CLR_RT_MethodDef_Instance *caller = nullptr);

//--//

Expand Down