mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 03:28:20 +02:00
Added Variant support to reflected method invocation
This commit is contained in:
parent
5bdaeadc25
commit
2eb7ce3e1a
7 changed files with 387 additions and 7 deletions
|
@ -32,6 +32,26 @@ namespace System.Reflection
|
|||
}
|
||||
}
|
||||
|
||||
public static Result<int64> 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<Variant> 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<Variant> 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)
|
||||
|
|
|
@ -53,6 +53,259 @@ namespace System.Reflection
|
|||
case FFIError;
|
||||
}
|
||||
|
||||
public Result<Variant, CallError> Invoke(Variant target, params Span<Variant> 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<FFIType*> ffiParamList = scope .(16);
|
||||
List<void*> ffiArgList = scope .(16);
|
||||
List<Variant> 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<Object>();
|
||||
|
||||
#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<Variant, CallError> Invoke(Object target, params Object[] args)
|
||||
{
|
||||
var retType = Type.[Friend]GetType(mMethodData.mReturnType);
|
||||
|
|
|
@ -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<T>() == v2.Get<T>()
|
||||
}
|
||||
|
||||
public static Result<Variant> CreateFromVariant(Variant varFrom, bool reference = true)
|
||||
{
|
||||
Variant varTo = varFrom;
|
||||
if (varTo.mStructType == 1)
|
||||
varTo.mStructType = 0;
|
||||
return varTo;
|
||||
}
|
||||
|
||||
/*public static Result<Variant> CreateFromObject(Object objectFrom, bool reference = true)
|
||||
{
|
||||
Type objType = objectFrom.[Friend]RawGetType();
|
||||
|
||||
}*/
|
||||
}
|
||||
}
|
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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 -> *
|
||||
|
|
|
@ -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<uint8>(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;
|
||||
|
|
|
@ -141,6 +141,10 @@ namespace Tests
|
|||
Test.Assert(result.Get<float>() == 123);
|
||||
result.Dispose();
|
||||
|
||||
result = methodInfo.Invoke(.(), .Create(100), .Create((int32)20), .Create(3.0f)).Get();
|
||||
Test.Assert(result.Get<float>() == 123);
|
||||
result.Dispose();
|
||||
|
||||
let attrC = methodInfo.GetCustomAttribute<AttrCAttribute>().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<float>() == 123);
|
||||
result.Dispose();
|
||||
|
||||
result = methodInfo.Invoke(.Create(ca), .Create(100), .Create((int32)20), .Create(3.0f)).Get();
|
||||
Test.Assert(result.Get<float>() == 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<int>() == 2123);
|
||||
result.Dispose();
|
||||
result = methodInfo.Invoke(.Create(ca2), .Create(123)).Get();
|
||||
Test.Assert(result.Get<int>() == 2123);
|
||||
result.Dispose();
|
||||
case 3:
|
||||
Test.Assert(methodInfo.Name == "__BfCtor");
|
||||
Test.Assert(methodInfo.IsConstructor);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue