jseval.py
author Atul Varma <varmaa@toolness.com>
Wed Jun 11 08:30:05 2008 -0700 (2 months ago)
changeset 0 1102944b174d
permissions -rw-r--r--
Origination.
        1 # jseval.py
        2 # By Atul Varma <varmaa@toolness.com>
        3 #
        4 # This is a simple example of embedding the SpiderMonkey engine into
        5 # Python using ctypes.
        6 
        7 import sys
        8 import ctypes
        9 
       10 # Sample Python function to inject into JavaScript space.
       11 def displayMessage(msg):
       12     print msg
       13 
       14 class JSEngine( object ):
       15     def __init__( self, dllpath ):
       16         self._dllpath = dllpath
       17 
       18     class JSClass( ctypes.Structure ):
       19         _fields_ = [("name", ctypes.c_char_p),
       20                     ("flags", ctypes.c_uint32),
       21                     # Mandatory non-null members
       22                     ("addProperty", ctypes.c_void_p),
       23                     ("delProperty", ctypes.c_void_p),
       24                     ("getProperty", ctypes.c_void_p),
       25                     ("setProperty", ctypes.c_void_p),
       26                     ("enumerate", ctypes.c_void_p),
       27                     ("resolve", ctypes.c_void_p),
       28                     ("convert", ctypes.c_void_p),
       29                     ("finalize", ctypes.c_void_p),
       30                     # Optionally non-null members
       31                     ("getObjectOps", ctypes.c_void_p),
       32                     ("checkAccess", ctypes.c_void_p),
       33                     ("call", ctypes.c_void_p),
       34                     ("construct", ctypes.c_void_p),
       35                     ("xdrObject", ctypes.c_void_p),
       36                     ("hasInstance", ctypes.c_void_p),
       37                     ("mark", ctypes.c_void_p),
       38                     ("reserveSlots", ctypes.c_void_p)
       39                     ]
       40 
       41     JSNativeFunction = ctypes.CFUNCTYPE(
       42         ctypes.c_int, # Return value
       43         ctypes.c_void_p, # Context
       44         ctypes.c_void_p, # Object
       45         ctypes.c_int, # argc
       46         ctypes.POINTER( ctypes.c_int ), # argv
       47         ctypes.POINTER( ctypes.c_int ), # retval
       48         )
       49 
       50     def _initRuntime( self ):
       51         self.jsdll = ctypes.cdll.LoadLibrary(self._dllpath)
       52         self.runtime = self.jsdll.JS_Init( 32768 )
       53 
       54         if self.runtime == 0:
       55             raise JSError( "JS_Init() failed." )
       56 
       57     def _shutdownRuntime( self ):
       58         self.jsdll.JS_Finish( self.runtime )
       59         del self.runtime
       60         del self.jsdll
       61         
       62     def _getNewContext( self ):
       63         context = self.jsdll.JS_NewContext(self.runtime, 8192)
       64         if context == 0:
       65             raise JSError( "JS_NewContext() failed." )
       66         return context
       67 
       68     def _createGlobalClass( self, name ):
       69         globalClass = self.JSClass(
       70             name, 0,
       71             ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
       72             ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
       73             ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
       74             ctypes.cast( self.jsdll.JS_PropertyStub, ctypes.c_void_p ),
       75             ctypes.cast( self.jsdll.JS_EnumerateStub, ctypes.c_void_p ),
       76             ctypes.cast( self.jsdll.JS_ResolveStub, ctypes.c_void_p ),
       77             ctypes.cast( self.jsdll.JS_ConvertStub, ctypes.c_void_p ),
       78             ctypes.cast( self.jsdll.JS_FinalizeStub, ctypes.c_void_p ),
       79             0, 0, 0, 0, 0, 0, 0, 0
       80             )
       81         return globalClass
       82 
       83     def _createGlobalObject( self, context, globalClass ):
       84         globalObj = self.jsdll.JS_NewObject(
       85             context,
       86             ctypes.byref( globalClass ),
       87             0,
       88             0
       89             )
       90 
       91         if not globalObj:
       92             raise JSError( "JS_NewObject() failed." )
       93 
       94         return globalObj
       95 
       96     def _installDisplayMessageFunction( self, context, globalObj ):
       97         def jsDisplayMessage( context, object, argc, argv, retval ):
       98             jsString = self.jsdll.JS_ValueToString( context, argv[0] )
       99             cString = ctypes.c_char_p( self.jsdll.JS_GetStringBytes( jsString ) )
      100             displayMessage( cString.value )
      101             retval[0] = 0
      102             return 1
      103 
      104         retval = self.jsdll.JS_DefineFunction(
      105             context,
      106             globalObj,
      107             "displayMessage",
      108             self.JSNativeFunction( jsDisplayMessage ),
      109             1,
      110             0
      111             )
      112 
      113         if retval == 0:
      114             raise JSError( "JS_DefineFunction() failed." )
      115         
      116     def eval( self, scriptString ):
      117         """
      118         Evaluates the given string of JavaScript code and returns the
      119         result as a string.
      120         
      121         The string must be ascii-encodable, as this function does not
      122         currently use JavaScript's unicode API.
      123         """
      124 
      125         # For more information on what's going on here, see:
      126         #
      127         # http://developer.mozilla.org  [continued on next line]
      128         #            /en/docs/JavaScript_C_Engine_Embedder%27s_Guide
      129         # http://www.mozilla.org/js/spidermonkey/tutorial.html
      130 
      131         script = scriptString.encode( "ascii" )
      132 
      133         # We carefully set up and shutdown everything on each pass, to
      134         # make the world a happier place.
      135         self._initRuntime()
      136         context = self._getNewContext()
      137         name = ctypes.c_char_p( "enso" )
      138         globalClass = self._createGlobalClass( name )
      139         globalObj = self._createGlobalObject( context, globalClass )
      140         self._installDisplayMessageFunction( context, globalObj )
      141         builtins = self.jsdll.JS_InitStandardClasses( context, globalObj )
      142 
      143         try:
      144             rval = ctypes.c_int()
      145 
      146             ok = self.jsdll.JS_EvaluateScript(
      147                 context,
      148                 globalObj,
      149                 script,
      150                 len(script),
      151                 "<string>",
      152                 0,
      153                 ctypes.byref( rval )
      154                 )
      155 
      156             if not ok:
      157                 raise JSEvalError( "JS_EvaluateScript() failed." )
      158 
      159             jsString = self.jsdll.JS_ValueToString( context, rval )
      160             cString = ctypes.c_char_p(
      161                 self.jsdll.JS_GetStringBytes( jsString )
      162                 )
      163             retval = cString.value
      164         finally:
      165             self.jsdll.JS_DestroyContext( context )
      166             self._shutdownRuntime()
      167 
      168         return retval
      169 
      170 class JSError( Exception ):
      171     pass
      172 
      173 class JSEvalError( JSError ):
      174     pass
      175 
      176 if __name__ == "__main__":
      177     args = sys.argv[1:]
      178     if len(args) < 1:
      179         print "usage: %s <path-to-spidermonkey-dll>" % sys.argv[0]
      180         sys.exit(1)
      181 
      182     e = JSEngine(sys.argv[1])
      183 
      184     e.eval("displayMessage('hi from spidermonkey! ' + Math.abs(-5));")