Browse Source

Better enum support, ObjectWrapper dependencies replaced by IObjectWrapper, SetWrapObjectHandler (#603)

Steffen Liersch 6 years ago
parent
commit
01dd580456

+ 78 - 2
Jint.Tests/Runtime/InteropTests.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Linq.Expressions;
 using System.Reflection;
 using System.Reflection;
@@ -1171,6 +1172,83 @@ namespace Jint.Tests.Runtime
             Assert.Equal(Colors.Blue | Colors.Green, s.Color);
             Assert.Equal(Colors.Blue | Colors.Green, s.Color);
         }
         }
 
 
+        enum TestEnumInt32 : int
+        {
+            None,
+            One = 1,
+            Min = int.MaxValue,
+            Max = int.MaxValue,
+        }
+
+        enum TestEnumUInt32 : uint
+        {
+            None,
+            One = 1,
+            Min = uint.MaxValue,
+            Max = uint.MaxValue,
+        }
+
+        enum TestEnumInt64 : long
+        {
+            None,
+            One = 1,
+            Min = long.MaxValue,
+            Max = long.MaxValue,
+        }
+
+        enum TestEnumUInt64 : ulong
+        {
+            None,
+            One = 1,
+            Min = ulong.MaxValue,
+            Max = ulong.MaxValue,
+        }
+
+        void TestEnum<T>(T enumValue)
+        {
+            object i = Convert.ChangeType(enumValue, Enum.GetUnderlyingType(typeof(T)));
+            string s = Convert.ToString(i, CultureInfo.InvariantCulture);
+            var o = new Tuple<T>(enumValue);
+            _engine.SetValue("o", o);
+            RunTest("assert(o.Item1 === " + s + ");");
+        }
+
+        [Fact]
+        public void ShouldWorkWithEnumInt32()
+        {
+            TestEnum(TestEnumInt32.None);
+            TestEnum(TestEnumInt32.One);
+            TestEnum(TestEnumInt32.Min);
+            TestEnum(TestEnumInt32.Max);
+        }
+
+        [Fact]
+        public void ShouldWorkWithEnumUInt32()
+        {
+            TestEnum(TestEnumUInt32.None);
+            TestEnum(TestEnumUInt32.One);
+            TestEnum(TestEnumUInt32.Min);
+            TestEnum(TestEnumUInt32.Max);
+        }
+
+        [Fact]
+        public void ShouldWorkWithEnumInt64()
+        {
+            TestEnum(TestEnumInt64.None);
+            TestEnum(TestEnumInt64.One);
+            TestEnum(TestEnumInt64.Min);
+            TestEnum(TestEnumInt64.Max);
+        }
+
+        [Fact]
+        public void ShouldWorkWithEnumUInt64()
+        {
+            TestEnum(TestEnumUInt64.None);
+            TestEnum(TestEnumUInt64.One);
+            TestEnum(TestEnumUInt64.Min);
+            TestEnum(TestEnumUInt64.Max);
+        }
+
         [Fact]
         [Fact]
         public void EnumIsConvertedToNumber()
         public void EnumIsConvertedToNumber()
         {
         {
@@ -1190,7 +1268,6 @@ namespace Jint.Tests.Runtime
             ");
             ");
         }
         }
 
 
-
         [Fact]
         [Fact]
         public void ShouldConvertToEnum()
         public void ShouldConvertToEnum()
         {
         {
@@ -1239,7 +1316,6 @@ namespace Jint.Tests.Runtime
             ");
             ");
         }
         }
 
 
-
         [Fact]
         [Fact]
         public void ShouldUseExplicitPropertySetter()
         public void ShouldUseExplicitPropertySetter()
         {
         {

+ 2 - 2
Jint/Native/Array/ArrayConstructor.cs

@@ -81,7 +81,7 @@ namespace Jint.Native.Array
                 return ConstructArrayFromArrayLike(objectInstance, callable, thisArg);
                 return ConstructArrayFromArrayLike(objectInstance, callable, thisArg);
             }
             }
 
 
-            if (objectInstance is ObjectWrapper wrapper && wrapper.Target is IEnumerable enumerable)
+            if (objectInstance is IObjectWrapper wrapper && wrapper.Target is IEnumerable enumerable)
             {
             {
                 return ConstructArrayFromIEnumerable(enumerable);
                 return ConstructArrayFromIEnumerable(enumerable);
             }
             }
@@ -258,7 +258,7 @@ namespace Jint.Native.Array
 
 
                 instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
                 instance._length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
             }
             }
