comparison jseval.py @ 0:1102944b174d

Origination.
author Atul Varma <varmaa@toolness.com>
date Wed, 11 Jun 2008 08:30:05 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1102944b174d
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));")