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