< Summary

Information
Class: Elsa.Expressions.JavaScript.Services.JintJavaScriptEvaluator
Assembly: Elsa.Expressions.JavaScript
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.JavaScript/Services/JintJavaScriptEvaluator.cs
Line coverage
98%
Covered lines: 67
Uncovered lines: 1
Coverable lines: 68
Total lines: 147
Line coverage: 98.5%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
EvaluateAsync()100%11100%
GetConfiguredEngine()100%44100%
ConfigureClrAccess(...)100%22100%
ConfigureObjectWrapper(...)100%22100%
ConfigureObjectConverters(...)100%11100%
ConfigureArgumentGetters(...)100%22100%
ConfigureConfigurationAccess(...)50%2266.66%
ExecuteExpressionAndGetResult(...)100%11100%
GetOrCreatePrepareScript(...)100%22100%
PrepareScript(...)100%11100%
Hash(...)100%11100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.JavaScript/Services/JintJavaScriptEvaluator.cs

#LineLine coverage
 1using System.Diagnostics.CodeAnalysis;
 2using System.Security.Cryptography;
 3using System.Text;
 4using Acornima.Ast;
 5using Elsa.Expressions.Helpers;
 6using Elsa.Expressions.Models;
 7using Elsa.Expressions.JavaScript.Contracts;
 8using Elsa.Expressions.JavaScript.Helpers;
 9using Elsa.Expressions.JavaScript.Notifications;
 10using Elsa.Expressions.JavaScript.ObjectConverters;
 11using Elsa.Expressions.JavaScript.Options;
 12using Elsa.Mediator.Contracts;
 13using Jint;
 14using Jint.Runtime.Interop;
 15using Microsoft.Extensions.Caching.Memory;
 16using Microsoft.Extensions.Configuration;
 17using Microsoft.Extensions.Options;
 18
 19// ReSharper disable ConvertClosureToMethodGroup
 20namespace Elsa.Expressions.JavaScript.Services;
 21
 22/// <summary>
 23/// Provides a JavaScript evaluator using Jint.
 24/// </summary>
 11325public class JintJavaScriptEvaluator(IConfiguration configuration, INotificationSender mediator, IOptions<JintOptions> s
 26    : IJavaScriptEvaluator
 27{
 11328    private readonly JintOptions _jintOptions = scriptOptions.Value;
 29
 30    /// <inheritdoc />
 31    [RequiresUnreferencedCode("The Jint library uses reflection and can't be statically analyzed.")]
 32    public async Task<object?> EvaluateAsync(string expression,
 33        Type returnType,
 34        ExpressionExecutionContext context,
 35        ExpressionEvaluatorOptions? options = null,
 36        Action<Engine>? configureEngine = null,
 37        CancellationToken cancellationToken = default)
 38    {
 18839        var engine = await GetConfiguredEngine(configureEngine, context, options, cancellationToken);
 18840        await mediator.SendAsync(new EvaluatingJavaScript(engine, context, expression), cancellationToken);
 18841        var result = ExecuteExpressionAndGetResult(engine, expression);
 18542        await mediator.SendAsync(new EvaluatedJavaScript(engine, context, expression, result), cancellationToken);
 43
 18544        return result.ConvertTo(returnType);
 18545    }
 46
 47    private async Task<Engine> GetConfiguredEngine(Action<Engine>? configureEngine, ExpressionExecutionContext context, 
 48    {
 18849        options ??= new();
 50
 18851        var engineOptions = new Jint.Options
 18852        {
 18853            ExperimentalFeatures = ExperimentalFeature.TaskInterop
 18854        };
 55
 18856        ConfigureClrAccess(engineOptions);
 18857        ConfigureObjectWrapper(engineOptions);
 18858        ConfigureObjectConverters(engineOptions);
 59
 18860        await mediator.SendAsync(new CreatingJavaScriptEngine(engineOptions, context), cancellationToken);
 18861        _jintOptions.ConfigureEngineOptionsCallback(engineOptions, context);
 62
 18863        var engine = new Engine(engineOptions);
 64
 18865        configureEngine?.Invoke(engine);
 18866        ConfigureArgumentGetters(engine, options);
 18867        ConfigureConfigurationAccess(engine);
 18868        _jintOptions.ConfigureEngineCallback(engine, context);
 69
 18870        return engine;
 18871    }
 72
 73    private void ConfigureClrAccess(Jint.Options options)
 74    {
 18875        if (_jintOptions.AllowClrAccess)
 1776            options.AllowClr();
 18877    }
 78
 79    private void ConfigureObjectWrapper(Jint.Options options)
 80    {
 18881        options.SetWrapObjectHandler((engine, target, type) =>
 18882        {
 34083            var instance = ObjectWrapper.Create(engine, target);
 18884
 34085            if (ObjectArrayHelper.DetermineIfObjectIsArrayLikeClrCollection(target.GetType()))
 32286                instance.Prototype = engine.Intrinsics.Array.PrototypeObject;
 18887
 34088            return instance;
 18889        });
 18890    }
 91
 92    private void ConfigureObjectConverters(Jint.Options options)
 93    {
 18894        options.Interop.ObjectConverters.AddRange([new ByteArrayConverter(), new EnumToStringConverter(), new JsonElemen
 18895    }
 96
 97    private void ConfigureArgumentGetters(Engine engine, ExpressionEvaluatorOptions options)
 98    {
 38299        foreach (var argument in options.Arguments)
 6100            engine.SetValue($"get{argument.Key}", (Func<object?>)(() => argument.Value));
 188101    }
 102
 103    private void ConfigureConfigurationAccess(Engine engine)
 104    {
 188105        if (_jintOptions.AllowConfigurationAccess)
 0106            engine.SetValue("getConfig", (Func<string, object?>)(name => configuration.GetSection(name).Value));
 188107    }
 108
 109    private object? ExecuteExpressionAndGetResult(Engine engine, string expression)
 110    {
 188111        var preparedScript = GetOrCreatePrepareScript(expression);
 186112        var result = engine.Evaluate(preparedScript);
 185113        return result.UnwrapIfPromise().ToObject();
 114    }
 115
 116    private Prepared<Script> GetOrCreatePrepareScript(string expression)
 117    {
 188118        var cacheKey = "jint:script:" + Hash(expression);
 119
 188120        return memoryCache.GetOrCreate(cacheKey, entry =>
 188121        {
 137122            if (_jintOptions.ScriptCacheTimeout.HasValue)
 137123                entry.SetSlidingExpiration(_jintOptions.ScriptCacheTimeout.Value);
 188124
 137125            return PrepareScript(expression);
 188126        })!;
 127    }
 128
 129    private Prepared<Script> PrepareScript(string expression)
 130    {
 137131        var prepareOptions = new ScriptPreparationOptions
 137132        {
 137133            ParsingOptions = new()
 137134            {
 137135                AllowReturnOutsideFunction = true
 137136            }
 137137        };
 137138        return Engine.PrepareScript(expression, options: prepareOptions);
 139    }
 140
 141    private string Hash(string input)
 142    {
 188143        var bytes = Encoding.UTF8.GetBytes(input);
 188144        var hash = SHA256.HashData(bytes);
 188145        return Convert.ToBase64String(hash);
 146    }
 147}