Browse Source

Ability to catch the CLR exception from an overridden arithmetic operator (#1191)

* Issue1186: Added type Dimensional and test ShouldBeJavaScriptException

* Issue1186: Added ExceptionHelper to method TryOperatorOverloading
AndrewLityagin 3 years ago
parent
commit
2303348789

+ 68 - 0
Jint.Tests/Runtime/Domain/Dimensional.cs

@@ -0,0 +1,68 @@
+using System;
+using System.Linq;
+
+namespace Jint.Tests.Runtime.Domain
+{
+    public class Dimensional : IComparable<Dimensional>
+    {
+        private readonly MeasureUnit[] PossibleMeasureUnits = new MeasureUnit[] { new MeasureUnit("Mass", "kg", 1.0), new MeasureUnit("Mass", "gr", 0.001), new MeasureUnit("Count", "piece", 1.0) };
+
+        public MeasureUnit MeasureUnit { get; private set; }
+
+        public double Value { get; private set; }
+
+        public double NormalizatedValue
+        {
+            get
+            {
+                return Value * MeasureUnit.RelativeValue;
+            }
+        }
+
+        public Dimensional(string measureUnit, double value)
+        {
+            MeasureUnit = GetMeasureUnitByName(measureUnit);
+            Value = value;
+        }
+
+        public static Dimensional operator +(Dimensional left, Dimensional right)
+        {
+            if (left.MeasureUnit.MeasureType != right.MeasureUnit.MeasureType)
+                throw new InvalidOperationException("Dimensionals with different measure types are non-summable");
+
+            return new Dimensional(left.MeasureUnit.RelativeValue <= right.MeasureUnit.RelativeValue ? left.MeasureUnit.Name : right.MeasureUnit.Name,
+                                                            left.Value * left.MeasureUnit.RelativeValue + right.Value * right.MeasureUnit.RelativeValue);
+        }
+
+        private MeasureUnit GetMeasureUnitByName(string name)
+        {
+            return PossibleMeasureUnits.FirstOrDefault(mu => mu.Name == name);
+        }
+
+        public int CompareTo(Dimensional? obj)
+        {
+            if (MeasureUnit.MeasureType != obj.MeasureUnit.MeasureType)
+                throw new InvalidOperationException("Dimensionals with different measure types are non-comparable");
+            return NormalizatedValue.CompareTo(obj.NormalizatedValue);
+        }
+
+        public override string ToString()
+        {
+            return Value.ToString() + " " + MeasureUnit.Name;
+        }
+    }
+
+    public class MeasureUnit
+    {
+        public string MeasureType { get; set; }
+        public string Name { get; set; }
+        public double RelativeValue { get; set; }
+
+        public MeasureUnit(string measureType, string Name, double relativeValue)
+        {
+            this.MeasureType = measureType;
+            this.Name = Name;
+            this.RelativeValue = relativeValue;
+        }
+    }
+}

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

@@ -2969,6 +2969,29 @@ namespace Jint.Tests.Runtime
             Assert.Equal(1, engine.Evaluate("Array.prototype.lastIndexOf.call(list, 'B')"));
             Assert.Equal(1, engine.Evaluate("Array.prototype.lastIndexOf.call(list, 'B')"));
         }
         }
 
 
+        [Fact]
+        public void ShouldBeJavaScriptException()
+        {
+            var engine = new Engine(cfg => cfg.AllowClr().AllowOperatorOverloading().CatchClrExceptions());
+            engine.SetValue("Dimensional", typeof(Dimensional));
+
+            engine.Execute(@"	
+				function Eval(param0, param1)
+				{ 
+					var result = param0 + param1;
+					return result;
+				}");
+            //Checking working cusom type
+            Assert.Equal(new Dimensional("kg", 90), (new Dimensional("kg", 30) + new Dimensional("kg", 60)));
+            Assert.Equal(new Dimensional("kg", 90), engine.Invoke("Eval", new object[] { new Dimensional("kg", 30), new Dimensional("kg", 60) }).ToObject());
+            Assert.Throws<InvalidOperationException>(() => new Dimensional("kg", 30) + new Dimensional("piece", 70));
+
+            //Checking throwing exception in ovveride operator
+            string errorMsg = string.Empty;
+            errorMsg = Assert.Throws<JavaScriptException>(() => engine.Invoke("Eval", new object[] {new Dimensional("kg", 30), new Dimensional("piece", 70)})).Message;
+            Assert.Equal("Dimensionals with different measure types are non-summable", errorMsg);
+        }
+
         private class Profile
         private class Profile
         {
         {
             public int AnyProperty => throw new NotSupportedException("NOT SUPPORTED");
             public int AnyProperty => throw new NotSupportedException("NOT SUPPORTED");

+ 4 - 2
Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs

@@ -2,6 +2,7 @@ using System;
 using System.Collections.Concurrent;
 using System.Collections.Concurrent;
 using System.Linq;
 using System.Linq;
 using System.Numerics;
 using System.Numerics;
+using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Extensions;
 using Jint.Extensions;
@@ -63,8 +64,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                         result = method.Call(context.Engine, null, arguments);
                         result = method.Call(context.Engine, null, arguments);
                         return true;
                         return true;
                     }
                     }
-                    catch
+                    catch (Exception e)
                     {
                     {
+                        ExceptionHelper.ThrowMeaningfulException(context.Engine, new TargetInvocationException(e.InnerException));
                         result = null;
                         result = null;
                         return false;
                         return false;
                     }
                     }
@@ -856,4 +858,4 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
             }
         }
         }
     }
     }
-}
+}