Ver código fonte

Rethrow CLR function exceptions as JS errors (#1246)

Christian Rondeau 3 anos atrás
pai
commit
8488e480bc

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

@@ -3086,5 +3086,64 @@ namespace Jint.Tests.Runtime
 
             Assert.Equal("called#5#20", string.Join("#", calls));
         }
+
+        [Fact]
+        public void CanUseClrFunction()
+        {
+            var engine = new Engine();
+            engine.SetValue("fn", new ClrFunctionInstance(engine, "fn", (_, args) => (JsValue) (args[0].AsInteger() + 1)));
+
+            var result = engine.Evaluate("fn(1)");
+            
+            Assert.Equal(2, result);
+        }
+
+        [Fact]
+        public void ShouldAllowClrExceptionsThrough()
+        {
+            var engine = new Engine(opts => opts.CatchClrExceptions(exc => false));
+            engine.SetValue("fn", new ClrFunctionInstance(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error")));
+            const string Source = @"
+function wrap() {
+  fn();
+}
+wrap();
+";
+            
+            Assert.Throws<InvalidOperationException>(() => engine.Execute(Source));
+        }
+
+        [Fact]
+        public void ShouldConvertClrExceptionsToErrors()
+        {
+            var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException));
+            engine.SetValue("fn", new ClrFunctionInstance(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error")));
+            const string Source = @"
+function wrap() {
+  fn();
+}
+wrap();
+";
+            
+            var exc = Assert.Throws<JavaScriptException>(() => engine.Execute(Source));
+            Assert.Equal(exc.Message, "This is a C# error");
+        }
+        
+        [Fact]
+        public void ShouldAllowCatchingConvertedClrExceptions()
+        {
+            var engine = new Engine(opts => opts.CatchClrExceptions(exc => exc is InvalidOperationException));
+            engine.SetValue("fn", new ClrFunctionInstance(engine, "fn", (_, _) => throw new InvalidOperationException("This is a C# error")));
+            const string Source = @"
+try {
+  fn();
+} catch (e) {
+  throw new Error('Caught: ' + e.message);
+}
+";
+            
+            var exc = Assert.Throws<JavaScriptException>(() => engine.Execute(Source));
+            Assert.Equal(exc.Message, "Caught: This is a C# error");
+        }
     }
 }

+ 21 - 1
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -1,3 +1,4 @@
+using System.Runtime.ExceptionServices;
 using Jint.Native;
 using Jint.Native.Function;
 using Jint.Runtime.Descriptors;
@@ -30,7 +31,26 @@ namespace Jint.Runtime.Interop
                 : new PropertyDescriptor(JsNumber.Create(length), lengthFlags);
         }
 
-        protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) => _func(thisObject, arguments);
+        protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            try
+            {
+                return _func(thisObject, arguments);
+            }
+            catch (Exception exc)
+            {
+                if (_engine.Options.Interop.ExceptionHandler(exc))
+                {
+                    ExceptionHelper.ThrowJavaScriptException(_realm.Intrinsics.Error, exc.Message);
+                }
+                else
+                {
+                    ExceptionDispatchInfo.Capture(exc).Throw();
+                }
+
+                return Undefined;
+            }
+        }
 
         public override bool Equals(JsValue? obj)
         {