Browse Source

Add ClrFunctionInstance as fallback to MethodInfoFunctionInstance (#872)

Bernhard Windisch 4 years ago
parent
commit
27865262a9

+ 7 - 0
Jint.Tests/Runtime/ExtensionMethods/CustomStringExtensions.cs

@@ -26,5 +26,12 @@ namespace Jint.Tests.Runtime.ExtensionMethods
         {
         {
             return Array.Empty<string>();
             return Array.Empty<string>();
         }
         }
+
+        public static string[] Split(this string value, int position)
+        {
+            var first = value.Substring(0, position);
+            var second = value.Substring(position);
+            return new string[] { first, second };
+        }
     }
     }
 }
 }

+ 21 - 0
Jint.Tests/Runtime/ExtensionMethods/ExtensionMethodsTest.cs

@@ -66,9 +66,30 @@ namespace Jint.Tests.Runtime.ExtensionMethods
             {
             {
                 opts.AddExtensionMethods(typeof(CustomStringExtensions));
                 opts.AddExtensionMethods(typeof(CustomStringExtensions));
             });
             });
+
+            //uses split function from StringPrototype
             var arr = engine.Execute("'yes,no'.split(',')").GetCompletionValue().AsArray();
             var arr = engine.Execute("'yes,no'.split(',')").GetCompletionValue().AsArray();
             Assert.Equal("yes", arr[0]);
             Assert.Equal("yes", arr[0]);
             Assert.Equal("no", arr[1]);
             Assert.Equal("no", arr[1]);
+
+            //uses split function from CustomStringExtensions
+            var arr2 = engine.Execute("'yes,no'.split(2)").GetCompletionValue().AsArray();
+            Assert.Equal("ye", arr2[0]);
+            Assert.Equal("s,no", arr2[1]);
+        }
+
+        [Fact]
+        public void OverridePrototypeFunctions()
+        {
+            var engine = new Engine(opts =>
+            {
+                opts.AddExtensionMethods(typeof(OverrideStringPrototypeExtensions));
+            });
+
+            //uses the overridden split function from OverrideStringPrototypeExtensions
+            var arr = engine.Execute("'yes,no'.split(',')").GetCompletionValue().AsArray();
+            Assert.Equal("YES", arr[0]);
+            Assert.Equal("NO", arr[1]);
         }
         }
 
 
         [Fact]
         [Fact]

+ 15 - 0
Jint.Tests/Runtime/ExtensionMethods/OverrideStringPrototypeExtensions.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Dynamic;
+using System.Linq;
+using Newtonsoft.Json;
+
+namespace Jint.Tests.Runtime.ExtensionMethods
+{
+    public static class OverrideStringPrototypeExtensions
+    {
+        public static string[] Split(this string value, string delimiter)
+        {
+            return value.Split(delimiter.ToCharArray()).Select(v => v.ToUpper()).ToArray();
+        }
+    }
+}

+ 37 - 13
Jint/Options.cs

@@ -79,7 +79,7 @@ namespace Jint
         }
         }
 
 
         /// <summary>
         /// <summary>
-         /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
+        /// Adds a <see cref="IObjectConverter"/> instance to convert CLR types to <see cref="JsValue"/>
         /// </summary>
         /// </summary>
         public Options AddObjectConverter(IObjectConverter objectConverter)
         public Options AddObjectConverter(IObjectConverter objectConverter)
         {
         {
@@ -114,21 +114,45 @@ namespace Jint
 
 
             foreach (var overloads in methods.GroupBy(x => x.Name))
             foreach (var overloads in methods.GroupBy(x => x.Name))
             {
             {
-                var functionInstance = new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()));
-                var descriptor = new PropertyDescriptor(functionInstance, PropertyFlag.None);
 
 
-                // make sure we register both lower case and upper case
+                PropertyDescriptor CreateMethodInstancePropertyDescriptor(ClrFunctionInstance clrFunctionInstance)
+                {
+                    var instance = clrFunctionInstance == null
+                        ? new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()))
+                        : new MethodInfoFunctionInstance(engine, MethodDescriptor.Build(overloads.ToList()), clrFunctionInstance);
+
+                    return new PropertyDescriptor(instance, PropertyFlag.NonConfigurable);
+                }
+
                 JsValue key = overloads.Key;
                 JsValue key = overloads.Key;
-                if (!prototype.HasOwnProperty(key))
+                PropertyDescriptor descriptorWithFallback = null;
+                PropertyDescriptor descriptorWithoutFallback = null;
+                
+                if (prototype.HasOwnProperty(key) && prototype.GetOwnProperty(key).Value is ClrFunctionInstance clrFunctionInstance)
+                {
+                    descriptorWithFallback = CreateMethodInstancePropertyDescriptor(clrFunctionInstance);
+                    prototype.SetOwnProperty(key, descriptorWithFallback);
+                }
+                else
                 {
                 {
-                    prototype.SetOwnProperty(key, descriptor);
+                    descriptorWithoutFallback = CreateMethodInstancePropertyDescriptor(null);
+                    prototype.SetOwnProperty(key, descriptorWithoutFallback);
                 }
                 }
