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.
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');");
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 = <<<EOD print("Hello from JavaScript");
EOD;
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).
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');");
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 my$j = new JE;
# Introduce a new JavaScript function, named print, # which calls a Perl function $j->new_function(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->method(alert => "text");
# So does this $j->eval("alert('text');");
# Introduce the ability to do remote requests use Net::FTP;
$j->bind_class(package => "Net::FTP", name => "FTP");
# Execute native Perl functionality (FTP module) # directly from JavaScript $j->eval(<<'--end--');
var connect = new FTP("ftp.mozilla.org"); connect.get("index.html");
--end--
Note the extreme bleed-through that you can achieve with JE (in addition to excellent object translation).
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.
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(); }
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).
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
While there was a previous effort to embed Spidermonkey in Ruby it is largely defunct now, superseded by the new (strangely named) Johnson project.
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.
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');");
Update: Another example of Johnson in action from John Barnette:
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
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.
The above defines a global variable __FILE__ which, when called, returns the file name of the current JavaScript file. It's defined using a getter in particular so that the proper file name will be determined no matter what file it's called in (so you can safely include the above in a library file and it'll still report the correct, current, filename).
Currently the above will only work in Spidermonkey as only it supports the .stack stacktrace Error property. If there are reasonable alternatives for WebKit or Opera (which also support __defineGetter__) let me know.
Note: I'm not the creator of HotRuby, as mentioned elsewhere - it is the work of a highly-skilled Japanese developer.
HotRuby is a project which aims to port the Ruby Virtual Machine over to ECMAScript (allowing it to run, directly, in a browser using JavaScript or indirectly using ActionScript in Flash).
Currently the code works by using Ruby 1.9's YARV (Yet Another Ruby VM) to compile down a Ruby script into opcodes, which are then serialized and passed to the browser for execution. It's a little bit indirect but it still capable of creating a compelling result.
If you were to run one of the examples in your browser the actual chain of execution would be something like this:
Script finds <script type="text/ruby"></script> tags and extracts the inline Ruby code from them.
The Ruby code is sent to the server via an XMLHttpRequest.
The server-side CGI script (in Ruby, using Ruby 1.9) compiles the incoming Ruby into its associated opcodes and serializes it into a JSON data structure.
The browser consumes the opcodes, translating it into JavaScript, and executes it.
To observe this full process we can take a look at the code in the provided benchmark and watch its full path through the server and final execution:
and a sample of the opcode data returned by the server:
["YARVInstructionSequence\/SimpleDataFormat",1,1,1,{"arg_size":0,"local_size":4,"stack_max":3},"","src","top",["startTime","sum","endTime"],0,[["break",null,"label_21","label_29","label_29",0]],[2,["putnil"],["getconstant","Time"],["send","new",0,null,0,null],["send","to_f",0,null,0,null],["setlocal",4],4,["putstring",""],["setlocal",3],"label_21",5,["putobject",50000],["send","times",0,["YARVInstructionSequence\/SimpleDataFormat",1,1,1,{"arg_size":1,"local_size":1,"stack_max":2},"block in ","src","block",["e"],[1,[],0,0,-1,-1,3],[["redo",null,"label_0","label_22","label_0",0],["next",null,"label_0","label_22","label_22",0]],["label_0",5,["getdynamic",3,1],["getdynamic",1,0],["send","to_s",0,null,0,null],["send","+",1,null,0,null],["dup"],["setdynamic",3,1],"label_22",["leave"]]],0,null],"label_29",["pop"],7,["putnil"],["getconstant","Time"],["send","new",0,null,0,null],["send","to_f",0,null,0,null],["setlocal",2],9,["putnil"],8,["getlocal",2],["getlocal",4],["send","-",1,null,0,null],["send","to_s",0,null,0,null],["putstring"," sec"],9,["send","+",1,null,0,null],["send","puts",1,null,8,null],8,["leave"]]]
and you can find the full client-side virtual machine here: HotRuby.js.
Perhaps most fascinating about this, though, is the speeds that are able to be achieved with this script. Granted, the above benchmark is rather contrived, but the end performance results are quite fascinating:
Firefox 3.0b5
2.47s
Firefox 2
6.71s
Ruby 1.8.2
12.25s
We can see a 2.71x speed improvement from Firefox 2 to Firefox 3 and a 5x performance improvement over regular Ruby 1.8.2, running on the command-line.
It's a fascinating time to be working with JavaScript. The performance improvements that are being provided to us by the browser afford us a realm of possibility that wasn't, previously, viable. The fact that we're even discussing running a virtual machine, implemented in JavaScript, is quite impressive. I'm curious to see what applications end up being built with this implementation - and within what context they end up using it.
In the bustle of announcements surrounding OSCON, Blackhat, and the Ajax Experience one single, incredibly important, announcement was made: The introduction of two new Mozilla projects: IronMonkey and ScreamingMonkey.
The critical, core, component of this is the Tamarin virtual machine (which is an Open Sourced version of the ActionScript Virtual Machine that powered the Adobe Flash Player). Tamarin already supports ECMAScript 3 (and, thusly, JavaScript, ActionScript, and JScript) and parts of the upcoming ECMAScript 4 specification.
Briefly, since they're both still under planning, here's what IronMonkey and ScreamingMonkey are setting out to achieve.
IronMonkey is setting out with the goal of mapping Microsoft's Common Intermediate Language (CIL) to ActionScript Byte Code (ABC), allowing additional language implementations, such as IronPython and IronRuby, to run in the Tamarin Virtual Machine.
To break all of this down: There are implementations of Python and Ruby that are capable of being compiled down to a Common Intermediate Language, which will then be able to be run on Tamarin via IronMonkey.
This is a huge, huge, deal. This means that JavaScript will no longer be the only viable scripting language in browsers that use that Tamarin engine. At the very least, there'll be two more languages to work with. However, if IronMonkey works out well, I wouldn't be surprised if we see an implementation of PHP that runs in the browser.
Tamarin being able to run JavaScript 2, Python, and Ruby really doesn't mean a whole lot (to the general web-developer public) if the languages don't work in all modern browsers (even though it'll give development on the Mozilla platform a considerable boost). This is where ScreamingMonkey comes into play.
ScreamingMonkey is the effort, being led by Mark Hammond, to allow the Tamarin engine to run within non-Mozilla browsers, starting with Internet Explorer.
Unfortunately, the Internet Explorer team is caught up fixing bugs in their existing ECMAScript implementation (JScript), thus their likelihood of implementing ECMAScript 4, in a reasonable time frame, is slim to none.
The result of this effort will be for a developer to be able to reference ECMAScript 4 or JavaScript 2 from a script tag and have it load a required plugin in order to execute it, for example:
There's a detailed plan of attack laid out and it will require a lot of work. The end result still needs to be actualized, but it will most likely be in the form of a standalone Tamarin runtime (possibly embedded in another distribution) that will be able to hook into its relevant browsers.
As with IronMonkey, this is a huge announcement. This immediately helps to give credence to using the upcoming JavaScript 2 language as its cross-browser support is practically assured. While we're still a long ways off from being able to use this particular project, the result of it will surely be incredible.
Regardless of the outcome of either one of these projects, it's obvious that browser scripting is beginning to shift in some appreciable ways. Although, should these projects succeed the resulting effect upon the web development industry will be incalculable.
I just got back from giving a presentation at the Boston Ruby Group on jQuery. You can find the contents of the slides below. There's going to be a video of the talk, so I'll post that too, when it's ready.
A fun online game that I've been playing, lately, is llor.nu (unroll, backwards). It's written using Ruby on Rails, has lots of nice Javscript/AJAXy things, and is Open Source.
The game, in and of itself, is rather simple - you roll the dice, collect money, pay rent, build buildings, and get more money. Which can be rather tedious - however, being a programmer, I saw an opprotunity to automate a bunch of the tedium and maximize my profits - and according to the discussion boards, I'm not alone. Recently, the game had an overhaul changing much of the dynamics, and much of my code. One of the things on the developer's 'to-do' list is to implement a public API, but until then, I've pulled together a number of commands (e.g. REST-like URL and parameter combos) that can get the job done. So, if you're interested in writing your own bot, it's definitely a good place to start.
Logging In
POST /login
user[login]=USERNAME&user[password]=PASSWORD
This is the essential first step of any bot - logging in to your account. The username and password are the ones that you signed up with originally.
Rolling The Dice
GET /js/take_turn/
The most simple of bots can do nothing but login and then roll the dice to kingdom come - under the current version of the game, it is very likely that you will become quite rich.
Buying A Building
GET /deeds/buy/NUM?levels=LEVELS
The next step, is to generate some form of perpetual income (from other hapless players, rolling their dice) - this can be done by buying an available plot and hoping that it'll be landed on. However, you must first know if you've landed on a plot that can be purchased - the easiest way to do this is to take the output from 'Rolling The Dice' and to check and see if it contains the text "buy and build". Some psuedo code:
login();
while( 1 ) {
data = GET('http://llor.nu/js/take_turn/');
if ( data.contains( 'buy and build' ) ) {
GET('http://llor.nu/deeds/buy/2?levels=1');
}
}
The next important step is to determine what type of building and how many levels to buy - There are four types of buildings numbered 2 (lowest worth), 3, 4, 5 (highest) - and multiple levels (1 to 30ish). You'll probably want to focus on buying on particular type of building, exclusively - it'll make your code, and management, much simpler.
That should be sufficient to get you started hacking around - just make sure that you don't call their poor server too frequently - leave a couple second delay it, to be nice. (Otherwise there may not be any more game to mess with.) If there's interest, I'll be happy to delve into the following:
Using ducks, Figuring out when to use a duck
Using hard hats, Figuring out when to use a hard hat
Figuring out how much money you have
Renaming Buildings
Upgrading Buildings
and Selling Buildings
At the very least, be sure to check out the game, it's very slick, to say the least (and look for me up on the high score board!).
Update: The 'can I buy' text has been changed to 'buy and build' (to work with today's update).
Unfortunately, those are really the only languages with any sort of repository, which is a shame. Regardless, I'm finding that being subscribed to both the Perl and Javascript RSS feeds is really helping me to catch the latest developments/advancements and include them in my projects.