From a34e5a737d08064217e7e603e817e195b07da4be Mon Sep 17 00:00:00 2001 From: Brian Fiete Date: Tue, 10 Oct 2023 10:36:04 -0700 Subject: [PATCH] Improved boxed value support in attribute data --- .../corlib/src/Reflection/AttributeInfo.bf | 39 +++++++++++- BeefLibs/corlib/src/Reflection/MethodInfo.bf | 2 +- BeefLibs/corlib/src/Type.bf | 2 +- BeefLibs/corlib/src/Variant.bf | 16 +++++ IDEHelper/Compiler/BfConstResolver.cpp | 4 +- IDEHelper/Compiler/BfIRBuilder.cpp | 12 +++- IDEHelper/Compiler/BfModule.cpp | 52 +++++++++++++++- IDEHelper/Compiler/CeMachine.cpp | 41 +++++++++++++ IDEHelper/Tests/src/Interop.bf | 2 + IDEHelper/Tests/src/Reflection.bf | 61 +++++++++++++++++++ 10 files changed, 222 insertions(+), 9 deletions(-) diff --git a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf index 2bdd4254..7c66bc4f 100644 --- a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf +++ b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf @@ -1,8 +1,13 @@ using System.Collections; +using System.Threading; + namespace System.Reflection { class AttributeInfo { + static Monitor sBoxedMonitor = new .() ~ delete _; + static Dictionary sBoxedValues = new .() ~ DeleteDictionaryAndValues!(_); + static mixin Decode(void* data) { *((*(T2**)&data)++) @@ -99,7 +104,23 @@ namespace System.Reflection case (TypeCode)typeof(TypeCode).MaxValue + 9: //BfConstType_TypeOf let argTypeId = Decode!(data); args[argIdx] = Type.[Friend]GetType((.)argTypeId); - case (TypeCode)255: + case (TypeCode)typeof(TypeCode).MaxValue + 18: // BfConstType_Box + let boxedTypeId = Decode!(data); + var boxedType = Type.[Friend]GetType_(boxedTypeId); + int dataSize = boxedType.InstanceSize - boxedType.[Friend]mMemberDataOffset; + using (sBoxedMonitor.Enter()) + { + if (sBoxedValues.TryAdd(data, var keyPtr, var valuePtr)) + { + Object boxedValue = boxedType.CreateObject().Value; + void* boxedDataPtr = (uint8*)Internal.UnsafeCastToPtr(boxedValue) + boxedType.[Friend]mMemberDataOffset; + Internal.MemCpy(boxedDataPtr, data, dataSize); + *valuePtr = boxedValue; + } + args[argIdx] = *valuePtr; + } + data = (uint8*)data + dataSize; + case (TypeCode)255: // String let stringId = Decode!(data); String str = String.[Friend]sIdStringLiterals[stringId]; args[argIdx] = str; @@ -195,6 +216,22 @@ namespace System.Reflection case (TypeCode)typeof(TypeCode).MaxValue + 9: //BfConstType_TypeOf let argTypeId = AttributeInfo.Decode!(mData); args[argIdx] = Variant.Create(Type.[Friend]GetType((.)argTypeId)); + case (TypeCode)typeof(TypeCode).MaxValue + 18: // BfConstType_Box + let boxedTypeId = AttributeInfo.Decode!(mData); + var boxedType = Type.[Friend]GetType_(boxedTypeId); + int dataSize = boxedType.InstanceSize - boxedType.[Friend]mMemberDataOffset; + using (sBoxedMonitor.Enter()) + { + if (sBoxedValues.TryAdd(mData, var keyPtr, var valuePtr)) + { + Object boxedValue = boxedType.CreateObject().Value; + void* boxedDataPtr = (uint8*)Internal.UnsafeCastToPtr(boxedValue) + boxedType.[Friend]mMemberDataOffset; + Internal.MemCpy(boxedDataPtr, mData, dataSize); + *valuePtr = boxedValue; + } + args[argIdx] = Variant.Create(*valuePtr); + } + mData = (uint8*)mData + dataSize; case (TypeCode)255: let stringId = AttributeInfo.Decode!(mData); String str = String.[Friend]sIdStringLiterals[stringId]; diff --git a/BeefLibs/corlib/src/Reflection/MethodInfo.bf b/BeefLibs/corlib/src/Reflection/MethodInfo.bf index 8ec9dd4f..b4ec4d38 100644 --- a/BeefLibs/corlib/src/Reflection/MethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/MethodInfo.bf @@ -312,7 +312,7 @@ namespace System.Reflection mixin AddArg(int argIdx, var arg, void* argPtr, Type paramType, bool splat) { - var argType = arg.VariantType; + var argType = arg.RawVariantType; void* dataPtr = arg.DataPtr; bool isPtrToPtr = false; bool isValid = true; diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index 2abc8929..02109e92 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -1003,7 +1003,7 @@ namespace System.Reflection public override bool IsSubtypeOf(Type checkBaseType) { TypeInstance curType = this; - if (curType.IsBoxed) + if ((curType.IsBoxed) && (checkBaseType.IsValueType)) { curType = curType.UnderlyingType as TypeInstance; if (curType == null) diff --git a/BeefLibs/corlib/src/Variant.bf b/BeefLibs/corlib/src/Variant.bf index f130e9ed..3289f1f9 100644 --- a/BeefLibs/corlib/src/Variant.bf +++ b/BeefLibs/corlib/src/Variant.bf @@ -71,6 +71,22 @@ namespace System } } + public Type RawVariantType + { + get + { + if (mStructType == 2) + { + return (Type)Internal.UnsafeCastToObject((void*)mData); + } + if (mStructType <= 1) + { + return Internal.UnsafeCastToObject((void*)mData).[Friend]RawGetType(); + } + return (Type)Internal.UnsafeCastToObject((void*)(mStructType & ~3)); + } + } + public bool HasValue { get diff --git a/IDEHelper/Compiler/BfConstResolver.cpp b/IDEHelper/Compiler/BfConstResolver.cpp index 0a92b9aa..291a9ffd 100644 --- a/IDEHelper/Compiler/BfConstResolver.cpp +++ b/IDEHelper/Compiler/BfConstResolver.cpp @@ -167,7 +167,7 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo } else { - mResult = mModule->Cast(expr, mResult, wantType, (BfCastFlags)(BfCastFlags_NoConversionOperator | (explicitCast ? BfCastFlags_Explicit : BfCastFlags_None))); + mResult = mModule->Cast(expr, mResult, wantType, (BfCastFlags)(BfCastFlags_WantsConst | BfCastFlags_NoConversionOperator | (explicitCast ? BfCastFlags_Explicit : BfCastFlags_None))); } } @@ -393,7 +393,7 @@ bool BfConstResolver::PrepareMethodArguments(BfAstNode* targetSrc, BfMethodMatch if (argExpr != NULL) { - argValue = mModule->Cast(argExpr, argValue, wantType); + argValue = mModule->Cast(argExpr, argValue, wantType, (BfCastFlags)(BfCastFlags_WantsConst | BfCastFlags_NoConversionOperator)); if (!argValue) return false; } diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index 555d6c30..fae50f2d 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -963,7 +963,17 @@ BfIRValue BfIRConstHolder::CreateConst(BfConstant* fromConst, BfIRConstHolder* f ptrToInt->mToType = fromPtrToInt->mToType; copiedConst = (BfConstant*)ptrToInt; } - + else if (fromConst->mConstType == BfConstType_Box) + { + auto fromBox = (BfConstantBox*)fromConst; + auto fromTarget = fromHolder->GetConstantById(fromBox->mTarget); + auto copiedTarget = CreateConst(fromTarget, fromHolder); + auto box = mTempAlloc.Alloc(); + box->mConstType = BfConstType_Box; + box->mTarget = copiedTarget.mId; + box->mToType = fromBox->mToType; + copiedConst = (BfConstant*)box; + } else { BF_FATAL("not handled"); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 9d3c038c..3e6890df 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -5729,6 +5729,49 @@ void BfModule::EncodeAttributeData(BfTypeInstance* typeInstance, BfType* argType for (int i = 0; i < argType->mSize; i++) data.Add(0); } + else if (constant->mConstType == BfConstType_Box) + { + auto box = (BfConstantBox*)constant; + PUSH_INT32(box->mToType.mId); + + BfType* resultType = NULL; + if (box->mToType.mKind == BfIRTypeData::TypeKind_TypeId) + resultType = mContext->FindTypeById(box->mToType.mId); + + if ((resultType != NULL) && (resultType->IsBoxed())) + { + auto boxedType = (BfBoxedType*)resultType; + + int dataOffset = 0; + if (!boxedType->mFieldInstances.IsEmpty()) + dataOffset = BF_MAX(boxedType->mFieldInstances.back().mDataOffset, 0); + + int dataSize = boxedType->mInstSize - dataOffset; + for (int i = 0; i < dataSize; i++) + data.Add(0); + + typeInstance->mConstHolder->WriteConstant(BfIRValue(BfIRValueFlags_Const, box->mTarget), &data[data.mSize - dataSize], boxedType->GetUnderlyingType()); + return; + } + + + //int dataOffset = + + //mBfIRBuilder->WriteConstant() + +// BfType* resultType = NULL; +// if (box->mToType.mKind == BfIRTypeData::TypeKind_TypeId) +// resultType = mContext->FindTypeById(box->mToType.mId); +// +// if ((resultType != NULL) && (resultType->IsBoxed())) +// { +// auto boxedType = (BfBoxedType*)resultType; +// EncodeAttributeData(typeInstance, boxedType->GetUnderlyingType(), BfIRValue(BfIRValueFlags_Const, box->mTarget), data, usedStringIdMap); +// return; +// } + + Fail(StrFormat("Unhandled constant box in '%s'", TypeToString(typeInstance).c_str())); + } // else if (constant->mConstType == BfConstType_Agg) // { @@ -5995,6 +6038,8 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary& usedStrin { BF_ASSERT((type->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted) || mIsComptimeModule); typeCode = typeInstance->mTypeDef->mTypeCode; + if (typeInstance->IsBoxed()) + typeCode = BfTypeCode_Object; } else if (type->IsPrimitiveType()) { @@ -11639,7 +11684,7 @@ void BfModule::CurrentAddToConstHolder(BfIRValue& irVal) { auto bitcast = (BfConstantBitCast*)constant; BfIRValue newVal; - if (bitcast->mTarget) + if (constant->mConstType == BfConstType_BitCast) { newVal = BfIRValue(BfIRValueFlags_Const, bitcast->mTarget); CurrentAddToConstHolder(newVal); @@ -11782,12 +11827,13 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con { if (!allowUnactualized) { - if ((wantType->IsInstanceOf(mCompiler->mStringTypeDef)) || + if ((wantType == NULL) || + (wantType->IsInstanceOf(mCompiler->mStringTypeDef)) || ((wantType->IsPointer()) && (wantType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8)))) { const StringImpl& str = mContext->mStringObjectIdMap[constant->mInt32].mString; BfIRValue stringObjConst = GetStringObjectValue(str, false, true); - if (wantType->IsPointer()) + if ((wantType != NULL) && (wantType->IsPointer())) return GetStringCharPtr(stringObjConst, true); return stringObjConst; } diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 81166c8b..6173cddf 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -4817,6 +4817,47 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType return module->CreateTypeDataRef(module->mContext->mTypes[typeId]); } + if (typeInst == module->mContext->mBfObjectType) + { + // Allow boxing + CE_CREATECONST_CHECKPTR(instData, ceModule->mSystem->mPtrSize); + addr_ce typeId = *(int*)(instData); + + BfType* type = GetBfType(typeId); + + if (type->IsInstanceOf(mCeMachine->mCompiler->mStringTypeDef)) + { + return CreateConstant(module, ptr, type, outType); + } + else if (type->IsBoxed()) + { + auto underlyingType = type->GetUnderlyingType(); + module->PopulateType(type); + + auto boxedType = (BfBoxedType*)type; + int dataOffset = boxedType->mFieldInstances.back().mDataOffset; + + auto origValue = CreateConstant(module, ptr + dataOffset, underlyingType, outType); + if (origValue) + { + if (outType != NULL) + *outType = typeInst; + return irBuilder->CreateConstBox(origValue, irBuilder->MapType(boxedType)); + } + } + +// else if (type->IsValueType()) +// { +// auto origValue = CreateConstant(module, ptr, type, outType); +// if (origValue) +// { +// auto boxedType = module->CreateBoxedType(type); +// irBuilder->PopulateType(boxedType); +// return irBuilder->CreateConstBox(origValue, irBuilder->MapType(boxedType)); +// } +// } + } + if (typeInst->IsObjectOrInterface()) { Fail(StrFormat("Reference type '%s' return value not allowed", module->TypeToString(typeInst).c_str())); diff --git a/IDEHelper/Tests/src/Interop.bf b/IDEHelper/Tests/src/Interop.bf index 5e243f12..cf2b60c0 100644 --- a/IDEHelper/Tests/src/Interop.bf +++ b/IDEHelper/Tests/src/Interop.bf @@ -1,3 +1,5 @@ +#pragma warning disable 168 + using System; namespace Tests diff --git a/IDEHelper/Tests/src/Reflection.bf b/IDEHelper/Tests/src/Reflection.bf index f7bb4ad6..ddad562a 100644 --- a/IDEHelper/Tests/src/Reflection.bf +++ b/IDEHelper/Tests/src/Reflection.bf @@ -213,6 +213,43 @@ namespace Tests } } + [AttributeUsage(.Field, .ReflectAttribute)] + struct OptionAttribute : Attribute, IOnFieldInit + { + public Object mDefaultValue; + public String mShortName; + public String mLongName; + + public this(Object defaultValue, String shortName, String longName) + { + mDefaultValue = defaultValue; + mShortName = shortName; + mLongName = longName; + } + + public void OnFieldInit(FieldInfo fieldInfo, Self* prev) + { + if (mDefaultValue != null) + { + Type defaultType = mDefaultValue.GetType(); + if (defaultType.IsBoxed) + defaultType = defaultType.UnderlyingType; + if ((defaultType == fieldInfo.FieldType) || (defaultType.UnderlyingType == fieldInfo.FieldType)) + return; + Runtime.FatalError(scope $"Default value type mismatch. Expected {fieldInfo.FieldType.GetFullName(.. scope .())} but got {defaultType.GetFullName(.. scope .())}"); + } + } + } + + class ClassF + { + [Option("123", "Short", "Long")] + String mStr; + + [Option((int32)123, "Short", "Long")] + int32 mInt; + } + [Test] static void TestTypes() { @@ -372,6 +409,30 @@ namespace Tests methodIdx++; } + + for (var fieldInfo in typeof(ClassF).GetFields()) + { + int idx = @fieldInfo.Index; + var oa = fieldInfo.GetCustomAttribute().Value; + + void TestOA() + { + switch (idx) + { + case 0: + Test.Assert(oa.mDefaultValue == "123"); + case 1: + Test.Assert(oa.mDefaultValue == (int32)123); + } + } + + TestOA(); + for (var option in fieldInfo.GetCustomAttributes()) + { + oa = option.Get(); + TestOA(); + } + } } [Test]