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 }