Mercurial > pymonkey
comparison src/context.cpp @ 117:ac8ca0ee7760
Moved all .cpp/.h files into 'src' dir and test suite into 'tests' dir.
author | Atul Varma <varmaa@toolness.com> |
---|---|
date | Mon, 17 Aug 2009 22:08:33 -0700 |
parents | context.cpp@00c1351b3e82 |
children | 5eda03c8e756 |
comparison
equal
deleted
inserted
replaced
116:06269ca0b36c | 117:ac8ca0ee7760 |
---|---|
1 /* ***** BEGIN LICENSE BLOCK ***** | |
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 | |
3 * | |
4 * The contents of this file are subject to the Mozilla Public License Version | |
5 * 1.1 (the "License"); you may not use this file except in compliance with | |
6 * the License. You may obtain a copy of the License at | |
7 * http://www.mozilla.org/MPL/ | |
8 * | |
9 * Software distributed under the License is distributed on an "AS IS" basis, | |
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
11 * for the specific language governing rights and limitations under the | |
12 * License. | |
13 * | |
14 * The Original Code is Pymonkey. | |
15 * | |
16 * The Initial Developer of the Original Code is Mozilla. | |
17 * Portions created by the Initial Developer are Copyright (C) 2007 | |
18 * the Initial Developer. All Rights Reserved. | |
19 * | |
20 * Contributor(s): | |
21 * Atul Varma <atul@mozilla.com> | |
22 * | |
23 * Alternatively, the contents of this file may be used under the terms of | |
24 * either the GNU General Public License Version 2 or later (the "GPL"), or | |
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), | |
26 * in which case the provisions of the GPL or the LGPL are applicable instead | |
27 * of those above. If you wish to allow use of your version of this file only | |
28 * under the terms of either the GPL or the LGPL, and not to allow others to | |
29 * use your version of this file under the terms of the MPL, indicate your | |
30 * decision by deleting the provisions above and replace them with the notice | |
31 * and other provisions required by the GPL or the LGPL. If you do not delete | |
32 * the provisions above, a recipient may use your version of this file under | |
33 * the terms of any one of the MPL, the GPL or the LGPL. | |
34 * | |
35 * ***** END LICENSE BLOCK ***** */ | |
36 | |
37 #include "context.h" | |
38 #include "object.h" | |
39 #include "function.h" | |
40 #include "utils.h" | |
41 | |
42 // This is the default JSOperationCallback for pymonkey-owned JS contexts, | |
43 // when they've defined one in Python. | |
44 static JSBool | |
45 PYM_operationCallback(JSContext *cx) | |
46 { | |
47 PYM_PyAutoEnsureGIL gil; | |
48 PYM_JSContextObject *context = (PYM_JSContextObject *) | |
49 JS_GetContextPrivate(cx); | |
50 | |
51 PyObject *callable = context->opCallback; | |
52 PyObject *args = PyTuple_Pack(1, (PyObject *) context); | |
53 if (args == NULL) { | |
54 JS_ReportOutOfMemory(cx); | |
55 return JS_FALSE; | |
56 } | |
57 PyObject *result = PyObject_Call(callable, args, NULL); | |
58 Py_DECREF(args); | |
59 if (result == NULL) { | |
60 PYM_pythonExceptionToJs(context); | |
61 return JS_FALSE; | |
62 } | |
63 | |
64 Py_DECREF(result); | |
65 return JS_TRUE; | |
66 } | |
67 | |
68 // This is the default JSErrorReporter for pymonkey-owned JS contexts. | |
69 static void | |
70 PYM_reportError(JSContext *cx, const char *message, JSErrorReport *report) | |
71 { | |
72 PYM_PyAutoEnsureGIL gil; | |
73 | |
74 // Convert JS warnings into Python warnings. | |
75 if (JSREPORT_IS_WARNING(report->flags)) { | |
76 if (report->filename) | |
77 PyErr_WarnExplicit(NULL, message, report->filename, report->lineno, | |
78 NULL, NULL); | |
79 else | |
80 PyErr_Warn(NULL, message); | |
81 } else | |
82 // TODO: Not sure if this will ever get called, but we should know | |
83 // if it is. | |
84 PyErr_Warn(NULL, "A JS error was reported."); | |
85 } | |
86 | |
87 static void | |
88 PYM_JSContextDealloc(PYM_JSContextObject *self) | |
89 { | |
90 if (self->opCallback) { | |
91 Py_DECREF(self->opCallback); | |
92 self->opCallback = NULL; | |
93 } | |
94 | |
95 if (self->cx) { | |
96 JS_DestroyContext(self->cx); | |
97 self->cx = NULL; | |
98 } | |
99 | |
100 Py_DECREF(self->runtime); | |
101 self->runtime = NULL; | |
102 | |
103 self->ob_type->tp_free((PyObject *) self); | |
104 } | |
105 | |
106 static PyObject * | |
107 PYM_getRuntime(PYM_JSContextObject *self, PyObject *args) | |
108 { | |
109 Py_INCREF(self->runtime); | |
110 return (PyObject *) self->runtime; | |
111 } | |
112 | |
113 static PyObject * | |
114 PYM_getObjectPrivate(PYM_JSContextObject *self, PyObject *args) | |
115 { | |
116 PYM_SANITY_CHECK(self->runtime); | |
117 PYM_JSObject *object; | |
118 | |
119 if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) | |
120 return NULL; | |
121 | |
122 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
123 | |
124 JSObject *obj = object->obj; | |
125 | |
126 if (JS_ObjectIsFunction(self->cx, obj)) { | |
127 jsval functionHolder; | |
128 if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { | |
129 JS_ClearPendingException(self->cx); | |
130 Py_RETURN_NONE; | |
131 } | |
132 if (!JSVAL_IS_OBJECT(functionHolder)) | |
133 Py_RETURN_NONE; | |
134 obj = JSVAL_TO_OBJECT(functionHolder); | |
135 } | |
136 | |
137 JSClass *klass = JS_GET_CLASS(self->cx, obj); | |
138 if (klass != &PYM_JS_ObjectClass) | |
139 Py_RETURN_NONE; | |
140 | |
141 PyObject *pyObject; | |
142 | |
143 if (!PYM_JS_getPrivatePyObject(self->cx, obj, &pyObject)) { | |
144 PYM_jsExceptionToPython(self); | |
145 return NULL; | |
146 } | |
147 | |
148 if (pyObject == NULL) | |
149 pyObject = Py_None; | |
150 | |
151 Py_INCREF(pyObject); | |
152 return pyObject; | |
153 } | |
154 | |
155 static PyObject * | |
156 PYM_clearObjectPrivate(PYM_JSContextObject *self, PyObject *args) | |
157 { | |
158 PYM_SANITY_CHECK(self->runtime); | |
159 PYM_JSObject *object; | |
160 | |
161 if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) | |
162 return NULL; | |
163 | |
164 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
165 | |
166 JSObject *obj = object->obj; | |
167 | |
168 if (JS_ObjectIsFunction(self->cx, obj)) { | |
169 jsval functionHolder; | |
170 if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { | |
171 JS_ClearPendingException(self->cx); | |
172 Py_RETURN_NONE; | |
173 } | |
174 if (!JSVAL_IS_OBJECT(functionHolder)) | |
175 Py_RETURN_NONE; | |
176 obj = JSVAL_TO_OBJECT(functionHolder); | |
177 } | |
178 | |
179 JSClass *klass = JS_GET_CLASS(self->cx, obj); | |
180 if (klass != &PYM_JS_ObjectClass) | |
181 Py_RETURN_NONE; | |
182 | |
183 if (!PYM_JS_setPrivatePyObject(self->cx, obj, Py_None)) { | |
184 PYM_jsExceptionToPython(self); | |
185 return NULL; | |
186 } | |
187 | |
188 Py_RETURN_NONE; | |
189 } | |
190 | |
191 static PyObject * | |
192 PYM_newObject(PYM_JSContextObject *self, PyObject *args) | |
193 { | |
194 PYM_SANITY_CHECK(self->runtime); | |
195 PyObject *privateObj = NULL; | |
196 | |
197 if (!PyArg_ParseTuple(args, "|O", &privateObj)) | |
198 return NULL; | |
199 | |
200 JSObject *obj = PYM_JS_newObject(self->cx, privateObj); | |
201 if (obj == NULL) { | |
202 PyErr_SetString(PYM_error, "PYM_JS_newObject() failed"); | |
203 return NULL; | |
204 } | |
205 | |
206 // If this fails, we don't need to worry about cleaning up | |
207 // obj because it'll get cleaned up at the next GC. | |
208 return (PyObject *) PYM_newJSObject(self, obj, NULL); | |
209 } | |
210 | |
211 static PyObject * | |
212 PYM_getProperty(PYM_JSContextObject *self, PyObject *args) | |
213 { | |
214 PYM_SANITY_CHECK(self->runtime); | |
215 PYM_JSObject *object; | |
216 char *buffer = NULL; | |
217 int size; | |
218 | |
219 if (!PyArg_ParseTuple(args, "O!es#", &PYM_JSObjectType, &object, | |
220 "utf-16", &buffer, &size)) | |
221 return NULL; | |
222 | |
223 if (self->runtime != object->runtime) { | |
224 PyMem_Free(buffer); | |
225 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
226 } | |
227 | |
228 jsval val; | |
229 JSBool result; | |
230 Py_BEGIN_ALLOW_THREADS; | |
231 // Note that we're manipulating buffer and size here to get rid of | |
232 // the BOM. | |
233 result = JS_GetUCProperty(self->cx, object->obj, (jschar *) (buffer + 2), | |
234 (size / 2) - 1, &val); | |
235 Py_END_ALLOW_THREADS; | |
236 | |
237 PyMem_Free(buffer); | |
238 | |
239 if (!result) { | |
240 PYM_jsExceptionToPython(self); | |
241 return NULL; | |
242 } | |
243 | |
244 return PYM_jsvalToPyObject(self, val); | |
245 } | |
246 | |
247 static PyObject * | |
248 PYM_gc(PYM_JSContextObject *self, PyObject *args) | |
249 { | |
250 PYM_SANITY_CHECK(self->runtime); | |
251 JS_GC(self->cx); | |
252 Py_RETURN_NONE; | |
253 } | |
254 | |
255 static PyObject * | |
256 PYM_initStandardClasses(PYM_JSContextObject *self, PyObject *args) | |
257 { | |
258 PYM_SANITY_CHECK(self->runtime); | |
259 PYM_JSObject *object; | |
260 | |
261 if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) | |
262 return NULL; | |
263 | |
264 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
265 | |
266 if (!JS_InitStandardClasses(self->cx, object->obj)) { | |
267 PyErr_SetString(PYM_error, "JS_InitStandardClasses() failed"); | |
268 return NULL; | |
269 } | |
270 | |
271 Py_RETURN_NONE; | |
272 } | |
273 | |
274 static PyObject * | |
275 PYM_evaluateScript(PYM_JSContextObject *self, PyObject *args) | |
276 { | |
277 PYM_SANITY_CHECK(self->runtime); | |
278 PYM_JSObject *object; | |
279 const char *source; | |
280 int sourceLen; | |
281 const char *filename; | |
282 int lineNo; | |
283 | |
284 if (!PyArg_ParseTuple(args, "O!s#si", &PYM_JSObjectType, &object, | |
285 &source, &sourceLen, &filename, &lineNo)) | |
286 return NULL; | |
287 | |
288 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
289 | |
290 jsval rval; | |
291 JSBool result; | |
292 Py_BEGIN_ALLOW_THREADS; | |
293 result = JS_EvaluateScript(self->cx, object->obj, source, sourceLen, | |
294 filename, lineNo, &rval); | |
295 Py_END_ALLOW_THREADS; | |
296 | |
297 if (!result) { | |
298 PYM_jsExceptionToPython(self); | |
299 return NULL; | |
300 } | |
301 | |
302 PyObject *pyRval = PYM_jsvalToPyObject(self, rval); | |
303 return pyRval; | |
304 } | |
305 | |
306 static PyObject * | |
307 PYM_defineProperty(PYM_JSContextObject *self, PyObject *args) | |
308 { | |
309 PYM_SANITY_CHECK(self->runtime); | |
310 PYM_JSObject *object; | |
311 PyObject *value; | |
312 char *name = NULL; | |
313 int namelen; | |
314 | |
315 if (!PyArg_ParseTuple(args, "O!es#O", &PYM_JSObjectType, &object, | |
316 "utf-16", &name, &namelen, &value)) | |
317 return NULL; | |
318 | |
319 if (self->runtime != object->runtime) { | |
320 PyMem_Free(name); | |
321 PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); | |
322 } | |
323 | |
324 jsval jsValue; | |
325 | |
326 if (PYM_pyObjectToJsval(self, value, &jsValue) == -1) { | |
327 PyMem_Free(name); | |
328 return NULL; | |
329 } | |
330 | |
331 // Note that we're manipulating buffer and size here to get rid of | |
332 // the BOM. | |
333 if (!JS_DefineUCProperty(self->cx, object->obj, (jschar *) (name + 2), | |
334 (namelen / 2) - 1, jsValue, NULL, NULL, | |
335 JSPROP_ENUMERATE)) { | |
336 // TODO: There's probably an exception pending on self->cx, | |
337 // what should we do about it? | |
338 PyMem_Free(name); | |
339 PyErr_SetString(PYM_error, "JS_DefineProperty() failed"); | |
340 return NULL; | |
341 } | |
342 | |
343 PyMem_Free(name); | |
344 Py_RETURN_NONE; | |
345 } | |
346 | |
347 static PyObject * | |
348 PYM_callFunction(PYM_JSContextObject *self, PyObject *args) | |
349 { | |
350 PYM_SANITY_CHECK(self->runtime); | |
351 PYM_JSObject *obj; | |
352 PYM_JSFunction *fun; | |
353 PyObject *funcArgs; | |
354 | |
355 if (!PyArg_ParseTuple(args, "O!O!O!", &PYM_JSObjectType, &obj, | |
356 &PYM_JSFunctionType, &fun, | |
357 &PyTuple_Type, &funcArgs)) | |
358 return NULL; | |
359 | |
360 PYM_ENSURE_RUNTIME_MATCH(self->runtime, obj->runtime); | |
361 PYM_ENSURE_RUNTIME_MATCH(self->runtime, fun->base.runtime); | |
362 | |
363 uintN argc = PyTuple_Size(funcArgs); | |
364 | |
365 jsval *argv = (jsval *) PyMem_Malloc(sizeof(jsval) * argc); | |
366 if (argv == NULL) | |
367 return PyErr_NoMemory(); | |
368 | |
369 jsval *currArg = argv; | |
370 | |
371 for (unsigned int i = 0; i < argc; i++) { | |
372 PyObject *item = PyTuple_GET_ITEM(funcArgs, i); | |
373 if (item == NULL || | |
374 PYM_pyObjectToJsval(self, item, currArg) == -1) { | |
375 PyMem_Free(argv); | |
376 return NULL; | |
377 } | |
378 currArg++; | |
379 } | |
380 | |
381 jsval rval; | |
382 | |
383 // TODO: This assumes that a JSFunction * is actually a subclass of | |
384 // a JSObject *, which may or may not be regarded as an implementation | |
385 // detail. | |
386 JSBool result; | |
387 Py_BEGIN_ALLOW_THREADS; | |
388 result = JS_CallFunction(self->cx, obj->obj, | |
389 (JSFunction *) fun->base.obj, | |
390 argc, argv, &rval); | |
391 Py_END_ALLOW_THREADS; | |
392 | |
393 PyMem_Free(argv); | |
394 | |
395 if (!result) { | |
396 PYM_jsExceptionToPython(self); | |
397 return NULL; | |
398 } | |
399 | |
400 return PYM_jsvalToPyObject(self, rval); | |
401 } | |
402 | |
403 static PyObject * | |
404 PYM_newFunction(PYM_JSContextObject *self, PyObject *args) | |
405 { | |
406 PYM_SANITY_CHECK(self->runtime); | |
407 PyObject *callable; | |
408 const char *name; | |
409 | |
410 if (!PyArg_ParseTuple(args, "Os", &callable, &name)) | |
411 return NULL; | |
412 | |
413 return (PyObject *) PYM_newJSFunctionFromCallable(self, callable, name); | |
414 } | |
415 | |
416 static PyObject * | |
417 PYM_setOperationCallback(PYM_JSContextObject *self, PyObject *args) | |
418 { | |
419 PYM_SANITY_CHECK(self->runtime); | |
420 PyObject *callable; | |
421 | |
422 if (!PyArg_ParseTuple(args, "O", &callable)) | |
423 return NULL; | |
424 | |
425 if (!PyCallable_Check(callable)) { | |
426 PyErr_SetString(PyExc_TypeError, "Callable must be callable"); | |
427 return NULL; | |
428 } | |
429 | |
430 JS_SetOperationCallback(self->cx, PYM_operationCallback); | |
431 | |
432 Py_INCREF(callable); | |
433 if (self->opCallback) | |
434 Py_DECREF(self->opCallback); | |
435 self->opCallback = callable; | |
436 | |
437 Py_RETURN_NONE; | |
438 } | |
439 | |
440 static PyObject * | |
441 PYM_triggerOperationCallback(PYM_JSContextObject *self, PyObject *args) | |
442 { | |
443 JS_TriggerOperationCallback(self->cx); | |
444 Py_RETURN_NONE; | |
445 } | |
446 | |
447 static PyMethodDef PYM_JSContextMethods[] = { | |
448 {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, | |
449 "Get the JavaScript runtime associated with this context."}, | |
450 {"new_object", (PyCFunction) PYM_newObject, METH_VARARGS, | |
451 "Create a new JavaScript object."}, | |
452 {"init_standard_classes", | |
453 (PyCFunction) PYM_initStandardClasses, METH_VARARGS, | |
454 "Add standard classes and functions to the given object."}, | |
455 {"evaluate_script", | |
456 (PyCFunction) PYM_evaluateScript, METH_VARARGS, | |
457 "Evaluate the given JavaScript code in the context of the given " | |
458 "global object, using the given filename" | |
459 "and line number information."}, | |
460 {"call_function", | |
461 (PyCFunction) PYM_callFunction, METH_VARARGS, | |
462 "Calls a JS function."}, | |
463 {"new_function", | |
464 (PyCFunction) PYM_newFunction, METH_VARARGS, | |
465 "Creates a new function callable from JS."}, | |
466 {"define_property", | |
467 (PyCFunction) PYM_defineProperty, METH_VARARGS, | |
468 "Defines a property on an object."}, | |
469 {"get_property", (PyCFunction) PYM_getProperty, METH_VARARGS, | |
470 "Gets the given property for the given JavaScript object."}, | |
471 {"gc", (PyCFunction) PYM_gc, METH_VARARGS, | |
472 "Performs garbage collection on the context's runtime."}, | |
473 {"set_operation_callback", (PyCFunction) PYM_setOperationCallback, | |
474 METH_VARARGS, | |
475 "Sets the operation callback for the context."}, | |
476 {"trigger_operation_callback", (PyCFunction) PYM_triggerOperationCallback, | |
477 METH_VARARGS, | |
478 "Triggers the operation callback for the context."}, | |
479 {"get_object_private", (PyCFunction) PYM_getObjectPrivate, METH_VARARGS, | |
480 "Returns the private Python object stored in the JavaScript object."}, | |
481 {"clear_object_private", (PyCFunction) PYM_clearObjectPrivate, METH_VARARGS, | |
482 "Clears any private Python object stored in the JavaScript object."}, | |
483 {NULL, NULL, 0, NULL} | |
484 }; | |
485 | |
486 PyTypeObject PYM_JSContextType = { | |
487 PyObject_HEAD_INIT(NULL) | |
488 0, /*ob_size*/ | |
489 "pymonkey.Context", /*tp_name*/ | |
490 sizeof(PYM_JSContextObject), /*tp_basicsize*/ | |
491 0, /*tp_itemsize*/ | |
492 /*tp_dealloc*/ | |
493 (destructor) PYM_JSContextDealloc, | |
494 0, /*tp_print*/ | |
495 0, /*tp_getattr*/ | |
496 0, /*tp_setattr*/ | |
497 0, /*tp_compare*/ | |
498 0, /*tp_repr*/ | |
499 0, /*tp_as_number*/ | |
500 0, /*tp_as_sequence*/ | |
501 0, /*tp_as_mapping*/ | |
502 0, /*tp_hash */ | |
503 0, /*tp_call*/ | |
504 0, /*tp_str*/ | |
505 0, /*tp_getattro*/ | |
506 0, /*tp_setattro*/ | |
507 0, /*tp_as_buffer*/ | |
508 Py_TPFLAGS_DEFAULT, /*tp_flags*/ | |
509 /* tp_doc */ | |
510 "JavaScript Context.", | |
511 0, /* tp_traverse */ | |
512 0, /* tp_clear */ | |
513 0, /* tp_richcompare */ | |
514 0, /* tp_weaklistoffset */ | |
515 0, /* tp_iter */ | |
516 0, /* tp_iternext */ | |
517 PYM_JSContextMethods, /* tp_methods */ | |
518 0, /* tp_members */ | |
519 0, /* tp_getset */ | |
520 0, /* tp_base */ | |
521 0, /* tp_dict */ | |
522 0, /* tp_descr_get */ | |
523 0, /* tp_descr_set */ | |
524 0, /* tp_dictoffset */ | |
525 0, /* tp_init */ | |
526 0, /* tp_alloc */ | |
527 0, /* tp_new */ | |
528 }; | |
529 | |
530 extern PYM_JSContextObject * | |
531 PYM_newJSContextObject(PYM_JSRuntimeObject *runtime, JSContext *cx) | |
532 { | |
533 PYM_JSContextObject *context = PyObject_New(PYM_JSContextObject, | |
534 &PYM_JSContextType); | |
535 if (context == NULL) | |
536 return NULL; | |
537 | |
538 context->opCallback = NULL; | |
539 context->runtime = runtime; | |
540 Py_INCREF(runtime); | |
541 | |
542 context->cx = cx; | |
543 JS_SetContextPrivate(cx, context); | |
544 JS_SetErrorReporter(cx, PYM_reportError); | |
545 | |
546 #ifdef JS_GC_ZEAL | |
547 // TODO: Consider exposing JS_SetGCZeal() to Python instead of | |
548 // hard-coding it here. | |
549 JS_SetGCZeal(cx, 2); | |
550 #endif | |
551 | |
552 return context; | |
553 } |