A few questions on features of Jint

Dec 13, 2009 at 8:42 PM

I'm looking to add some scripting capabilites to my .NET client application.  Jint looks like a great tool for the job, but from the documentation and discussions I couldn't determine if a few things were possible.  I was hoping a Jint expert could tell me if the following were doable via Jint:

1) I want to have a system where a script can be long running, using things like setTimer and such.  Most of the Jint examples appear to be synchronous calls.  This model would involve a more long-running script.  Can Jint support this?

2) Relatedly, I would like events to be firable in both directions between Javascript and my application. I think having script invoke my application code is straighforward (assuming #1 is possible.) Howver, let's say I expose an object to my script with events, and I want my script to be able to wire/unwire from those events.  Is there any clean way to achieve that?  For my types, I could presumably add my own methods to do it, but for BCL types maybe not.

3) Finally, I'm a bit confused on how Jint works with types defined in .NET.  For example, let's say I wanted to make an async WebRequest in script.  Can I create a javascript function that conforms to the AsyncCallback delegate and pass it directly to WebRequest.BeginGetResponse?

Thanks in advance for your help!

Dec 14, 2009 at 2:45 PM
Edited Dec 14, 2009 at 2:45 PM

Actually I think we handle any of those scenarios, though they are very interesting and really doable with small effort. I will create some work items for it, and you will be able to register to them to be warned when they are done.

The setTimeout will be done differently as it is actually a method from the window object, which is out of the scope of this project. But we will definitely get a way to doing it.

Dec 14, 2009 at 5:24 PM

Thanks for the fast response.  Can you offer some suggestions on how you'd architect #2 and #3 in the context of the Jint source?  I have a limited window to do some prototyping, and I'm perfectly happy to make the necessary changes myself.  But as I'm new to the source base, I'd be grateful for a little direction.

Also, is there a reason you don't expose a method to simply set globals in the JintEngine?  I notice internally you have a global scope object where calls to setFunction go.  But if I wanted to create my own window object to provide a similar programming environment to that in a modern browser, that doesn't seem doable.   

Dec 15, 2009 at 6:56 AM

I have to check what is the best strategy for #2 and #3.

To redefine the global object, for instance with a window object, please check the /env folder in the Test project. It's doable using a script, and we are working on it ...

Dec 16, 2009 at 12:47 AM

Thanks.  I'm playing with the code now and it looks really nice.  I had a few hiccups, but I'll post them as a seperate discussion.  The one thing I really need to get started with my scenario is some way let my script pass functions to my .NET methods, and let my .NET code later call said functions (this can enable async network requests, setTimeout, etc.) I didn't see any examples on how to do this, and my initial attempt to simply pass a function to a method accepting an object failed.  After some digging, i've made some modifications to get tihs working but as I'm new to the codebase, I'm not sure what I might be breaking in the process.  Here's what I had to do:

1) Modify ClrFunction.Execute, so the parameter loop looks like this:

for (int i = 0; i < parameters.Length; i++)
{
   if (parameters[i] is JsFunction)
   {
       clrParameters[i] = parameters[i];
   }
   else
   {
       clrParameters[i] = parameters[i].Value;
   }
}

This allows me to get the actual function in my callback (a JsFunction instance) instead of it's Value (which returns it's prototype) wihch is undefined.

2) In order to call the function, I had to add a new method to the JintEngine and ExecutionVisitor to execute a function.  I'm quite certain that what I've done won't work fully, but it does appear to execute my function with the appropriate scoping, etc.  I am pretty sure I've broken debugging for said called-back functions.  Here's what I've done.  Any advice for a better design would be appreciated:

In JintEngine:

public object RunFunction(JsFunction function, JsInstance[] parameters)
{
    if (function == null)
        throw new
            ArgumentException("Function can't be null", "function");

    visitor.DebugMode = this.DebugMode;
    visitor.PermissionSet = permissionSet;
   
    if (DebugMode)
    {
        visitor.Step += new EventHandler<DebugInformation>(OnStep);
    }

    try
    {
        visitor.Execute(function, parameters);
    }
    catch (SecurityException)
    {
        throw;
    }
    catch (JintException e)
    {
        if (visitor.CallStack.Count > 0)
        {
            Statement errorStatement = visitor.CallStack.Peek();

            throw new JintException(e.Message + Environment.NewLine + errorStatement.Start.ToString(), e.InnerException);
        }
        else
        {
            throw;
        }
    }
    catch (Exception e)
    {
        if (visitor.CallStack.Count > 0)
        {
            Statement errorStatement = visitor.CallStack.Peek();
           
            throw new JintException(e.Message + Environment.NewLine + errorStatement.Start.ToString(), e);
               
        }
        else
        {
            throw new JintException("An error occured while running the script. See inner exception for details.", e);
        }
    }

    return visitor.Result == null ? null : visitor.Result.ToObject();
}

In ExecutionVisitor: 

public void Execute(JsFunction function, JsInstance[] parameters)
{
    if (DebugMode)
    {
        OnStep(CreateDebugInformation(function.Statement));
        CallStack.Push(function.Statement);
    }

    JsObject instance = new JsObject(new JsPrototype(function.Prototype));
    JsInstance result = function.Execute(this, instance, parameters);

    if (DebugMode)
    {
        CallStack.Pop();
    }

    if (exit)
    {
        exit = false;
        return;
    }
}

 

Dec 17, 2009 at 1:26 PM

I'm currently trying to implement it. I used a different trick for the jsFunction.Value, and will use a custom wrapper to publish a Delegate directly, wrapping the JsFunction statements.

Dec 17, 2009 at 9:34 PM

I've written some code to do the delegate wrapping on top of the 0.86 codebase.  I'd be happy to share it with you (along with my other tweaks) if they would be useful to you.  What's the best way to send you the sources (without actually submitting them?) Can I zip up and share my tweaked sources and you can diff them?

Dec 18, 2009 at 8:43 AM

You can send it by mail via the contact form, or create a work item and attach your work.

Dec 31, 2009 at 8:17 PM

I'll attach my various changes this weekend, so you can look through them and take whatever you would like.  I've tried to keep my changes well commented you know why I've done what I've done.