Why embed JavaScript?


In the summer of 2006 i was at a job interview where one of the interviewers posed a very interesting question:
Why would I want to use JavaScript if I'm not writing code for a web browser?
The questioner was an experienced programmer, which led me to believe that this question hints at the misunderstood role of JavaScript. So let's talk a bit about why one would want to embed JavaScript in an application other than a web browser...

First, we need to understand what it means to "embed" a scripting language, whether it is JavaScript or some other language. Embedding scripting support has two main properties:
  1. It allows us to "bind" (or "attach") native (C/C++) objects to JavaScript objects, such that we can manipulate the native objects from script code. The ubiquitous example of this is the document object available to web browsers.
  2. It allows us to run script code from inside our native application, whether or not we expose this ability to users of our applications. Reasons for doing so might include access to features of the scripting language which the native language does not have or using the scripting engine as a garbage collector.
For our purposes, the first property is primarily of interest.

Let's pretend we have an application with some sort of Document class. Whether this Document class is a text document, a graphics document, an audio file, a hybrid multimedia document, or something completely different is unimportant for our purposes. What is important is that we understand what we gain by adding scripting support to it. This is not to imply that all abstract Document types gain anything at all from the ability to script them, but many can gain a great deal of flexibility.

By binding native classes, like our Document class, to script code we gain the ability to remotely control them without making changes to native code. As the embedders, we have full control over which features of our document become scriptable and which not (admittedly, sometimes technical or practical limitations will limit what features we can script). As a completely hypothetical example, assume for a moment that our Document class is some sort of text manipulation widget, like part of a word processor. We could bind features such as moving the cursor, searching for text, etc., as shown in this simple example:
var doc = new Document( "/home/me/my_cat.txt" );
var position = doc.findText( "my cat" );
if( -1 != position ) {
    doc.moveCursor( position );
    doc.insertText( "I love " );
}
doc.moveCursor( -1 ); // moves to end of document
doc.insertText( "\nHere is a new line." );
doc.save();
For a graphical Document object, we might query or manipulate pixel colors. For an audio file Document we might adjust noise levels. For your own Document class, you can bind whatever features are useful for you.

The answer to the question "why use SpiderApe instead of plain old SpiderMonkey?" is easy: simplicity. The Ape API is much simpler to use than the SpiderMonkey API, especially when it comes to type conversions. Additionally, Ape provides type safety when we convert between native types and JS types, which is something the Monkey API doesn't do. Since the majority of Monkey client code is typically functions to bind native objects and functions to the JS engine, wrapping existing native functionality, Ape code is typically much more concise than equivalent Monkey code. For projects which only want to execute JS code, and not bind any native functionality, Ape is probably overkill. For those doing any significant amount of binding, however, Ape can greatly simplify the process as well as make it safer (from a type-safety point of view).

Embedding non-JavaScript languages

As most of you probably know, many other languages are embeddable. These include, but are not limited to, Lua, Perl, PHP, and Python. Here we will muse a bit on why one may or may not want to consider support for embedding those languages in one's software, as opposed to embedding JavaScript. The reader should be warned that the following opinions are just that - opinions - and are biased.

Why not use SWIG?

SWIG (Simplified Wrapper and Interface Generator) is a really powerful, fairly easy to use tool which automatically generates C/C++ bindings with a large number of scripting languages. SWIG is really cool, but provides no support for JavaScript (or, more correctly, no support for a specific JavaScript engine). After tinkering with SWIG a bit, it becomes clear that SWIG essentially relies on a language's ability to plug in new features at runtime via DLLs. SpiderMonkey does not natively support such a feature. Also, SpiderMonkey's use of a JSContext pointer for all of the important operations would seem to foil SWIG's good intentions, as SWIG has no way of knowing which JSContext to initialize everything with. So, while SWIG seems to be an awesome solution to the problem of binding C/C++ code to many other languages (Python, Perl, PHP, Ruby, Lua, etc. etc. etc.), it would seem to not be feasible to support the SpiderMonkey engine. In theory it would be possible to base a SWIG-based generator on top of the SpiderApe API (which provides plugin support on top of SpiderMonkey and a more flexible data type conversion system), but it would be a large undertaking.