+
+                // make sure we register both lower case and upper case
                 if (char.IsUpper(overloads.Key[0]))
                 if (char.IsUpper(overloads.Key[0]))
                 {
                 {
                     key = char.ToLower(overloads.Key[0]) + overloads.Key.Substring(1);
                     key = char.ToLower(overloads.Key[0]) + overloads.Key.Substring(1);
-                    if (!prototype.HasOwnProperty(key))
+
+                    if (prototype.HasOwnProperty(key) && prototype.GetOwnProperty(key).Value is ClrFunctionInstance lowerclrFunctionInstance)
+                    {
+                        descriptorWithFallback = descriptorWithFallback ?? CreateMethodInstancePropertyDescriptor(lowerclrFunctionInstance);
+                        prototype.SetOwnProperty(key, descriptorWithFallback);
+                    }
+                    else
                     {
                     {
-                        prototype.SetOwnProperty(key, descriptor);
+                        descriptorWithoutFallback = descriptorWithoutFallback ?? CreateMethodInstancePropertyDescriptor(null);
+                        prototype.SetOwnProperty(key, descriptorWithoutFallback);
                     }
                     }
                 }
                 }
             }
             }
@@ -283,13 +307,13 @@ namespace Jint
             {
             {
                 configuration?.Invoke(engine);
                 configuration?.Invoke(engine);
             }
             }
-            
+
             // add missing bits if needed
             // add missing bits if needed
             if (_allowClr)
             if (_allowClr)
             {
             {
                 engine.Global.SetProperty("System", new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
                 engine.Global.SetProperty("System", new PropertyDescriptor(new NamespaceReference(engine, "System"), PropertyFlag.AllForbidden));
                 engine.Global.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
                 engine.Global.SetProperty("importNamespace", new PropertyDescriptor(new ClrFunctionInstance(
-                    engine, 
+                    engine,
                     "importNamespace",
                     "importNamespace",
                     func: (thisObj, arguments) => new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))), PropertyFlag.AllForbidden));
                     func: (thisObj, arguments) => new NamespaceReference(engine, TypeConverter.ToString(arguments.At(0)))), PropertyFlag.AllForbidden));
             }
             }
@@ -298,7 +322,7 @@ namespace Jint
             {
             {
                 AttachExtensionMethodsToPrototypes(engine);
                 AttachExtensionMethodsToPrototypes(engine);
             }
             }
-            
+
             // ensure defaults
             // ensure defaults
             engine.ClrTypeConverter ??= new DefaultTypeConverter(engine);
             engine.ClrTypeConverter ??= new DefaultTypeConverter(engine);
         }
         }
@@ -330,12 +354,12 @@ namespace Jint
 
 
         internal TimeZoneInfo _LocalTimeZone => _localTimeZone;
         internal TimeZoneInfo _LocalTimeZone => _localTimeZone;
 
 
-        internal IReferenceResolver  ReferenceResolver => _referenceResolver;
+        internal IReferenceResolver ReferenceResolver => _referenceResolver;
 
 
         private sealed class DefaultReferenceResolver : IReferenceResolver
         private sealed class DefaultReferenceResolver : IReferenceResolver
         {
         {
             public static readonly DefaultReferenceResolver Instance = new DefaultReferenceResolver();
             public static readonly DefaultReferenceResolver Instance = new DefaultReferenceResolver();
-            
+
             private DefaultReferenceResolver()
             private DefaultReferenceResolver()
             {
             {
             }
             }

+ 14 - 1
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -11,6 +11,7 @@ namespace Jint.Runtime.Interop
     {
     {
         private static readonly JsString _name = new JsString("Function");
         private static readonly JsString _name = new JsString("Function");
         private readonly MethodDescriptor[] _methods;
         private readonly MethodDescriptor[] _methods;
+        private readonly ClrFunctionInstance _fallbackClrFunctionInstance;
 
 
         public MethodInfoFunctionInstance(Engine engine, MethodDescriptor[] methods)
         public MethodInfoFunctionInstance(Engine engine, MethodDescriptor[] methods)
             : base(engine, _name)
             : base(engine, _name)
@@ -19,6 +20,12 @@ namespace Jint.Runtime.Interop
             _prototype = engine.Function.PrototypeObject;
             _prototype = engine.Function.PrototypeObject;
         }
         }
 
 
+        public MethodInfoFunctionInstance(Engine engine, MethodDescriptor[] methods, ClrFunctionInstance fallbackClrFunctionInstance)
+            : this(engine, methods)
+        {
+            _fallbackClrFunctionInstance = fallbackClrFunctionInstance;
+        }
+
         public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
         public override JsValue Call(JsValue thisObject, JsValue[] jsArguments)
         {
         {
             JsValue[] ArgumentProvider(MethodDescriptor method)
             JsValue[] ArgumentProvider(MethodDescriptor method)
@@ -28,7 +35,9 @@ namespace Jint.Runtime.Interop
                     var jsArgumentsTemp = new JsValue[1 + jsArguments.Length];
                     var jsArgumentsTemp = new JsValue[1 + jsArguments.Length];
                     jsArgumentsTemp[0] = thisObject;
                     jsArgumentsTemp[0] = thisObject;
                     Array.Copy(jsArguments, 0, jsArgumentsTemp, 1, jsArguments.Length);
                     Array.Copy(jsArguments, 0, jsArgumentsTemp, 1, jsArguments.Length);
-                    jsArguments = jsArgumentsTemp;
+                    return method.HasParams
+                        ? ProcessParamsArrays(jsArgumentsTemp, method)
+                        : jsArgumentsTemp;
                 }
                 }
                 return method.HasParams
                 return method.HasParams
                     ? ProcessParamsArrays(jsArguments, method)
                     ? ProcessParamsArrays(jsArguments, method)
@@ -109,6 +118,10 @@ namespace Jint.Runtime.Interop
                 }
                 }
             }
             }
 
 
+            if (_fallbackClrFunctionInstance != null)
+            {
+                return _fallbackClrFunctionInstance.Call(thisObject, jsArguments);
+            }
             return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "No public methods with the specified arguments were found.");
             return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "No public methods with the specified arguments were found.");
         }
         }