Browse Source

Support object wrapper iterator (#749)

Marko Lahma 5 years ago
parent
commit
a9ea78d08b

+ 60 - 1
Jint.Tests/Runtime/InteropTests.cs

@@ -2032,7 +2032,7 @@ namespace Jint.Tests.Runtime
         }
         }
 
 
         [Fact]
         [Fact]
-        public void ShouldNotResolvetoPrimitiveSymbol()
+        public void ShouldNotResolveToPrimitiveSymbol()
         {
         {
             var engine = new Engine(options => 
             var engine = new Engine(options => 
                 options.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly));
                 options.AllowClr(typeof(FloatIndexer).GetTypeInfo().Assembly));
@@ -2078,5 +2078,64 @@ namespace Jint.Tests.Runtime
             engine.SetValue("dictionaryTest", new DictionaryTest());
             engine.SetValue("dictionaryTest", new DictionaryTest());
             engine.Execute("dictionaryTest.test2({ values: { a: 1 } });");
             engine.Execute("dictionaryTest.test2({ values: { a: 1 } });");
         }
         }
+
+        [Fact]
+        public void ShouldSupportSpreadForDictionary()
+        {
+            var engine = new Engine();
+            var state = new Dictionary<string, object>
+            {
+                {"invoice", new Dictionary<string, object> {["number"] = "42"}}
+            };
+            engine.SetValue("state", state);
+
+            var result = (IDictionary<string, object>) engine
+                .Execute("({ supplier: 'S1', ...state.invoice })")
+                .GetCompletionValue()
+                .ToObject();
+
+            Assert.Equal("S1", result["supplier"]);
+            Assert.Equal("42", result["number"]);            
+        }
+        
+        [Fact]
+        public void ShouldSupportSpreadForDictionary2()
+        {
+            var engine = new Engine();
+            var state = new Dictionary<string, object>
+            {
+                {"invoice", new Dictionary<string, object> {["number"] = "42"}}
+            };
+            engine.SetValue("state", state);
+
+            var result = (IDictionary<string, object>) engine
+                .Execute("function getValue() { return {supplier: 'S1', ...state.invoice}; }")
+                .Invoke("getValue")
+                .ToObject();
+            
+            Assert.Equal("S1", result["supplier"]);
+            Assert.Equal("42", result["number"]);    
+        }        
+
+        [Fact]
+        public void ShouldSupportSpreadForObject()
+        {
+            var engine = new Engine();
+            var person = new Person
+            {
+                Name = "Mike",
+                Age = 20
+            };
+            engine.SetValue("p", person);
+
+            var result = (IDictionary<string, object>) engine
+                .Execute("({ supplier: 'S1', ...p })")
+                .GetCompletionValue()
+                .ToObject();
+
+            Assert.Equal("S1", result["supplier"]);
+            Assert.Equal("Mike", result["Name"]);         
+            Assert.Equal(20d, result["Age"]);         
+        }
     }
     }
 }
 }

+ 2 - 2
Jint/Native/Iterator/IteratorInstance.cs

@@ -296,12 +296,12 @@ namespace Jint.Native.Iterator
             }
             }
         }
         }
 
 
-        internal class ObjectWrapper : IIterator
+        internal class ObjectIterator : IIterator
         {
         {
             private readonly ObjectInstance _target;
             private readonly ObjectInstance _target;
             private readonly ICallable _nextMethod;
             private readonly ICallable _nextMethod;
 
 
-            public ObjectWrapper(ObjectInstance target)
+            public ObjectIterator(ObjectInstance target)
             {
             {
                 _target = target;
                 _target = target;
                 _nextMethod = target.Get(CommonProperties.Next, target) as ICallable
                 _nextMethod = target.Get(CommonProperties.Next, target) as ICallable

+ 1 - 1
Jint/Native/JsValue.cs

@@ -202,7 +202,7 @@ namespace Jint.Native
             }
             }
             else
             else
             {
             {
-                iterator = new IteratorInstance.ObjectWrapper(obj);
+                iterator = new IteratorInstance.ObjectIterator(obj);
             }
             }
             return true;
             return true;
         }
         }

+ 2 - 1
Jint/Runtime/Descriptors/Specialized/IndexDescriptor.cs

@@ -13,7 +13,8 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly PropertyInfo _indexer;
         private readonly PropertyInfo _indexer;
         private readonly MethodInfo _containsKey;
         private readonly MethodInfo _containsKey;
 
 
