From f23efd43fe2f94833d3bbe3cabfcfafb440ed344 Mon Sep 17 00:00:00 2001 From: LazuliKao Date: Sat, 5 Aug 2023 22:38:43 +0800 Subject: [PATCH] fix: fix clr exception stack format in js --- src/Hook/JsLog/JsLog.cs | 4 +- src/QuickJS/Extensions/JsValueExtension.cs | 53 ++++++- src/QuickJS/Native.cs | 139 +++++++++++++++++- src/QuickJS/Wrapper/AutoDropJsValue.cs | 34 +++++ .../Wrapper/ClrProxy/ClrInstanceProxy.cs | 43 ++---- src/QuickJS/Wrapper/ClrProxy/ClrTypeProxy.cs | 40 ++--- src/QuickJS/Wrapper/ClrProxyBase.cs | 22 ++- src/QuickJS/Wrapper/JsContextWrapper.cs | 8 +- .../Wrapper/Reflect/InstanceMemberFinder.cs | 9 +- src/QuickJS/Wrapper/Reflect/MethodHelper.cs | 4 +- .../Wrapper/Reflect/StaticMemberFinder.cs | 9 +- 11 files changed, 274 insertions(+), 91 deletions(-) diff --git a/src/Hook/JsLog/JsLog.cs b/src/Hook/JsLog/JsLog.cs index f51685b..b37f4d1 100644 --- a/src/Hook/JsLog/JsLog.cs +++ b/src/Hook/JsLog/JsLog.cs @@ -162,7 +162,7 @@ static unsafe (string file, int line) GetJsSourceInfo(JsContext* ctx) { Native.JS_ThrowInternalError(ctx, ""); using var error = Native.JS_GetException(ctx); - var stack = error.Value.GetStringProperty(ctx, "stack"); + var stack = error.Value.GetStringProperty(ctx, JsAtom.BuildIn.Stack); // at trace (native) // at (test.js:3) var lineStr = stack.Split('\n')[1]; //at (test.js:3) @@ -218,7 +218,7 @@ static unsafe string ParseLog(JsContext* ctx, ReadOnlySpan argv) if (arg.Tag == JsTag.Object && Native.JS_IsError(ctx, arg)) { sb.AppendLine(); - sb.AppendLine(arg.GetStringProperty(ctx, "stack")); + sb.AppendLine(arg.GetStringProperty(ctx, JsAtom.BuildIn.Stack)); } else { diff --git a/src/QuickJS/Extensions/JsValueExtension.cs b/src/QuickJS/Extensions/JsValueExtension.cs index c6a900e..8ad1457 100644 --- a/src/QuickJS/Extensions/JsValueExtension.cs +++ b/src/QuickJS/Extensions/JsValueExtension.cs @@ -119,7 +119,22 @@ string propertyName ) { using var val = Native.JS_GetPropertyStr(ctx, @this, propertyName); - return Native.JS_ToCString(ctx, val.Value); + return val.ToString(); + } + + public static unsafe string GetStringProperty(this JsValue @this, JsContext* ctx, JsAtom atom) + { + using var val = Native.JS_GetPropertyInternal(ctx, @this, atom); + return val.ToString(); + } + + public static unsafe AutoDropJsValue GetProperty( + this JsValue @this, + JsContext* ctx, + JsAtom atom + ) + { + return Native.JS_GetPropertyInternal(ctx, @this, atom); } public static unsafe AutoDropJsValue GetProperty( @@ -131,6 +146,17 @@ string propertyName return Native.JS_GetPropertyStr(ctx, @this, propertyName); } + public static unsafe bool HasProperty(this JsValue @this, JsContext* ctx, JsAtom atom) + { + return Native.JS_HasProperty(ctx, @this, atom); + } + + public static unsafe bool HasProperty(this JsValue @this, JsContext* ctx, string propertyName) + { + using var atom = Native.JS_NewAtom(ctx, propertyName); + return HasProperty(@this, ctx, atom.Value); + } + //JS_ToString public static unsafe string ToString(this JsValue @this, JsContext* ctx) { @@ -143,12 +169,33 @@ public static unsafe string ToString(this JsValue @this, JsContextWrapper ctx) = public static unsafe bool SetProperty( this JsValue @this, JsContext* ctx, - JsValue propertyName, + JsValue propertyKey, + JsValue value, + JsPropertyFlags flags = JsPropertyFlags.CWE + ) + { + return Native.JS_SetPropertyValue(ctx, @this, propertyKey, value, flags); + } + + public static unsafe bool SetProperty( + this JsValue @this, + JsContext* ctx, + string propertyName, + JsValue value + ) + { + return Native.JS_SetPropertyStr(ctx, @this, propertyName, value); + } + + public static unsafe bool SetProperty( + this JsValue @this, + JsContext* ctx, + JsAtom atom, JsValue value, JsPropertyFlags flags = JsPropertyFlags.CWE ) { - return Native.JS_SetPropertyValue(ctx, @this, propertyName, value, flags); + return Native.JS_SetPropertyInternal(ctx, @this, atom, value, flags); } public static unsafe bool DefineProperty( diff --git a/src/QuickJS/Native.cs b/src/QuickJS/Native.cs index 747223c..5f15987 100644 --- a/src/QuickJS/Native.cs +++ b/src/QuickJS/Native.cs @@ -241,6 +241,30 @@ public static void JS_SetConstructorBit(JsContext* ctx, JsValue @this, bool val) ); #endregion + #region JS_GetPropertyInternal + //JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,JSAtom prop, JSValueConst receiver,JS_BOOL throw_ref_error); + public static AutoDropJsValue JS_GetPropertyInternal( + JsContext* ctx, + JsValue obj, + JsAtom prop, + bool throwRefError = false + ) + { + var result = ( + (delegate* unmanaged) + _ptrJsGetPropertyInternal.Value + )(ctx, obj, prop, obj, throwRefError ? (byte)1 : (byte)0); + if (result.IsException()) + { + ThrowPendingException(ctx); + } + return new AutoDropJsValue(result, ctx); + } + + private static readonly Lazy _ptrJsGetPropertyInternal = GetPointerLazy( + "JS_GetPropertyInternal" + ); + #endregion #region JS_GetPropertyStr public static AutoDropJsValue JS_GetPropertyStr( JsContext* ctx, @@ -336,6 +360,32 @@ JsPropertyFlags flags "JS_DefinePropertyValue" ); #endregion + + #region JS_SetPropertyInternal + + //int JS_SetPropertyInternal(JSContext *ctx, JSValueConst this_obj,JSAtom prop, JSValue val, int flags) + public static bool JS_SetPropertyInternal( + JsContext* ctx, + JsValue thisObj, + JsAtom prop, + JsValue val, + JsPropertyFlags flags = JsPropertyFlags.Throw + ) + { + var func = (delegate* unmanaged) + _ptrJsSetPropertyInternal.Value; + var result = func(ctx, thisObj, prop, val, (int)flags); + if (result == -1) + { + ThrowPendingException(ctx); + } + return result == 1; + } + + private static readonly Lazy _ptrJsSetPropertyInternal = GetPointerLazy( + "JS_SetPropertyInternal" + ); + #endregion #region JS_DefinePropertyValueStr //int JS_DefinePropertyValueStr(JSContext *ctx, JSValueConst this_obj, // const char *prop, JSValue val, int flags) @@ -680,30 +730,89 @@ public static AutoDropJsValue JS_Invoke( //private static readonly Lazy _ptrJsThrowError = GetPointerLazy("JS_ThrowError2"); //#endregion + #region JS_HasProperty + + //int JS_HasProperty(JSContext *ctx, JSValueConst obj, JSAtom prop) + public static bool JS_HasProperty(JsContext* ctx, JsValue obj, JsAtom prop) + { + var func = (delegate* unmanaged)_ptrJsHasProperty.Value; + var result = func(ctx, obj, prop); + if (result == -1) + ThrowPendingException(ctx); + return result != 0; + } + + private static readonly Lazy _ptrJsHasProperty = GetPointerLazy("JS_HasProperty"); + + #endregion + #region JS_Throw + //JSValue JS_Throw(JSContext *ctx, JSValue obj) + public static JsValue JS_Throw(JsContext* ctx, JsValue obj) + { + var func = (delegate* unmanaged)_ptrJsThrow.Value; + var result = func(ctx, obj); + if (!result.IsException()) + { + Log.Logger.Error("throw error may failed"); + } + return result; + } + + private static readonly Lazy _ptrJsThrow = GetPointerLazy("JS_Throw"); + + #endregion #region JS_ThrowInternalError //JSValue __attribute__((format(printf, 2, 3))) JS_ThrowInternalError(JSContext *ctx, const char *fmt, ...); public static JsValue JS_ThrowInternalError(JsContext* ctx, Exception exception) { - return JS_ThrowInternalError(ctx, exception.ToString()); + return JS_ThrowInternalError(ctx, exception.Message, exception.StackTrace); } - public static JsValue JS_ThrowInternalError(JsContext* ctx, string message) + public static JsValue JS_ThrowInternalError( + JsContext* ctx, + string message, + string? stack = null + ) { - var func = (delegate* unmanaged)_ptrJsThrowInternalError.Value; + var func = (delegate* unmanaged) + _ptrJsThrowInternalError.Value; + fixed ( + byte* format = StringUtils.StringToManagedUtf8( + "%s" /* prevent format*/ + ) + ) fixed ( byte* ptr = StringUtils.StringToManagedUtf8( - message.Replace("%%", "%") /* prevent format*/ + message /* prevent format*/ ) ) { - var result = func(ctx, ptr); + var result = func(ctx, format, ptr); if (!result.IsException()) { //it seem always return exception type //so if not exception, it means throw failed ? Log.Logger.Error("throw error may failed"); } +#if true// get thrown exception and append clr stack after js + if (!string.IsNullOrWhiteSpace(stack)) + { + var error = JS_GetException(ctx); //get error object just throw + if (error.HasProperty(JsAtom.BuildIn.Stack)) + { + var jsStack = error.GetStringProperty(JsAtom.BuildIn.Stack); + //append clr stack + jsStack += string.Join( + Environment.NewLine, + from line in stack.Split(Environment.NewLine) + select " " + line.TrimStart() + ); + error.SetProperty(JsAtom.BuildIn.Stack, JS_NewString(ctx, jsStack).Steal()); + } + JS_Throw(ctx, error.Value); //rethrow processed error + } +#endif return result; } } @@ -935,6 +1044,26 @@ public static AutoDropJsValue JS_GetPropertyUint32(JsContext* ctx, JsValue thisO #endregion + #region JS_SetPropertyStr + + //int JS_SetPropertyStr(JSContext *ctx, JSValueConst this_obj,const char* prop, JSValue val) + public static bool JS_SetPropertyStr(JsContext* ctx, JsValue thisObj, string prop, JsValue val) + { + var func = (delegate* unmanaged) + _ptrJsSetPropertyStr.Value; + fixed (byte* p = StringUtils.StringToManagedUtf8(prop)) + { + var result = func(ctx, thisObj, p, val); + if (result == -1) + ThrowPendingException(ctx); + return result != 0; + } + } + + private static readonly Lazy _ptrJsSetPropertyStr = GetPointerLazy("JS_SetPropertyStr"); + + #endregion + #region JS_SetPropertyValue // int JS_SetPropertyValue(JSContext* ctx, JSValueConst this_obj,JSValue prop, JSValue val, int flags) public static bool JS_SetPropertyValue( diff --git a/src/QuickJS/Wrapper/AutoDropJsValue.cs b/src/QuickJS/Wrapper/AutoDropJsValue.cs index b388635..51646f3 100644 --- a/src/QuickJS/Wrapper/AutoDropJsValue.cs +++ b/src/QuickJS/Wrapper/AutoDropJsValue.cs @@ -100,6 +100,14 @@ public string GetStringProperty(string propertyName) } } + public string GetStringProperty(JsAtom atom) + { + unsafe + { + return _value.GetStringProperty(_context, atom); + } + } + public override string ToString() { unsafe @@ -163,4 +171,30 @@ public unsafe void DefineFunction( using var value = ctx.NewJsFunction(funcName, argumentLength, func, protoType); _value.DefineProperty(ctx.Context, funcName, value.Steal(), propFlags); } + + public unsafe bool HasProperty(JsAtom atom) + { + return _value.HasProperty(_context, atom); + } + + public unsafe bool HasProperty(string propertyName) + { + return _value.HasProperty(_context, propertyName); + } + + public void SetProperty(JsAtom atom, JsValue value) + { + unsafe + { + _value.SetProperty(_context, atom, value); + } + } + + public void SetProperty(string propertyName, JsValue value) + { + unsafe + { + _value.SetProperty(_context, propertyName, value); + } + } } diff --git a/src/QuickJS/Wrapper/ClrProxy/ClrInstanceProxy.cs b/src/QuickJS/Wrapper/ClrProxy/ClrInstanceProxy.cs index 29166e7..a79261e 100644 --- a/src/QuickJS/Wrapper/ClrProxy/ClrInstanceProxy.cs +++ b/src/QuickJS/Wrapper/ClrProxy/ClrInstanceProxy.cs @@ -42,18 +42,16 @@ public string ToString(string? format, IFormatProvider? formatProvider) protected override JsPropertyEnum[] GetOwnPropertyNames(JsContextWrapper ctxInstance) { - var ownPropertyNames = MemberFinder - .EnumMembers() - .Select( - member => - new JsPropertyEnum - { - Atom = ctxInstance.NewAtom(member.Name).Steal(), - IsEnumerable = member is FieldInfo or PropertyInfo //function is not enumerable in for .. in loop in js - } - ) - .ToArray(); - Log.Logger.Trace("property" + ownPropertyNames.Length); + var ownPropertyNames = ( + from member in MemberFinder.EnumMembers() + let enumerable = member is FieldInfo or PropertyInfo //function is not enumerable in for .. in loop in js + where enumerable || member is MethodInfo + select new JsPropertyEnum + { + Atom = ctxInstance.NewAtom(member.Name).Steal(), + IsEnumerable = enumerable + } + ).ToArray(); return ownPropertyNames; } @@ -103,11 +101,7 @@ void InvokeAsProperty( data.Flags |= JsPropertyFlags.HasGet; data.Getter = ctxInstance .NewJsFunctionObject( - (_, thisObj, argv) => - { - Log.Logger.Trace("get"); - return getHelper.Call(ctxInstance, argv, thisObj).Steal(); - } + (_, thisObj, argv) => getHelper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(); } @@ -116,11 +110,7 @@ void InvokeAsProperty( data.Flags |= JsPropertyFlags.HasSet; data.Setter = ctxInstance .NewJsFunctionObject( - (_, thisObj, argv) => - { - Log.Logger.Trace("set"); - return setHelper.Call(ctxInstance, argv, thisObj).Steal(); - } + (_, thisObj, argv) => setHelper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(); } @@ -128,7 +118,6 @@ void InvokeAsProperty( //bool withIndex = propName.TryGetIndex(ctxInstance, out uint idx); if (propName.TryGetIndex(ctxInstance, out var idx)) //todo support multi dimension indexer { - Log.Logger.Trace("GetOwnProperty: [" + idx + "]"); if (MemberFinder.TryGetIndexer(out var indexer)) { InvokeAsProperty(indexer, out data, new object[] { idx }); @@ -136,7 +125,6 @@ void InvokeAsProperty( } } var name = propName.ToString(ctxInstance); - Log.Logger.Trace("GetOwnProperty:" + name); if (!MemberFinder.TryFindMember(name, out var member)) { data = default; @@ -159,15 +147,10 @@ void InvokeAsProperty( Flags = JsPropertyFlags.HasValue, Value = ctxInstance .NewJsFunctionObject( - (_, thisObj, argv) => - { - Log.Logger.Trace("call"); - return helper.Call(ctxInstance, argv, thisObj).Steal(); - } + (_, thisObj, argv) => helper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(), }; - Log.Logger.Trace("method"); return true; } case FieldInfo field: diff --git a/src/QuickJS/Wrapper/ClrProxy/ClrTypeProxy.cs b/src/QuickJS/Wrapper/ClrProxy/ClrTypeProxy.cs index 982c464..c6f4821 100644 --- a/src/QuickJS/Wrapper/ClrProxy/ClrTypeProxy.cs +++ b/src/QuickJS/Wrapper/ClrProxy/ClrTypeProxy.cs @@ -60,18 +60,16 @@ public void Dispose() protected override JsPropertyEnum[] GetOwnPropertyNames(JsContextWrapper ctxInstance) { - var ownPropertyNames = MemberFinder - .EnumStaticMembers() - .Select( - member => - new JsPropertyEnum - { - Atom = ctxInstance.NewAtom(member.Name).Steal(), - IsEnumerable = member is FieldInfo or PropertyInfo //function is not enumerable in for .. in loop in js - } - ) - .ToArray(); - Log.Logger.Trace("property" + ownPropertyNames.Length); + var ownPropertyNames = ( + from member in MemberFinder.EnumStaticMembers() + let enumerable = member is FieldInfo or PropertyInfo //function is not enumerable in for .. in loop in js + where enumerable || member is MethodInfo + select new JsPropertyEnum + { + Atom = ctxInstance.NewAtom(member.Name).Steal(), + IsEnumerable = enumerable + } + ).ToArray(); return ownPropertyNames; } @@ -82,7 +80,6 @@ JsAtom propName ) { var name = propName.ToString(ctxInstance); - Log.Logger.Trace("GetOwnProperty:" + name); if (!MemberFinder.TryFindMember(name, out var member)) { data = default; @@ -102,15 +99,10 @@ JsAtom propName Flags = JsPropertyFlags.HasValue, Value = ctxInstance .NewJsFunctionObject( - (_, thisObj, argv) => - { - Log.Logger.Trace("call"); - return helper.Call(ctxInstance, argv, thisObj).Steal(); - } + (_, thisObj, argv) => helper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(), }; - Log.Logger.Trace("method"); return true; } case PropertyInfo property: @@ -135,10 +127,7 @@ JsAtom propName data.Getter = ctxInstance .NewJsFunctionObject( (_, thisObj, argv) => - { - Log.Logger.Trace("get"); - return getHelper.Call(ctxInstance, argv, thisObj).Steal(); - } + getHelper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(); } @@ -148,10 +137,7 @@ JsAtom propName data.Setter = ctxInstance .NewJsFunctionObject( (_, thisObj, argv) => - { - Log.Logger.Trace("set"); - return setHelper.Call(ctxInstance, argv, thisObj).Steal(); - } + setHelper.Invoke(ctxInstance, argv, thisObj).Steal() ) .Steal(); } diff --git a/src/QuickJS/Wrapper/ClrProxyBase.cs b/src/QuickJS/Wrapper/ClrProxyBase.cs index 5535fb4..f5085ef 100644 --- a/src/QuickJS/Wrapper/ClrProxyBase.cs +++ b/src/QuickJS/Wrapper/ClrProxyBase.cs @@ -257,7 +257,12 @@ internal static unsafe void JsClassFinalizer(JsRuntime* rt, JsValue value) { Log.Logger.Warning("JsClassFinalizer " + e); } - Log.Logger.Trace("JsClassFinalizer " + opaque + " Current Active: " + --activeCount); + + if (--activeCount == 0) + { + //Log.Logger.Trace("JsClassFinalizer " + opaque + " Current Active: " + activeCount); + Log.Logger.Trace("JsClassFinalizer " + opaque + " All Active ClrProxy Cleared."); + } } else { @@ -325,14 +330,20 @@ private protected static unsafe bool JsGetInstanceReturnTrueIfThrow( { if (!TryGetInstance(@this, out instance)) { - Native.JS_ThrowInternalError(ctx, "JsClassCall: unknown object from js."); + Native.JS_ThrowInternalError( + ctx, + new NullReferenceException("JsClassCall: unknown object from js.") + ); ctxInstance = null; return true; } if (!JsContextWrapper.TryGet((nint)ctx, out ctxInstance)) { - Native.JS_ThrowInternalError(ctx, "JsClassCall: unknown context from js."); + Native.JS_ThrowInternalError( + ctx, + new NullReferenceException("JsClassCall: unknown context from js.") + ); return true; } return false; @@ -354,7 +365,10 @@ internal static unsafe JsValue ProtoTypeToString( var argv = new ReadOnlySpan(argvPtr, argc); if (!TryGetInstance(thisObj, out var data)) { - return Native.JS_ThrowInternalError(ctx, "could not find thisObj calling toString"); + return Native.JS_ThrowInternalError( + ctx, + new NullReferenceException("could not find thisObj calling toString") + ); } string? format = null; if (argv.InsureArgumentCount(0, 1) == 1) diff --git a/src/QuickJS/Wrapper/JsContextWrapper.cs b/src/QuickJS/Wrapper/JsContextWrapper.cs index f203ff2..3241608 100644 --- a/src/QuickJS/Wrapper/JsContextWrapper.cs +++ b/src/QuickJS/Wrapper/JsContextWrapper.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Hosihikari.VanillaScript.Loader; +using Hosihikari.VanillaScript.QuickJS.Extensions; using Hosihikari.VanillaScript.QuickJS.Helper; using Hosihikari.VanillaScript.QuickJS.Types; using static Hosihikari.VanillaScript.QuickJS.Types.JsClassDef; @@ -325,15 +326,14 @@ private JsClassId GetOrCreateType(string name, bool hasCall, bool hasExotic) def.Exotic = (JsClassExoticMethods*) ClrProxyBase.JsClassExoticMethods.Value.ToPointer(); classId = RegisterClass(name, def); - var proto = NewObject().Steal(); - Native.JS_DefinePropertyValue( + var proto = NewObject(); + proto.DefineProperty( Context, - proto, JsAtom.BuildIn.ToStringFunc, NewStaticJsFunction("toString", 1, &ClrProxyBase.ProtoTypeToString).Steal(), JsPropertyFlags.Writable | JsPropertyFlags.Configurable ); - Native.JS_SetClassProto(Context, classId, proto); + Native.JS_SetClassProto(Context, classId, proto.Steal()); } return classId; } diff --git a/src/QuickJS/Wrapper/Reflect/InstanceMemberFinder.cs b/src/QuickJS/Wrapper/Reflect/InstanceMemberFinder.cs index 4fa421d..8b7dcd3 100644 --- a/src/QuickJS/Wrapper/Reflect/InstanceMemberFinder.cs +++ b/src/QuickJS/Wrapper/Reflect/InstanceMemberFinder.cs @@ -14,13 +14,8 @@ public InstanceMemberFinder(Type type) _type = type; } - public IEnumerable EnumMembers() - { - foreach (var member in _type.GetMembers(BindingFlags.Public | BindingFlags.Instance)) - { - yield return member; - } - } + public IEnumerable EnumMembers() => + _type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod); public bool TryGetIndexer( [NotNullWhen(true)] out PropertyInfo? result, diff --git a/src/QuickJS/Wrapper/Reflect/MethodHelper.cs b/src/QuickJS/Wrapper/Reflect/MethodHelper.cs index 375025e..ab25f30 100644 --- a/src/QuickJS/Wrapper/Reflect/MethodHelper.cs +++ b/src/QuickJS/Wrapper/Reflect/MethodHelper.cs @@ -10,7 +10,7 @@ public class IndexerMethodHelper : MethodHelper { private readonly object[] _indexer; - public override AutoDropJsValue Call( + public override AutoDropJsValue Invoke( JsContextWrapper ctx, ReadOnlySpan argv, JsValue thisObj @@ -73,7 +73,7 @@ public MethodHelper(MethodInfo method, object? instance = null) _parametersCache = new Lazy(_method.GetParameters); } - public virtual AutoDropJsValue Call( + public virtual AutoDropJsValue Invoke( JsContextWrapper ctx, ReadOnlySpan argv, JsValue thisObj diff --git a/src/QuickJS/Wrapper/Reflect/StaticMemberFinder.cs b/src/QuickJS/Wrapper/Reflect/StaticMemberFinder.cs index 853d3ec..032351a 100644 --- a/src/QuickJS/Wrapper/Reflect/StaticMemberFinder.cs +++ b/src/QuickJS/Wrapper/Reflect/StaticMemberFinder.cs @@ -33,11 +33,6 @@ public bool TryFindMember(string name, [NotNullWhen(true)] out MemberInfo? resul return true; } - public IEnumerable EnumStaticMembers() - { - foreach (var member in _type.GetMembers(BindingFlags.Public | BindingFlags.Static)) - { - yield return member; - } - } + public IEnumerable EnumStaticMembers() => + _type.GetMembers(BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod); }