Skip to content

Commit

Permalink
Rethrow CLR function exceptions as JS errors (#1246)
Browse files Browse the repository at this point in the history
  • Loading branch information
christianrondeau committed Jul 26, 2022
1 parent e9567b9 commit 8488e48
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
59 changes: 59 additions & 0 deletions Jint.Tests/Runtime/InteropTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3086,5 +3086,64 @@ public void TypeReferenceShouldGetIntermediaryPrototype()

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");
}
}
}
22 changes: 21 additions & 1 deletion Jint/Runtime/Interop/ClrFunctionInstance.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Runtime.ExceptionServices;
using Jint.Native;
using Jint.Native.Function;
using Jint.Runtime.Descriptors;
Expand Down Expand Up @@ -30,7 +31,26 @@ public sealed class ClrFunctionInstance : FunctionInstance, IEquatable<ClrFuncti
: 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)
{
Expand Down

0 comments on commit 8488e48

Please sign in to comment.