소스 검색

Improved method resolution (#576)

Marko Lahma 6 년 전
부모
커밋
3519bc7b23
4개의 변경된 파일70개의 추가작업 그리고 53개의 파일을 삭제
  1. 7 0
      Jint.Tests/Runtime/EngineTests.cs
  2. 27 38
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  3. 6 4
      Jint/Runtime/Interop/TypeReference.cs
  4. 30 11
      Jint/Runtime/TypeConverter.cs

+ 7 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -2028,6 +2028,13 @@ var prep = function (fn) { fn(); };
             Assert.True(result);
         }
 
+        [Fact]
+        public void CanStringifyToConsole()
+        {
+            var engine = new Engine(options => options.AllowClr(typeof(Console).Assembly));
+            engine.Execute("System.Console.WriteLine(JSON.stringify({x:12, y:14}));");
+        }
+
         [Fact]
         public void ShouldNotCompareClrInstancesWithObjects()
         {

+ 27 - 38
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -1,5 +1,4 @@
-using System;
-using System.Globalization;
+using System.Globalization;
 using System.Linq.Expressions;
 using System.Reflection;
 using Jint.Native;
@@ -25,11 +24,18 @@ namespace Jint.Runtime.Interop
 
         public JsValue Invoke(MethodInfo[] methodInfos, JsValue thisObject, JsValue[] jsArguments)
         {
-            var arguments = ProcessParamsArrays(jsArguments, methodInfos);
+            JsValue[] ArgumentProvider(MethodInfo method, bool hasParams) =>
+                hasParams
+                    ? ProcessParamsArrays(jsArguments, method)
+                    : jsArguments;
+
             var converter = Engine.ClrTypeConverter;
 
-            foreach (var method in TypeConverter.FindBestMatch(methodInfos, arguments))
+            foreach (var tuple in TypeConverter.FindBestMatch(methodInfos, ArgumentProvider))
             {
+                var method = tuple.Item1;
+                var arguments = tuple.Item2;
+
                 var parameters = new object[arguments.Length];
                 var methodParameters = method.GetParameters();
                 var argumentsMatch = true;
@@ -99,49 +105,32 @@ namespace Jint.Runtime.Interop
         }
 
         /// <summary>
-        /// Reduces a flat list of parameters to a params array
+        /// Reduces a flat list of parameters to a params array, if needed
         /// </summary>
-        private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodInfo[] methodInfos)
+        private JsValue[] ProcessParamsArrays(JsValue[] jsArguments, MethodInfo methodInfo)
         {
-            foreach (var methodInfo in methodInfos)
-            {
-                var parameters = methodInfo.GetParameters();
+            var parameters = methodInfo.GetParameters();
 
-                bool hasParamArrayAttribute = false;
-                foreach (var parameter in parameters)
-                {
-                    if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute)))
-                    {
-                        hasParamArrayAttribute = true;
-                        break;
-                    }
-                }
-                if (!hasParamArrayAttribute)
-                    continue;
+            var nonParamsArgumentsCount = parameters.Length - 1;
+            if (jsArguments.Length < nonParamsArgumentsCount)
+                return jsArguments;
 
-                var nonParamsArgumentsCount = parameters.Length - 1;
-                if (jsArguments.Length < nonParamsArgumentsCount)
-                    continue;
-
-                var argsToTransform = jsArguments.Skip(nonParamsArgumentsCount);
+            var argsToTransform = jsArguments.Skip(nonParamsArgumentsCount);
 
-                if (argsToTransform.Length == 1 && argsToTransform[0].IsArray())
-                    continue;
+            if (argsToTransform.Length == 1 && argsToTransform[0].IsArray())
+                return jsArguments;
 
-                var jsArray = Engine.Array.Construct(Arguments.Empty);
-                Engine.Array.PrototypeObject.Push(jsArray, argsToTransform);
+            var jsArray = Engine.Array.Construct(Arguments.Empty);
+            Engine.Array.PrototypeObject.Push(jsArray, argsToTransform);
 
-                var newArgumentsCollection = new JsValue[nonParamsArgumentsCount + 1];
-                for (var j = 0; j < nonParamsArgumentsCount; ++j)
-                {
-                    newArgumentsCollection[j] = jsArguments[j];
-                }
-
-                newArgumentsCollection[nonParamsArgumentsCount] = jsArray;
-                return newArgumentsCollection;
+            var newArgumentsCollection = new JsValue[nonParamsArgumentsCount + 1];
+            for (var j = 0; j < nonParamsArgumentsCount; ++j)
+            {
+                newArgumentsCollection[j] = jsArguments[j];
             }
 
