Browse Source

Support ExpandoObject in EnumerateOwnPropertyKeys (#862)

Marko Lahma 4 years ago
parent
commit
c117961479
2 changed files with 70 additions and 1 deletions
  1. 60 0
      Jint.Tests/Runtime/InteropTests.cs
  2. 10 1
      Jint/Runtime/Interop/ObjectWrapper.cs

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

@@ -43,6 +43,66 @@ namespace Jint.Tests.Runtime
         {
         {
             _engine.Execute(source);
             _engine.Execute(source);
         }
         }
+        
+        public class Foo
+        {
+            public static Bar GetBar() => new Bar();
+        }
+
+        public class Bar
+        {
+            public string Test { get; set; } = "123";
+        }
+
+        [Fact]
+        public void ShouldStringifyNetObjects()
+        {
+            _engine.SetValue("foo", new Foo());
+            var json = _engine.Execute("JSON.stringify(foo.GetBar())").GetCompletionValue().AsString();
+            Assert.Equal("{\"Test\":\"123\"}", json);
+        }
+
+        [Fact]
+        public void EngineShouldStringifyAnExpandoObjectCorrectly()
+        {
+            var engine = new Engine();
+
+            dynamic expando = new ExpandoObject();
+            expando.foo = 5;
+            expando.bar = "A string";
+            engine.SetValue(nameof(expando), expando);
+
+            var result = engine.Execute($"JSON.stringify({nameof(expando)})").GetCompletionValue().AsString();
+            Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result);
+        }
+
+        [Fact]
+        public void EngineShouldStringifyADictionaryOfStringAndObjectCorrectly()
+        {
+            var engine = new Engine();
+
+            var dictionary = new Dictionary<string,object> {
+                { "foo", 5 },
+                { "bar", "A string"},
+            };
+            engine.SetValue(nameof(dictionary), dictionary);
+
+            var result = engine.Execute($"JSON.stringify({nameof(dictionary)})").GetCompletionValue().AsString();
+            Assert.Equal("{\"foo\":5,\"bar\":\"A string\"}", result);
+        }
+
+        [Fact]
+        public void EngineShouldRoundtripParsedJSONBackToStringCorrectly()
+        {
+            var engine = new Engine();
+
+            const string json = "{\"foo\":5,\"bar\":\"A string\"}";
+            var parsed = engine.Execute($"JSON.parse('{json}')").GetCompletionValue().ToObject();
+            engine.SetValue(nameof(parsed), parsed);
+            
+            var result = engine.Execute($"JSON.stringify({nameof(parsed)})").GetCompletionValue().AsString();
+            Assert.Equal(json, result);
+        }
 
 
         [Fact]
         [Fact]
         public void PrimitiveTypesCanBeSet()
         public void PrimitiveTypesCanBeSet()

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

@@ -147,7 +147,16 @@ namespace Jint.Runtime.Interop
             var processed = basePropertyKeys.Count > 0 ? new HashSet<JsValue>() : null;
             var processed = basePropertyKeys.Count > 0 ? new HashSet<JsValue>() : null;
 
 
             var includeStrings = (types & Types.String) != 0;
             var includeStrings = (types & Types.String) != 0;
-            if (Target is IDictionary dictionary && includeStrings)
+            if (includeStrings && Target is IDictionary<string, object> stringKeyedDictionary) // expando object for instance
+            {
+                foreach (var key in stringKeyedDictionary.Keys)
+                {
+                    var jsString = JsString.Create(key);
+                    processed?.Add(jsString);
+                    yield return jsString;
+                }
+            }
+            else if (includeStrings && Target is IDictionary dictionary)
             {
             {
                 // we take values exposed as dictionary keys only 
                 // we take values exposed as dictionary keys only 
                 foreach (var key in dictionary.Keys)
                 foreach (var key in dictionary.Keys)