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:
- 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.
- 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.
- Lua was supposedly created with embedding in mind, and is
therefore presumably simple to embed. It's syntax is unusual, though, and in my opinion this makes it more difficult
to get started with. Similar comments could apply to Ruby.
- Perl would seem to be a practical language for embedding, but
one must assume that it is difficult or impractical to actually embed because there are so few
applications which embed it (i can think of only a single one off the top of my head:
Apache's mod_perl).
- Python is a popular choice. i personally consider this unfortunate,
as i think Python is an over-used miscarriage of computer science. Nonetheless, the range of
applications which offer embedded Python support suggests that it is easy to embed. And, like
Amiga and
Apple Macintosh enthusiasts, users of Python swear by it.
- PHP is one of my favourite languages. The only significant reason i can
think of against embedding it is the high logistical overhead of including the PHP runtime environment in one's
project.
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.