-            return jsArguments;
+            newArgumentsCollection[nonParamsArgumentsCount] = jsArray;
+            return newArgumentsCollection;
         }
 
     }

+ 6 - 4
Jint/Runtime/Interop/TypeReference.cs

@@ -53,8 +53,10 @@ namespace Jint.Runtime.Interop
 
             var constructors = ReferenceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
 
-            foreach (var method in TypeConverter.FindBestMatch(constructors, arguments))
+            foreach (var tuple in TypeConverter.FindBestMatch(constructors, (info, b) => arguments))
             {
+                var method = tuple.Item1;
+
                 var parameters = new object[arguments.Length];
                 var methodParameters = method.GetParameters();
                 try
@@ -76,9 +78,9 @@ namespace Jint.Runtime.Interop
                         }
                     }
 
-                    var constructor = (ConstructorInfo)method;
+                    var constructor = (ConstructorInfo) method;
                     var instance = constructor.Invoke(parameters);
-                    var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
+                    var result = TypeConverter.ToObject(Engine, FromObject(Engine, instance));
 
                     // todo: cache method info
 
@@ -196,7 +198,7 @@ namespace Jint.Runtime.Interop
                 }
             }
 
-            if (methodInfo?.Count == 0)
+            if (methodInfo == null || methodInfo.Count == 0)
             {
                 return PropertyDescriptor.Undefined;
             }

+ 30 - 11
Jint/Runtime/TypeConverter.cs

@@ -462,32 +462,50 @@ namespace Jint.Runtime
             }
         }
 
-        public static IEnumerable<MethodBase> FindBestMatch<T>(T[] methods, JsValue[] arguments) where T : MethodBase
+        public static IEnumerable<Tuple<MethodBase, JsValue[]>> FindBestMatch<T>(T[] methods, Func<T, bool, JsValue[]> argumentProvider) where T : MethodBase
         {
-            var matchingByParameterCount = new List<T>();
+            List<Tuple<T, JsValue[]>> matchingByParameterCount = null;
             foreach (var m in methods)
             {
-                if (m.GetParameters().Length == arguments.Length)
+                bool hasParams = false;
+                var parameterInfos = m.GetParameters();
+                foreach (var parameter in parameterInfos)
                 {
-                    matchingByParameterCount.Add(m);
+                    if (Attribute.IsDefined(parameter, typeof(ParamArrayAttribute)))
+                    {
+                        hasParams = true;
+                        break;
+                    }
+                }
+
+                var arguments = argumentProvider(m, hasParams);
+                if (parameterInfos.Length == arguments.Length)
+                {
+                    if (methods.Length == 0 && arguments.Length == 0)
+                    {
+                        yield return new Tuple<MethodBase, JsValue[]>(m, arguments);
+                        yield break;
+                    }
+
+                    matchingByParameterCount = matchingByParameterCount ?? new List<Tuple<T, JsValue[]>>();
+                    matchingByParameterCount.Add(new Tuple<T, JsValue[]>(m, arguments));
                 }
             }
 
-            if (matchingByParameterCount.Count == 1 && arguments.Length == 0)
+            if (matchingByParameterCount == null)
             {
-                yield return matchingByParameterCount[0];
                 yield break;
             }
 
-            foreach (var method in matchingByParameterCount)
+            foreach (var tuple in matchingByParameterCount)
             {
                 var perfectMatch = true;
-                var parameters = method.GetParameters();
+                var parameters = tuple.Item1.GetParameters();
+                var arguments = tuple.Item2;
                 for (var i = 0; i < arguments.Length; i++)
                 {
                     var arg = arguments[i].ToObject();
                     var paramType = parameters[i].ParameterType;
-
                     if (arg == null)
                     {
                         if (!TypeIsNullable(paramType))
@@ -505,14 +523,15 @@ namespace Jint.Runtime
 
                 if (perfectMatch)
                 {
-                    yield return method;
+                    yield return new Tuple<MethodBase, JsValue[]>(tuple.Item1, arguments);
                     yield break;
                 }
             }
 
             for (var i = 0; i < matchingByParameterCount.Count; i++)
             {
-                yield return matchingByParameterCount[i];
+                var tuple = matchingByParameterCount[i];
+                yield return new Tuple<MethodBase, JsValue[]>(tuple.Item1, tuple.Item2);
             }
         }