Sfoglia il codice sorgente

Provide a way to override interop members (#758)

Sébastien Ros 5 anni fa
parent
commit
68733e1cf6

+ 16 - 0
Jint.Tests/Runtime/Domain/HiddenMembers.cs

@@ -0,0 +1,16 @@
+namespace Jint.Tests.Runtime.Domain
+{
+    public class HiddenMembers
+    {
+        public string Member1 { get; set; } = "Member1";
+        public string Member2 { get; set; } = "Member2";
+        public string Method1()
+        {
+            return "Method1";
+        }
+        public string Method2()
+        {
+            return "Method2";
+        }
+    }
+}

+ 44 - 0
Jint.Tests/Runtime/InteropTests.cs

@@ -8,6 +8,7 @@ using System.Reflection;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
 using Jint.Tests.Runtime.Converters;
 using Jint.Tests.Runtime.Domain;
@@ -2173,5 +2174,48 @@ namespace Jint.Tests.Runtime
             clrValue = engine.Execute("showProps(jsObj, 'theObject')").GetCompletionValue().AsString();
             Assert.Equal(jsValue, clrValue);
         }
+
+        [Fact]
+        public void ShouldHideSpecificMembers()
+        {
+            var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
+            {
+                if (target is HiddenMembers)
+                {
+                    if (member == nameof(HiddenMembers.Member2) || member == nameof(HiddenMembers.Method2))
+                    {
+                        return JsValue.Undefined;
+                    }
+                }
+
+                return null;
+            }));
+
+            engine.SetValue("m", new HiddenMembers());
+
+            Assert.Equal("Member1", engine.Execute("m.Member1").GetCompletionValue().ToString());
+            Assert.Equal("undefined", engine.Execute("m.Member2").GetCompletionValue().ToString());
+            Assert.Equal("Method1", engine.Execute("m.Method1()").GetCompletionValue().ToString());
+            // check the method itself, not its invokation as it would mean invoking "undefined"
+            Assert.Equal("undefined", engine.Execute("m.Method2").GetCompletionValue().ToString());
+        }
+
+        [Fact]
+        public void ShouldOverrideMembers()
+        {
+            var engine = new Engine(options => options.SetMemberAccessor((e, target, member) =>
+            {
+                if (target is HiddenMembers && member == nameof(HiddenMembers.Member1))
+                {
+                    return "Orange";
+                }
+
+                return null;
+            }));
+            
+            engine.SetValue("m", new HiddenMembers());
+
+            Assert.Equal("Orange", engine.Execute("m.Member1").GetCompletionValue().ToString());
+        }
     }
 }

+ 1 - 34
Jint/Engine.cs

@@ -108,40 +108,6 @@ namespace Jint
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerConfigurable;
         internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerNonConfigurable;
 
-        internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
-        {
-            public ClrPropertyDescriptorFactoriesKey(Type type, Key propertyName)
-            {
-                Type = type;
-                PropertyName = propertyName;
-            }
-
-            private readonly Type Type;
-            private readonly Key PropertyName;
-
-            public bool Equals(ClrPropertyDescriptorFactoriesKey other)
-            {
-                return Type == other.Type && PropertyName == other.PropertyName;
-            }
-
-            public override bool Equals(object obj)
-            {
-                if (ReferenceEquals(null, obj))
-                {
-                    return false;
-                }
-                return obj is ClrPropertyDescriptorFactoriesKey other && Equals(other);
-            }
-
-            public override int GetHashCode()
-            {
-                unchecked
-                {
-                    return (Type.GetHashCode() * 397) ^ PropertyName.GetHashCode();
-                }
-            }
-        }
-
         internal readonly Dictionary<ClrPropertyDescriptorFactoriesKey, Func<Engine, object, PropertyDescriptor>> ClrPropertyDescriptorFactories =
             new Dictionary<ClrPropertyDescriptorFactoriesKey, Func<Engine, object, PropertyDescriptor>>();
 
