Microsoft SUA JavaScript Shell

Recently I needed to test some JavaScript code on a Microsoft Vista Ultimate operating system using the command line. This is something that I have done before on Linux platforms using a JavaScript shell but had never done on a Microsoft platform. For those who are not familiar with a JavaScript shell, it is a command line interface to a JavaScript engine. Similar to Python or Ruby, the JavaScript shell has two modes of operation. You can use it as an interactive shell, in which you type JavaScript code at a prompt and get instant results, or you can invoke a JavaScript program.

The easiest way that I know of to build a JavaScript shell is to download and build the SpiderMonkey JavaScript engine which comes with a JavaScript Shell. SpiderMonkey is one of two JavaScript engines which the Mozilla software project supports. It is used in the Firefox browser and elsewhere. The other JavaScript engine is Rhino. Rhino is written using the Java language whereas SpiderMonkey is a pure C language implementation which conforms to ECMA-262 Edition 3. Included in SpiderMonkey is a JavaScript shell. Source code for both JavaScript engines is readily available on the Mozilla website.

For Microsoft Vista I had a choice of either building a native Windows executable which could be accessed via the Vista command prompt (similar to the old DOS prompt) or, because Vista Ultimate comes with Subsystem for Unix-based Applications (SUA), a shell which was accessable via a more familar Unix-like environment. I choice to go the SUA route.

Many people are unaware of Microsoft's SUA offering. Basicly it is a full-featured POSIX-compliant subsystem for Windows NT-based operating systems. For their own reasons, Microsoft limits the availablity of SUA to certain products such as Vista Enterprise and Ultimate editions and Windows Server 2008. It comes with hundreds of utilities including the GNU compiler collection, Korn shell, and development headers and libraries. See here for further information.

SUA is a subsystem which runs natively on top of the Windows kernel. which was orginally developed by Softway Systems. The original name for the product was OpenNT but that was later changed to Interix. Interix and most of the development team was acquired by Microsoft in 1999 and the product was renamed Services For Unix (SFU) in 2005. With the release of Microsft Vista, it was integrated into the operating system as a separate installable component and was renamed Subsystem for Unix-based Applications. There is an active user community around SUA which is hosted by Interop Systems and sponsored by Microsoft.

This is the Makefile which I hacked together to build a static js executable from the SpiderMonkey 1.8 sources for use with Microsoft Vista SUA.

JSVERSION = js-1.8.0

CC = gcc

