Taritsyn / JavaScriptEngineSwitcher

JavaScript Engine Switcher determines unified interface for access to the basic features of popular JavaScript engines. This library allows you to quickly and easily switch to using of another JavaScript engine.
Apache License 2.0
440 stars 49 forks source link

V8 script timeout #32

Closed imsmart-tech closed 5 years ago

imsmart-tech commented 7 years ago

Hi,

Is there a way to get a script timeout with V8 like it seems to be possible with Jint? I am trying to code the timeout in C# and that is not so obvious so I thought that must be easier to implement the timeout like it's done for Jint.

Paul

Taritsyn commented 7 years ago

Hello, Paul!

A good idea, you should first suggest it to developers of the Microsoft ClearScript.V8.

imsmart-tech commented 7 years ago

Done. https://github.com/Microsoft/ClearScript/issues/7 Let's see how it will be handled. :-|

imsmart-tech commented 7 years ago

Check their answer in the link above. Can I call V8 directly or it needs to wrapped? I feel it should be included in the wrapper anyway. Let me know...

Taritsyn commented 7 years ago

Can I call V8 directly or it needs to wrapped?

You can use the V8ScriptEngine class directly, but you will have to abandon the JavaScript Engine Switcher (at least you have to uninstall the JavaScriptEngineSwitcher.V8-* packages) and install the ClearScript.V8 package.

I feel it should be included in the wrapper anyway.

Interrupt method will be included in the IJsEngine interface, but it will take time, because it require changing the public API. Wait for release of version 3.0.

Taritsyn commented 7 years ago

Hello, Paul!

I feel it should be included in the wrapper anyway. Let me know...

This feature was implemented in version 3.0.0 Alpha 1. While this feature is supported by only two modules: JavaScriptEngineSwitcher.V8 and JavaScriptEngineSwitcher.ChakraCore.

My version of the example with timeout looks like this:

Contents of the JsEngineExtensions.cs file:

using System;
using System.Threading;

using JavaScriptEngineSwitcher.Core;

namespace TestScriptTimeout
{
    public static class JsEngineExtensions
    {
        public static object TimedEvaluate(this IJsEngine engine, TimeSpan timeout, string code)
        {
            if (engine.SupportsScriptInterruption)
            {
                using (var timer = new Timer(state => engine.Interrupt()))
                {
                    try
                    {
                        timer.Change(timeout, TimeSpan.FromMilliseconds(Timeout.Infinite));
                        return engine.Evaluate(code);
                    }
                    catch (JsScriptInterruptedException exception)
                    {
                        throw new TimeoutException("Script execution timed out", exception);
                    }
                }
            }
            else
            {
                throw new NotSupportedException(string.Format("JavaScript engine with name '{0}' does " +
                    "not support interruption of the script execution.", engine.Name));
            }
        }
    }
}

Contents of the Program.cs file:

using System;

using JavaScriptEngineSwitcher.ChakraCore;
using JavaScriptEngineSwitcher.Core;
using JavaScriptEngineSwitcher.Msie;
using JavaScriptEngineSwitcher.V8;

namespace TestScriptTimeout
{
    class Program
    {
        static Program()
        {
            JsEngineSwitcher engineSwitcher = JsEngineSwitcher.Instance;
            engineSwitcher.EngineFactories
                .AddChakraCore()
                .AddMsie(options =>
                {
                    options.EngineMode = JsEngineMode.ChakraIeJsRt;
                })
                .AddV8()
                ;
            engineSwitcher.DefaultEngineName = V8JsEngine.EngineName;
        }

        static void Main(string[] args)
        {
            using (var engine = JsEngineSwitcher.Instance.CreateDefaultEngine())
            {
                try
                {
                    engine.TimedEvaluate(TimeSpan.FromSeconds(5), "while (true) {}");
                }
                catch (TimeoutException)
                {
                    Console.WriteLine("Timed out!");
                }
                catch (NotSupportedException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
}
Taritsyn commented 7 years ago

In version 3.0.0 Alpha 2, this feature is now supported by third module - JavaScriptEngineSwitcher.Vroom.

Taritsyn commented 7 years ago

In version 3.0.0 Alpha 3, this feature is now supported by fourth module - JavaScriptEngineSwitcher.Msie.

Taritsyn commented 5 years ago

Hello, Paul!

In version 3.0.0 the ability to interrupt execution of the script was added to the following modules: JavaScriptEngineSwitcher.ChakraCore, JavaScriptEngineSwitcher.Msie, JavaScriptEngineSwitcher.V8 and JavaScriptEngineSwitcher.Vroom.

With all the changes, my version of the example with timeout looks like this:

Contents of the JsEngineExtensions.cs file:

using System;
using System.Threading;

using JavaScriptEngineSwitcher.Core;

namespace TestScriptTimeout
{
    public static class JsEngineExtensions
    {
        public static object TimedEvaluate(this IJsEngine engine, TimeSpan timeout,
            string expression)
        {
            if (engine.SupportsScriptInterruption)
            {
                using (var timer = new Timer(state => engine.Interrupt()))
                {
                    try
                    {
                        timer.Change(timeout, TimeSpan.FromMilliseconds(Timeout.Infinite));

                        return engine.Evaluate(expression);
                    }
                    catch (JsInterruptedException e)
                    {
                        throw new TimeoutException("Script execution timed out", e);
                    }
                }
            }
            else
            {
                throw new NotSupportedException(string.Format("JavaScript engine with name " +
                    "'{0}' does not support interruption of the script execution.", engine.Name));
            }
        }
    }
}

Contents of the Program.cs file:

using System;

using JavaScriptEngineSwitcher.ChakraCore;
using JavaScriptEngineSwitcher.Core;
using JavaScriptEngineSwitcher.Msie;
using JavaScriptEngineSwitcher.V8;
using JavaScriptEngineSwitcher.Vroom;

namespace TestScriptTimeout
{
    class Program
    {
        static Program()
        {
            IJsEngineSwitcher engineSwitcher = JsEngineSwitcher.Current;
            engineSwitcher.EngineFactories
                .AddChakraCore()
                .AddMsie(options =>
                {
                    options.EngineMode = JsEngineMode.ChakraIeJsRt;
                })
                .AddV8()
                .AddVroom()
                ;
            engineSwitcher.DefaultEngineName = ChakraCoreJsEngine.EngineName;
        }

        static void Main(string[] args)
        {
            const string expression = @"function sleep(millisecondsTimeout) {
    var totalMilliseconds = new Date().getTime() + millisecondsTimeout;

    while (new Date() < totalMilliseconds)
    { }
}

sleep(600 * 1000);";

            using (IJsEngine engine = JsEngineSwitcher.Current.CreateDefaultEngine())
            {
                try
                {
                    object result = engine.TimedEvaluate(TimeSpan.FromSeconds(5), expression);
                }
                catch (TimeoutException)
                {
                    Console.WriteLine("Timed out!");
                }
                catch (NotSupportedException e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }
    }
}