Browse Source

Implementing CLR method invocation

Sebastien Ros 11 years ago
parent
commit
578c6014ce

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

@@ -63,6 +63,21 @@ namespace Jint.Tests.Runtime
             ");
         }
 
+        [Fact]
+        public void CanInvokeObjectMethods()
+        {
+            var p = new Person
+            {
+                Name = "Mickey Mouse"
+            };
+
+            _engine.SetValue("p", p);
+
+            RunTest(@"
+                assert(p.ToString() === 'Mickey Mouse');
+            ");
+        }
+
         [Fact]
         public void CanSetObjectProperties()
         {
@@ -117,6 +132,11 @@ namespace Jint.Tests.Runtime
         public class Person
         {
             public string Name { get; set; }
+
+            public override string ToString()
+            {
+                return Name;
+            }
         }
     }
 }

+ 1 - 0
Jint/Jint.csproj

@@ -158,6 +158,7 @@
     <Compile Include="Runtime\Environments\LexicalEnvironment.cs" />
     <Compile Include="Runtime\Environments\ObjectEnvironmentRecord.cs" />
     <Compile Include="Runtime\ExpressionIntepreter.cs" />
+    <Compile Include="Runtime\Interop\MethodInfoFunctionInstance.cs" />
     <Compile Include="Runtime\Interop\ClrFunctionInstance.cs" />
     <Compile Include="Runtime\Interop\ObjectWrapper .cs" />
     <Compile Include="Runtime\Interop\SetterFunctionInstance.cs" />

+ 4 - 5
Jint/Runtime/Descriptors/Specialized/ClrDataDescriptor.cs

@@ -5,16 +5,16 @@ namespace Jint.Runtime.Descriptors.Specialized
 {
     public sealed class ClrDataDescriptor : PropertyDescriptor
     {
+        private readonly Engine _engine;
         private readonly PropertyInfo _propertyInfo;
         private readonly object _item;
-        private JsValue? _value;
 
         public ClrDataDescriptor(Engine engine, PropertyInfo propertyInfo, object item)
         {
+            _engine = engine;
             _propertyInfo = propertyInfo;
             _item = item;
 
-            _value = JsValue.FromObject(engine, _propertyInfo.GetValue(_item, null));
             Writable = propertyInfo.CanWrite;
         }
 
@@ -22,13 +22,12 @@ namespace Jint.Runtime.Descriptors.Specialized
         {
             get
             {
-                return _value;
+                return JsValue.FromObject(_engine, _propertyInfo.GetValue(_item, null));
             }
 
             set
             {
-                _value = value;
-                _propertyInfo.SetValue(_item, _value.GetValueOrDefault().ToObject(), null);
+                _propertyInfo.SetValue(_item, value.GetValueOrDefault().ToObject(), null);
             }
         }
     }

+ 0 - 2
Jint/Runtime/Interop/DelegateWrapper.cs

@@ -11,12 +11,10 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class DelegateWrapper : FunctionInstance
     {
-        private readonly Engine _engine;
         private readonly Delegate _d;
 
         public DelegateWrapper(Engine engine, Delegate d) : base(engine, null, null, false)
         {
-            _engine = engine;
             _d = d;
         }
 

+ 55 - 0
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+using Jint.Native;
+using Jint.Native.Function;
+
+namespace Jint.Runtime.Interop
+{
+    public sealed class MethodInfoFunctionInstance : FunctionInstance
+    {
+        private readonly MethodInfo[] _methods;
+        private readonly Func<JsValue, JsValue[], JsValue> _func;
+
+        public MethodInfoFunctionInstance(Engine engine, MethodInfo[] methods)
+            : base(engine, null, null, false)
+        {
+            _methods = methods;
+            Prototype = engine.Function.PrototypeObject;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            // filter methods with the expected number of parameters
+            var methods = _methods
+                .Where(m => m.GetParameters().Count() == arguments.Length)
+                .ToArray()
+                ;
+
+            if (!methods.Any())
+            {
+                throw new JavaScriptException(Engine.TypeError, "Invalid number of arguments");
+            }
+
+            // todo: look for compatible types    
+            var method = methods.First();
+            var parameters = new List<object>();
+            for (int i = 0; i < arguments.Length; i++)
+            {
+                parameters[i] = Convert.ChangeType(arguments[i].ToObject(), method.GetParameters()[i].ParameterType,
+                    CultureInfo.InvariantCulture);
+            }
+
+            var obj = thisObject.ToObject() as ObjectWrapper;
+
+            if (obj == null)
+            {
+                throw new JavaScriptException(Engine.TypeError, "Can't call a CLR method on a non CLR instance");
+            }
+
+            return JsValue.FromObject(Engine, method.Invoke(obj.Target, parameters.ToArray()));
+        }
+    }
+}

+ 19 - 10
Jint/Runtime/Interop/ObjectWrapper .cs

@@ -12,11 +12,11 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class ObjectWrapper : ObjectInstance
     {
-        private readonly Object _obj;
+        public Object Target { get; set; }
 
         public ObjectWrapper(Engine engine, Object obj): base(engine)
         {
-            _obj = obj;
+            Target = obj;
         }
 
         public override PropertyDescriptor GetOwnProperty(string propertyName)
@@ -27,21 +27,30 @@ namespace Jint.Runtime.Interop
                 return x;
             }
 
-            var type = _obj.GetType();
+            var type = Target.GetType();
+            
+            // look for a property
             var property = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
                 .FirstOrDefault(m => m.Name == propertyName);
 
-            if (property == null)
-            {
-                return PropertyDescriptor.Undefined;
-            }
-            else
+            if (property != null)
             {
-                var descriptor = new ClrDataDescriptor(Engine, property, _obj);
+                var descriptor = new ClrDataDescriptor(Engine, property, Target);
                 Properties.Add(propertyName, descriptor);
                 return descriptor;
             }
-            
+
+            // if no properties were found then look for a method 
+            var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public)
+                .Where(m => m.Name == propertyName)
+                .ToArray();
+
+            if (methods.Any())
+            {
+                return new PropertyDescriptor(new MethodInfoFunctionInstance(Engine, methods), false, true, false);
+            }
+
+            return PropertyDescriptor.Undefined;
         }
     }
 }