diff --git a/Jint.Tests/Runtime/InteropTests.cs b/Jint.Tests/Runtime/InteropTests.cs index cf83edd67..3b1593def 100644 --- a/Jint.Tests/Runtime/InteropTests.cs +++ b/Jint.Tests/Runtime/InteropTests.cs @@ -3483,4 +3483,66 @@ void Debug(object o) Assert.Equal("KeyValuePair`2: [test, val]", result[2]); Assert.Equal("String: test", result[3]); } -} \ No newline at end of file + + private class ClrMembersVisibilityTestClass + { + public int A { get; set; } = 10; + + public int F() + { + return 4; + } + } + + [Fact] + public void ShouldNotSeeClrMethods() + { + var engine = new Engine(opt => + { + opt.Interop.ObjectWrapperReportedMemberTypes = MemberTypes.Field | MemberTypes.Property; + }); + + engine.SetValue("clrInstance", new ClrMembersVisibilityTestClass()); + + var val = engine.GetValue("clrInstance"); + + var obj = val.AsObject(); + var props = obj.GetOwnProperties().Select(x => x.Key.ToString()).ToList(); + + props.Should().BeEquivalentTo(["A"]); + } + + [Fact] + public void ShouldSeeClrMethods() + { + var engine = new Engine(); + + engine.SetValue("clrInstance", new ClrMembersVisibilityTestClass()); + + var val = engine.GetValue("clrInstance"); + var obj = val.AsObject(); + var props = obj.GetOwnProperties().Select(x => x.Key.ToString()).ToList(); + + props.Should().BeEquivalentTo(["A", "F"]); + } + + private class ClrMembersVisibilityTestClass2 + { + public int Get_A { get; set; } = 5; + } + + [Fact] + public void ShouldSeeClrMethods2() + { + var engine = new Engine(); + + engine.SetValue("clrInstance", new ClrMembersVisibilityTestClass2()); + + var val = engine.GetValue("clrInstance"); + + var obj = val.AsObject(); + var props = obj.GetOwnProperties().Select(x => x.Key.ToString()).ToList(); + + props.Should().BeEquivalentTo(["Get_A"]); + } +} diff --git a/Jint/Options.cs b/Jint/Options.cs index 9b9bca765..1cee53c6c 100644 --- a/Jint/Options.cs +++ b/Jint/Options.cs @@ -364,6 +364,13 @@ public class InteropOptions /// Whether the engine should throw an error when a member is not found on a CLR object. Defaults to false. /// public bool ThrowOnUnresolvedMember { get; set; } + + /// + /// Types of CLR members reported by when enumerating properties/serializing . + /// Supported values are: , , . + /// All other values are ignored. + /// + public MemberTypes ObjectWrapperReportedMemberTypes { get; set; } = MemberTypes.Field | MemberTypes.Property | MemberTypes.Method; } public class ConstraintOptions diff --git a/Jint/Runtime/Interop/ObjectWrapper.cs b/Jint/Runtime/Interop/ObjectWrapper.cs index b79f3afe3..0846b09b2 100644 --- a/Jint/Runtime/Interop/ObjectWrapper.cs +++ b/Jint/Runtime/Interop/ObjectWrapper.cs @@ -225,7 +225,7 @@ public override IEnumerable> GetOwnPro yield return new KeyValuePair(key, GetOwnProperty(key)); } } - + private IEnumerable EnumerateOwnPropertyKeys(Types types) { // prefer object order, add possible other properties after @@ -235,8 +235,7 @@ private IEnumerable EnumerateOwnPropertyKeys(Types types) var keys = (ICollection) _typeDescriptor.KeysAccessor!.GetValue(Target)!; foreach (var key in keys) { - var jsString = JsString.Create(key); - yield return jsString; + yield return JsString.Create(key); } } else if (includeStrings && Target is IDictionary dictionary) @@ -248,28 +247,46 @@ private IEnumerable EnumerateOwnPropertyKeys(Types types) if (stringKey is not null || _engine.TypeConverter.TryConvert(key, typeof(string), CultureInfo.InvariantCulture, out stringKey)) { - var jsString = JsString.Create((string) stringKey!); - yield return jsString; + yield return JsString.Create((string) stringKey!); } } } else if (includeStrings) { - // we take public properties and fields - foreach (var p in ClrType.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)) + // we take public properties, fields and methods + const BindingFlags BindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public; + + if ((_engine.Options.Interop.ObjectWrapperReportedMemberTypes & MemberTypes.Property) == MemberTypes.Property) + { + foreach (var p in ClrType.GetProperties(BindingFlags)) + { + var indexParameters = p.GetIndexParameters(); + if (indexParameters.Length == 0) + { + yield return JsString.Create(p.Name); + } + } + } + + if ((_engine.Options.Interop.ObjectWrapperReportedMemberTypes & MemberTypes.Field) == MemberTypes.Field) { - var indexParameters = p.GetIndexParameters(); - if (indexParameters.Length == 0) + foreach (var f in ClrType.GetFields(BindingFlags | BindingFlags.DeclaredOnly)) { - var jsString = JsString.Create(p.Name); - yield return jsString; + yield return JsString.Create(f.Name); } } - foreach (var f in ClrType.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public)) + if ((_engine.Options.Interop.ObjectWrapperReportedMemberTypes & MemberTypes.Method) == MemberTypes.Method) { - var jsString = JsString.Create(f.Name); - yield return jsString; + foreach (var m in ClrType.GetMethods(BindingFlags | BindingFlags.DeclaredOnly)) + { + if (m.IsSpecialName) + { + continue; + } + + yield return JsString.Create(m.Name); + } } } }