One thing that I absolutely adore is the sheer embeddability of JavaScript. It’s a (comparatively) simple language that is fiercely flexible. I tend to liken JavaScript to water – alone it’s painfully simple but it can take the form of its container – and mixing it with anything enhances its flavor.
Since JavaScript, alone, is so dumb (we’ve become spoiled by browsers which provide DOM, setTimeout/setInterval, and even a global object reference – none of which are necessarily required my an ECMAScript implementation) we must rely upon the spice of ‘larger’ languages to help it gain some taste.
We can start by taking a look at some of the most popular languages that are available today: Python, Perl, PHP, Ruby, and Java — all of which have, at least, one embeddable JavaScript interpreter.
One thing is the same in all the interpreter implementations, as well, they all have the ability to introduce (at least) simple objects into interpreter (from the parent language) and extract values again. Sometimes this may be as simple as executing a JavaScript function and getting its return value (which is often translated from its internal JavaScript form back into the native language).
There’s a couple points upon which I like to evaluate the embeddability of a JavaScript interpreter, specifically:
- Object Translation: If objects/values are passed to/from the interpreter to/from the native language – is that translation handled automatically?
- Simplicity: How hard is it to get up-and-running? (Is extra compilation required or is it written using native language code?)
- Bleed-through: Can JavaScript communicate to the base language or is it a one-way-street?
The first point is the easiest one to find compatibility with – virtually all embeddable JavaScript interpreters do some form of object translation. Some do it better (like JE and Rhino) but it generally shouldn’t be a problem for simple scripts.
PHP
» PHPJS
A flexible, pure-PHP, JavaScript interpreter that even accepts compiling to an intermediary bytecode. There is no bleed-through but good object translation.
include "js.php"; # Introduce a new JavaScript function, named print, # which calls a PHP function function show($msg) { echo "$msg\n"; } $jsi_vars["print"] = "show"; # Add in a new pure-JavaScript function which calls # our previously-introduced print function jsc::compile("function alert(msg){ print('Alert:' + msg); }"); # Prints out 'Alert: text' js_exec("alert('text');");
» J2P5: Pure-PHP ECMAScript Engine
Probably the simplest of the available interpreters – there doesn’t appear to be a clear way of communicating from JavaScript-to-PHP (or vice versa). Although, it is implemented in pure-PHP which allows for some nice cross-platform compatibility.
include “js/js.php”;
$script = << An embedding of Mozilla’s Spidermonkey JavaScript engine into Perl. Since it uses Spidermonkey it’ll require that extra source code and compilation. Object translation is good but there’s no bleed-through (JavaScript calling native Perl functionality that isn’t explicitly defined). » JE: Pure-Perl ECMAScript Engine A pure-Perl JavaScript engine – shows incredible promise (probably my favorite new engine in addition to Rhino). Has tremendous potential (including the ability to serialize the runtime environment!). use JE; # Instantiate the interpreter # Introduce a new JavaScript function, named print, # Add in a new pure-JavaScript function which calls # Prints out ‘Alert: text’ # So does this # Introduce the ability to do remote requests $j->bind_class( package => “Net::FTP”, name => “FTP” ); # Execute native Perl functionality (FTP module) » Rhino Probably one of the most famous embeddable JavaScript implementations. Implemented in pure Java has excellent object translation and perfect bleed-through. The bleeding can even be controlled explicitly at runtime (to create a more confined environment). In the following code (using in some of the jQuery build system) two functions are defined which utilize a number of native Java packages – all without ever writing a single line of Java code. Previously this was one of the most depressing embedding efforts (and, really, the only one available for Python) but it has since had some new life blown into it – which is much appreciated. It supports decent object translation and bleed-through (including the ability to call native classes, much like in JE). While there was a previous effort to embed Spidermonkey in Ruby it is largely defunct now, superseded by the new (strangely named) Johnson project. » Johnson This is a, relatively, new project that’s working to completely overhaul how communication is currently done in-between Ruby and JavaScript (using Spidermonkey to power the engine). There is complete bleed-through: Ruby can dig in and manipulate JavaScript objects at run-time and JavaScript can call back and access native Ruby functionality. Additionally they’re examining the ability to provide native browser environments using the env.js script that I created last year. While the cross-platform ease-of-use isn’t there yet the project absolutely has a ton of potential. Update: Another example of Johnson in action from John Barnette: There’s a ton of potential in all of these projects – providing interesting features for blurring the lines in-between JavaScript and different host languages. For your next project it should be pretty easy to find a solution to embedding JavaScript as your quick-and-dirty scripting language of choice.use JavaScript::SpiderMonkey;
# Instantiate the interpreter
my $j = JavaScript::SpiderMonkey->new();
# Introduce a new JavaScript function, named print,
# which calls a Perl function
$j->function_set("print", sub { print @_, "\n" } );
# Add in a new pure-JavaScript function which calls
# our previously-introduced print function
$j->eval("function alert(msg){ print('Alert:' + msg); }");
# Prints out 'Alert: text'
$j->eval("alert('text');");
my $j = new JE;
# which calls a Perl function
$j->new_function(print => sub { print @_, “\n” } );
# our previously-introduced print function
$j->eval(“function alert(msg){ print(‘Alert:’ + msg); }”);
$j->method(alert => “text”);
$j->eval(“alert(‘text’);”);
use Net::FTP;
# directly from JavaScript
$j->eval(<<'--end--');
var connect = new FTP("ftp.mozilla.org");
connect.get("index.html");
--end--[/perl]
Note the extreme bleed-through that you can achieve with JE (in addition to excellent object translation).
Java
importPackage(java.io);
function writeFile( file, stream ) {
var buffer = new PrintWriter( new FileWriter( file ) );
buffer.print( stream );
buffer.close();
}
function read( file ) {
var f = new File(file);
var reader = new BufferedReader(new FileReader(f));
var line = null;
var buffer = new java.lang.StringBuffer(f.length());
while( (line = reader.readLine()) != null) {
buffer.append(line);
buffer.append("\n");
}
return buffer.toString();
}
Python
from spidermonkey import Runtime
j = RunTime().new_context()
# Introduce a new JavaScript function, named print,
# which calls a Python function
j.bind_callable("print", lambda msg: print(msg));
# Add in a new pure-JavaScript function which calls
# our previously-introduced print function
j.eval_script("function alert(msg){ print('Alert:' + msg); }");
# Prints out 'Alert: text'
j.eval_script("alert('text');");
Ruby
require "johnson"
j = Johnson::Runtime.new
# Introduce a new JavaScript function, named print,
# which calls a Ruby function
j.print = lambda {|msg| puts msg};
# Add in a new pure-JavaScript function which calls
# our previously-introduced print function
j.evaluate("function alert(msg){ print('Alert:' + msg); }");
# Prints out 'Alert: text'
j.evaluate("alert('text');");
require "johnson"
class Monkey
def eat
puts "Nom, nom, nom, bananas!"
end
end
j = Johnson::Runtime.new
# toss a monkey into the runtime
m = Monkey.new
j[:m] = m
# add a JSland function to our monkey...
j.evaluate("m.chow = function() { this.eat() }")
# ...and now it's available as an instance method on our native Ruby object!
m.chow
Fowl (June 15, 2008 at 2:58 am)
What about from C#/.Net?
The DLR supports Javascript with full access to native (managed) types.
http://tirania.org/blog/archive/2007/May-03-1.html
http://blogs.msdn.com/deepak/archive/2007/05/02/managed-jscript-is-availaible.aspx
Or does JScript not count?
Fowl (June 15, 2008 at 3:14 am)
^^ Above is not intended to be snarky, I just wanted your opinion. ^^
funtomas (June 15, 2008 at 3:56 am)
I belive you’ve read Stevey’s Blog Rants, haven’t you.
Porfirio (June 15, 2008 at 4:24 am)
Sorry but JScript is just the worst implementation of JavaScript, i would not count it. It sucks
Dominic Mitchell (June 15, 2008 at 4:25 am)
For Perl, there’s also JavaScript.pm, which worked better when I last looked at them both. I wrote JavaScript::JSLint on top of it.
Doekman (June 15, 2008 at 6:07 am)
@Fowl: technically, Managed JavaScript is not embedding JavaScript in .NET, but making it a first class citizen. But since one can run javascript with it, it is interesting :-)
Michael Foord (June 15, 2008 at 9:05 am)
Managed JScript is a fresh implementation of Javascript, very different from the JScript that runs in IE. It is *claimed* to be very ECMA compliant.
It runs on the DLR (Dynamic Language Runtime), but currently only a version for running in Silverlight is available. We’ve asked them for a version that will run on standard .NET, but no answer yet.
The interoperability between DLR languages is looking very good. You can play with it using the example ‘DLR Console’ application.
John Resig (June 15, 2008 at 9:27 am)
@Fowl: I will have to agree with Michael Foord – Managed JScript is probably the closest thing here (embedding the ECMAScript language). But yeah, JScript is kind of “cheating” – it’s its own whole language.
@funthomas: Yep – I’m very familiar with his posts. I’m terribly excited to see all the love that Rhino is getting – and I’m glad to see that he’s the one making it happen.
@Dominic Mitchell: Thanks for the pointer! It appears to be virtually identical to JavaScript::Spidermonkey. Not that that’s a bad thing, but glad to see progress being made!
Alan Hogan (June 15, 2008 at 12:17 pm)
I don’t know why Rhino would even exist, Java and JavaScript are the same thing. juuuust kidding
Dan Webb (June 15, 2008 at 4:16 pm)
Johnson looks very interesting and I’d love to have a play with it. However, it doesn’t seem to be on ruby forge as yet and if I clone from Github it doesn’t seem to build out of the box.
John, can you tell me how you got it working?
pd (June 15, 2008 at 10:39 pm)
Does this concept relate to ordinary dynamic web site creation? I write Perl code that spits out HTML via Text::Template. All my JS code is written in one of two files that are called via the .
Where, if at all, does embedded JS fit into that picture?
Michael O'Brien (June 16, 2008 at 10:47 am)
Also check out Ejscript, a new ES4 implementation. Can easily be embedded or used server side. http://www.ejscript.org
John Barnette (June 16, 2008 at 3:34 pm)
@Dan Webb: We’re probably not going to slap anything up on RubyForge until we roll a 1.0 release of Johnson, but it should certainly build out of the box. If you drop by #johnson on Freenode or the johnson-talk mailing list we’d be happy to help you out.
Metal Hurlant (June 16, 2008 at 4:00 pm)
A few clarifications on the PHP options for running JS:
(disclaimer: I wrote J4P5)
– phpjs is a thin layer of syntactic sugar and doesn’t actually implement most of ecmascript.
– j4p5 was my crazy experiment. It implements most of ecmascript 3. It compiles javascript source to generated php code. Compilation is darn slow, runtime speed is kinda slow. If you’re insane enough to use it, install APC at least.
– Omar Kilani has written a small php extension that embeds spidermonkey (http://aurore.net/projects/php-js/ ) Honestly, this was probably the least work of the three, and it also happens to be the most practical choice.
Neither of these projects is actively maintained, so tread at your own risk.