Commits

Anonymous committed fb4ea77

Rewriring python-spidermonkey. Think of a new name people.

Comments (0)

Files changed (19)

 
 try:
     import Pyrex.Compiler.Main as Compiler
-    res = Compiler.compile(["spidermonkey.pyx"], timestamps=True)
+    res = Compiler.compile(["spidermonkey/spidermonkey.pyx"], timestamps=True)
 except ImportError:
-    if not os.path.exists("spidermonkey.c"):
-        sys.stderr.write("Pyrex is required for compiliation.")
+    print "Pyrex not found: Skipping source generation."
 
 arch = os.uname()[0].lower()
 jslib = {"darin": "js", "linux": "mozjs"}.get(arch, "js")
 """,
     ext_modules =  [
         Extension("spidermonkey",
-            sources=["spidermonkey.c"],
+            sources=["spidermonkey/spidermonkey.c"],
             extra_compile_args=["-DXP_UNIX", "-DJS_THREADSAFE"],
             include_dirs=["/usr/include/js", "/usr/local/include/js", "/usr/include/mozjs", "/opt/local/include/js"],
             library_dirs=["/usr/lib", "/usr/local/lib", "/opt/local/lib"],

spidermonkey/jsapi.pxi

+ctypedef int uint8
+ctypedef int uint16
+ctypedef int uint32
+ctypedef int uintN
+ctypedef int size_t
+
+ctypedef char jschar
+ctypedef double jsdouble
+ctypedef int jsid
+ctypedef int jsint
+ctypedef int jsuint
+IF UNAME_MACHINE == "x86_64":
+    ctypedef long jsval
+ELSE:
+    ctypedef int jsval
+ctypedef int JSBool
+
+cdef extern from "string.h":
+    cdef char* strcpy(char* restrict, char* restrict)
+
+cdef extern from "stdlib.h":
+    cdef void* malloc(size_t size)
+    cdef void free(void* mem)
+
+cdef extern from "Python.h":
+    cdef struct PyObject
+
+cdef extern from "jsapi.h":
+    cdef struct JSClass
+    cdef struct JSContext
+    cdef struct JSErrorReport
+    cdef struct JSFunction
+    cdef struct JSFunctionSpec
+    cdef struct JSString
+    cdef struct JSObject
+    cdef struct JSObjectOps
+    cdef struct JSRuntime
+    cdef struct JSPropertySpec
+    cdef struct JSXDRState
+
+    cdef enum:
+        JS_TRUE
+        JS_FALSE
+        JSVAL_VOID
+        JSVAL_INT
+        JSPROP_ENUMERATE
+        JSPROP_READONLY
+        JS_CLASS_NO_INSTANCE
+        JSCLASS_HAS_PRIVATE
+
+    cdef enum JSType:
+        JSTYPE_VOID
+        JSTYPE_OBJECT
+        JSTYPE_FUNCTION
+        JSTYPE_STRING
+        JSTYPE_NUMBER
+        JSTYPE_BOOLEAN
+        JSTYPE_LIMIT
+
+    cdef enum JSAccessMode:
+        JSACC_PROTO = 0
+        JSACC_PARENT = 1
+        JSACC_IMPORT = 2
+        JSACC_WATCH = 3
+        JSACC_READ = 4
+        JSACC_WRITE = 8
+        JSACC_LIMIT
+
+    ctypedef JSBool (*JSPropertyOp) (JSContext* cx, JSObject* obj, jsval id, jsval* vp)
+    ctypedef JSBool (*JSEnumerateOp) (JSContext* cx, JSObject* obj)
+    ctypedef JSBool (*JSResolveOp) (JSContext* cx, JSObject* obj, jsval id)
+    ctypedef JSBool (*JSConvertOp) (JSContext* cx, JSObject* obj, JSType type, jsval* vp)
+    ctypedef void (*JSFinalizeOp) (JSContext* cx, JSObject* obj)
+    ctypedef JSObjectOps* (*JSGetObjectOps) (JSContext* cx, JSClass* clasp)
+    ctypedef JSBool (*JSCheckAccessOp) (JSContext* cx, JSObject* obj, jsval id, JSAccessMode mode, jsval* vp)
+    ctypedef JSBool (*JSXDRObjectOp) (JSXDRState* xdr, JSObject** objp)
+    ctypedef JSBool (*JSHasInstanceOp) (JSContext* cx, JSObject* obj, jsval v, JSBool* bp)
+    ctypedef uint32 (*JSMarkOp) (JSContext* cx, JSObject* obj, void* arg)
+    ctypedef JSBool (*JSNative) (JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval)
+    ctypedef void (*JSErrorReporter) (JSContext* cx, char* message, JSErrorReport* report)
+    
+    cdef struct JSClass:
+        char *name
+        uint32 flags
+
+        # Mandatory non-null function pointer members.
+        JSPropertyOp addProperty
+        JSPropertyOp delProperty
+        JSPropertyOp getProperty
+        JSPropertyOp setProperty
+        JSEnumerateOp enumerate
+        JSResolveOp resolve
+        JSConvertOp convert
+        JSFinalizeOp finalize
+
+        # Optionally non-null members start here.
+        JSGetObjectOps getObjectOps
+        JSCheckAccessOp checkAccess
+        JSNative call
+        JSNative construct
+        JSXDRObjectOp xdrObject
+        JSHasInstanceOp hasInstance
+        JSMarkOp mark
+        void* reserveSlots
+
+    cdef struct JSErrorReport:
+        char* filename
+        uintN lineno
+        char* linebuf
+        char* tokenptr
+        jschar* uclinebuf
+        jschar* uctokenptr
+        uintN flags
+        uintN errorNumber
+        jschar* ucmessage
+        jschar** messageArgs
+
+    cdef struct JSFunctionSpec:
+        char *name
+        JSNative call
+        uint8 nargs
+        uint8 flags
+        uint16 extra  # number of arg slots for local GC roots
+
+    cdef struct JSIdArray:
+        jsint length
+        jsid  vector[1]  # actually, length jsid words
+
+    cdef JSBool JSVAL_IS_BOOLEAN(jsval v)
+    cdef JSBool JSVAL_IS_DOUBLE(jsval v)
+    cdef JSBool JSVAL_IS_GCTHING(jsval v)
+    cdef JSBool JSVAL_IS_INT(jsval v)
+    cdef JSBool JSVAL_IS_NULL(jsval v)
+    cdef JSBool JSVAL_IS_NUMBER(jsval v)
+    cdef JSBool JSVAL_IS_OBJECT(jsval v)
+    cdef JSBool JSVAL_IS_PRIMITIVE(jsval v)
+    cdef JSBool JSVAL_IS_STRING(jsval v)
+    cdef JSBool JSVAL_IS_VOID(jsval v)
+
+    cdef JSBool JSVAL_TO_BOOLEAN(jsval v)
+    cdef jsdouble* JSVAL_TO_DOUBLE(jsval v)
+    cdef void* JSVAL_TO_GCTHING(jsval v)
+    cdef int JSVAL_TO_INT(jsval v)
+    cdef JSObject* JSVAL_TO_OBJECT(jsval v)
+    cdef JSString* JSVAL_TO_STRING(jsval v)
+    cdef void* JSVAL_TO_PRIVATE(jsval v)
+    cdef JSString* JSVAL_TO_STRING(jsval v)
+
+    cdef jsval BOOLEAN_TO_JSVAL(JSBool b)
+    cdef jsval DOUBLE_TO_JSVAL(jsdouble* dp)
+    cdef jsval INT_TO_JSVAL(int i)
+    cdef jsval OBJECT_TO_JSVAL(JSObject* obj)    
+    cdef jsval PRIVATE_TO_JSVAL(void* p)
+    cdef jsval STRING_TO_JSVAL(JSString* str)
+
+    cdef int JSVAL_TAG(jsval v)
+    cdef void JSVAL_SETTAG(jsval v, int t)
+    cdef void JSVAL_CLRTAG(jsval v)
+
+    # Runtime Functions
+    cdef JSRuntime* JS_NewRuntime(uint32 maxbytes)
+    cdef void JS_DestroyRuntime(JSRuntime* rt)
+    cdef JSContext* JS_ContextIterator(JSRuntime* rt, JSContext** iterp)
+
+    # Context Functions
+    cdef JSContext* JS_NewContext(JSRuntime* rt, size_t stackChunkSize)
+    cdef void JS_DestroyContext(JSContext* cx)
+    cdef JSRuntime* JS_GetRuntime(JSContext* cx)
+    cdef JSObject* JS_GetGlobalObject(JSContext* cx)
+    cdef void* JS_GetContextPrivate(JSContext* cx)
+    cdef void JS_SetContextPrivate(JSContext* cx, void* data)
+    
+    # Type Functions
+    cdef JSType JS_TypeOfValue(JSContext* cx, jsval v)
+
+    # Class Functions
+    cdef JSBool JS_InitStandardClasses(JSContext* cx, JSObject* obj)
+    cdef JSObject* JS_GetClassObject(JSClass* klass)
+
+    cdef JSObject* JS_InitClass(
+        JSContext* cx,
+        JSObject* obj,
+        JSObject* parent_proto,
+        JSClass* clasp,
+        JSNative constructor,
+        uintN nargs,
+        JSPropertySpec* ps,
+        JSFunctionSpec* fs,
+        JSPropertySpec* static_ps,
+        JSFunctionSpec* static_fs
+    )
+
+    cdef JSBool JS_PropertyStub(JSContext* cx, JSObject* obj, jsval id, jsval* vp)
+    cdef JSBool JS_EnumerateStub(JSContext* cx, JSObject* obj)
+    cdef JSBool JS_ResolveStub(JSContext* cx, JSObject* obj, jsval id)
+    cdef JSBool JS_ConvertStub(JSContext* cx, JSObject* obj, JSType type, jsval* vp)
+    cdef void JS_FinalizeStub(JSContext* cx, JSObject* obj)
+
+    # Object Functions
+    # Create an object as a property of another object.
+    cdef JSObject* JS_DefineObject(
+        JSContext* cx,
+        JSObject* obj,
+        char* name,
+        JSClass* clasp,
+        JSObject* proto,
+        uintN attrs
+    )
+    
+    # Create an object
+    cdef JSObject* JS_NewObject(
+        JSContext* cx,
+        JSClass* clasp,
+        JSObject* proto,
+        JSObject* parent
+    )
+    
+    cdef JSClass* JS_GetClass(JSContext* cx, JSObject* obj)
+    cdef JSIdArray* JS_Enumerate(JSContext* cx, JSObject* obj)
+    cdef void* JS_GetPrivate(JSContext* cx, JSObject* obj)
+    cdef JSBool JS_SetPrivate(JSContext* cx, JSObject* obj, void* data)
+
+    # Property Methods
+    cdef JSBool JS_GetProperty(JSContext* cx, JSObject* obj, char* name, jsval* vp)
+    cdef JSBool JS_SetProperty(JSContext* cx, JSObject* obj, char* name, jsval* vp)
+
+    cdef JSBool JS_DefineProperty(
+        JSContext* cx,
+        JSObject* obj,
+        char* name,
+        jsval value,
+        JSPropertyOp getter,
+        JSPropertyOp setter,
+        uintN attrs
+    )
+
+    # Array Functions
+    cdef JSBool JS_IsArrayObject(JSContext* cx, JSObject* obj)
+    cdef JSObject* JS_NewArrayObject(JSContext* cx, jsint length, jsval* vector)
+    cdef JSBool JS_GetArrayLength(JSContext* cx, JSObject* obj, jsuint* lengthp)
+    cdef JSBool JS_GetElement(JSContext* cx, JSObject* obj, jsint index, jsval* vp)
+    cdef JSBool JS_SetElement(JSContext* cx, JSObject* obj, jsint index, jsval* vp)
+
+    cdef JSBool JS_DefineElement(
+        JSContext* cx,
+        JSObject* obj,
+        jsint index,
+        jsval value,
+        JSPropertyOp getter,
+        JSPropertyOp setter,
+        uintN attrs
+    )
+
+    # Function Functions
+    cdef char* JS_GetFunctionName(JSFunction* fun)
+    cdef JSBool JS_ObjectIsFunction(JSContext* cx, JSObject* obj)
+    cdef JSObject* JS_GetFunctionObject(JSFunction* fun)
+    cdef JSFunction* JS_ValueToFunction(JSContext* cx, jsval v)
+    
+    # Set a list of functions on an object
+    cdef JSBool JS_DefineFunctions(JSContext* cx, JSObject* obj, JSFunctionSpec* fs)
+    
+    # Create a function property
+    cdef JSFunction* JS_DefineFunction(
+        JSContext* cx,
+        JSObject* obj,
+        char* name,
+        JSNative call,
+        uintN nargs,
+        uintN attrs
+    )
+    
+    # Create an anonymous function
+    cdef JSFunction* JS_NewFunction(
+        JSContext* cx,
+        JSNative call,
+        uintN nargs,
+        uintN flags,
+        JSObject* parent,
+        char* name
+    )
+    
+    # Call a named function
+    cdef JSBool JS_CallFunctionName(
+        JSContext* cx,
+        JSObject* obj,
+        char* name,
+        uintN argc,
+        jsval* argv,
+        jsval* rval
+    ) except *
+    
+    # Call a function by jsval reference
+    cdef JSBool JS_CallFunctionValue(
+        JSContext* cx,
+        JSObject* obj,
+        jsval fun,
+        uintN argc,
+        jsval* argv,
+        jsval* rval
+    ) except *
+
+    # String Functions
+    cdef JSString* JS_NewStringCopyN(JSContext* cx, char* s, size_t n)
+    cdef char* JS_GetStringBytes(JSString* str)
+    cdef size_t JS_GetStringLength(JSString* str)
+
+    # Double Functions
+    cdef jsdouble* JS_NewDouble(JSContext* cx, jsdouble d)
+    cdef JSBool JS_NewDoubleValue(JSContext* cx, jsdouble d, jsval* rval)
+
+    # IdArray Functions
+    cdef JSBool JS_IdToValue(JSContext* cx, jsid id, jsval* vp)
+    cdef void JS_DestroyIdArray(JSContext* cx, JSIdArray* ida)
+
+    # Memory Functions
+    cdef void JS_GC(JSContext *cx)
+    cdef void JS_MaybeGC(JSContext* cx)
+    cdef void JS_free(JSContext* cx, void* p)
+    cdef JSBool JS_AddRoot(JSContext* cx, void* rp)
+    cdef JSBool JS_AddNamedRoot(JSContext* cx, void* rp, char* name)
+    cdef JSBool JS_RemoveRoot(JSContext* cx, void* rp)
+
+    # Error Reporting
+    cdef JSBool JS_IsExceptionPending(JSContext* cx)
+    cdef JSBool JS_GetPendingException(JSContext* cx, jsval* vp)
+    cdef void JS_SetPendingException(JSContext* cx, jsval v)
+    cdef void JS_ClearPendingException(JSContext* cx)
+    
+    cdef JSErrorReporter JS_SetErrorReporter(JSContext* cx, JSErrorReporter er)
+    cdef void JS_ReportError(JSContext* cx, char* format, ...)
+
+    # Main Entry Functions
+    cdef JSBool JS_EvaluateScript(
+        JSContext* cx,
+        JSObject* obj,
+        char* bytes,
+        uintN length,
+        char* filename,
+        uintN lineno,
+        jsval* rval
+    )
+
+cdef extern from "jshelpers.c":
+    cdef JSClass js_global_class
+    cdef JSObject* js_make_global_object(JSContext *cx)
+
+    cdef void js_context_attach(JSContext* cx, PyObject* obj)
+    cdef object js_context_fetch(JSContext* cx)
+    cdef object js_context_destroy(JSContext* cx)
+
+    cdef void js_object_attach(JSObject* js_obj, PyObject* obj)
+    cdef object js_object_fetch(JSObject* js_obj)
+    cdef object js_object_destroy(JSObject* js_obj)
+
+cdef void *xmalloc(size_t size) except NULL:
+    cdef void *mem
+    mem = malloc(size)
+    if <int>mem == 0:
+        raise MemoryError()
+    return mem
+
+# JavaScript -> Python
+cdef class Context
+cdef class Function
+cdef class GC
+cdef class Runtime
+cdef class Value
+
+# Python -> JavaScript
+cdef class ClassAdapter
+cdef class ObjectAdapter
+
+class JSError(Exception): pass
+

spidermonkey/jsarray.pxi

+
+def js_is_array(Context cx, jsval v):
+    cdef JSObject* obj
+    if not JSVAL_ISOBJECT(v):
+        return False
+    obj = JSVAL_TO_OBJECT(v)
+    return JS_IsArrayObject(cx.cx, obj)
+
+def py_is_array(Context cx, object py_obj):
+    return isinstance(py_obj, (types.ListType, types.TupleType))
+
+cdef object js2py_array(Context cx, jsval v):
+    cdef jsuint nr_elems
+    cdef jsval elem
+    cdef JSObject *jsobj
+    cdef int i
+    cdef list ret
+    
+    jsobj = JSVAL_TO_OBJECT(v)
+    nr_elems = JS_GetArrayLength(cx.cx, jsobj, &nr_elems)
+    ret = [None] * nr_elems
+
+    for i from 0 <= i < nr_elems:
+        if not JS_GetElement(cx.cx, jsobj, i, &elem):
+            raise JSError("Failed to convert JavaScript array to Python.")
+        ret[i] = js2py(cx, elem)
+
+    return ret
+
+cdef jsval py2js_array(Context cx, list py_obj, JSObject* parent):
+    cdef int nr_elems, i
+    cdef JSObject* ret
+    cdef jsval elem
+
+    nr_elems = len(py_obj)
+    ret = JS_NewArrayObject(cx.cx, 0, NULL)
+    
+    for i from 0 <= i < nr_elems:
+        elem = py2js(cx, py_obj[i], parent)
+        if not JS_SetElement(cx.cx, ret, i, &elem):
+            raise JSError("Failed to convert Python sequence type to JavaScript.")
+    
+    return OBJECT_TO_JSVAL(ret)

spidermonkey/jsclass.pxi

+
+cdef JSBool __constructor_callback__(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval):
+    cdef Context pycx
+    cdef ClassAdapter adapter
+    cdef ObjectAdapter objadapter
+    cdef JSObject* instobj
+    cdef int i
+    
+    try:
+        pycx = js_context_fetch(cx)
+        adapter = js_object_fetch(obj)
+        
+        args = []
+        for i from 0 <= i < argc:
+            args.append(js2py(pycx, argv[i]))
+
+        if hasattr(adaptor, "__jsinit__"):
+            py_rval = adaptor.py_class.__jsinit__(pycx, *args)
+        else:
+            py_rval = adaptor.py_class(pycx, *args)
+
+        rval[0] = py2js(pycx, py_rval, NULL)
+
+        # Register after conversion so we don't keep it around
+        # if conversion fails.
+        if JSVAL_IS_OBJECT(rval[0]):
+            objadapter = ObjectAdapter(pycx, adapter, None, py_rval)
+            instobj = JSVAL_TO_OBJECT(rval[0])
+            js_object_attach(instobj, <PyObject*> py_rval)
+            objadapter.js_obj = instobj
+        pycx.register(py_rval)
+        
+        return JS_TRUE
+    except:
+        return report_python_error(cx)
+
+cdef JSBool __resolve_global_callback__(JSContext* cx, JSObject* js_obj, jsval jsv):
+    cdef Context pycx
+    cdef ObjectAdapter adapter
+    cdef object py_obj
+    cdef object key
+    cdef int i
+
+    try:
+        pycx = js_context_fetch(cx)
+        adapter = js_object_fetch(js_obj)
+        py_obj = adapter.obj
+        key = js2py(pycx, jsv)
+        
+        if isinstance(key, types.StringTypes) and hasattr(py_obj, key):
+            # Bind to root object.
+            # Will ref the obj so it doesn't get discarded.
+            pycx.bind(key, getattr(py_obj, key))
+
+        return JS_TRUE
+    except:
+        return report_python_error(cx)
+
+cdef JSBool __get_property_callback__(JSContext* cx, JSObject* js_obj, jsval jsv, jsval* rval):
+    cdef Context pycx
+    cdef ObjectAdapter adapter
+    cdef object py_obj
+    cdef object key
+    cdef object attr
+
+    try:
+        pycx = js_context_fetch(cx)
+        adapter = js_object_fetch(js_obj)
+        py_obj = adapter.obj
+        key = js2py(pycx, jsv)
+        
+        if isinstance(key, (types.IntType, types.LongType)):
+            try:
+                attr = getattr(py_obj, key)
+                rval[0] = py2js(pycx, attr, NULL)
+                pycx.register_py(attr)
+            except:
+                rval[0] = JS_VOID
+        elif isinstance(key, types.StringTypes) and hasattr(py_obj, key):
+            try:
+                attr = getattr(py_obj, key)
+                rval[0] = py2js(pycx, attr, NULL)
+                pycx.register_py(attr)
+            except:
+                rval[0] = JS_VOID
+        elif key is None:
+            rval[0] = JS_VOID
+        else:
+            raise AssertionError("Invalid key: %r" % key)
+            
+        return JS_TRUE
+    except:
+        return report_python_error(cx)
+
+cdef JSBool __set_property_callback__(JSContext* cx, JSObject* js_obj, jsval jsv, jsval rval[0]):
+    cdef Context pycx
+    cdef ObjectAdapter adapter
+    cdef object py_obj
+    cdef object key
+    cdef object val
+
+    try:
+        pycx = js_context_fetch(cx)
+        adapter = js_object_fetch(js_obj)
+        py_obj = adapter.py_obj
+        key = js2py(pycx, jsv)
+        value = js2py(pycx, rval[0])
+
+        if isinstance(key, (types.IntType, types.LongType)):
+            py_obj[key] = value
+        elif isinstance(key, types.StringTypes) and hasattr(py_obj, key):
+            attr = getattr(py_obj, key)
+            if not callable(attr):
+                setattr(py_obj, key, value)
+        else:
+            raise AssertionError("Invalid key: %r" % key)
+
+        return JS_TRUE
+    except:
+        return report_python_error(cx)
+
+
+cdef void __finalize_callback__(JSContext* cx, JSObject* js_obj):
+    cdef Context pycx
+    cdef ObjectAdapter py_obj
+
+    try:
+        pycx = js_context_fetch(cx)
+        py_obj = js_object_destroy(js_obj)
+    except:
+        report_python_error(cx)
+
+def js_classname(obj):
+    if inspect.isclass(obj):
+        return obj.__name__
+    else:
+        return obj.__class__.__name__
+
+cdef class ClassAdapter:
+    cdef Context cx
+    cdef ObjectAdapter parent
+    cdef JSClass* js_class
+    cdef object py_class
+
+    def __cinit__(ClassAdapter self, Context cx, ObjectAdapter parent, py_class, bind_constructur, is_global, flags):
+        cdef JSObject* obj
+        
+        self.cx = cx
+        self.parent = parent
+        self.py_class = py_class
+        
+        name = js_classname(py_class)
+
+        self.js_class = <JSClass*> xmalloc(sizeof(JSClass))
+        self.js_class.name = <char*> xmalloc((len(name) + 1) * sizeof(char))
+        strcpy(self.js_class.name, name)
+
+        self.js_class.flags = flags
+        self.js_class.addProperty = JS_PropertyStub
+        self.js_class.delProperty = JS_PropertyStub
+        self.js_class.getProperty = __get_property_callback__
+        self.js_class.setProperty = __set_property_callback__
+        self.js_class.enumerate = JS_EnumerateStub
+        self.js_class.convert = JS_ConvertStub
+        self.js_class.finalize = __finalize_callback__
+        self.js_class.getObjectOps = NULL
+        self.js_class.checkAccess = NULL
+        self.js_class.call = NULL
+        self.js_class.construct = NULL
+        self.js_class.xdrObject = NULL
+        self.js_class.hasInstance = NULL
+        self.js_class.mark = NULL
+        self.js_class.reserveSlots = NULL
+        
+        if is_global:
+            self.js_class.resolve = __resolve_global_callback__
+        else:
+            self.js_class.resolve = JS_ResolveStub
+        
+        if bind_constructor:
+            if JS_InitClass(self.cx.cx, parent.js_obj, NULL, self.js_class,
+                            __constructor_callback__, 0, NULL, NULL, NULL, NULL) == NULL: 
+                raise JSError("Failed to bind Python adapter class.")
+
+        js_object_attach(obj, <PyObject*> self)
+
+    def __dealloc__(self):
+        free(self.js_class.name)
+        free(self.js_class)
+
+    def as_value(Context self):
+        cdef JSObject* obj
+        obj = JS_GetClassObject(self.js_class)
+        return js_create_value(OBJECT_TO_JSVAL(obj))
+
+
+
+
+
+
+
+
+
+

spidermonkey/jscontext.pxi

+
+cdef class Context:
+    cdef JSContext* cx
+    cdef Runtime rt
+    cdef ObjectAdapter root
+    cdef GC gc
+    cdef object reg
+    cdef object err
+
+    def __cinit__(self, Runtime rt, root):
+        self.rt = rt
+
+        STACK_CHUNK_SIZE = 8192
+
+        self.cx = JS_NewContext(rt.rt, STACK_CHUNK_SIZE)
+        if self.cx == NULL:
+            raise JSError("Failed to create Context")
+
+    def __init__(Context self, Runtime rt, root):
+        cdef JSObject* obj
+
+        self.gc = GC(self)
+        self.reg = Registry()
+        self.err = None
+
+        self.reg = {}
+
+        js_context_attach(self.cx, <PyObject*> self)
+
+        if root:
+            ca = self.install_class(root.__class__, False, True)
+            self.root = ObjectAdapter(self, ca, None)
+            if not self.root:
+                raise JSError("Failed to bind global object.")
+        else:
+            obj = js_make_global_object(self.cx)
+            self.root = ObjectAdapter(self, None, None, None)
+            js_object_attach(obj, <PyObject*> None)
+            self.root.js_obj = obj
+            if not self.root:
+                raise JSError("Failed to create default global object.")
+        
+        if not JS_InitStandardClasses(self.cx, self.root.js_obj):
+            raise JSError("Failed to initialize standard classes.")
+        
+        JS_SetErrorReporter(self.cx, __report_error_callback__)
+        
+    def __dealloc__(self):
+        JS_DestroyContext(self.cx)
+
+    def install_class(self, py_class, bind_constructor=True, is_global=False, flags=0):
+        """\
+        Install a Python class into the JavaScript runtime.
+        """
+        if not inspect.isclass(klass):
+            raise TypeError("Unable to install %r as a class." % klass)
+        if not isinteger(flags):
+            raise TypeError("Flags is not an integer.")
+
+        c = ClassAdaptor(self, python_class, bind_constructor, is_global, flags)
+        self.classes[c.to_value()] = c
+        return c
+
+    def bind(Context self, name, obj, parent=None):
+        """\
+        Attach a Python object to the JavaScript runtime.
+        
+        You should be able to attach most types of Python objects
+        including builtin types, object instances, functions etc. About
+        the only thing you can't bind is a Python class which should
+        be isntalled into the JavaScript environment using
+        Context.install_class
+        
+        This call will bind the provided object `obj` to the JS root. The
+        Python value is also referenced to keep it from being garbage
+        collected.
+        """
+        cdef ClassAdapter ca
+        cdef jsval jsv
+
+        if not isinstance(name, types.StringTypes):
+            raise TypeError("Name must be a string.")
+
+        ca = self.install_class(obj)
+        jsv = py2js(self, obj, self.root.js_obj)
+        if not JS_DefineProperty(self.cx, self.root.js_obj, name, jsv,
+                                    __get_property_callback__, __set_property_callback__, 0):
+            raise JSError("Failed to bind Python object to the global object.")
+
+    def execute(Context self, object script):
+        """\
+        Execute JavaScript source code.
+        """
+        cdef jsval rval
+        try:
+            if not isinstance(script, types.StringTypes):
+                raise TypeError("Script must be a string.")
+
+            if not JS_EvaluateScript(self.cx, self.root.js_obj, script, len(script), "Python", 0, &rval):
+                raise JSError("Failed to execute script: %s" % self._error)
+        
+            return js2py(self, rval)
+        finally:
+            self._gc.maybe()
+
+    def register(Context self, object obj):
+        """\
+        Register a Python object in the context so that it doesn't
+        get garbage collected.
+        """
+        val = id(obj)
+        assert val not in self.reg, "Objected previously registered."
+        self.reg[val] = obj
+    
+    def unregister(Context self, object obj):
+        val = id(obj)
+        assert val in self.reg, "Objected not registered."
+        del self.reg[val]
+    
+    

spidermonkey/jsdouble.pxi

+
+def js_is_double(Context cx, jsval jsv):
+    return JSVAL_IS_DOUBLE(jsv)
+
+def py_is_double(Context cx, object py_obj):
+    return isinstance(py_obj, types.FloatType)
+
+cdef object js2py_double(Context cx, jsval jsv):
+    return JSVAL_TO_DOUBLE(jsv)[0]
+
+cdef jsval py2js_double(Context cx, double py_obj, JSObject* parent):
+    cdef jsval ret
+    if not JS_NewDoubleValue(cx.cx, py_obj, &ret):
+        raise JSError("Failed to convert double to JavaScript")
+    return ret

spidermonkey/jserror.pxi

+
+
+cdef JSBool report_python_error(JSContext* cx):
+    tb = traceback.print_exc()
+    JS_ReportError(cx, tb)
+    return JS_FALSE
+
+cdef void __report_error_callback__(JSContext* cx, char* message, JSErrorReport* report):
+    cdef Context pycx
+    pycx = js_context_fetch(cx)
+
+    lines = report.linebuf
+    lines = lines.split("\n")
+
+    if len(lines) == 0:
+        msg = ["%s:\n<no code>" % message]
+    else:
+        i = report.lineno
+        n = 2  # lines of context on either side of where error occurred
+
+        start, end = max(i-n, 0), min(i+n+1, len(lines))
+
+        msg = []
+        for ln in lines[start:i]:
+            msg.append("   %s" % ln)
+        msg.append("-> %s\n" % lines[i])
+        for ln in lines[i+1:end]:
+            msg.append("   %s" % ln)
+        msg.append("")
+        msg.append('JavaScript error at line %d: "%s"' % (report.lineno, message))
+    context.set_error("\n".join(mesg))
+

spidermonkey/jsfunction.pxi

+
+cdef class Function:
+    cdef Context cx
+    cdef jsval func
+    
+    def __init__(Function self, Context cx):
+        self.cx = cx
+    
+    def __hash__(self):
+        cdef int ret
+        ret = self.func
+        return ret
+    
+    def __call__(Function self, *args):
+        cdef jsval* argv
+        cdef jsval rval
+        cdef jsval jsarg
+
+        nr_args = len(args)
+        argv = <jsval*> xmalloc(sizeof(jsval) * nr_args)
+        try:
+            for i from 0 <= i < nr_args:
+                arg = args[i]
+                jsarg = py2js(self.cx, arg, NULL)
+                argv[i] = jsarg
+
+            if not JS_CallFunctionValue(self.cx.cx, self.cx.root.js_obj, self.func, nr_args, argv, &rval):
+                raise JSError("Failed to execute function: %s" % self.cx._error)
+        finally:
+            free(argv)
+
+        retval = js2py(self.cx, rval)
+        self.cx.gc.maybe()
+        return retval
+
+    def __dealloc__(Function self):
+        JS_RemoveRoot(self.cx.cx, &self.func)
+
+cdef JSBool __function_callback__(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval):
+    cdef Context pycx
+    cdef jsval jsfunc
+    cdef int i
+
+    try:
+        pycx = js_context_fetch(cx)
+        callback = pycx.get_function(js_create_value(argv[-2]))
+
+        args = [None] * argc
+        for i from 0 <= i < argc:
+            args[i] = js2py(pycx, argv[i])
+
+        py_rval = callback(*args)
+        rval[0] = py2js(pycx, py_rval, obj)
+    except:
+        return report_python_error(cx)
+
+    return JS_TRUE
+
+def js_is_function(Context cx, jsval jsv):
+    return JS_TypeOfValue(cx.cx, jsv) == JSTYPE_FUNCTION
+
+def py_is_function(Context cx, object py_obj):
+    return callable(py_obj)
+
+cdef object js2py_function(Context cx, jsval jsv):
+    cdef Function ret
+    ret = Function(cx)
+    ret.func = jsv
+    cx.gc.add_root(&jsv)
+    return ret
+
+cdef jsval py2js_function(Context cx, object py_obj, JSObject* parent):
+    cdef JSFunction* func
+    cdef JSObject* obj
+    cdef jsval rval
+    
+    func = JS_NewFunction(cx.cx, __function_callback__, 0, 0, parent, NULL)
+    obj = JS_GetFunctionObject(func)
+    js_object_attach(obj, <PyObject*> py_obj)
+    return OBJECT_TO_JSVAL(obj)

spidermonkey/jsgc.pxi

+
+cdef class GC:
+    cdef Context cx
+
+    def __cinit__(GC self, Context cx):
+        self.cx = cx
+
+    cdef void run(GC self):
+        JS_GC(self.cx.cx)
+    
+    cdef void run_mayb(GC self):
+        JS_MaybeGC(self.cx.cx)
+    
+    cdef void add_root(GC self, void* rp):
+        if not JS_AddRoot(self.cx.cx, rp):
+            raise JSError("Failed to add a GC root")
+    
+    cdef void add_named_root(GC self, void* rp, char* name):
+        if not JS_AddNamedRoot(self.cx.cx, rp, name):
+            raise JSError("Failed to add a named GC root: %s" % name)
+
+    cdef void rem_root(GC self, void* rp):
+        if not JS_RemoveRoot(self.cx.cx, rp):
+            raise JSError("Failed to remove GC root")

spidermonkey/jshash.pxi

+
+def js_is_hash(Context cx, jsval v):
+    return JSVAL_IS_OBJECT(v)
+
+def py_is_hash(Context cx, object py_obj):
+    return isinstance(py_obj, (types.DictionaryType, types.DictType))
+
+cdef object js2py_hash(Context cx, jsval v):
+    cdef JSObject* hash
+    cdef JSObject *obj
+    cdef JSIdArray *props
+    cdef jsval jskey
+    cdef jsval jsv
+    cdef int i
+    cdef dict ret
+
+    hash = JSVAL_TO_OBJECT(v)
+
+    props = JS_Enumerate(cx.cx, hash)
+    if props == NULL:
+        raise JSError("Failed to enumerate hash properties.")
+
+    ret = {}
+
+    try:
+        for i from 0 <= i < props.length:
+            if not JS_IdToValue(cx.cx, (props.vector)[i], &jskey):
+                raise JSError("Failed to convert dict to JavaScript.")
+        
+            if js_is_string(jskey):
+                key = js2py_string(cx, jskey)
+                if not JS_GetProperty(cx.cx, hash, key, &jsv):
+                    raise JSError("Faield to retrieve textual hash property.")
+            elif js_is_int(jskey):
+                key = js2py_int(cx, jskey)
+                if not JS_GetElement(cx.cx, hash, key, &jsv):
+                    raise JSError("Failed to retrive numeric hash property.")
+            else:
+                raise AssertionError("Invalid JavaScript property.")
+
+            ret[key] = js2py(cx, jsv)
+    finally:
+        JS_DestroyIdArray(cx.cx, props)
+
+    return ret
+
+
+cdef jsval py2js_hash(Context cx, dict py_obj, JSObject* parent):
+    cdef JSObject* obj
+    cdef jsval elem
+    
+    obj = JS_NewObject(cx.cx, NULL, NULL, parent)
+    if obj == NULL:
+        raise JSError("Failed to create new JavaScript object for dict instance.")
+
+    for k, v in py_obj.iteritems():
+        elem = py2js(cx, v, obj)
+        if not JS_SetProperty(cx.cx, obj, k, &elem):
+            raise JSError("Failed to set JavaScript property for dict instance.")
+
+    return OBJECT_TO_JSVAL(obj)

spidermonkey/jshelpers.c

+void
+js_context_attach(JSContext* cx, PyObject* obj)
+{
+    Py_INCREF(obj);
+    JS_SetContextPrivate(cx, (void*) obj);
+}
+
+PyObject* 
+js_context_fetch(JSContext* cx)
+{
+    PyObject* obj = (PyObject*) JS_GetContextPrivate(cx);
+    Py_INCREF(obj);
+    return obj;
+}
+
+PyObject*
+js_context_destroy(JSContext* cx)
+{
+    PyObject* ret = (PyObject*) JS_GetContextPrivate(cx);
+    return ret;
+}
+
+void
+js_object_attach(JSContext* cx, JSObject* js_obj, PyObject* py_obj)
+{
+    Py_INCREF(py_obj);
+    JS_SetPrivate(cx, js_obj, (void*) py_obj);
+}
+
+PyObject*
+js_object_fetch(JSContext* cx, JSObject* js_obj)
+{
+    PyObject* py_obj = (PyObject*) JS_GetPrivate(cx, js_obj);
+    Py_INCREF(py_obj);
+    return py_obj;
+}
+
+PyObject*
+js_object_destroy(JSContext* cx, JSObject* js_obj)
+{
+    return (PyObject*) JS_GetPrivate(cx, js_obj);
+}
+
+static JSClass js_global_class =
+{
+    "RootObjectClass",
+    0,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_PropertyStub,
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    JS_FinalizeStub,
+    JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+JSObject *
+js_make_global_object(JSContext *cx)
+{
+    return JS_NewObject(cx, &global_class, 0, 0);
+}

spidermonkey/jsint.pxi

+
+def js_is_int(Context cx, jsval jsv):
+    return JSVAL_IS_INT(jsv)
+
+def py_is_int(Context cx, object py_obj):
+    return isinstance(py_obj, (types.IntType, types.LongType))
+
+cdef object js2py_int(Context cx, jsval jsv):
+    return JSVAL_TO_INT(jsv)
+
+cdef jsval py2js_int(Context cx, jsint py_obj, JSObject* parent):
+    return INT_TO_JSVAL(py_obj)
+

spidermonkey/jsobject.pxi

+
+cdef class ObjectAdapter:
+    cdef Context cx
+    cdef ClassAdapter cl_adapter
+    cdef ObjectAdapter parent
+    cdef JSObject* js_obj
+    cdef object py_obj
+
+    def __cinit__(ObjectAdapter self, Context cx, ClassAdapter adapter, ObjectAdapter parent, object obj):
+        self.cx = cx
+        self.cl_adapter = adapter
+        self.parent = parent
+        self.js_obj = NULL
+        self.py_obj = obj
+
+

spidermonkey/jsruntime.pxi

+
+cdef class Runtime:
+    cdef JSRuntime* rt
+
+    def __cinit__(self):
+        self.rt = JS_NewRuntime(1000000)
+        if self.rt == NULL:
+            raise JSError("Failed to create JavaScript Runtime.")
+
+    def __dealloc__(self):
+        JS_DestroyRuntime(self.rt)
+
+    def create_context(self, root=None):
+        cx = Context(self, root)
+        return cx

spidermonkey/jsstring.pxi

+
+
+
+def js_is_string(Context cx, jsval jsv):
+    return JSVAL_IS_STRING(jsv)
+
+def py_is_string(Context cx, object py_obj):
+    return isinstance(py_obj, types.StringTypes)
+
+cdef object js2py_string(Context cx, jsval jsv):
+    cdef JSString* s
+    s = JSVAL_TO_STRING(jsv)
+    return JS_GetStringBytes(s)
+
+#################################################
+# NEEED TO REWWRITE TO USE JS_NewExternalString #
+#################################################
+cdef jsval py2js_string(Context cx, object py_obj, JSObject* parent):
+    cdef JSString* s
+    s = JS_NewStringCopyN(cx.cx, py_obj, len(py_obj))
+    return STRING_TO_JSVAL(s)

spidermonkey/jsvalue.pxi

+
+cdef jsval py2js(Context cx, object py_obj, JSObject* parent) except 0:
+    if py_is_void(cx, py_obj):
+        return py2js_void(cx, py_obj, parent)
+    elif py_is_int(cx, py_obj):
+        return py2js_int(cx, py_obj, parent)
+    elif py_is_double(cx, py_obj):
+        return py2js_double(cx, py_obj, parent)
+    elif py_is_string(cx, py_obj):
+        return py2js_string(cx, py_obj, parent)
+    elif py_is_array(cx, py_obj):
+        return py2js_array(cx, py_obj, parent)
+    elif py_is_hash(cx, py_obj):
+        return py2js_hash(cx, py_obj, parent)
+    elif py_is_function(cx, py_obj):
+        return py2js_function(cx, py_obj, parent)
+    #elif py_is_object(cx, py_obj):
+    #    return py2js_object(cx, py_obj, parent)
+    #elif py_is_class(cx, py_obj):
+    #    return py2js_class(cx, py_obj, parent)
+    else:
+        raise TypeError("Unable to convert Python value to JavaScript: %r" % py_obj)
+
+cdef object js2py(Context cx, jsval jsv):
+    if js_is_void(cx, jsv):
+        return js2py_void(cx, jsv)
+    elif js_is_int(cx, jsv):
+        return js2py_int(cx, jsv)
+    elif js_is_double(cx, jsv):
+        return js2py_double(cx, jsv)
+    elif js_is_string(cx, jsv):
+        return js2py_string(cx, jsv)
+    elif js_is_array(cx, jsv):
+        return js2py_array(cx, jsv)
+    elif js_is_function(cx, jsv):
+        return js2py_function(cx, jsv)
+    elif js_is_object(cx, jsv):
+        return js2py_object(cx, jsv)
+    elif js_is_hash(cx, jsv):
+        return js2py_hash(cx, jsv)
+    else:
+        raise TypeError("Unable to convert JavaScript value to Python: %r" % jsv)
+
+cdef class Value:
+    cdef jsval jsv
+
+cdef Value js_create_value(jsval jsv):
+    cdef Value v
+    v = Value()
+    v.jsv = jsv
+    return v

spidermonkey/jsvoid.pxi

+
+def js_is_void(Context cx, jsval jsv):
+    return JSVAL_IS_VOID(jsv)
+
+def py_is_void(Context cx, object py_obj):
+    return isinstance(py_obj, (types.NoneType))
+
+cdef object js2py_void(Context cx, jsval jsv):
+    return None
+
+cdef jsval py2js_void(Context cx, object py_obj, JSObject* parent):
+    return JS_VOID

spidermonkey/spidermonkey.pyx

+
+# Import API
+include "jsapi.pxi"
+
+# Basic Necessities
+include "jserror.pxi"
+include "jsgc.pxi"
+
+# Core Classes
+include "jsruntime.pxi"
+include "jscontext.pxi"
+
+# Adapters
+include "jsclass.pxi"
+include "jsobject.pxi"
+include "jsfunction.pxi"
+
+# Type Conversions
+include "jsarray.pxi"
+include "jsdouble.pxi"
+include "jshash.pxi"
+include "jsint.pxi"
+include "jsstring.pxi"
+include "jsvoid.pxi"
+
+include "jsvalue.pxi"
+import unittest
+from unittest import TestCase
+
+from types import IntType, MethodType
+
+from spidermonkey import Runtime, JSError
+
+# XXX call_fn
+
+class context_tests(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+
+    def test_scope(self):
+        # multiple evaluations in a Context share same scope
+        cx = self.cx
+        cx.eval_script("var x = 42;")
+        self.assert_(cx.eval_script("x;") == 42)
+
+class test_conversions_to_Python(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+    def test_primitive_types(self):
+        self.assert_(self.cx.eval_script("42;") == 42)
+        self.assert_(self.cx.eval_script("42.5;") == 42.5)
+        self.assert_(self.cx.eval_script('"spam";') == "spam")
+        self.assert_(self.cx.eval_script("undefined;") is None)
+        self.assert_(self.cx.eval_script("null;") is None)
+        self.assert_(self.cx.eval_script("true;") is True)
+        self.assert_(self.cx.eval_script("false;") is False)
+        #self.assert_(self.cx.eval_script("Infinity;") is XXX)
+        #self.assert_(self.cx.eval_script("NaN;") is XXX)
+
+    def test_container_types(self):
+        self.assert_(self.cx.eval_script("[1,2,3];") == [1,2,3])
+        self.assert_(self.cx.eval_script(
+            'var d = {0: 0, "a": 1, 2: "b", "c": "d", "blah": 2.5}; d;') ==
+                     {0: 0, "a": 1, 2: "b", "c": "d", "blah": 2.5})
+        self.assert_(self.cx.eval_script(
+            '["foo", 2, {"bar": 2.3, "spam": [1,2,3]}];') ==
+                     ["foo", 2, {"bar": 2.3, "spam": [1,2,3]}])
+
+class test_conversions_two_way(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+        self.x = []
+        def echo(arg):
+            self.x.append(arg)
+            return arg
+        self.cx.bind_callable("echo", echo)
+
+    def check(self, script, arg):
+        self.assert_(self.cx.eval_script(script) == arg)
+        self.assert_(self.x.pop() == arg)
+
+    def test_primitive_types(self):
+        self.check("echo(42);", 42)
+        self.check("echo(42.5);", 42.5)
+        self.check('echo("spam");', "spam")
+        self.check("echo(undefined);", None)
+        self.check("echo(null);", None)
+        self.check("echo(true);", True)
+        self.check("echo(false);", False)
+        #self.check("echo(Infinity);", XXX)
+        #self.check("echo(NaN);", XXX)
+
+class test_global(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+
+    def test_get_global(self):
+        self.cx.eval_script("foo = 1; bar = undefined; baz = null; bing = 2;")
+        self.cx.get_global("foo")
+        # XXXX are these two correct?
+        self.assertRaises(ValueError, self.cx.get_global, ("bar"))
+        self.assertRaises(ValueError, self.cx.get_global, ("baz"))
+        self.cx.get_global("bing")
+        self.assertRaises(ValueError, self.cx.get_global, ("spam"))
+
+    def bind_attribute(self):
+        class foo:
+            def __init__(self): self.bar = 1
+        f = foo()
+        self.cx.bind_attribute("spam", f, "bar")
+        self.assert_(self.cx.eval_script("spam;") == f.bar)
+        self.assert_(self.cx.get_global("spam") == f.bar)
+
+class test_class(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+        class spam(object):
+            def __init__(self):
+                self.args = []
+                self.val = 42
+                self._private = "no peeking"
+            def foo(self, *args):
+                self.args.append(args)
+            def _private_method(self): assert False
+            def __getitem__(self, key):
+                assert type(key) == IntType
+                self.args.append(key)
+                return self.val
+            def __setitem__(self, key, value):
+                assert type(key) == IntType
+                self.args.append((key, value))
+                self.val = value
+        self.cx.bind_class(spam)
+        self.spam = spam()
+        self.cx.bind_object("bs", self.spam)
+
+    def test_private(self):
+        self.assert_(self.cx.eval_script("bs._private;") is None)
+        self.cx.eval_script("bs._private = 1;")
+        self.assert_(self.spam._private == "no peeking")
+        self.assert_(self.cx.eval_script("bs._private_method;") is None)
+        self.assert_(self.cx.eval_script("bs._private_method = 1;"))
+        self.assert_(isinstance(self.spam._private_method, MethodType))
+        # in fact, even normal methods shouldn't be assignable to
+        self.assert_(self.cx.eval_script("bs.foo = 1;"))
+        self.assert_(isinstance(self.spam.foo, MethodType))
+
+    def test_bind_module(self):
+        self.assertRaises(TypeError, self.cx.bind_class, __builtins__)
+
+    def test_no_constructor(self):
+        class foo: pass
+        self.cx.bind_class(foo, bind_constructor=False)
+        self.assertRaises(JSError,
+                          self.cx.eval_script, "var f = new foo();")
+        f2 = foo()
+        self.cx.bind_object("f2", f2)
+        self.assert_(self.cx.get_global("f2") is f2)
+
+    def test_js_name_js_constructor(self):
+        def constructor(cx):
+            return bar(42)
+        class bar:
+            js_name = "eggs"
+            js_constructor = (constructor,)
+            def __init__(self, arg): self.arg = arg
+        self.cx.bind_class(bar)
+        self.cx.eval_script("var e = new eggs();")
+        e = self.cx.get_global("e")
+        self.assert_(e.arg == 42)
+
+    def test_assign_new_property(self):
+        self.cx.eval_script("bs.xyzzy = 1;")
+        self.assert_(self.cx.eval_script("bs.xyzzy;") == 1)
+
+    def test_identity(self):
+        self.assert_(self.spam is self.cx.eval_script("bs;"))
+
+    def test_call(self):
+        self.cx.eval_script('bs.foo("hi");')
+        self.assert_(self.spam.args.pop() == ("hi",))
+
+    def test_construct(self):
+        s = self.cx.eval_script("""\
+            var s = new spam();
+            s.foo(1, "blah", ["1", 2, "three"]);
+            s;
+        """)
+        self.assert_(s.args.pop() == (1, "blah", ["1", 2, "three"]))
+
+    def test_getsetitem(self):
+        # property lookup with an integer is mapped to __get/__setitem__
+        assert self.cx.eval_script("bs[0];") == 42
+        self.cx.eval_script("bs[0] = 2;")
+        assert self.cx.eval_script("bs[0];") == 2
+
+class test_bind_global(TestCase):
+    def setUp(self):
+        class Nonce(object): pass
+        class Window(object):
+            def __init__(self):
+                self.arg = Nonce()
+                self.window = self
+                self.name = "foobar"
+                self.val = 42
+            def foo(self, arg):
+                self.arg = arg
+        self.window = Window()
+        rt = Runtime()
+        self.cx = rt.new_context(self.window)
+        self.cx.bind_class(Nonce)
+
+    def test_bind_global(self):
+        self.cx.eval_script('name;')
+        self.cx.eval_script('window;')
+        self.assert_(self.cx.eval_script('window;') is self.window)
+        self.assert_(self.cx.eval_script('window.arg;') is self.window.arg)
+        self.assert_(self.cx.eval_script('arg;') is self.window.arg)
+        self.cx.eval_script('foo(12);')
+        self.assert_(self.cx.get_global('arg') == 12)
+        self.cx.eval_script('var spam = 13;')
+        self.assert_(self.cx.get_global('spam') == 13)
+        self.cx.eval_script('window.arg = 14;')
+        self.assert_(self.cx.eval_script('window.arg;') == 14)
+
+class test_define_function(TestCase):
+    def setUp(self):
+        rt = Runtime()
+        self.cx = rt.new_context()
+    
+    def test_define_function(self):
+        resp = self.cx.eval_script("function(val) {return val * 2;}")
+        self.assertEqual(resp(2), 4)
+
+    def test_with_dict(self):
+        resp = self.cx.eval_script("function(doc) {if(doc.data) return doc.data;}")
+        self.assertEqual(resp({"data": 2}), 2)
+        self.assertEqual(resp({}), None)
+
+if __name__ == "__main__":
+    unittest.main()