From 8794aa03cc85ff449836b5ab4d2a6166edc1bf0f Mon Sep 17 00:00:00 2001 From: Marko Lahma Date: Sat, 21 Oct 2023 12:53:10 +0300 Subject: [PATCH] Add fast path without try-catch for CLR function call (#1651) --- Jint/Options.cs | 4 +- Jint/Runtime/Interop/ClrFunctionInstance.cs | 107 ++++++++++---------- 2 files changed, 58 insertions(+), 53 deletions(-) diff --git a/Jint/Options.cs b/Jint/Options.cs index 56f7b4d236..74a477434e 100644 --- a/Jint/Options.cs +++ b/Jint/Options.cs @@ -295,7 +295,7 @@ public class InteropOptions /// to the CLR host and interrupt the script execution. If handler returns true these exceptions are converted /// to JS errors that can be caught by the script. /// - public ExceptionHandlerDelegate ExceptionHandler { get; set; } = static exception => false; + public ExceptionHandlerDelegate ExceptionHandler { get; set; } = _defaultExceptionHandler; /// /// Assemblies to allow scripts to call CLR types directly like System.IO.File. @@ -328,6 +328,8 @@ public class InteropOptions /// public Func CreateTypeReferenceObject = (_, _, _) => null; + internal static readonly ExceptionHandlerDelegate _defaultExceptionHandler = static exception => false; + /// /// When not null, is used to serialize any CLR object in an /// passing through 'JSON.stringify'. diff --git a/Jint/Runtime/Interop/ClrFunctionInstance.cs b/Jint/Runtime/Interop/ClrFunctionInstance.cs index d777dc5a05..674baf3c4e 100644 --- a/Jint/Runtime/Interop/ClrFunctionInstance.cs +++ b/Jint/Runtime/Interop/ClrFunctionInstance.cs @@ -1,78 +1,81 @@ +using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using Jint.Native; using Jint.Native.Function; using Jint.Runtime.Descriptors; -namespace Jint.Runtime.Interop +namespace Jint.Runtime.Interop; + +/// +/// Wraps a Clr method into a FunctionInstance +/// +public sealed class ClrFunctionInstance : FunctionInstance, IEquatable { - /// - /// Wraps a Clr method into a FunctionInstance - /// - public sealed class ClrFunctionInstance : FunctionInstance, IEquatable + internal readonly Func _func; + private readonly bool _bubbleExceptions; + + public ClrFunctionInstance( + Engine engine, + string name, + Func func, + int length = 0, + PropertyFlag lengthFlags = PropertyFlag.AllForbidden) + : base(engine, engine.Realm, new JsString(name)) { - internal readonly Func _func; + _func = func; - public ClrFunctionInstance( - Engine engine, - string name, - Func func, - int length = 0, - PropertyFlag lengthFlags = PropertyFlag.AllForbidden) - : base(engine, engine.Realm, new JsString(name)) - { - _func = func; + _prototype = engine._originalIntrinsics.Function.PrototypeObject; - _prototype = engine._originalIntrinsics.Function.PrototypeObject; + _length = lengthFlags == PropertyFlag.AllForbidden + ? PropertyDescriptor.AllForbiddenDescriptor.ForNumber(length) + : new PropertyDescriptor(JsNumber.Create(length), lengthFlags); - _length = lengthFlags == PropertyFlag.AllForbidden - ? PropertyDescriptor.AllForbiddenDescriptor.ForNumber(length) - : new PropertyDescriptor(JsNumber.Create(length), lengthFlags); - } + _bubbleExceptions = _engine.Options.Interop.ExceptionHandler == InteropOptions._defaultExceptionHandler; + } + + protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) => _bubbleExceptions ? _func(thisObject, arguments) : CallSlow(thisObject, arguments); - protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments) + [MethodImpl(MethodImplOptions.NoInlining)] + private JsValue CallSlow(JsValue thisObject, JsValue[] arguments) + { + try { - try + return _func(thisObject, arguments); + } + catch (Exception e) when (e is not JavaScriptException) + { + if (_engine.Options.Interop.ExceptionHandler(e)) { - return _func(thisObject, arguments); + ExceptionHelper.ThrowJavaScriptException(_realm.Intrinsics.Error, e.Message); } - catch (Exception e) when (e is not JavaScriptException) + else { - if (_engine.Options.Interop.ExceptionHandler(e)) - { - ExceptionHelper.ThrowJavaScriptException(_realm.Intrinsics.Error, e.Message); - } - else - { - ExceptionDispatchInfo.Capture(e).Throw(); - } - - return Undefined; + ExceptionDispatchInfo.Capture(e).Throw(); } + + return Undefined; } + } + + public override bool Equals(JsValue? obj) => Equals(obj as ClrFunctionInstance); - public override bool Equals(JsValue? obj) + public bool Equals(ClrFunctionInstance? other) + { + if (ReferenceEquals(null, other)) { - return Equals(obj as ClrFunctionInstance); + return false; } - public bool Equals(ClrFunctionInstance? other) + if (ReferenceEquals(this, other)) { - if (ReferenceEquals(null, other)) - { - return false; - } - - if (ReferenceEquals(this, other)) - { - return true; - } - - if (_func == other._func) - { - return true; - } + return true; + } - return false; + if (_func == other._func) + { + return true; } + + return false; } }