- SwigJS -

NOTE: I stopped developing this over 5 years ago, but was recently contacted about it. I found the code and made a few comments about building on Windows and packaged it as a zip file. The module code builds under Swig 1.1. The code is available under the GPL, so if anyone would like to pick this up and make it useful for the Mozilla and SWIG communities, please do so. Whatever code you generate with it though can be used for any purpose.
w h a t  i s  i t ?
Swig is a tool for taking C/C++ programs and automatically generating wrapper functions for a target scripting language to access those C functions. Currently, it supports Perl, Python, TCL, and guile. I'm working to incorporate support for the JavaScript Libraries available at mozilla.org

Download: swigjs_2005_07_13.zip
d e s i g n  g o a l s

My goal for this is to allow any reasonable C header (and eventually C++) to be wrapped normally. My definition of reasonable is that it contains only function prototypes, enumerations, and constants. Global variables are acceptable, but to modify them, functions should be used.

In addition to simply wrapping the C functions and counterparts, I will provide a simple way to associate those functions into a JSClass object. Exmples of the two ways an API could be accessed:

typedef enum {male, female} Sex; Person* new_Person(); void Person_set_sex(Person* p, Sex s); Sex Person_get_sex(Person* p);

This C code, could be accessed one of two ways:

var p = new_Person(); Person_set_sex(p, male); print(Person_get_sex(p));

Or the much more intuitive:

var p = new Person(); p.set_sex(male); print(p.get_sex()); // maybe like this: // p.set_sex(Person.male) // ?
s t a t u s
I seem to have some good basic support for most native C types, as well as the default way to deal with structs. This is nothing but a work in progress that probably works with nothing advanced and useful yet ;) However, I'll upload the source in a few days when I get around to cleaning up all the junk.
e x a m p l e
For a trivial example, consider a C program with a "Person" struct, then observe the way of accessing this from JavaScript via Swig-generated wrappers. The "Person()" line is what tells you this is not a real C header, it is really a Swig interface file. Please see the SWIG homepage for the details.

Input File:

typedef struct Person { int age; char * fname; char * lname; char * hphone; char * wphone; char * email; Person(); } Person; int foo(double a, int b, char* str); double bar(double b, char * str); void print(char * str);

Now, to access this code from JavaScript, I executed this script:
// create a new Person object var p = new_Person(); Person_age_set(p, 21); Person_fname_set(p, "Josh"); Person_lname_set(p, "Gough"); Person_hphone_set(p, "987-654-3210"); Person_wphone_set(p, "1-800-Where-do-you-want-to-crash-today?"); Person_email_set(p, "someemail@somesite.com"); // print some info about p print ("First name: " + Person_fname_get(p)); print ("Last name: " + Person_lname_get(p)); print ("Age: " + Person_age_get(p)); print ("Home phone: " + Person_hphone_get(p)); print ("Work phone: " + Person_wphone_get(p)); print ("Email: " + Person_email_get(p));

Ideally, I would like to have this so you just have to do "p.fname ='Josh'", but this is how swig builds access to structs by default. I'll work on the other way pretty soon.

And the output....
First name: Josh Last name: Gough Age: 21 Home phone: 987-654-3210 Work phone: 1-800-Where-do-you-want-to-crash-today? Email: someemail@somesite.com

Below is an example of the actual C code produced by Swig for this. The file produced is 24K, saving lots and lots and lots of typing and time!
static char * Person_email_set(Person *obj, char *val) { if (obj->email) free(obj->email); obj->email = (char *) malloc(strlen(val)+1); strcpy(obj->email,val); return val; } static JSBool _wrap_Person_email_set(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char * _result; Person * _arg0; char * _arg1; if ((argc < 2) || (argc > 2)) return JS_FALSE; if ( !(JSVAL_IS_OBJECT(argv[0])) || !JSVAL_IS_STRING(argv[1])) return JS_FALSE; {_arg0 = (Person *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));} {_arg1 = JS_GetStringBytes(JSVAL_TO_STRING(argv[1]));} _result = (char *)Person_email_set(_arg0,_arg1); {JSString *jstr = JS_NewString(cx, _result, (size_t) strlen(_result)); *rval = STRING_TO_JSVAL(jstr);} return JS_TRUE; } #define Person_email_get(_swigobj) ((char *) _swigobj->email) static JSBool _wrap_Person_email_get(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { char * _result; Person * _arg0; if ((argc < 1) || (argc > 1)) return JS_FALSE; if ( !(JSVAL_IS_OBJECT(argv[0]))) return JS_FALSE; {_arg0 = (Person *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(argv[0]));} _result = (char *)Person_email_get(_arg0); {JSString *jstr = JS_NewString(cx, _result, (size_t) strlen(_result)); *rval = STRING_TO_JSVAL(jstr);} return JS_TRUE; } #define new_Person() ((Person *) calloc(1,sizeof(Person))) static JSBool _wrap_new_Person(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { Person * _result; if ((argc < 0) || (argc > 0)) return JS_FALSE; _result = (Person *)new_Person(); {JSObject * jso = JS_NewObject(cx, &generic_private, NULL, obj); if (!JS_SetPrivate(cx, jso, (void *) _result)) { printf("COULDN't SET THE PRIVATE VALUE\n"); return JS_FALSE; } *rval = OBJECT_TO_JSVAL(jso); } return JS_TRUE; } /* FUNCTION DEF */ static JSFunctionSpec example_methods[] = { { "new_Person", _wrap_new_Person, 0 }, { "Person_email_get", _wrap_Person_email_get, 1 }, { "Person_email_set", _wrap_Person_email_set, 2 }, { "Person_wphone_get", _wrap_Person_wphone_get, 1 }, { "Person_wphone_set", _wrap_Person_wphone_set, 2 }, { "Person_hphone_get", _wrap_Person_hphone_get, 1 }, { "Person_hphone_set", _wrap_Person_hphone_set, 2 }, { "Person_lname_get", _wrap_Person_lname_get, 1 }, { "Person_lname_set", _wrap_Person_lname_set, 2 }, { "Person_fname_get", _wrap_Person_fname_get, 1 }, { "Person_fname_set", _wrap_Person_fname_set, 2 }, { "Person_age_get", _wrap_Person_age_get, 1 }, { "Person_age_set", _wrap_Person_age_set, 2 }, { "print", _wrap_print, 1 }, { "bar", _wrap_bar, 2 }, { "foo", _wrap_foo, 3 }, {0} }; JSBool Top_init(JSContext *cx, JSObject *global_obj) { if (!JS_DefineFunctions(cx, global_obj, example_methods)) { return JS_FALSE; } return JS_TRUE; }

Josh Gough
Last modified: Wed Jul 13 2005 9:01 PM EST