Building Mozilla SpiderMonkey for Mac OS X

As part of my development of a JavaScript implementation of Apple’s Cocoa Bindings technology, I’ve been keen to implement a unit testing framework. There are several JavaScript unit testing libraries that run in the browser, but I’m most interested in something that runs from the command line. That way it can be part of my build process — including JavaScript Lint.

The Mozilla project has packaged their reference implementation of JavaScript, SpiderMonkey, which compiles just fine on Mac OS X. But the default configuration of the JavaScript shell doesn’t include file operations (other than the ability to load scripts). This is a pretty severe limitation, but fortunately, there’s an extension for file support.

The file extension requires the Netscape Portable Runtime library (NSPR). I wasn’t able to find any documentation on how to build SpiderMonkey with NSPR to enable file support. So after a few aborted attempts, I’ve finally documented everything I needed to do.

At the end of this, you should have a SpiderMonkey JavaScript shell that includes file operations.

  1. Create a folder to contain everything. I called it mozilla.

  2. Get the 4.4.1 release of NSPR from the Mozilla Web site.

    The libraries don’t actually work on OS X 10.4, but we can steal the binary from Firefox. So we’re only using the release for the headers. Put the NSPR release in the mozilla folder. You should now have ~/mozilla/nspr-4.4.1.

  3. Get the latest SpiderMonkey source code.

    Unpack the source into the mozilla folder. You’ll now have the ~/mozilla/js folder which contains the src folder.

  4. Tweak the Makefile

    If you’re building the standalone JavaScript shell, you’ll be using the Makefile.ref makefile. This needs some slight tweaking before it’s ready. When you define JS_THREADSAFE SpiderMonkey will use the NSPR runtime, except the standalone makefile doesn’t know where the runtime lives.

    Add -I../../nspr-4.4.1/include to the INCLUDES definition and -L../../nspr-4.4.1/lib to the OTHER_LIBS definition:

    ifdef JS_THREADSAFE
    DEFINES += -DJS_THREADSAFE
    INCLUDES += -I../../dist/$(OBJDIR)/include -I../../nspr-4.4.1/include
    ifdef USE_MSVC
    OTHER_LIBS += ../../dist/$(OBJDIR)/lib/libnspr${NSPR_LIBSUFFIX}.lib
    else
    OTHER_LIBS += -L../../dist/$(OBJDIR)/lib -lnspr${NSPR_LIBSUFFIX} -L../../nspr-4.4.1/lib
    endif
    endif
    
  5. Enable file support.

    You’ll need to make a couple changes to the source in order to get file support in SpiderMonkey. First, comment out the definition of LAZY_STANDARD_CLASSES, which appears on line 1975 in js.c. This seems to tell SpiderMonkey that it can initialise the standard classes in a lazy fashion. Unfortunately, the File object never gets initialised. So we comment this out.

    Next, there’s a slight bug in the file library itself. In the js_fileDirectoryName function in jsfile.c (around line 380), you’ll see the following:

    /* add terminating separator */
    index = strlen(result)-1;
    result[index] = FILESEPARATOR;
    result[index+1] = '\0';
    

    The problem is this will overwrite the last character in the directory name; simply removing the -1 will solve the problem. You should end up with:

    /* add terminating separator */
    index = strlen(result);
    result[index] = FILESEPARATOR;
    result[index+1] = '\0';
    

    There’s one more bug in the file library’s routine to read a line that we need to fix. In the file_readln function in jsfile.c (around line 1717), you’ll see the following:

    if ((endofline==JS_TRUE)) {
        str = JS_NewUCStringCopyN(cx, JS_GetStringChars(file->linebuffer),
                                  offset);
        *rval = STRING_TO_JSVAL(str);
        return JS_TRUE;
    }else{
        goto out;
    }
    

    Now aside from the ghastly coding style, this check doesn’t allow for reading the final line in the file where there’s no trailing linefeed. So we can simply slip in a check to see whether any characters were read in addition to whether a newline was encountered.

    if ((endofline==JS_TRUE)||(offset>0)) {
        str = JS_NewUCStringCopyN(cx, JS_GetStringChars(file->linebuffer),
                                  offset);
        *rval = STRING_TO_JSVAL(str);
        return JS_TRUE;
    }else{
        goto out;
    }
    
  6. Build SpiderMonkey.

    After making the changes above, you should be ready to build the SpiderMonkey JavaScript shell. Type the following at the command line:

    make -f Makefile.ref JS_THREADSAFE=1 JS_HAS_FILE_OBJECT=1
    
  7. Copy the NSPR dynamic library from Firefox.

    The shell won’t run without libnspr4.dylib. But the dynamic library included with the NSPR 4.4.1 library won’t work on Tiger. So the answer is to steal the library that’s compiled into Firefox. If you’ve installed Firefox in your /Applications folder, you should be able to type:

    ~/mozilla/js/src> cp /Applications/Firefox.app/Contents/MacOS/libnspr4.dylib Darwin_DBG.OBJ
    

    This will copy the dynamic library from the Firefox application to SpiderMonkey’s build folder.

7 Comments

  1. GazHay
    Posted 19 Aug 2006 at 10:45 am | Permalink

    Further changes are required for building on Intel Macs, as the make command finds that the nspr build is for ppc, and does not match arch’s return value i386.

  2. Posted 19 Aug 2006 at 10:46 am | Permalink

    Yeah, unfortunately, I don’t have an Intel Mac, so this isn’t something I can fix. If you have the details, please let me know. I’ll update this post.

  3. GazHay
    Posted 19 Aug 2006 at 11:19 am | Permalink

    OK, got it, I downloaded nspr-4-6-2 source, navigated to that directory, and ran ./configure and then make I moved the resulting libnspr4.dylib file to the nspr-4-4-1/lib replacing the one that was there for ppc. I then built spidermonkey and stole the libs from firefox, and all is well.

  4. manu3000
    Posted 21 Aug 2006 at 11:51 am | Permalink

    hi

    sorry, i’m pretty useless but i didn’t manage to build SpiderMonkey (just building it on its own — no file support) on my system (PPC 10.4)

    i tried ./configure from the /src directory (taht works usually) but here i got this depressing : ” ./configure: No such file or directory ” message !

    Can you help ? Thanks

  5. Posted 21 Aug 2006 at 11:59 am | Permalink

    I’m not at my Mac right now, but I think all you need to do to build (without file support) is run:

    make -f Makefile.ref
    

    This uses the special standalone makefile. I don’t recall ever running ./configure.

  6. Posted 5 Feb 2007 at 6:35 pm | Permalink

    Yeah, if you don’t need file support it’s actually pretty easy to just build the interpreter. Detailed instructions (not that they’re complicated) here

  7. Charles Turner
    Posted 19 Jun 2007 at 6:17 am | Permalink

    Hi-

    I took a slightly different approach that seems to work well. I was unsure about version incompatibility what with this page being about a year old. I installed originally from Fink, which has the latest versions of spidermonkey and nspr, but the file class didn’t seem to function.

    I removed spidermonkey and spidermonky-shlibs that Fink had installed, leaving the Fink installation of nspr and readline5. I got the latest spidermonkey tarball (1.60) via the link above.

    I added the following lines to Makefile.ref (as per above):

    -I/sw/include/nspr -L/sw/lib

    I made no changes to the JS source files.

    I compiled with:

    make -f Makefile.ref JSTHREADSAFE=1 JSHASFILEOBJECT=1

    Not elaborately tested, but the file class seems to work now.