Mercurial > scratch
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));")