changeset 0:1102944b174d

Origination.
author Atul Varma <varmaa@toolness.com>
date Wed, 11 Jun 2008 08:30:05 -0700
parents
children dace3d3473e1
files jseval.py
diffstat 1 files changed, 184 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jseval.py	Wed Jun 11 08:30:05 2008 -0700
@@ -0,0 +1,184 @@
+# jseval.py
+# By Atul Varma <varmaa@toolness.com>
+#
+# This is a simple example of embedding the SpiderMonkey engine into
+# Python using ctypes.
+
+import sys
+import ctypes
+
+# Sample Python function to inject into JavaScript space.
+def displayMessage(msg):
+    print msg
+
+class JSEngine( object ):
+    def __init__( self, dllpath ):
+        self._dllpath = dllpath
+
+    class JSClass( ctypes.Structure ):
+        _fields_ = [("name", ctypes.c_char_p),
+                    ("flags", ctypes.c_uint32),
+                    # Mandatory non-null members
+                    ("addProperty", ctypes.c_void_p),
+                    ("delProperty", ctypes.c_void_p),
+                    ("getProperty", ctypes.c_void_p),
+                    ("setProperty", ctypes.c_void_p),
+                    ("enumerate", ctypes.c_void_p),
+                    ("resolve", ctypes.c_void_p),
+                    ("convert", ctypes.c_void_p),
+                    ("finalize", ctypes.c_void_p),
+                    # Optionally non-null members
+                    ("getObjectOps", ctypes.c_void_p),
+                    ("checkAccess", ctypes.c_void_p),
+                    ("call", ctypes.c_void_p),
+                    ("construct", ctypes.c_void_p),
+                    ("xdrObject", ctypes.c_void_p),
+                    ("hasInstance", ctypes.c_void_p),
+                    ("mark", ctypes.c_void_p),
+                    ("reserveSlots", ctypes.c_void_p)
+                    ]
+
+    JSNativeFunction = ctypes.CFUNCTYPE(
+        ctypes.c_int, # Return value
+        ctypes.c_void_p, # Context
+        ctypes.c_void_p, # Object
+        ctypes.c_int, # argc
+        ctypes.POINTER( ctypes.c_int ), # argv
+        ctypes.POINTER( ctypes.c_int ), # retval
+        )
+
+    def _initRuntime( self ):
+        self.jsdll = ctypes.cdll.LoadLibrary(self._dllpath)
+        self.runtime = self.jsdll.JS_Init( 32768 )
+
+        if self.runtime == 0:
+            raise JSError( "JS_Init() failed." )
+
+    def _shutdownRuntime( self ):
+        self.jsdll.JS_Finish( self.runtime )
+        del self.runtime
+        del self.jsdll
+        
+    def _getNewContext( self ):
+        context = self.jsdll.JS_NewContext(self.runtime, 8192)
+        if context == 0:
+            raise JSError( "JS_NewContext() failed." )
+        return context
+
+    def _createGlobalClass( self, name ):
+        globalClass = self.JSClass(
+            name, 0,
+            ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_EnumerateStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_ResolveStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_ConvertStub, ctypes.c_void_p ),
+            ctypes.cast( self.jsdll.JS_FinalizeStub, ctypes.c_void_p ),
+            0, 0, 0, 0, 0, 0, 0, 0
+            )
+        return globalClass
+
+    def _createGlobalObject( self, context, globalClass ):
+        globalObj = self.jsdll.JS_NewObject(
+            context,
+            ctypes.byref( globalClass ),
+            0,
+            0
+            )
+
+        if not globalObj:
+            raise JSError( "JS_NewObject() failed." )
+
+        return globalObj
+
+    def _installDisplayMessageFunction( self, context, globalObj ):
+        def jsDisplayMessage( context, object, argc, argv, retval ):
+            jsString = self.jsdll.JS_ValueToString( context, argv[0] )
+            cString = ctypes.c_char_p( self.jsdll.JS_GetStringBytes( jsString ) )
+            displayMessage( cString.value )
+            retval[0] = 0
+            return 1
+
+        retval = self.jsdll.JS_DefineFunction(
+            context,
+            globalObj,
+            "displayMessage",
+            self.JSNativeFunction( jsDisplayMessage ),
+            1,
+            0
+            )
+
+        if retval == 0:
+            raise JSError( "JS_DefineFunction() failed." )
+        
+    def eval( self, scriptString ):
+        """
+        Evaluates the given string of JavaScript code and returns the
+        result as a string.
+        
+        The string must be ascii-encodable, as this function does not
+        currently use JavaScript's unicode API.
+        """
+
+        # For more information on what's going on here, see:
+        #
+        # http://developer.mozilla.org  [continued on next line]
+        #            /en/docs/JavaScript_C_Engine_Embedder%27s_Guide
+        # http://www.mozilla.org/js/spidermonkey/tutorial.html
+
+        script = scriptString.encode( "ascii" )
+
+        # We carefully set up and shutdown everything on each pass, to
+        # make the world a happier place.
+        self._initRuntime()
+        context = self._getNewContext()
+        name = ctypes.c_char_p( "enso" )
+        globalClass = self._createGlobalClass( name )
+        globalObj = self._createGlobalObject( context, globalClass )
+        self._installDisplayMessageFunction( context, globalObj )
+        builtins = self.jsdll.JS_InitStandardClasses( context, globalObj )
+
+        try:
+            rval = ctypes.c_int()
+
+            ok = self.jsdll.JS_EvaluateScript(
+                context,
+                globalObj,
+                script,
+                len(script),
+                "<string>",
+                0,
+                ctypes.byref( rval )
+                )
+
+            if not ok:
+                raise JSEvalError( "JS_EvaluateScript() failed." )
+
+            jsString = self.jsdll.JS_ValueToString( context, rval )
+            cString = ctypes.c_char_p(
+                self.jsdll.JS_GetStringBytes( jsString )
+                )
+            retval = cString.value
+        finally:
+            self.jsdll.JS_DestroyContext( context )
+            self._shutdownRuntime()
+
+        return retval
+
+class JSError( Exception ):
+    pass
+
+class JSEvalError( JSError ):
+    pass
+
+if __name__ == "__main__":
+    args = sys.argv[1:]
+    if len(args) < 1:
+        print "usage: %s <path-to-spidermonkey-dll>" % sys.argv[0]
+        sys.exit(1)
+
+    e = JSEngine(sys.argv[1])
+
+    e.eval("displayMessage('hi from spidermonkey! ' + Math.abs(-5));")