Sfoglia il codice sorgente

Harmonize property iteration for ObjectWrapper (#750)

Marko Lahma 5 anni fa
parent
commit
5bd7f46576
2 ha cambiato i file con 78 aggiunte e 9 eliminazioni
  1. 36 0
      Jint.Tests/Runtime/InteropTests.cs
  2. 42 9
      Jint/Runtime/Interop/ObjectWrapper.cs

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

@@ -2137,5 +2137,41 @@ namespace Jint.Tests.Runtime
             Assert.Equal("Mike", result["Name"]);         
             Assert.Equal(20d, result["Age"]);         
         }
+
+        [Fact]
+        public void ShouldBeAbleToJsonStringifyClrObjects()
+        {
+            var engine = new Engine();
+
+            engine.Execute("var jsObj = { 'key1' :'value1', 'key2' : 'value2' }");
+
+            engine.SetValue("netObj", new Dictionary<string, object>()
+            {
+                {"key1", "value1"},
+                {"key2", "value2"},
+            });
+
+            var jsValue = engine.Execute("jsObj['key1']").GetCompletionValue().AsString();
+            var clrValue = engine.Execute("netObj['key1']").GetCompletionValue().AsString();
+            Assert.Equal(jsValue, clrValue);
+
+            jsValue = engine.Execute("JSON.stringify(jsObj)").GetCompletionValue().AsString();
+            clrValue = engine.Execute("JSON.stringify(netObj)").GetCompletionValue().AsString();
+            Assert.Equal(jsValue, clrValue);
+
+            // Write properties on screen using showProps function defined on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
+            engine.Execute(@"function showProps(obj, objName) {
+  var result = """";
+  for (var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+      result += objName + ""."" + i + "" = "" + obj[i] + ""\n"";
+    }
+    }
+  return result;
+}");
+            jsValue = engine.Execute("showProps(jsObj, 'theObject')").GetCompletionValue().AsString();
+            clrValue = engine.Execute("showProps(jsObj, 'theObject')").GetCompletionValue().AsString();
+            Assert.Equal(jsValue, clrValue);
+        }
     }
 }

+ 42 - 9
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -85,20 +85,38 @@ namespace Jint.Runtime.Interop
 
         public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
         {
-            var propertyKeys = base.GetOwnPropertyKeys(types);
-            
-            if (Target is IDictionary dictionary)
+            return new List<JsValue>(EnumerateOwnPropertyKeys(types));
+        }
+
+        public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
+        {
+            foreach (var key in EnumerateOwnPropertyKeys(Types.String | Types.Symbol))
+            {
+                yield return new KeyValuePair<JsValue, PropertyDescriptor>(key, GetOwnProperty(key));
+            }
+        }
+
+        private IEnumerable<JsValue> EnumerateOwnPropertyKeys(Types types)
+        {
+            var basePropertyKeys = base.GetOwnPropertyKeys(types);
+            // prefer object order, add possible other properties after
+            var processed = basePropertyKeys.Count > 0 ? new HashSet<JsValue>() : null;
+
+            var includeStrings = (types & Types.String) != 0;
+            if (Target is IDictionary dictionary && includeStrings)
             {
                 // 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));
+                        var jsString = JsString.Create((string) stringKey);
+                        processed?.Add(jsString);
+                        yield return jsString;
                     }
                 }
             }
-            else
+            else if (includeStrings)
             {
                 // we take public properties and fields
                 var type = Target.GetType();
@@ -107,17 +125,32 @@ namespace Jint.Runtime.Interop
                     var indexParameters = p.GetIndexParameters();
                     if (indexParameters.Length == 0)
                     {
-                        propertyKeys.Add(p.Name);
+                        var jsString = JsString.Create(p.Name);
+                        processed?.Add(jsString);
+                        yield return jsString;
                     }
                 }
-            
+
                 foreach (var f in type.GetFields(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public))
                 {
-                    propertyKeys.Add(f.Name);
+                    var jsString = JsString.Create(f.Name);
+                    processed?.Add(jsString);
+                    yield return jsString;
                 }
             }
 
-            return propertyKeys;
+            if (processed != null)
+            {
+                // we have base keys
+                for (var i = 0; i < basePropertyKeys.Count; i++)
+                {
+                    var key = basePropertyKeys[i];
+                    if (processed.Add(key))
+                    {
+                        yield return key;
+                    }
+                }
+            }
         }
 
         public override PropertyDescriptor GetOwnProperty(JsValue property)