-            else if (arguments.Length == 1 && arguments[0] is ObjectWrapper objectWrapper)
+            else if (arguments.Length == 1 && arguments[0] is IObjectWrapper objectWrapper)
             {
             {
                 if (objectWrapper.Target is IEnumerable enumerable)
                 if (objectWrapper.Target is IEnumerable enumerable)
                 {
                 {

+ 14 - 3
Jint/Native/JsValue.cs

@@ -328,13 +328,24 @@ namespace Jint.Native
                 return new DelegateWrapper(engine, d);
                 return new DelegateWrapper(engine, d);
             }
             }
 
 
-            if (value.GetType().IsEnum)
+            Type t = value.GetType();
+            if (t.IsEnum)
             {
             {
-                return JsNumber.Create((int) value);
+                Type ut = Enum.GetUnderlyingType(t);
+
+                if (ut == typeof(ulong))
+                    return JsNumber.Create(System.Convert.ToDouble(value));
+
+                if (ut == typeof(uint) || ut == typeof(long))
+                    return JsNumber.Create(System.Convert.ToInt64(value));
+
+                return JsNumber.Create(System.Convert.ToInt32(value));
             }
             }
 
 
             // if no known type could be guessed, wrap it as an ObjectInstance
             // if no known type could be guessed, wrap it as an ObjectInstance
-            return new ObjectWrapper(engine, value);
+            var h = engine.Options._WrapObjectHandler;
+            ObjectInstance o = h != null ? h(value) : null;
+            return o ?? new ObjectWrapper(engine, value);
         }
         }
 
 
         private static JsValue Convert(Engine e, object v)
         private static JsValue Convert(Engine e, object v)

+ 15 - 0
Jint/Options.cs

@@ -4,6 +4,7 @@ using System.Globalization;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native;
+using Jint.Native.Object;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop;
 
 
 namespace Jint
 namespace Jint
@@ -15,6 +16,7 @@ namespace Jint
         private bool _allowDebuggerStatement;
         private bool _allowDebuggerStatement;
         private bool _allowClr;
         private bool _allowClr;
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
         private readonly List<IObjectConverter> _objectConverters = new List<IObjectConverter>();
+        private Func<object, ObjectInstance> _wrapObjectHandler;
         private int _maxStatements;
         private int _maxStatements;
         private long _memoryLimit;
         private long _memoryLimit;
         private int _maxRecursionDepth = -1;
         private int _maxRecursionDepth = -1;
@@ -75,6 +77,17 @@ namespace Jint
             return this;
             return this;
         }
         }
 
 
+        /// <summary>
+        /// If no known type could be guessed, objects are normally wrapped as an
+        /// ObjectInstance using class ObjectWrapper. This function can be used to
+        /// register a handler for a customized handling.
+        /// </summary>
+        public Options SetWrapObjectHandler(Func<object, ObjectInstance> wrapObjectHandler)
+        {
+            _wrapObjectHandler = wrapObjectHandler;
+            return this;
+        }
+
         /// <summary>
         /// <summary>
         /// Allows scripts to call CLR types directly like <example>System.IO.File</example>
         /// Allows scripts to call CLR types directly like <example>System.IO.File</example>
         /// </summary>
         /// </summary>
@@ -174,6 +187,8 @@ namespace Jint
 
 
         internal List<IObjectConverter> _ObjectConverters => _objectConverters;
         internal List<IObjectConverter> _ObjectConverters => _objectConverters;
 
 
+        internal Func<object, ObjectInstance> _WrapObjectHandler => _wrapObjectHandler;
+
         internal long _MemoryLimit => _memoryLimit;
         internal long _MemoryLimit => _memoryLimit;
 
 
         internal int _MaxStatements => _maxStatements;
         internal int _MaxStatements => _maxStatements;

+ 5 - 5
Jint/Runtime/Interop/TypeReference.cs

@@ -98,14 +98,14 @@ namespace Jint.Runtime.Interop
 
 
         public override bool HasInstance(JsValue v)
         public override bool HasInstance(JsValue v)
         {
         {
-            ObjectWrapper wrapper = v.As<ObjectWrapper>();
-
-            if (ReferenceEquals(wrapper, null))
+            if (v.IsObject())
             {
             {
-                return base.HasInstance(v);
+                var wrapper = v.AsObject() as IObjectWrapper;
+                if (wrapper != null)
+                    return wrapper.Target.GetType() == ReferenceType;
             }
             }
 
 
-            return wrapper.Target.GetType() == ReferenceType;
+            return base.HasInstance(v);
         }
         }
 
 
         public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)
         public override bool DefineOwnProperty(string propertyName, PropertyDescriptor desc, bool throwOnError)