PERLCONNECT AND JS.pm

USER-LEVEL DOCUMENTATION
Vladimir Livshits
 
  1. Overview
  2. Features
  3. Design
  4. Installation
  5. Current Limitations and Futher Work

Overview

PerlConnect and JS.pm provide glue for the developer between JavaScript and Perl languages. It currently consists of two parts, PerlConnect implemented in C and JS.pm (tentative name), a Perl module written using XSUBs. PerlConnect and JS.pm allow calling Perl from JS and JS from Perl, respectively. Whenever possible, it is attempted to archieve the maximum level of transparency for calling one language from the other. This is done by converting values between the two languages, creating wrappers around objects, and emulating the standard syntax.

PerlConnect Features

PerlConnect allows running a Perl interpreter concurrently with your JavaScript embedding and executing Perl commands from JavaScript. You usually need to create a Perl interpreter by saysing something like this:
p = new Perl('Sys::Hostname', 'Test::Harness')
In addition to creating an interpreter, this will also include the libraries you  pass to the Perl constructor, which is equivalent to use Sys::Hostname; use Test::Harness. You can always include libraries explicitly by using p.eval('use Sys::Hostname; use Test::Harness'). There is also another way to do this: p.use('Sys::Hostname', 'Test::Harness'). As you can see, TMTOWTDI. Natura    lly, you will want to look at the result of your function calls and eval statements. Suppose, you do something like this:
line = p.eval("'-' x 80")
Perl's eval returns the last statement evaluated, unless you explicitly say return. So now line contains 80 dashes. You can do similar things with non-scalar data types:
p.use('Time::gmtime');
t = p.eval('Time::gmtime::gmtime')    // returns [49,0,4,24,6,98,5,204,0]
assigns a Perl array to t. You can print t, use the for/in syntax to walk through it, compute its lenght, etc. You can read and assign to individual elements using the standard syntax. However, PerlValues, that is, the value we get from Perl, don't support all the standard operations, for isntance, don't expect t.reverse() to work. Hashes can also be returned from Perl:
info=p.eval("{ver=>$], pid=>$$}")
Now you can look at individual hash keys like this:
info["ver"] or info.pid
Suppose you want to use Perl to perform pattern-based string replacement. Here's how you can do it from JavaScript:
p.eval("\
    sub perl_replace(){\
        my($string, $find, $replace) = @_;\
        eval(\"\\$string =~ s/$find/$replace/g;\");\
        return $string;\
    }"
);
and now
p.perl_replace('Quick brown fox jumped over a lazy dog', 'dog', 'rhino')
produces what you'd expect.

You can use the same syntax to call procedures defined in modules other than main. The example with gmtime can be rewritten like this:

p.use('Time::gmtime');
t = p.Time.gmtime.gmtime()    // returns [49,0,4,24,6,98,5,204,0]
 You can reference variables exported by modules others than main like this:
a=p.Foo.$bar or a=p.Foo["$bar"] or
a=p.Foo["@bar"]or a=p.Foo["%bar"]
Each of the stetements above returns a either an immediate JS value, for scalar types, or a PerlValue for compound types. a.type contains the type of the PerlValue in a. This may change because we may end up separating Perl hashes and arrays into separate classes.

JS.pm Features

JS.pm is much less tested then PerlConnect. You should be able to do similar things from Perl. Just say
use JS;
$js = new JS();
and now you can do something like this:
$js->eval(q/
Object o = {};
o.a = 'p';
o.b = 'q';
return o;
/);
//TODO: more features and examples

PerlConnect Design

PerlConnect is written in C. It uses both JavaScript and Perl APIs and implements a mapping between the two. The are the following JavaScript objects implemented by PerlConnect:
 
Object What it does
Perl Perl Interpreter Object. It's prototype type is PerlModule, it corresponds to main::. Supports eval, call, use.
PerlModule Implements JS-like syntax for Perl modules. Doesn't export provide any methods. path property shown the name of the Perl module the object represents.
PerlValue Represents a value returned from eval, call, or obtained by using the subscript notation (p.Foo["@bar"]). Its Perl type is stored in the type property.
 
See comments in the code, jsperl.c and JS.pm for more info.
 

JS.pm Design

