1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-15 06:44:10 +02:00
Beef/BeefLibs/corlib/src/Reflection/MethodInfo.bf

808 lines
20 KiB
Beef
Raw Normal View History

2019-08-23 11:56:54 -07:00
using System;
using System.Reflection;
using System.FFI;
using System.Diagnostics;
using System.Collections;
2019-08-23 11:56:54 -07:00
namespace System.Reflection
{
[CRepr, AlwaysInclude]
public struct MethodInfo
{
TypeInstance mTypeInstance;
TypeInstance.MethodData* mMethodData;
2019-08-23 11:56:54 -07:00
public this(TypeInstance typeInstance, TypeInstance.MethodData* methodData)
{
mTypeInstance = typeInstance;
mMethodData = methodData;
}
2020-06-18 04:29:31 -07:00
public bool IsInitialized => mMethodData != null;
public StringView Name => mMethodData.[Friend]mName;
public int ParamCount => mMethodData.[Friend]mParamCount;
public bool IsConstructor => mMethodData.mName === "__BfCtor" || mMethodData.mName === "__BfStaticCtor";
public bool IsDestructor => mMethodData.mName === "__BfStaticDtor" || mMethodData.mName === "__BfStaticDtor";
2020-07-14 08:26:38 -07:00
public Type ReturnType => Type.[Friend]GetType(mMethodData.mReturnType);
2020-06-18 04:29:31 -07:00
2019-08-23 11:56:54 -07:00
public Type GetParamType(int paramIdx)
{
Debug.Assert((uint)paramIdx < (uint)mMethodData.mParamCount);
return Type.[Friend]GetType(mMethodData.mParamData[paramIdx].mType);
2019-08-23 11:56:54 -07:00
}
public StringView GetParamName(int paramIdx)
{
Debug.Assert((uint)paramIdx < (uint)mMethodData.mParamCount);
return mMethodData.mParamData[paramIdx].mName;
}
public Result<T> GetCustomAttribute<T>() where T : Attribute
2019-08-23 11:56:54 -07:00
{
return mTypeInstance.[Friend]GetCustomAttribute<T>(mMethodData.mCustomAttributesIdx);
2019-08-23 11:56:54 -07:00
}
public enum CallError
{
case None;
case TargetExpected;
case TargetNotExpected;
case InvalidTarget;
case InvalidArgument(int32 paramIdx);
case ParamCountMismatch;
case FFIError;
2019-08-23 11:56:54 -07:00
}
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)
{
2020-07-06 09:55:19 -07:00
SplatArg((TypeInstance)fieldType, (uint8*)ptr + (int)fieldData.mData);
}
else
{
ffiParamList.Add(FFIType.Get(fieldType, null, null));
2020-07-06 09:55:19 -07:00
ffiArgList.Add((uint8*)ptr + (int)fieldData.mData);
}
}
}
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;
bool added = false;
if (var refParamType = paramType as RefType)
{
if (argType.IsPointer)
{
Type elemType = argType.UnderlyingType;
if (elemType != refParamType.UnderlyingType)
isValid = false;
ffiParamList.Add(&FFIType.Pointer);
ffiArgList.Add(dataPtr);
added = true;
}
else
{
if ((Type)argType != refParamType.UnderlyingType)
isValid = false;
ffiParamList.Add(&FFIType.Pointer);
int* stackDataPtr = scope:mixin int();
*stackDataPtr = (int)dataPtr;
ffiArgList.Add(stackDataPtr);
added = true;
}
}
else 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 (added)
{
// Already handled
}
else 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);
}
}
void* funcPtr = null;
int ifaceOffset = -1;
if (mMethodData.mFlags.HasFlag(.Static))
{
if (target.HasValue)
return .Err(.TargetNotExpected);
}
else
{
if (!target.HasValue)
return .Err(.TargetExpected);
var thisType = mTypeInstance;
if (mTypeInstance.IsInterface)
{
if (target.IsObject)
{
var targetObject = target.Get<Object>();
thisType = targetObject.GetType() as TypeInstance;
if (thisType == null)
return .Err(.InvalidTarget);
}
else
{
TypeInstance.InterfaceData* interfaceData = null;
2020-09-14 11:00:34 -07:00
var variantType = target.VariantType;
if (variantType.IsPointer)
thisType = variantType.UnderlyingType as TypeInstance;
else
thisType = variantType as TypeInstance;
var checkType = thisType;
CheckLoop: while (checkType != null)
{
for (int ifaceIdx < checkType.[Friend]mInterfaceCount)
{
if (checkType.[Friend]mInterfaceDataPtr[ifaceIdx].mInterfaceType == mTypeInstance.TypeId)
{
interfaceData = &checkType.[Friend]mInterfaceDataPtr[ifaceIdx];
break CheckLoop;
}
}
checkType = checkType.BaseType;
}
if (interfaceData == null)
return .Err(.InvalidTarget);
2020-09-16 07:17:06 -07:00
int ifaceMethodIdx = interfaceData.mStartInterfaceTableIdx + mMethodData.mMethodIdx;
if (ifaceMethodIdx >= thisType.[Friend]mInterfaceMethodCount)
return .Err(.InvalidTarget);
funcPtr = *(thisType.[Friend]mInterfaceMethodTable + ifaceMethodIdx);
}
ifaceOffset = mTypeInstance.[Friend]mMemberDataOffset;
}
bool splatThis = thisType.IsSplattable && !mMethodData.mFlags.HasFlag(.Mutating);
2020-09-14 12:56:52 -07:00
#if BF_PLATFORM_WINDOWS && BF_32_BIT
if ((mTypeInstance.IsInterface) && (splatThis))
abi = .MS_CDecl;
#endif
AddArg!::(-1, ref target, &target.[Friend]mData, thisType, splatThis);
}
if (args.Length != mMethodData.mParamCount)
return .Err(.ParamCountMismatch);
var variantData = Variant.Alloc(retType, var 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.[Friend]mData, 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);
}
if (funcPtr == null)
{
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 (ifaceOffset >= 0)
{
void* ifaceVirtualTable = *(void**)((uint8*)classVData + ifaceOffset);
funcPtr = (void*)*(int*)((uint8*)ifaceVirtualTable + mMethodData.mVirtualIdx);
}
else 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;
}
2019-08-23 11:56:54 -07:00
public Result<Variant, CallError> Invoke(Object target, params Object[] args)
{
var retType = Type.[Friend]GetType(mMethodData.mReturnType);
2019-08-23 11:56:54 -07:00
FFIABI abi = .Default;
#if BF_PLATFORM_WINDOWS && BF_32_BIT
if (mMethodData.mFlags.HasFlag(.ThisCall))
abi = .ThisCall;
else if (!mMethodData.mFlags.HasFlag(.Static))
2019-08-23 11:56:54 -07:00
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)
2019-08-23 11:56:54 -07:00
{
let fieldData = ref type.[Friend]mFieldDataPtr[fieldIdx];
let fieldType = Type.[Friend]GetType(fieldData.mFieldTypeId);
2019-08-23 11:56:54 -07:00
if (fieldData.mFlags.HasFlag(.Static))
{
if (isEnum)
break; // Already got payload and discriminator
continue;
}
if (fieldType.[Friend]mSize == 0)
2019-08-23 11:56:54 -07:00
continue;
if (fieldType.IsStruct)
{
2020-07-06 09:55:19 -07:00
SplatArg((TypeInstance)fieldType, (uint8*)ptr + (int)fieldData.mData);
2019-08-23 11:56:54 -07:00
}
else
{
ffiParamList.Add(FFIType.Get(fieldType, null, null));
2020-07-06 09:55:19 -07:00
ffiArgList.Add((uint8*)ptr + (int)fieldData.mData);
2019-08-23 11:56:54 -07:00
}
}
}
mixin AddArg(int argIdx, Object arg, void* argPtr, Type paramType, bool splat)
{
bool unbox = false;
bool unboxToPtr = false;
let argType = arg.[Friend]RawGetType();
void* dataPtr = (uint8*)Internal.UnsafeCastToPtr(arg) + argType.[Friend]mMemberDataOffset;
2019-08-23 11:56:54 -07:00
bool isValid = true;
bool added = false;
if (var refParamType = paramType as RefType)
{
if (argType.IsBoxedStructPtr || argType.IsBoxedPrimitivePtr)
{
var elemType = argType.BoxedPtrType;
if (elemType != refParamType.UnderlyingType)
isValid = false;
ffiParamList.Add(&FFIType.Pointer);
ffiArgList.Add(dataPtr);
added = true;
}
else
{
var elemType = argType.UnderlyingType;
if (elemType != refParamType.UnderlyingType)
{
if (elemType.IsTypedPrimitive)
elemType = elemType.UnderlyingType;
if (elemType != refParamType.UnderlyingType)
isValid = false;
}
ffiParamList.Add(&FFIType.Pointer);
int* stackDataPtr = scope:mixin int();
*stackDataPtr = (int)dataPtr;
ffiArgList.Add(stackDataPtr);
added = true;
}
}
else if (paramType.IsValueType)
2019-08-23 11:56:54 -07:00
{
bool handled = true;
if (!argType.IsBoxed)
return .Err(.InvalidArgument((.)argIdx));
Type underlyingType = argType.UnderlyingType;
if ((paramType.IsPrimitive) && (underlyingType.IsTypedPrimitive)) // Boxed primitive?
underlyingType = underlyingType.UnderlyingType;
2019-10-09 16:16:01 -07:00
if (argType.IsBoxedStructPtr || argType.IsBoxedPrimitivePtr)
2019-08-23 11:56:54 -07:00
{
2019-10-09 16:16:01 -07:00
dataPtr = *(void**)dataPtr;
handled = true;
}
2019-08-23 11:56:54 -07:00
if (!handled)
{
if (!underlyingType.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 (added)
{
// Already handled
}
else if (paramType.IsStruct)
2019-08-23 11:56:54 -07:00
{
TypeInstance paramTypeInst = (TypeInstance)paramType;
if (paramType.Size == 0)
{
// Do nothing
}
else if (splat)
{
if (paramTypeInst.[Friend]mFieldDataCount > 0)
2019-08-23 11:56:54 -07:00
{
SplatArg(paramTypeInst, dataPtr);
}
else
{
let splatData = (TypeInstance.FieldSplatData*)paramTypeInst.[Friend]mFieldDataPtr;
2019-08-23 11:56:54 -07:00
for (int splatIdx < 3)
{
let splatTypeId = splatData.mSplatTypes[splatIdx];
if (splatTypeId == 0)
break;
let splatType = Type.[Friend]GetType(splatTypeId);
2019-08-23 11:56:54 -07:00
ffiParamList.Add(GetFFIType!:mixin(splatType));
ffiArgList.Add((uint8*)dataPtr + splatData.mSplatOffsets[splatIdx]);
}
}
}
else
{
// Pass by ref
ffiParamList.Add(&FFIType.Pointer);
unboxToPtr = true;
unbox = true;
}
}
else if (paramType.IsValueType)
{
ffiParamList.Add(GetFFIType!:mixin(paramType));
unbox = true;
}
else
{
ffiParamList.Add(&FFIType.Pointer);
ffiArgList.Add(argPtr);
}
if (unbox)
{
if (unboxToPtr)
{
2020-05-11 10:16:24 -07:00
int* stackDataPtr = scope:mixin int();
2019-08-23 11:56:54 -07:00
*stackDataPtr = (int)dataPtr;
ffiArgList.Add(stackDataPtr);
}
else
ffiArgList.Add(dataPtr);
}
}
void* funcPtr = mMethodData.mFuncPtr;
int virtualOffset = 0;
int ifaceOffset = -1;
2019-08-23 11:56:54 -07:00
if (mMethodData.mFlags.HasFlag(.Static))
{
if (target != null)
return .Err(.TargetNotExpected);
}
else
{
if (target == null)
return .Err(.TargetExpected);
var thisType = mTypeInstance;
if (mTypeInstance.IsInterface)
{
thisType = target.[Friend]RawGetType() as TypeInstance;
if (thisType == null)
return .Err(.InvalidTarget);
ifaceOffset = mTypeInstance.[Friend]mMemberDataOffset;
/*TypeInstance.InterfaceData* interfaceData = null;
var checkType = thisType;
CheckLoop: while (checkType != null)
{
for (int ifaceIdx < checkType.[Friend]mInterfaceCount)
{
if (checkType.[Friend]mInterfaceDataPtr[ifaceIdx].mInterfaceType == mTypeInstance.TypeId)
{
interfaceData = &checkType.[Friend]mInterfaceDataPtr[ifaceIdx];
break CheckLoop;
}
}
checkType = checkType.BaseType;
}
if (interfaceData == null)
return .Err(.InvalidTarget);
virtualOffset = interfaceData.mStartVirtualIdx * sizeof(int);*/
}
bool splatThis = thisType.IsSplattable && !mMethodData.mFlags.HasFlag(.Mutating);
AddArg!::(-1, target, &target, thisType, splatThis);
2019-08-23 11:56:54 -07:00
}
if (args.Count != mMethodData.mParamCount)
return .Err(.ParamCountMismatch);
var variantData = Variant.Alloc(retType, var retVal);
2019-08-23 11:56:54 -07:00
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];
let argType = Type.[Friend]GetType(paramData.mType);
2019-08-23 11:56:54 -07:00
AddArg!::(@arg, 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);
}
2019-08-23 11:56:54 -07:00
else
{
if (caller.Prep(abi, 0, ffiRetType, null) case .Err)
return .Err(.FFIError);
}
2019-08-23 11:56:54 -07:00
if (mMethodData.mFlags.HasFlag(.Virtual))
{
#if BF_ENABLE_OBJECT_DEBUG_FLAGS
void* classVData = (void*)(target.[Friend]mClassVData & ~0xFF);
#else
void* classVData = target.[Friend]mClassVData;
#endif
if (ifaceOffset >= 0)
{
void* ifaceVirtualTable = *(void**)((uint8*)classVData + ifaceOffset);
funcPtr = (void*)*(int*)((uint8*)ifaceVirtualTable + mMethodData.mVirtualIdx + virtualOffset);
}
else if (mMethodData.mVirtualIdx >= 0x100000)
2019-08-23 11:56:54 -07:00
{
void* extAddr = (void*)*((int*)classVData + (mMethodData.mVirtualIdx>>20 - 1));
funcPtr = (void*)*((int*)extAddr + (mMethodData.mVirtualIdx & 0xFFFFF) + virtualOffset);
2019-08-23 11:56:54 -07:00
}
else
{
funcPtr = (void*)*(int*)((uint8*)classVData + mMethodData.mVirtualIdx + virtualOffset);
2019-08-23 11:56:54 -07:00
}
}
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;
}
2020-07-14 08:26:38 -07:00
public override void ToString(String strBuffer)
{
let retType = Type.[Friend]GetType(mMethodData.mReturnType);
retType.ToString(strBuffer);
strBuffer.Append(' ');
strBuffer.Append(mMethodData.mName);
strBuffer.Append('(');
for (int paramIdx < mMethodData.mParamCount)
{
if (paramIdx > 0)
strBuffer.Append(", ");
let paramData = mMethodData.mParamData[paramIdx];
let paramType = Type.[Friend]GetType(paramData.mType);
paramType.ToString(strBuffer);
strBuffer.Append(' ');
strBuffer.Append(paramData.mName);
}
strBuffer.Append(')');
}
public struct Enumerator : IEnumerator<MethodInfo>
2019-08-23 11:56:54 -07:00
{
BindingFlags mBindingFlags;
TypeInstance mTypeInstance;
int32 mIdx;
public this(TypeInstance typeInst, BindingFlags bindingFlags)
2019-08-23 11:56:54 -07:00
{
mTypeInstance = typeInst;
mBindingFlags = bindingFlags;
mIdx = -1;
}
public void Reset() mut
{
mIdx = -1;
}
public void Dispose()
{
}
public bool MoveNext() mut
{
if (mTypeInstance == null)
return false;
for (;;)
{
mIdx++;
if (mIdx == mTypeInstance.[Friend]mMethodDataCount)
2019-08-23 11:56:54 -07:00
return false;
var methodData = &mTypeInstance.[Friend]mMethodDataPtr[mIdx];
bool matches = (mBindingFlags.HasFlag(BindingFlags.Static) && (methodData.mFlags.HasFlag(.Static)));
matches |= (mBindingFlags.HasFlag(BindingFlags.Instance) && (!methodData.mFlags.HasFlag(.Static)));
2019-08-23 11:56:54 -07:00
if (matches)
break;
}
return true;
}
public MethodInfo Current
{
get
{
var methodData = &mTypeInstance.[Friend]mMethodDataPtr[mIdx];
2019-08-23 11:56:54 -07:00
return MethodInfo(mTypeInstance, methodData);
}
}
public Result<MethodInfo> GetNext() mut
{
if (!MoveNext())
return .Err;
return Current;
}
}
}
}