OS_ARCH := $(subst /,_,$(shell uname -s | sed /\ /s//_/))
OS_RELEASE := $(shell uname -r)
OS_CONFIG := $(OS_ARCH)$(OS_RELEASE)
OBJDIR = ./$(OS_CONFIG)

DEFINES = -DXP_UNIX -DPOSIX_SOURCE -DHAVE_LOCALTIME_R

LIBDIR := lib
INCLUDES += -I$(OBJDIR)
RANLIB = ranlib
LDFLAGS = -lm

OPTIMIZER =
INTERP_OPTIMIZER =

CFLAGS = $(OPTIMIZER) $(DEFINES) $(INCLUDES)
INTERP_CFLAGS = $(INTERP_OPTIMIZER) $(DEFINES) $(INCLUDES)

PREDIRS += editline
DEFINES += -DEDITLINE

JS_CFILES = jsapi.c jsarena.c jsarray.c jsatom.c jsbool.c jscntxt.c jsdate.c \
jsdbgapi.c jsdhash.c jsdtoa.c jsemit.c jsexn.c jsfun.c jsgc.c jshash.c \
jsinterp.c jsinvoke.c jsiter.c jslock.c jslog2.c jslong.c jsmath.c \
jsnum.c jsobj.c jsopcode.c jsparse.c jsprf.c jsregexp.c jsscan.c \
jsscope.c jsscript.c jsstr.c jsutil.c jsxdrapi.c jsxml.c prmjtime.c

JS_HFILES = jsarray.h jsatom.h jsbool.h jsconfig.h jscntxt.h jsdate.h \
jsemit.h jsexn.h jsfun.h jsgc.h jsinterp.h jsiter.h jslibmath.h \
jslock.h jsmath.h jsnum.h jsobj.h jsopcode.h jsparse.h jsarena.h \
jsclist.h jsdhash.h jsdtoa.h jshash.h jslong.h jstypes.h jsprvtd.h \
jspubtd.h jsregexp.h jsscan.h jsscope.h jsscript.h jsstr.h jsutil.h \
jsxdrapi.h jsxml.h

API_HFILES = jsapi.h jsdbgapi.h

OTHER_HFILES = jsbit.h jscompat.h jscpucfg.h jsotypes.h jsstddef.h jsopcode.tbl \
jsproto.tbl js.msg jsshell.msg jskeyword.tbl prmjtime.h resource.h


ifdef JS_HAS_FILE_OBJECT
JS_CFILES += jsfile.c
JS_HFILES += jsfile.h
endif

LIB_CFILES = $(JS_CFILES)
PROG_CFILES = js.c

LIB_OBJS = $(addprefix $(OBJDIR)/, $(LIB_CFILES:.c=.o))
PROG_OBJS = $(addprefix $(OBJDIR)/, $(PROG_CFILES:.c=.o))

CFILES = $(LIB_CFILES) $(PROG_CFILES)
OBJS = $(LIB_OBJS) $(PROG_OBJS)

LIBRARY = $(OBJDIR)/libjs.a
PROGRAM = $(OBJDIR)/js
TARGETS = $(PROGRAM)

EDIT_LIBRARY = $(OBJDIR)/libedit.a
EDIT_DIR = ./editline
EDIT_CFILES = $(EDIT_DIR)/editline.c $(EDIT_DIR)/sysunix.c
EDIT_OBJS = $(OBJDIR)/editline.o $(OBJDIR)/sysunix.o
EDIT_CFLAGS = -DANSI_ARROWS -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT -DSYS_UNIX \
-DHAVE_STDLIB -DUNIQUE_HISTORY $(DEFINES) -I$(EDIT_DIR)


define MAKE_OBJDIR
if test ! -d $(@D); then rm -rf $(@D); mkdir -p $(@D); fi
endef


all:
$(MAKE) $(TARGETS)

$(PROGRAM): $(PROG_OBJS) $(LIBRARY) $(EDIT_LIBRARY)
$(CC) -o $@ $(CFLAGS) $(PROG_OBJS) $(LIBRARY) $(EDIT_LIBRARY) $(LDFLAGS)

$(OBJDIR)/%.o: %.c %.h
@$(MAKE_OBJDIR)
$(CC) -o $@ -c $(CFLAGS) $*.c

$(OBJDIR)/editline.o: $(EDIT_DIR)/editline.c $(EDIT_DIR)/editline.h
@$(MAKE_OBJDIR)
$(CC) -o $@ -c $(EDIT_CFLAGS) $(EDIT_DIR)/editline.c

$(OBJDIR)/sysunix.o: $(EDIT_DIR)/sysunix.c $(EDIT_DIR)/editline.h
@$(MAKE_OBJDIR)
$(CC) -o $@ -c $(EDIT_CFLAGS) $(EDIT_DIR)/sysunix.c

$(OBJDIR)/%.o: %.c
@$(MAKE_OBJDIR)
$(CC) -o $(OBJDIR)/jscpucfg jscpucfg.c
$(OBJDIR)/jscpucfg > $(OBJDIR)/jsautocfg.h
$(CC) -o $(OBJDIR)/jskwgen jskwgen.c
$(OBJDIR)/jskwgen $(OBJDIR)/jsautokw.h
$(CC) -o $@ -c $(CFLAGS) $*.c

$(OBJDIR)/jsinterp.o: jsinterp.c jsinterp.h
@$(MAKE_OBJDIR)
$(CC) -o $@ -c $(INTERP_CFLAGS) jsinterp.c

$(EDIT_LIBRARY): $(EDIT_OBJS)
$(AR) rv $@ $?
$(RANLIB) $@

$(LIBRARY): $(LIB_OBJS)
$(AR) rv $@ $?
$(RANLIB) $@

clean:
rm -rf $(OBJS)

clobber:
rm -rf $(OBJDIR)

tarball:
mkdir -p ./$(JSVERSION)/editline
cp *.[ch] ./$(JSVERSION)
cp *.tbl ./$(JSVERSION)
cp *.js ./$(JSVERSION)
cp *.msg ./$(JSVERSION)
cp Makefile ./$(JSVERSION)
cp README ./$(JSVERSION)
cp editline/*.[ch] ./$(JSVERSION)/editline
cp editline/README ./$(JSVERSION)/editline
cp editline/editline.3 ./$(JSVERSION)/editline
tar cvf $(JSVERSION).tar ./$(JSVERSION)
gzip $(JSVERSION).tar
rm -rf ./$(JSVERSION)

Note you need to use gcc 4.2 instead of gcc 3.2 otherwise you will get an undefined _JS_DHashTableOperate symbol due to an obsure gcc bug. However with gcc 4.2, you will see a large number of compiler warnings relating to alignment is greater than maximum object file. Fortunately you can safely ignore them. By the way, I choose to build a static executable because building shared libraries on SUA using gcc is somewhat problematic.



Once you have build an executable, a simple smoketest is to use the executable to execute the prefect.js JavaScript script which is included with the SpiderMonkey source code. You should get output similar to the following:

$ ./Interix6.0/js perfect.js

A number is 'perfect' if it is equal to the sum of its
divisors (excluding itself).

The perfect numbers up to 500 are:
6 = 1 + 2 + 3
28 = 1 + 2 + 4 + 7 + 14
496 = 1 + 2 + 4 + 8 + 16 + 31 + 62 + 124 + 248
That's all.
$

Well, this should be enough to enable you to build your own JavaScript shell on Microsoft's Subsystem for Unix-based Applications. Good Luck!