-        public IndexDescriptor(Engine engine, Type targetType, string key, object target) : base(PropertyFlag.CustomJsValue)
+        public IndexDescriptor(Engine engine, Type targetType, string key, object target)
+            : base(PropertyFlag.Enumerable | PropertyFlag.CustomJsValue)
         {
         {
             _engine = engine;
             _engine = engine;
             _target = target;
             _target = target;

+ 2 - 1
Jint/Runtime/Descriptors/Specialized/PropertyInfoDescriptor.cs

@@ -10,7 +10,8 @@ namespace Jint.Runtime.Descriptors.Specialized
         private readonly PropertyInfo _propertyInfo;
         private readonly PropertyInfo _propertyInfo;
         private readonly object _item;
         private readonly object _item;
 
 
-        public PropertyInfoDescriptor(Engine engine, PropertyInfo propertyInfo, object item) : base(PropertyFlag.CustomJsValue)
+        public PropertyInfoDescriptor(Engine engine, PropertyInfo propertyInfo, object item) 
+            : base(PropertyFlag.Enumerable | PropertyFlag.CustomJsValue)
         {
         {
             _engine = engine;
             _engine = engine;
             _propertyInfo = propertyInfo;
             _propertyInfo = propertyInfo;

+ 47 - 4
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -1,9 +1,11 @@
 using System;
 using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Reflection;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Object;
 using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
 using Jint.Runtime.Descriptors.Specialized;
 
 
@@ -28,9 +30,10 @@ namespace Jint.Runtime.Interop
                     return;
                     return;
                 }
                 }
                 IsArrayLike = true;
                 IsArrayLike = true;
+
                 var functionInstance = new ClrFunctionInstance(engine, "length", (thisObj, arguments) => JsNumber.Create((int) lengthProperty.GetValue(obj)));
                 var functionInstance = new ClrFunctionInstance(engine, "length", (thisObj, arguments) => JsNumber.Create((int) lengthProperty.GetValue(obj)));
                 var descriptor = new GetSetPropertyDescriptor(functionInstance, Undefined, PropertyFlag.Configurable);
                 var descriptor = new GetSetPropertyDescriptor(functionInstance, Undefined, PropertyFlag.Configurable);
-                AddProperty(CommonProperties.Length, descriptor);
+                SetProperty(KnownKeys.Length, descriptor);
             }
             }
         }
         }
 
 
@@ -80,18 +83,58 @@ namespace Jint.Runtime.Interop
             return true;
             return true;
         }
         }
 
 
-        public override PropertyDescriptor GetOwnProperty(JsValue property)
+        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
         {
         {
-            if (property.IsSymbol())
+            var propertyKeys = base.GetOwnPropertyKeys(types);
+            
+            if (Target is IDictionary dictionary)
             {
             {
-                return PropertyDescriptor.Undefined;
+                // we take values exposed as dictionary keys only 
+                foreach (var key in dictionary.Keys)
+                {
+                    if (_engine.ClrTypeConverter.TryConvert(key, typeof(string), CultureInfo.InvariantCulture, out var stringKey))
+                    {
+                        propertyKeys.Add(JsString.Create((string) stringKey));
+                    }
+                }
             }
             }
+            else
+            {
+                // we take public properties and fields
+                var type = Target.GetType();
+                foreach (var p in type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+                {
+                    var indexParameters = p.GetIndexParameters();
+                    if (indexParameters.Length == 0)
+                    {
+                        propertyKeys.Add(p.Name);
+                    }
+                }
+            
+                foreach (var f in type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
+                {
+                    propertyKeys.Add(f.Name);
+                }
+            }
+
+            return propertyKeys;
+        }
 
 
+        public override PropertyDescriptor GetOwnProperty(JsValue property)
+        {
             if (TryGetProperty(property, out var x))
             if (TryGetProperty(property, out var x))
             {
             {
                 return x;
                 return x;
             }
             }
 
 
+            if (property.IsSymbol() && property == GlobalSymbolRegistry.Iterator)
+            {
+                var iteratorFunction = new ClrFunctionInstance(Engine, "iterator", (thisObject, arguments) => _engine.Iterator.Construct(this), 1, PropertyFlag.Configurable);
+                var iteratorProperty = new PropertyDescriptor(iteratorFunction, PropertyFlag.Configurable | PropertyFlag.Writable);
+                SetProperty(GlobalSymbolRegistry.Iterator, iteratorProperty);
+                return iteratorProperty;
+            }
+            
             var type = Target.GetType();
             var type = Target.GetType();
             var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, property.ToString());
             var key = new Engine.ClrPropertyDescriptorFactoriesKey(type, property.ToString());
 
 

+ 1 - 0
Jint/Runtime/KnownKeys.cs

@@ -4,5 +4,6 @@ namespace Jint.Runtime
     {
     {
         internal static readonly Key Arguments = "arguments";
         internal static readonly Key Arguments = "arguments";
         internal static readonly Key Eval = "eval";
         internal static readonly Key Eval = "eval";
+        internal static readonly Key Length = "length";
     }
     }
 }
 }