jsish
Interp
Not logged in

Interps

An interp encapsulates the run-time state of a javascript interpreter.

Among other things, this gives us a way to dynamically configure and/or query interp options:

Interp.conf();

which outputs something like:

{ args:[  ], callTrace:0, compat:false, ... }

In Jsi there are several ways to change an interps options:

Interp.conf({typeCheck:["error"]});
jsish -W error foo.s
JSI_INTERP_OPTS='{typeCheck:["parse"]}' jsish foo.js

Sub-interps

A sub-interp provides a separate environment in which to run scripts, eg.

var interp1 = new Interp();
interp1.source('myfile.jsi');
interp1.eval('myfunc();');
delete interp1;

When we use eval to execute code in an interp, execution is normally in the foreground.

To use asynchronous execution we set the second argument to true.

var i = new Interp();
i.eval("doWork();", true);
//...
update(100);

See the Interp Reference for interface details about creating and using sub-interps.


Safe-Interps

Sub-interps may be created with the isSafe option for restricting access to the file system:

var interp1 = new Interp({isSafe:true});
File.write('/tmp/stuff.txt', abc'); // This will throw an error...

Additional options are available to relax these limitations, such as:

var interp1 = new Interp({isSafe:true, safeWriteDirs:['/tmp'], , safeReadDirs:['/tmp']});

There is also a wrapper ../lib/JsiSafe.jsi for executing scripts safely:

jsish -i Safe tests/while.js a b c
jsish -i Safe tests/file.js a b c;  // kicks an error.

Thread-Interps

A sub-interp can be created in a threaded:

var interp1 = new Interp({subthread:true, scriptFile:'mythrd.js'});
interp1.eval("doWork();", true);

Note that if a scriptFile/scriptStr option is not given at create time, the thread blocks waiting for events.

Using a non-asynchronous eval() on a threaded interp, will block the caller until the thread calls sleep() or update() with sleep time. The same goes for source() and uplevel().

Several Interp commands are not supported with threads, such as:


Aliases

alias() lets you define a sub-interp command that invokes another command in the parent interpreter.

We can also add arguments to be prepended to the called method.

var i = new Interp();

function myAlias(interp,name,arg1,arg2) {
   puts('myAlias: interp name arg1 '+arg2);
}

function myAlias2(arg1,arg2) {
   puts('myAlias2: arg1 '+arg2);
}

i.alias('foo', myAlias, [i, 'foo']);
i.alias('bar', myAlias2,null);
puts(i.alias()); puts(i.alias('foo'));
puts(i.alias('foo', myAlias));
i.eval('bar(1,2)');
i.eval('var bb = {x:1};');
i.alias('bb.fig', myAlias, [i, 'bb.fig']);
i.eval('bb.fig(1)');
i.alias('bb.fig', myAlias, [i, 'bb.FIG']);
i.eval('bb.fig(1)');
puts(i.alias());

To delete an aliases just redefine as null.

i.alias('bb.fig', null, null);
puts(i.alias());
try { i.eval('bb.fig(1)'); } catch(e) { puts("CAUGHT ERROR: "+e); }; puts("OK");

Interpreters can not set aliases on themselves, or on threaded interpreters.


Options

When we create an interpreter, we can pass in an number of options. The available options are described here.

Note that options marked as initOnly may only be set at interpreter creation time.

We can also set or get options for the current interpreter in the usual way:

Interp.conf(); // Dump all options
Interp.conf('strict'); // Get one option
Interp.conf({strict:false, maxDepth:99}); // Set one or more options.

Events

Events in javascript are traditionally created by the standard setTimeout()/setInterval() methods.

In Jsi, various commands and extensions (eg. websockets) implement asynchronous features that use events.

Events explicitly get processed by calls to update(). For more details see Events.


Call

Due to overhead of parsing, eval() is actually the least efficient way to run commands.

The two other, faster alternatives are call() and send().

call() will invoke a method that is already defined in an interp.

This is much more efficient as there is no parsing involved, and just like eval() it takes an async argument.

var cmd = "
    function recv2(s) { puts('recv2: '+s.toString()); return {x:8, y:9}; };
    thrd = Info.interp().thread;
    puts(thrd);
    puts('Child starting: '+(thrd?'true':'false'));
    while (thrd) {
        update(1000);
        puts('Child-loop');
    };
";

var cnt=0, i = new Interp({subthread:true, scriptStr:cmd});

Sys.sleep(1000);
var obj = {AA:1,BB:2};
var aobj = [obj];
var ret;
while (cnt<10) {
  update(100);
  puts("Main-loop");
  if (((cnt++)%3)==0)
      i.eval("puts('Hello from main!!!!!!!!!!!!!!!!!!!!!!!!');");
  ret = i.call('recv2',aobj);
  puts(ret);
}

Send

send() is used to pass messages via the function named in the recvCallback option of the interps.

This asynchronous function, which simply receives an array of messages, is actually the most efficient way to communicate between interps.

var cmd = "
    function recv(s) { puts('recv: '+s.toString()); };
    thrd = Info.interp().thread;
    puts(thrd);
    puts('Child starting: '+(thrd?'true':'false'));
    while (thrd) {
        update(500);
        puts('Child-loop');
        Interp.send({abc:1});
    };
";

function myrecv(s) { puts('>>>>>>>>>>MYRECV: '+s.toString()); };
Interp.conf({recvCallback:'myrecv'});

var cnt=0, i = new Interp({subthread:true, scriptStr:cmd, recvCallback:'recv'});

Sys.sleep(1000);
var obj = {AA:1,BB:2};
var aobj = [obj];
while (cnt<10) {
  update(100);
  puts("Main-loop");
  i.send(obj);
} 

Interps and Data

To reduce coupling and increase integrity within interpreters, data objects are never shared.

To accomplish this, all data is internally converted to and from JSON at the Interp-Interp interface.

This all happens automatically, so normally users don't need to be worry about the details.

But there are potential performance issues with this JSON translation.


Environment

The environment variable JSI_INTERP_OPTS can be used for running scripts with certain Interp options set:

JSI_INTERP_OPTS="{memDebug:1, maxOpCnt:1000000}" jsish myscript.js

This is important as it allows options to be set before an interpreter starts.

For example, in the case of memDebug, it is vital that no Value memory be allocated before hand, otherwise detection of memory leaks will not be accurate.

Thus this is the only way this particular option can be set.


Backward Compatibility

The compat option is provided to support backward compatibility with code written which uses options available in future releases of Jsi. Running such code with an older Jsi can cause script parse errors due to unknown options. We can prevent this by adding to the script:

Interp.conf({compat:true}); 

This provides an alternative to adding conditionals with Info.version().

The application may still not work correctly, but at least parsing won't fail.


References

For more details see the Interp Reference and Event Reference.