Browse Source

Change TypeResolver cache to be bound to engine instance (#1436)

Marko Lahma 2 years ago
parent
commit
7035bc4674

+ 21 - 0
Jint.Tests/Runtime/InteropTests.MemberAccess.cs

@@ -1,6 +1,7 @@
 using System.Collections;
 using System.Collections;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
 using Jint.Native;
 using Jint.Native;
+using Jint.Runtime;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 using Jint.Tests.Runtime.Domain;
 using Jint.Tests.Runtime.Domain;
 
 
@@ -90,6 +91,26 @@ namespace Jint.Tests.Runtime
             Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString());
             Assert.Equal("Method1", engine.Evaluate("m.GetType().GetMethod('Method1').Invoke(m, [])").AsString());
         }
         }
 
 
+        [Fact]
+        public void ShouldBeAbleToVaryGetTypeConfigurationBetweenEngines()
+        {
+            static string TestAllowGetTypeOption(bool allowGetType)
+            {
+                var uri = new Uri("https://github.com/sebastienros/jint");
+                const string Input = nameof(uri) + ".GetType();";
+
+                using var engine = new Engine(options => options.Interop.AllowGetType = allowGetType);
+                engine.SetValue(nameof(uri), JsValue.FromObject(engine, uri));
+                var result = engine.Evaluate(Input).ToString();
+                return result;
+            }
+
+            Assert.Equal("System.Uri", TestAllowGetTypeOption(allowGetType: true));
+
+            var ex = Assert.Throws<JavaScriptException>(() => TestAllowGetTypeOption(allowGetType: false));
+            Assert.Equal("Property 'GetType' of object is not a function", ex.Message);
+        }
+
         [Fact]
         [Fact]
         public void ShouldProtectFromReflectionServiceUsage()
         public void ShouldProtectFromReflectionServiceUsage()
         {
         {

+ 4 - 0
Jint/Engine.cs

@@ -61,6 +61,10 @@ namespace Jint
         internal Intrinsics _originalIntrinsics = null!;
         internal Intrinsics _originalIntrinsics = null!;
         internal Host _host = null!;
         internal Host _host = null!;
 
 
+        // we need to cache reflection accessors on engine level as configuration options can affect outcome
+        internal readonly record struct ClrPropertyDescriptorFactoriesKey(Type Type, Key PropertyName);
+        internal Dictionary<ClrPropertyDescriptorFactoriesKey, ReflectionAccessor> _reflectionAccessors = new();
+
         /// <summary>
         /// <summary>
         /// Constructs a new engine instance.
         /// Constructs a new engine instance.
         /// </summary>
         /// </summary>

+ 4 - 7
Jint/Runtime/Interop/TypeResolver.cs

@@ -13,9 +13,6 @@ namespace Jint.Runtime.Interop
     {
     {
         public static readonly TypeResolver Default = new();
         public static readonly TypeResolver Default = new();
 
 
-        private readonly record struct ClrPropertyDescriptorFactoriesKey(Type Type, Key PropertyName);
-        private Dictionary<ClrPropertyDescriptorFactoriesKey, ReflectionAccessor> _reflectionAccessors = new();
-
         /// <summary>
         /// <summary>
         /// Registers a filter that determines whether given member is wrapped to interop or returned as undefined.
         /// Registers a filter that determines whether given member is wrapped to interop or returned as undefined.
         /// By default allows all but will also be limited by <see cref="InteropOptions.AllowGetType"/> configuration.
         /// By default allows all but will also be limited by <see cref="InteropOptions.AllowGetType"/> configuration.
@@ -52,9 +49,9 @@ namespace Jint.Runtime.Interop
             Func<ReflectionAccessor?>? accessorFactory = null,
             Func<ReflectionAccessor?>? accessorFactory = null,
             bool forWrite = false)
             bool forWrite = false)
         {
         {
-            var key = new ClrPropertyDescriptorFactoriesKey(type, member);
+            var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, member);
 
 
-            var factories = _reflectionAccessors;
+            var factories = engine._reflectionAccessors;
             if (factories.TryGetValue(key, out var accessor))
             if (factories.TryGetValue(key, out var accessor))
             {
             {
                 return accessor;
                 return accessor;
@@ -63,8 +60,8 @@ namespace Jint.Runtime.Interop
             accessor = accessorFactory?.Invoke() ?? ResolvePropertyDescriptorFactory(engine, type, member, forWrite);
             accessor = accessorFactory?.Invoke() ?? ResolvePropertyDescriptorFactory(engine, type, member, forWrite);
 
 
             // racy, we don't care, worst case we'll catch up later
             // racy, we don't care, worst case we'll catch up later
-            Interlocked.CompareExchange(ref _reflectionAccessors,
-                new Dictionary<ClrPropertyDescriptorFactoriesKey, ReflectionAccessor>(factories)
+            Interlocked.CompareExchange(ref engine._reflectionAccessors,
+                new Dictionary<Engine.ClrPropertyDescriptorFactoriesKey, ReflectionAccessor>(factories)
                 {
                 {
                     [key] = accessor
                     [key] = accessor
                 }, factories);
                 }, factories);