@@ -238,6 +204,7 @@ namespace Jint
 
             ClrTypeConverter = new DefaultTypeConverter(this);
         }
+    
 
         internal LexicalEnvironment GlobalEnvironment { get; }
         public GlobalObject Global { get; }

+ 20 - 0
Jint/Options.cs

@@ -10,6 +10,8 @@ using Jint.Runtime.References;
 
 namespace Jint
 {
+    public delegate JsValue MemberAccessorDelegate(Engine engine, object target, string member);
+
     public sealed class Options
     {
         private readonly List<IConstraint> _constraints = new List<IConstraint>();
@@ -19,6 +21,7 @@ namespace Jint
         private bool _allowClrWrite = true;
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
         private Func<Engine, object, ObjectInstance> _wrapObjectHandler;
+        private MemberAccessorDelegate _memberAccessor;
         private int _maxRecursionDepth = -1;
         private TimeSpan _regexTimeoutInterval = TimeSpan.FromSeconds(10);
         private CultureInfo _culture = CultureInfo.CurrentCulture;
@@ -86,6 +89,22 @@ namespace Jint
             return this;
         }
 
+
+        /// <summary>
+        /// Registers a delegate that is called when CLR members are invoked. This allows
+        /// to change what values are returned for specific CLR objects, or if any value 
+        /// is returned at all.
+        /// </summary>
+        /// <param name="accessor">
+        /// The delegate to invoke for each CLR member. If the delegate 
+        /// returns <c>null</c>, the standard evaluation is performed.
+        /// </param>
+        public Options SetMemberAccessor(MemberAccessorDelegate accessor)
+        {
+            _memberAccessor = accessor;
+            return this;
+        }
+
         /// <summary>
         /// Allows scripts to call CLR types directly like <example>System.IO.File</example>
         /// </summary>
@@ -198,6 +217,7 @@ namespace Jint
         internal List<IConstraint> _Constraints => _constraints;
 
         internal Func<Engine, object, ObjectInstance> _WrapObjectHandler => _wrapObjectHandler;
+        internal MemberAccessorDelegate _MemberAccessor => _memberAccessor;
 
         internal int MaxRecursionDepth => _maxRecursionDepth;
 

+ 38 - 0
Jint/Runtime/Interop/ClrPropertyDescriptorFactoriesKey.cs

@@ -0,0 +1,38 @@
+using System;
+
+namespace Jint.Runtime.Interop
+{
+    internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
+    {
+        public ClrPropertyDescriptorFactoriesKey(Type type, Key propertyName)
+        {
+            Type = type;
+            PropertyName = propertyName;
+        }
+
+        private readonly Type Type;
+        private readonly Key PropertyName;
+
+        public bool Equals(ClrPropertyDescriptorFactoriesKey other)
+        {
+            return Type == other.Type && PropertyName == other.PropertyName;
+        }
+
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+            return obj is ClrPropertyDescriptorFactoriesKey other && Equals(other);
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                return (Type.GetHashCode() * 397) ^ PropertyName.GetHashCode();
+            }
+        }
+    }
+}

+ 14 - 2
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -167,9 +167,21 @@ namespace Jint.Runtime.Interop
                 SetProperty(GlobalSymbolRegistry.Iterator, iteratorProperty);
                 return iteratorProperty;
             }
-            
+
+            var memberAccessor = Engine.Options._MemberAccessor;
+
+            if (memberAccessor != null)
+            {
+                var result = memberAccessor.Invoke(Engine, Target, property.ToString());
+
+                if (result != null)
+                {
+                    return new PropertyDescriptor(result, PropertyFlag.OnlyEnumerable);
+                }
+            }
+
             var type = Target.GetType();
-            var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, property.ToString());
+            var key = new ClrPropertyDescriptorFactoriesKey(type, property.ToString());
 
             if (!_engine.ClrPropertyDescriptorFactories.TryGetValue(key, out var factory))
             {