0
|
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));")
|