Emscripted-Ruby
Emscripted Ruby is a build script that uses Emscripten to compile Ruby MRI 1.8.7 for use in a browser. The main difference is the conversion from C to C++ and the switch from setjmp/longjmp (which can't be implemented in JavaScript) to C++ exceptions. Note that we are not using Ruby 1.9.x due to its reliance on threads, which cannot be ported directly using Emscripten.
The project is fairly young. Right now it suffers from a memory leak due to a bug in the emscripting of the Ruby garbage collector. The performance is also a problem, as so far we've enabled runtime checking of all signing and overflow issues. Switching to just the needed sign/overflow corrections should boost performance significantly.
The build script is licensed under the MIT license. The modifications follow Ruby's GNU GPL2 license.
How to Build
The latest pre-built files are found in the dist
folder:
ruby.js
is the raw human-readable compiled source produced by Emscripten.ruby.opt.js
is the human-readable compiled source after being passed through the redundant variable elimination script.ruby.closure.js
isruby.opt.js
after being passed through the Closure Compiler. This is what you will likely want to use.lib
is the folder containing the Ruby standard library. You will want to copy this alongside one of the above JavaScript files to your app.
If you want to build it manually, run ./makeruby
. If you want to use the
Closure Compiler, you will need to check out and build a recent version, then
provide the path to it in makeruby
. That script also contains a few
configurable options. You might need to tweak RELOOP
, PREINITIALIZED_MEMORY
and RUN_CLOSURE
until you get optimal performance in all your target browsers.
For the manual build, you will need to have Emscripten and Emmaken configured as per the Emscripten documentation.
How to Use
Copy one of the three JavaScript files from dist
to a place accessible to your
web page, along with the whole lib
folder. In your app, include the JavaScript
file you copied. This will provide a global variable called Ruby
. All your
interactions with Emscripted Ruby goes through that. The functions it offers are
as follows:
initialize(input, output, error)
: This will initialize the Ruby engine and should be called once before any calls toeval()
orstringify()
. The arguments are 3 optional callbacks:input
: Will be called when Ruby asks for something onstdin
. Should return an ASCII character code ornull
if no more input is available. Defaults toreadline()
in a command-line engine andwindow.prompt()
in a browser.output
: Will be called when Ruby prints something tostdout
and passed a byte value which might be an ASCII character code or part of a UTF8 character. Its return value is ignored.error
: Same asoutput
, but forstderr
.
eval(string)
: Evaluates the given Ruby code and returns the result as a Ruby object. Currently there is no JavaScript API to tinker with this object other thanstringify()
, but you can use all the C API functions if you really want. Their names are prefixed with underscore in JavaScript though. Read the Emscripten docs for some hints about the marshalling required to convert to and from C types.stringify(object)
: Converts an object returned byeval()
to a human-readable string representation (equivalent ofobject.to_s()
).
Note that including the JavaScript file directly into your web page will pull a huge number of Emscripten-generated globals into the global namespace, so you might want to put it in an iframe or a Web Worker. We can't simply wrap the code in an anonymous closure because that significantly reduces performance.