JSConnect is written using XSUBs, the language in which Perl extentions are implemented. The source files are JS.xs and typemap. After processing them using the XSUBs compiler, xsubpp, the resulting C file should be compiled into a DLL. JS.pm provides bootstraping mechanism to load this DLL. See perlxs and perlxsstut man pages for more info. The following Perl packages (objects) are implemented:
 
Package What it contains
JS Doesn't not do anything in particular at this point except defining a constuctor. So one can say $js = new JS(), which will create a new runtime, add a context to it and return that Context. JS also defines a bunch of private functions called from C by PerlConnect. They are not exposed by default, but pushed onto @EXPORT_OK array instead.
JS::Runtime Corresponds to JSRuntime* struct. Provides a constructor and destructor. The destructor is invoked automatically, so you don't have to worry about Runtime deallocation. Constructor syntax is the following: $rt = new JS::Runtime(10_000), where the parameter is the same number you pass to JS_NewRuntime. There are many private functions created in JS.xs that are not exported by default.
JS::Context Corresponds to JSContext* struct. Provides a constructor and destructor. The destructor is invoked automatically, so you don't have to worry about Context deallocation. Constructor syntax is the following: $rt = new JS::Context($rt, 1_000), where the parameter is the same number you pass to JS_NewContext. There are many private functions created in JS.xs that are not exported by default.
JS::Object Corresponds to JSObject* struct. There is not that much here yet. This object is intended as a wrapper around the JSObject* stuct. Support for tying hashes and possibly arrays with JS::Objects is coming.
[JS::Array] I am not quite sure if this is needed. One might probably get away with just JS::Object defined. If it's implemented, it will be very much similar to JS::Object above.
 

All the modules above follow the convention of storing the variable they return in the $this variable of the current class. So $JS::Context::this will always be the last context created. Currently, this is where JS API function that require contexts get it.

PerlConnect Installation

PerlConnect requires js/ref and the Perl libraries and headers. The only js/ref file that must be included in jsapi.h. You need to compile it together with Perl libraries. Referer to the perlembed man page for more details.
On WINNT:
There are MSDEV Workspace and project files in the main PerlConnect directory. There are two projects included in the PerlConnect workspace: PerlConnect and PerlConnect shell. You can use the latter to test PerlConnect. You will probably need to adjust the library and include paths. In particular, set PERL_SRC environment variable to point to the directory where you unpacked and compiled Perl sources.
On UNIX:
Currently, you can use PerlConnect with the js.c shell. To compile it on UNIX, you need to set the PERLCONNECT environment variable. Then you can use the Makefile in js/ref directory. The makefile should read the variable from the current environment. Id this doesn't happent, you can try using gmake PERLCONNECT=1. This will compile PerlConnect into js.

JS.pm Installation

The following source files are distributed: JS.pm, JS.xs, JS.c (produced from JS.xs by xsubpp) and JS.def (required by the Visual C compiler).
On WINNT:
There is also a project file, JS.dsp. It automatically belongs to the PerlConnect workspace. This project file builds a DLL, JS.dll which is supposed to reside where Perl's DynaLoader can find it. On my machine I put it under c:\perl\lib\auto\JS\JS.dll. You can also put it in c:\perl\lib\. See DynaLoader documentation for more info on how dynamic libraries are found.
On UNIX:
//TODO: details of the makefiles
 

Current Limitations of the Package and Futher Work

  1. Perl variables currently can't be assigned to, that is, p["$a"]=100 doesn't do anything.
  2. You can only have one interpreter running at a time. Despite the fact that you can create multiple Perl objects on JavaScript, they all share the same namespace. We can probably use Safe.pm to implement independent namespaces.
  3. Module names resolution reports an error only when you try to evaluate the last element of the resolution chain. Here is what I mean: if you reference p.Foo.Bar.Var and For or Bar don't exist, it will only complain that p.Foo.Bar.Var is not a valid variable. Perl 5.005 provides exists Foo::{Bar::} to check if Foo::Bar is a valid package.
  4. Dynamic loading of the Perl interpreter only if it is required.
  5. Recursive printing of Perl's arrays and hashes can be added. See Data::Dumper.pm
  6. Full support for tied hashes and arrays in Perl
  7. Calling JavaScript functions and accessing variables from Perl. JavaScript calling syntax support using AUTOLOADing.
  8. JS can be made a directory with Object.pm, Context.pm, etc. in it. See how C or Tk are organized on CPAN
  9. Distribution model for JS.pm. Perl provides something by default. See h2xs man page, for example.