The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

JavaScript::Lite - Bare-bones interface to SpiderMonkey ECMAscript API

SYNOPSIS

  use JavaScript::Lite;
  
  my $js = JavaScript::Lite->new;
  $js->assign(numbers => [ 2, 4, 6, 8, 10 ]);
  $js->assign(start => 1);
  $js->eval(q{
    function add_next() {
      var n;
      if(n = numbers.shift()) {
        start = start + n;
        return start;
      } else {
        return;
      }
    }
  });
  
  while(my $next = $js->invoke("add_next")) {
    print "$next\n";
  }

DESCRIPTION

JavaScript::Lite is a bare-bones interface to the SpiderMonkey ECMAscript engine. It aims to provide as little functionality (and therefore as little overhead) as is neccessary to connect perl with ECMAscript. Efficiency is the goal here; the intended environments are places where you are going to be calling from perl into ECMAscript thousands (or millions) of times in succession (such as using ECMAscript to drive the NPC logic in a perl-based MMORPG, or writing a spam filter in perl where the end users can write custom spam rules in ECMAscript, or...)

NOTE: This is very, very alpha software. I intend to keep the API more-or-less stable, but there may be quirks, and new features will be added in future releases (so long as they do not slow the existing features down!).

FEATURES

Does not bind perl variables to ECMA variables.
Does not bind perl objects / classes to ECMA objects / classes; only copying of structures from perl to ECMA is supported.
Only allows ECMAscript to return scalars to perl (no complex data structures).
Does not allow ECMAscript to invoke perl.
Does not allow ECMA method calls from perl - only global function calls.
Does not run ECMAscript's garbage collection automatically.

If you want powerful, flexible, full-featured blending of ECMAscript with perl, please see the JavaScript package. So why would you want to use JavaScript::Lite?

JavaScript::Lite can run much, much faster than the JavaScript distribution. This is because the flexibility that JavaScript offers you comes at a cost; class/object translations are expensive, and due to the fact that each language has different memory management, allowing both perl to call ECMA, and ECMA to call perl can cause irrecoverable memory leaks (as can allowing complex data structures to flow in both directions); the ECMA garbage collector can be expensive to run (even in "maybe" mode), and just the additional overhead of tracking all of this object/function binding/linking can be expensive.

In other words, here are some more features;

Does not juggle two garbage collectors, therefore no memory leaks.
Does not track object bindings between two languages, therefore no slowdown over time.
Does not wrap every call in translation logic, therefore little overhead.

METHODS

new([$max_mem])

Constructor; creates and returns a new JavaScript::Lite object (and underlying runtime/context). $max_mem is the maximum memory (in bytes) the JavaScript environment should be allowed to consume. Defaults to 1MB.

(If you go over this limit, JavaScript::Lite should raise an exception. Unfortunately, it doesn't yet... it causes a segmentation fault instead. :-( So be careful.)

eval($code[, $filename])

Evaluate a block of JavaScript code, returning the result as a scalar. If $filename is specified, tells the javascript interpreter that the code came from this file. Otherwise, the default filename "(eval)" is used.

If the code fails to compile, an exception is raised, which must be cleared if you want to keep using your JavaScript context; see "clear_error" below.

Note that this will not return JavaScript objects/structures; only strings, numbers, or undef.

eval_void($code, $filename)

Evaluate a block of ECMAscript, returning nothing. If you don't care about the return value of the script, this method works slightly faster than eval above. $filename is required.

eval_file($filename)

Reads the file $filename from your filesystem and evaluates it as ECMAscript, returning any scalar result.

eval_file_void($filename)

Same as eval_file, except that no result is returned to perl.

invoke($name)

Invoke the global ECMAscript function called $name. Invoking methods on ECMAscript objects is not yet supported. Passing arguments into the ECMAscript function is not yet supported, but should be in the next release.

Like eval, returns any scalar return value that the function may have returned.

collect()

Tell the SpiderMonkey garbage collector that it may run if it so wishes. If you don't do this every so often, your ECMAscript context will run out of memory and crash. This is not done automatically because even considering running the garbage collector can add significant overhead.

clear_error()

Clear any error condition that may have been raised in ECMAscript. You must do this if you want to continue using your JavaScript::Lite object after an eval or invoke raises an error.

assign($name, $value)

Assign $value to the global ECMAscript variable $name. $value may be a scalar, hashref, arrayref, or any nested combination. The entire structure passed into $value will be copied into ECMAscript. Globs and coderefs are not supported. $value must not be a self-referencing structure, or else JavaScript::Lite will crash (see BUGS below).

assign_property($object, $name, $value)

Assign $value to the property $name on the global ECMAscript object called $object. As with assign, $value may be a scalar, hashref, arrayref, or any nested combination.

$object must be a top-level ECMAscript global object. Nesting more than one layer deep (eg; "some_object.some.deep.property") is not supported. For that functionality, see the JavaScript distribution.

branch_callback($callback[, $interval])

Run $callback every $interval branches in the javascript. Branches are caused by things such as for() or while() loop invocation. $callback should a a subroutine reference; if the subroutine returns a true value, the javascript will continue running. If it returns a false value or dies, the javascript will terminate and an exception will be thrown.

If you do not specify $interval, the callback will be executed during every branch in javascript. This will slow down the script considerably; for best results, you should use a value at least in the several thousands.

clear_branch_counter

Explicitly resets the branch counter to zero

BUGS

If your JavaScript::Lite object runs out of memory (as defined by $max_mem when you create the object), it can cause a segmentation fault. I want it to raise an exception instead, I just haven't figured out how yet. :-(

JavaScript::Lite does not detect self-referencing data structures. And since it tries to make a copy of the data you pass in, if you pass in a self-referencing structure, it will consume all available memory until it crashes. For example, this will always crash:

  my $insane_hash = { foo => "bar" };
  $insane_hash->{baz} = $insane_hash;
  $cx->assign(insane_hash => $insane_hash);

I haven't quite decided if this is a bug or a feature yet... there is RAM and CPU overhead in tracking self-referencing structures, so doing so would slow JavaScript::Lite down. If you need to use them, use the JavaScript module instead, which will let you bind directly to them instead of copying.

THANKS

Claes Jakobsson

for the original JavaScript module.

Miron Cuperman

for creating a situation where I would need to write this.

The Chicago Perl Mongers

for hosting YAPC::NA 2008, where this was hacked together.

LICENSE

This is free software, you may use and distribute it under the same terms as perl itself.

This software uses mozilla's SpiderMonkey JSAPI for it's ECMAscript implementation, so in order for it to be useful to you, you must accept the terms of the mozilla license as well.

AUTHOR

Tyler "Crackerjack" MacDonald <japh@crackerjack.net>

SEE ALSO

JavaScript, http://developer.mozilla.org/en/docs/SpiderMonkey