Download ScriptingHost-Jint2.zip, last updated 31/08/2020 (66.93 KB)

Download
  • md5: 3ee9967fdf782810e5e943f1b7e319f9
  • sha1: 865279a70fc8634273c37a85ff8a5bffd21087c3
  • sha256: 1a5c1588ba9e8f2390f5f3bd41f0c7f4b8c86feb65c9cf1b460aca49488addfc
using Jint;
using Jint.Native;
using Jint.Parser;
using Jint.Parser.Ast;
using Jint.Runtime;
using Jint.Runtime.Interop;
using System;

// Adding Scripting to .NET Applications
// https://www.cyotek.com/blog/adding-scripting-to-net-applications

// Copyright © 2020 Cyotek Ltd. All Rights Reserved.

// This work is licensed under the MIT License.
// See LICENSE.TXT for the full text

// Found this example useful?
// https://www.paypal.me/cyotek

namespace Cyotek.Scripting.JavaScript
{
  public abstract class ScriptEnvironment
  {
    #region Public Fields

    public const string MainFunctionName = "main";

    #endregion Public Fields

    #region Private Fields

    private Engine _engine;

    private bool _interactive;

    #endregion Private Fields

    #region Protected Constructors

    protected ScriptEnvironment()
    {
      _interactive = true;
    }

    #endregion Protected Constructors

    #region Public Properties

    public bool Interactive
    {
      get { return _interactive; }
      set { _interactive = value; }
    }

    #endregion Public Properties

    #region Public Methods

    public void AddFunction(string name, Delegate value)
    {
      this.InitializeEngine();

      _engine.SetValue(name, value);
    }

    public void AddType(string name, Type type)
    {
      this.InitializeEngine();

      _engine.SetValue(name, TypeReference.CreateTypeReference(_engine, type));
    }

    public void AddValue(string name, object value)
    {
      this.InitializeEngine();

      _engine.SetValue(name, value);
    }

    public void Execute(string input)
    {
      Program program;

      program = new JavaScriptParser().Parse(input);

      this.InitializeEngine();

      _engine.Execute(program);

      if (ScriptEnvironment.HasMainFunction(program) && !ScriptEnvironment.HasMainCaller(program))
      {
        _engine.Invoke(MainFunctionName);
      }
    }

    public void Invoke(string name)
    {
      this.InitializeEngine();

      _engine.Invoke(name);
    }

    public void Load(string script)
    {
      Program program;

      program = new JavaScriptParser().Parse(script);

      this.InitializeEngine();

      _engine.Execute(program);
    }

    public virtual string TransformValue(object value, bool useLiterals)
    {
      string result;

      if (value is JsValue jsValue)
      {
        result = ScriptEnvironment.TransformValue(jsValue, useLiterals);
      }
      else if (value is null)
      {
        result = useLiterals ? "null" : null;
      }
      else
      {
        result = value.ToString();
      }

      return result;
    }

    public void WrappedExecute(string script)
    {
      try
      {
        JsValue completionValue;
        JsValue result;
        string output;

        this.Execute(script);

        completionValue = _engine.GetCompletionValue();
        result = _engine.GetValue(completionValue);
        output = ScriptEnvironment.TransformValue(result, true);

        this.WriteLine(output);
      }
      catch (Exception ex)
      {
        this.WriteLine(string.Format("{0}: {1}", ex.GetType().Name, ex.GetBaseException().Message));
      }
    }

    #endregion Public Methods

    #region Protected Methods

    protected abstract void ClearScreen();

    protected string GetValueString(object obj)
    {
      return (obj ?? "NULL").ToString();
    }

    protected virtual void InitializeEnvironment()
    {
      this.AddFunction("print", new Action<object>(this.WriteLine));
      this.AddFunction("log", new Action<object>(this.WriteLine));
      this.AddFunction("cls", new Action(this.ClearScreen));

      // interactive functions
      this.AddFunction("alert", new Action<object>(this.ShowAlert));
      this.AddFunction("confirm", new Func<object, bool>(this.ShowConfirm));
      this.AddFunction("prompt", new Func<object, object, string>(this.ShowPrompt));
    }

    protected abstract void ShowAlert(string message);

    protected abstract bool ShowConfirm(string message);

    protected abstract string ShowPrompt(string message, string defaultValue);

    protected abstract void WriteLine(string value);

    #endregion Protected Methods

    #region Private Methods

    private static bool HasMainCaller(Program program)
    {
      bool result;

      result = false;

      foreach (Statement statement in program.Body)
      {
        if (statement is ExpressionStatement expressionStatement
            && expressionStatement.Expression is CallExpression callExpression
            && callExpression.Callee is Identifier identifier
            && string.Equals(identifier.Name, MainFunctionName, StringComparison.OrdinalIgnoreCase))
        {
          result = true;
          break;
        }
      }

      return result;
    }

    private static bool HasMainFunction(Program program)
    {
      bool result;

      result = false;

      for (int i = 0; i < program.FunctionDeclarations.Count; i++)
      {
        FunctionDeclaration functionDeclaration;

        functionDeclaration = program.FunctionDeclarations[i];

        if (string.Equals(functionDeclaration.Id.Name, MainFunctionName, StringComparison.OrdinalIgnoreCase))
        {
          result = true;
          break;
        }
      }

      return result;
    }

    private static string TransformValue(JsValue jsValue, bool useLiterals)
    {
      string result;

      switch (jsValue.Type)
      {
        case Types.String:
          result = jsValue.AsString();
          break;

        case Types.Undefined:
          result = useLiterals ? "undefined" : null;
          break;

        case Types.Null:
          result = useLiterals ? "null" : null;
          break;

        case Types.Boolean:
          result = jsValue.AsBoolean().ToString();
          break;

        case Types.Number:
          result = jsValue.AsNumber().ToString();
          break;

        case Types.Object:
          result = jsValue.ToObject().ToString();
          break;

        case Types.None:
          result = string.Empty;
          break;

        default:
          result = jsValue.AsString();
          break;
      }

      return result;
    }

    private void InitializeEngine()
    {
      if (_engine == null)
      {
        _engine = new Engine();

        this.InitializeEnvironment();
      }
    }

    private void ShowAlert(object message)
    {
      if (_interactive)
      {
        this.ShowAlert(this.TransformValue(message, false));
      }
    }

    private bool ShowConfirm(object message)
    {
      return _interactive && this.ShowConfirm(this.TransformValue(message, false));
    }

    private string ShowPrompt(object message, object defaultValue)
    {
      return _interactive
         ? this.ShowPrompt(this.TransformValue(message, false), this.TransformValue(defaultValue, false))
         : null;
    }

    private void WriteLine(object value)
    {
      this.WriteLine(this.TransformValue(value, true));
    }

    #endregion Private Methods
  }
}

Donate

Donate