diff --git a/BeefLibs/corlib/src/Reflection/Convert.bf b/BeefLibs/corlib/src/Reflection/Convert.bf index 8802c824..3418eae7 100644 --- a/BeefLibs/corlib/src/Reflection/Convert.bf +++ b/BeefLibs/corlib/src/Reflection/Convert.bf @@ -32,6 +32,26 @@ namespace System.Reflection } } + public static Result ToInt64(Variant variant) + { + var variant; + var dataPtr = variant.DataPtr; + switch (variant.VariantType.[Friend]mTypeCode) + { + case .Int8: return (.)*(int8*)dataPtr; + case .Int16: return (.)*(int16*)dataPtr; + case .Int32: return (.)*(int32*)dataPtr; + case .Int64: return (.)*(int64*)dataPtr; + case .UInt8, .Char8: return (.)*(uint8*)dataPtr; + case .UInt16, .Char16: return (.)*(uint16*)dataPtr; + case .UInt32, .Char32: return (.)*(uint32*)dataPtr; + case .UInt64: return (.)*(uint64*)dataPtr; + case .Int: return (.)*(int*)dataPtr; + case .UInt: return (.)*(uint*)dataPtr; + default: return .Err; + } + } + public static bool IntCanFit(int64 val, Type type) { switch (type.[Friend]mTypeCode) @@ -55,6 +75,53 @@ namespace System.Reflection } } + public static Result ConvertTo(Variant variant, Type type) + { + var variant; + + if (variant.VariantType == type) + { + return Variant.CreateFromVariant(variant); + } + + var varType = variant.VariantType; + void* dataPtr = variant.DataPtr; + + if (varType.IsPrimitive) + { + if (varType.IsInteger) + { + int64 intVal = ToInt64(variant); + switch (type.[Friend]mTypeCode) + { + case .Float: + float val = (.)intVal; + return Variant.Create(type, &val); + case .Double: + double val = (.)intVal; + return Variant.Create(type, &val); + default: + } + + if (IntCanFit(intVal, type)) + { + return Variant.Create(type, &intVal); + } + } + else if (varType.IsFloatingPoint) + { + if ((type.[Friend]mTypeCode == .Double) && + (varType.[Friend]mTypeCode == .Float)) + { + double val = (.)*(float*)dataPtr; + return Variant.Create(type, &val); + } + } + } + + return .Err; + } + public static Result ConvertTo(Object obj, Type type) { if (obj.GetType() == type) @@ -63,6 +130,7 @@ namespace System.Reflection } var (objType, dataPtr) = GetTypeAndPointer(obj); + if (objType.IsPrimitive) { if (objType.IsInteger) diff --git a/BeefLibs/corlib/src/Reflection/MethodInfo.bf b/BeefLibs/corlib/src/Reflection/MethodInfo.bf index 1befd24e..f549bbd7 100644 --- a/BeefLibs/corlib/src/Reflection/MethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/MethodInfo.bf @@ -53,6 +53,259 @@ namespace System.Reflection case FFIError; } + public Result Invoke(Variant target, params Span args) + { + var retType = Type.[Friend]GetType(mMethodData.mReturnType); + + FFIABI abi = .Default; +#if BF_PLATFORM_WINDOWS && BF_32_BIT + if (mMethodData.mFlags.HasFlag(.ThisCall)) + abi = .ThisCall; + else if (!mMethodData.mFlags.HasFlag(.Static)) + abi = .StdCall; +#endif + + List ffiParamList = scope .(16); + List ffiArgList = scope .(16); + List tempVariants = scope .(4); + + var target; + + mixin GetFFIType(Type type) + { + int wantSize = 0; + FFIType* ffiType = FFIType.Get(type, null, &wantSize); + if ((ffiType == null) && (wantSize != 0)) + { + void* allocBytes = scope:mixin uint8[wantSize]*; + ffiType = FFIType.Get(type, allocBytes, &wantSize); + } + + ffiType + } + + void SplatArg(TypeInstance type, void* ptr) + { + if (type.BaseType != null) + SplatArg(type.BaseType, ptr); + + bool isEnum = type.IsEnum; + for (int fieldIdx < type.[Friend]mFieldDataCount) + { + let fieldData = ref type.[Friend]mFieldDataPtr[fieldIdx]; + let fieldType = Type.[Friend]GetType(fieldData.mFieldTypeId); + if (fieldData.mFlags.HasFlag(.Static)) + { + if (isEnum) + break; // Already got payload and discriminator + continue; + } + if (fieldType.[Friend]mSize == 0) + continue; + + if (fieldType.IsStruct) + { + SplatArg((TypeInstance)fieldType, (uint8*)ptr + fieldData.mDataOffset); + } + else + { + ffiParamList.Add(FFIType.Get(fieldType, null, null)); + ffiArgList.Add((uint8*)ptr + fieldData.mDataOffset); + } + } + } + + mixin AddArg(int argIdx, var arg, void* argPtr, Type paramType, bool splat) + { + var argType = arg.VariantType; + void* dataPtr = arg.DataPtr; + bool isPtrToPtr = false; + bool isValid = true; + + if (paramType.IsValueType) + { + if (argType.IsPointer) + { + if (!paramType.IsPointer) + { + isPtrToPtr = true; + argType = argType.UnderlyingType; + } + /*else + { + dataPtr = *(void**)dataPtr; + }*/ + } + + if (!argType.IsSubtypeOf(paramType)) + { + if (Convert.ConvertTo(arg, paramType) case .Ok(var variant)) + { + tempVariants.Add(variant); + dataPtr = variant.GetValueData(); + } + else + isValid = false; + } + } + else + { + if (!argType.IsSubtypeOf(paramType)) + isValid = false; + } + + if (!isValid) + { + if (argIdx == -1) + return .Err(.InvalidTarget); + else + return .Err(.InvalidArgument((.)argIdx)); + } + + if (paramType.IsStruct) + { + TypeInstance paramTypeInst = (TypeInstance)paramType; + + if (paramType.Size == 0) + { + // Do nothing + } + else if (splat) + { + if (isPtrToPtr) + dataPtr = *((void**)dataPtr); + + if (paramTypeInst.[Friend]mFieldDataCount > 0) + { + SplatArg(paramTypeInst, dataPtr); + } + else + { + let splatData = (TypeInstance.FieldSplatData*)paramTypeInst.[Friend]mFieldDataPtr; + for (int splatIdx < 3) + { + let splatTypeId = splatData.mSplatTypes[splatIdx]; + if (splatTypeId == 0) + break; + + let splatType = Type.[Friend]GetType(splatTypeId); + ffiParamList.Add(GetFFIType!:mixin(splatType)); + ffiArgList.Add((uint8*)dataPtr + splatData.mSplatOffsets[splatIdx]); + } + + } + } + else + { + if (!isPtrToPtr) + { + int* stackDataPtr = scope:mixin int(); + *stackDataPtr = (int)dataPtr; + ffiArgList.Add(stackDataPtr); + } + else + ffiArgList.Add(dataPtr); + // Pass by ref + ffiParamList.Add(&FFIType.Pointer); + } + } + else if (paramType.IsValueType) + { + ffiParamList.Add(GetFFIType!:mixin(paramType)); + ffiArgList.Add(dataPtr); + } + else + { + ffiParamList.Add(&FFIType.Pointer); + ffiArgList.Add(argPtr); + } + } + + if (mMethodData.mFlags.HasFlag(.Static)) + { + if (target.HasValue) + return .Err(.TargetNotExpected); + } + else + { + if (!target.HasValue) + return .Err(.TargetExpected); + + bool splatThis = mTypeInstance.IsSplattable && !mMethodData.mFlags.HasFlag(.Mutating); + AddArg!::(-1, ref target, &target, mTypeInstance, splatThis); + } + + if (args.Length != mMethodData.mParamCount) + return .Err(.ParamCountMismatch); + + Variant retVal; + void* variantData = Variant.Alloc(retType, out retVal); + void* retData = variantData; + + // Struct return? Manually add it as an arg after 'this'. Revisit this - this is architecture-dependent. + int unusedRetVal; + FFIType* ffiRetType = null; + if (retType.IsStruct) + { + ffiRetType = &FFIType.Void; + ffiParamList.Add(&FFIType.Pointer); + ffiArgList.Add(&variantData); + retData = &unusedRetVal; + } + else + ffiRetType = GetFFIType!::(retType); + + for (var arg in ref args) + { + let paramData = ref mMethodData.mParamData[@arg.Index]; + let argType = Type.[Friend]GetType(paramData.mType); + AddArg!::(@arg.Index, ref arg, &arg, argType, paramData.mParamFlags.HasFlag(.Splat)); + } + + FFICaller caller = .(); + if (ffiParamList.Count > 0) + { + if (caller.Prep(abi, (.)ffiParamList.Count, ffiRetType, &ffiParamList[0]) case .Err) + return .Err(.FFIError); + } + else + { + if (caller.Prep(abi, 0, ffiRetType, null) case .Err) + return .Err(.FFIError); + } + + void* funcPtr = mMethodData.mFuncPtr; + if (mMethodData.mFlags.HasFlag(.Virtual)) + { + Object objTarget = target.Get(); + +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + void* classVData = (void*)(objTarget.[Friend]mClassVData & ~0xFF); +#else + void* classVData = objTarget.[Friend]mClassVData; +#endif + if (mMethodData.mVirtualIdx >= 0x100000) + { + void* extAddr = (void*)*((int*)classVData + (mMethodData.mVirtualIdx>>20 - 1)); + funcPtr = (void*)*((int*)extAddr + (mMethodData.mVirtualIdx & 0xFFFFF)); + } + else + { + funcPtr = (void*)*(int*)((uint8*)classVData + mMethodData.mVirtualIdx); + } + } + + if (ffiArgList.Count > 0) + caller.Call(funcPtr, retData, &ffiArgList[0]); + else + caller.Call(funcPtr, retData, null); + + for (var variant in ref tempVariants) + variant.Dispose(); + + return retVal; + } + public Result Invoke(Object target, params Object[] args) { var retType = Type.[Friend]GetType(mMethodData.mReturnType); diff --git a/BeefLibs/corlib/src/Variant.bf b/BeefLibs/corlib/src/Variant.bf index 4963c9f2..fdefb44e 100644 --- a/BeefLibs/corlib/src/Variant.bf +++ b/BeefLibs/corlib/src/Variant.bf @@ -49,6 +49,26 @@ namespace System } } + public void* DataPtr + { + get mut + { + if (IsObject) + { + if (mStructType == 2) + return null; + Object obj = Internal.UnsafeCastToObject((void*)mData); + return (uint8*)Internal.UnsafeCastToPtr(obj) + obj.GetType().[Friend]mMemberDataOffset; + } + + var type = VariantType; + if (type.Size <= sizeof(int)) + return (void*)&mData; + else + return (void*)mData; + } + } + protected override void GCMarkMembers() { if ((mStructType == 1) || (mStructType == 0)) @@ -222,8 +242,9 @@ namespace System if (IsObject) { if (mStructType == 2) - *((Object*)dest) =null; - *((Object*)dest) = Internal.UnsafeCastToObject((void*)mData); + *((Object*)dest) = null; + else + *((Object*)dest) = Internal.UnsafeCastToObject((void*)mData); return; } @@ -282,5 +303,19 @@ namespace System { v1.Get() == v2.Get() } + + public static Result CreateFromVariant(Variant varFrom, bool reference = true) + { + Variant varTo = varFrom; + if (varTo.mStructType == 1) + varTo.mStructType = 0; + return varTo; + } + + /*public static Result CreateFromObject(Object objectFrom, bool reference = true) + { + Type objType = objectFrom.[Friend]RawGetType(); + + }*/ } } \ No newline at end of file diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 005be8d0..3dfb8f58 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -1541,10 +1541,9 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst goto NoMatch; } - if (paramsArrayType->IsArray()) - { - auto arrayType = (BfArrayType*)paramsArrayType; - paramsElementType = arrayType->mGenericTypeInfo->mTypeGenericArguments[0]; + if ((paramsArrayType->IsArray()) || (paramsArrayType->IsInstanceOf(mModule->mCompiler->mSpanTypeDef))) + { + paramsElementType = paramsArrayType->GetUnderlyingType(); while (argIdx < (int)mArguments.size()) { diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 33f061ca..59121ba4 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -8930,7 +8930,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp // * <-> Var if ((typedVal.mType->IsVar()) || (toType->IsVar())) { - return GetDefaultValue(toType); + return mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(toType)); } // Generic param -> * diff --git a/IDEHelper/DbgExprEvaluator.cpp b/IDEHelper/DbgExprEvaluator.cpp index f2587f35..b28c2491 100644 --- a/IDEHelper/DbgExprEvaluator.cpp +++ b/IDEHelper/DbgExprEvaluator.cpp @@ -1226,6 +1226,12 @@ void DbgExprEvaluator::BeefTypeToString(const DbgTypedValue& val, String& outStr _TypeCode mTypeCode; uint8 mAlign; }; + + struct _PointerType : _Type + { + _TypeCode mElementType; + }; + struct _String { @@ -1343,6 +1349,14 @@ void DbgExprEvaluator::BeefTypeToString(const DbgTypedValue& val, String& outStr else { BfTypeCode typeCode = (BfTypeCode)mDebugger->ReadMemory(typeAddr + (int)offsetof(_Type, mTypeCode)); + if (typeCode == BfTypeCode_Pointer) + { + _TypeId elementType = mDebugger->ReadMemory<_TypeId>(typeAddr + offsetof(_PointerType, mElementType)); + BeefTypeToString(GetBeefTypeById(elementType), outStr); + outStr += "*"; + return; + } + switch (typeCode) { case BfTypeCode_None: outStr += "void"; break; diff --git a/IDEHelper/Tests/src/Reflection.bf b/IDEHelper/Tests/src/Reflection.bf index 0dd2f5a7..0ea437f3 100644 --- a/IDEHelper/Tests/src/Reflection.bf +++ b/IDEHelper/Tests/src/Reflection.bf @@ -141,6 +141,10 @@ namespace Tests Test.Assert(result.Get() == 123); result.Dispose(); + result = methodInfo.Invoke(.(), .Create(100), .Create((int32)20), .Create(3.0f)).Get(); + Test.Assert(result.Get() == 123); + result.Dispose(); + let attrC = methodInfo.GetCustomAttribute().Get(); Test.Assert(attrC.mA == 71); Test.Assert(attrC.mB == 72); @@ -149,6 +153,10 @@ namespace Tests var result = methodInfo.Invoke(ca, 100, (int32)20, 3.0f).Get(); Test.Assert(result.Get() == 123); result.Dispose(); + + result = methodInfo.Invoke(.Create(ca), .Create(100), .Create((int32)20), .Create(3.0f)).Get(); + Test.Assert(result.Get() == 123); + result.Dispose(); case 2: Test.Assert(methodInfo.Name == "GetA"); var result = methodInfo.Invoke(ca, 123).Get(); @@ -157,6 +165,9 @@ namespace Tests result = methodInfo.Invoke(ca2, 123).Get(); Test.Assert(result.Get() == 2123); result.Dispose(); + result = methodInfo.Invoke(.Create(ca2), .Create(123)).Get(); + Test.Assert(result.Get() == 2123); + result.Dispose(); case 3: Test.Assert(methodInfo.Name == "__BfCtor"); Test.Assert(methodInfo.IsConstructor);