Browse Source

Add fast path without try-catch for CLR function call (#1651)

Marko Lahma 1 year ago
parent
commit
8794aa03cc
2 changed files with 58 additions and 53 deletions
  1. 3 1
      Jint/Options.cs
  2. 55 52
      Jint/Runtime/Interop/ClrFunctionInstance.cs

+ 3 - 1
Jint/Options.cs

@@ -295,7 +295,7 @@ namespace Jint
         /// 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.
         /// </summary>
-        public ExceptionHandlerDelegate ExceptionHandler { get; set; } = static exception => false;
+        public ExceptionHandlerDelegate ExceptionHandler { get; set; } = _defaultExceptionHandler;
 
         /// <summary>
         /// Assemblies to allow scripts to call CLR types directly like <example>System.IO.File</example>.
@@ -328,6 +328,8 @@ namespace Jint
         /// </summary>
         public Func<Engine, Type, JsValue[], object?> CreateTypeReferenceObject = (_, _, _) => null;
 
+        internal static readonly ExceptionHandlerDelegate _defaultExceptionHandler = static exception => false;
+
         /// <summary>
         /// When not null, is used to serialize any CLR object in an
         /// <see cref="IObjectWrapper"/> passing through 'JSON.stringify'.

+ 55 - 52
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;
+
+/// <summary>
+/// Wraps a Clr method into a FunctionInstance
+/// </summary>
+public sealed class ClrFunctionInstance : FunctionInstance, IEquatable<ClrFunctionInstance>
 {
-    /// <summary>
-    /// Wraps a Clr method into a FunctionInstance
-    /// </summary>
-    public sealed class ClrFunctionInstance : FunctionInstance, IEquatable<ClrFunctionInstance>
+    internal readonly Func<JsValue, JsValue[], JsValue> _func;
+    private readonly bool _bubbleExceptions;
+
+    public ClrFunctionInstance(
+        Engine engine,
+        string name,
+        Func<JsValue, JsValue[], JsValue> func,
+        int length = 0,
+        PropertyFlag lengthFlags = PropertyFlag.AllForbidden)
+        : base(engine, engine.Realm, new JsString(name))
     {
-        internal readonly Func<JsValue, JsValue[], JsValue> _func;
+        _func = func;
 
-        public ClrFunctionInstance(
-            Engine engine,
-            string name,
-            Func<JsValue, JsValue[], JsValue> 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;
     }
 }