This project has moved. For the latest updates, please go here.

Noesis like execution/parameter interaction

Oct 22, 2011 at 1:58 AM
Edited Oct 22, 2011 at 6:38 AM

I had been using Noesis' Javascript engine (V8). And it worked well. But it's thread bound to a single process. So, I'm trying Jurassic.

I'd like to be able to do something like the below - can you comment on this?

All it's supposed to do is proof that I can get events into the script and then back out into the host environment.

(two text boxes in a WinForm, two buttons)
[NOTE: code is now operational]

using System;
using System.Text;
using System.Timers;
using System.Windows.Forms;
using Jurassic;

namespace TestRig {

    public partial class FrmMain : FormILogger {
        ScriptEngine jsEngine;
        NamedTimer statusTimer;

        public FrmMain() {
            InitializeComponent();
            this.Load += new EventHandler(FrmMain_Load);
        }

        void FrmMain_Load(object sender, EventArgs e) {
            this.statusTimer = new NamedTimer("Heartbeat");
            this.statusTimer.Interval = 1000;
            this.statusTimer.Elapsed += new ElapsedEventHandler(statusTimer_Elapsed);
            this.jsEngine = new ScriptEngine();
            this.jsEngine.SetGlobalFunction("WriteLine"new Action<string>(this.WriteLine));
            this.jsEngine.Execute("function S_UserInitiatedEvent(m){WriteLine('User:' + m);}");
            this.jsEngine.Execute("function S_TimerEvent(t){WriteLine('Time:' + t);}");
        }

        private void btnRun_Click(object sender, EventArgs e) {
            this.statusTimer.Start();
            string script = textBox1.Text;
            try {
                jsEngine.Execute(script);
            } catch (Exception ex) {
                WriteLine(ex.Message);
            }
        }

        void statusTimer_Elapsed(object sender, ElapsedEventArgs e) {
            try {
                ((Jurassic.Library.FunctionInstance)jsEngine.GetGlobalValue("S_TimerEvent")).Call(null, e.SignalTime.ToString());
            } catch (Exception ex) {
                WriteLine(ex.Message);
            }
        }
                
        public void WriteLine(string message) {
            if (this.InvokeRequired){
                this.Invoke(new Action<string>(this.WriteLine), new object[]{message});
                return;
            }else{
                this.textBox2.Text += message + Environment.NewLine;
            }
        }

        private void btnSendEvent_Click(object sender, EventArgs e) {
            try {
                ((Jurassic.Library.FunctionInstance)jsEngine.GetGlobalValue("S_UserInitiatedEvent")).Call(null"initiated event.");
            } catch (Exception ex) {
                WriteLine(ex.Message);
            }
        }
    }

    public interface ILogger {
        void WriteLine(string message);
    }

    public class NamedTimer : System.Timers.Timer {
        public string Name { getset; }
        public NamedTimer(string name) {
            this.Name = name;
        }
    }
}
Coordinator
Oct 22, 2011 at 4:06 AM

Yup, I can make a few comments;

  • First, and most important, Jurassic is not "bound" to a specific thread, but it's also not thread-safe.  The script engine object and any related objects (to be precise, any ObjectInstance-derived instances created by the script engine) must be protected by locks.
  • Instead of jsEngine.Execute(new StringScriptSource(script)) you can use jsEngine.Execute(script).
  • This code is simple but clumsy and prone to breakage: jsEngine.Execute("S_TimerEvent('" + e.SignalTime.ToString() + "')")
    Instead, use: ((Jurassic.Library.FunctionInstance)jsEngine.GetGlobalValue("S_TimerEvent")).Call(null, e.SignalTime.ToString())
  • The code this.jsEngine.SetGlobalValue("scriptHost", this) will not work in the currently released version of Jurassic (v2.1) - however it should work as you expect if you are building from source.  To call a .NET method in v2.1, see the documentation tab.
Oct 22, 2011 at 6:30 AM
Edited Oct 22, 2011 at 6:32 AM

Thank you for your instruction.

I've modified the above code to work.

I'll continue to expand this demonstration to see how far I can take it.

Note: the events coming back in from the engine required marshalling back onto the main UI thread.

AM