# HG changeset patch # User Atul Varma # Date 1250572113 25200 # Node ID ac8ca0ee77602ce22621a7d62c0cb9bf4ef0d691 # Parent 06269ca0b36c3282df32e444f4bcfcdc49abe238 Moved all .cpp/.h files into 'src' dir and test suite into 'tests' dir. diff -r 06269ca0b36c -r ac8ca0ee7760 context.cpp --- a/context.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,553 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "context.h" -#include "object.h" -#include "function.h" -#include "utils.h" - -// This is the default JSOperationCallback for pymonkey-owned JS contexts, -// when they've defined one in Python. -static JSBool -PYM_operationCallback(JSContext *cx) -{ - PYM_PyAutoEnsureGIL gil; - PYM_JSContextObject *context = (PYM_JSContextObject *) - JS_GetContextPrivate(cx); - - PyObject *callable = context->opCallback; - PyObject *args = PyTuple_Pack(1, (PyObject *) context); - if (args == NULL) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - PyObject *result = PyObject_Call(callable, args, NULL); - Py_DECREF(args); - if (result == NULL) { - PYM_pythonExceptionToJs(context); - return JS_FALSE; - } - - Py_DECREF(result); - return JS_TRUE; -} - -// This is the default JSErrorReporter for pymonkey-owned JS contexts. -static void -PYM_reportError(JSContext *cx, const char *message, JSErrorReport *report) -{ - PYM_PyAutoEnsureGIL gil; - - // Convert JS warnings into Python warnings. - if (JSREPORT_IS_WARNING(report->flags)) { - if (report->filename) - PyErr_WarnExplicit(NULL, message, report->filename, report->lineno, - NULL, NULL); - else - PyErr_Warn(NULL, message); - } else - // TODO: Not sure if this will ever get called, but we should know - // if it is. - PyErr_Warn(NULL, "A JS error was reported."); -} - -static void -PYM_JSContextDealloc(PYM_JSContextObject *self) -{ - if (self->opCallback) { - Py_DECREF(self->opCallback); - self->opCallback = NULL; - } - - if (self->cx) { - JS_DestroyContext(self->cx); - self->cx = NULL; - } - - Py_DECREF(self->runtime); - self->runtime = NULL; - - self->ob_type->tp_free((PyObject *) self); -} - -static PyObject * -PYM_getRuntime(PYM_JSContextObject *self, PyObject *args) -{ - Py_INCREF(self->runtime); - return (PyObject *) self->runtime; -} - -static PyObject * -PYM_getObjectPrivate(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - - if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) - return NULL; - - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - - JSObject *obj = object->obj; - - if (JS_ObjectIsFunction(self->cx, obj)) { - jsval functionHolder; - if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { - JS_ClearPendingException(self->cx); - Py_RETURN_NONE; - } - if (!JSVAL_IS_OBJECT(functionHolder)) - Py_RETURN_NONE; - obj = JSVAL_TO_OBJECT(functionHolder); - } - - JSClass *klass = JS_GET_CLASS(self->cx, obj); - if (klass != &PYM_JS_ObjectClass) - Py_RETURN_NONE; - - PyObject *pyObject; - - if (!PYM_JS_getPrivatePyObject(self->cx, obj, &pyObject)) { - PYM_jsExceptionToPython(self); - return NULL; - } - - if (pyObject == NULL) - pyObject = Py_None; - - Py_INCREF(pyObject); - return pyObject; -} - -static PyObject * -PYM_clearObjectPrivate(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - - if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) - return NULL; - - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - - JSObject *obj = object->obj; - - if (JS_ObjectIsFunction(self->cx, obj)) { - jsval functionHolder; - if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { - JS_ClearPendingException(self->cx); - Py_RETURN_NONE; - } - if (!JSVAL_IS_OBJECT(functionHolder)) - Py_RETURN_NONE; - obj = JSVAL_TO_OBJECT(functionHolder); - } - - JSClass *klass = JS_GET_CLASS(self->cx, obj); - if (klass != &PYM_JS_ObjectClass) - Py_RETURN_NONE; - - if (!PYM_JS_setPrivatePyObject(self->cx, obj, Py_None)) { - PYM_jsExceptionToPython(self); - return NULL; - } - - Py_RETURN_NONE; -} - -static PyObject * -PYM_newObject(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PyObject *privateObj = NULL; - - if (!PyArg_ParseTuple(args, "|O", &privateObj)) - return NULL; - - JSObject *obj = PYM_JS_newObject(self->cx, privateObj); - if (obj == NULL) { - PyErr_SetString(PYM_error, "PYM_JS_newObject() failed"); - return NULL; - } - - // If this fails, we don't need to worry about cleaning up - // obj because it'll get cleaned up at the next GC. - return (PyObject *) PYM_newJSObject(self, obj, NULL); -} - -static PyObject * -PYM_getProperty(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - char *buffer = NULL; - int size; - - if (!PyArg_ParseTuple(args, "O!es#", &PYM_JSObjectType, &object, - "utf-16", &buffer, &size)) - return NULL; - - if (self->runtime != object->runtime) { - PyMem_Free(buffer); - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - } - - jsval val; - JSBool result; - Py_BEGIN_ALLOW_THREADS; - // Note that we're manipulating buffer and size here to get rid of - // the BOM. - result = JS_GetUCProperty(self->cx, object->obj, (jschar *) (buffer + 2), - (size / 2) - 1, &val); - Py_END_ALLOW_THREADS; - - PyMem_Free(buffer); - - if (!result) { - PYM_jsExceptionToPython(self); - return NULL; - } - - return PYM_jsvalToPyObject(self, val); -} - -static PyObject * -PYM_gc(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - JS_GC(self->cx); - Py_RETURN_NONE; -} - -static PyObject * -PYM_initStandardClasses(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - - if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) - return NULL; - - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - - if (!JS_InitStandardClasses(self->cx, object->obj)) { - PyErr_SetString(PYM_error, "JS_InitStandardClasses() failed"); - return NULL; - } - - Py_RETURN_NONE; -} - -static PyObject * -PYM_evaluateScript(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - const char *source; - int sourceLen; - const char *filename; - int lineNo; - - if (!PyArg_ParseTuple(args, "O!s#si", &PYM_JSObjectType, &object, - &source, &sourceLen, &filename, &lineNo)) - return NULL; - - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - - jsval rval; - JSBool result; - Py_BEGIN_ALLOW_THREADS; - result = JS_EvaluateScript(self->cx, object->obj, source, sourceLen, - filename, lineNo, &rval); - Py_END_ALLOW_THREADS; - - if (!result) { - PYM_jsExceptionToPython(self); - return NULL; - } - - PyObject *pyRval = PYM_jsvalToPyObject(self, rval); - return pyRval; -} - -static PyObject * -PYM_defineProperty(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *object; - PyObject *value; - char *name = NULL; - int namelen; - - if (!PyArg_ParseTuple(args, "O!es#O", &PYM_JSObjectType, &object, - "utf-16", &name, &namelen, &value)) - return NULL; - - if (self->runtime != object->runtime) { - PyMem_Free(name); - PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); - } - - jsval jsValue; - - if (PYM_pyObjectToJsval(self, value, &jsValue) == -1) { - PyMem_Free(name); - return NULL; - } - - // Note that we're manipulating buffer and size here to get rid of - // the BOM. - if (!JS_DefineUCProperty(self->cx, object->obj, (jschar *) (name + 2), - (namelen / 2) - 1, jsValue, NULL, NULL, - JSPROP_ENUMERATE)) { - // TODO: There's probably an exception pending on self->cx, - // what should we do about it? - PyMem_Free(name); - PyErr_SetString(PYM_error, "JS_DefineProperty() failed"); - return NULL; - } - - PyMem_Free(name); - Py_RETURN_NONE; -} - -static PyObject * -PYM_callFunction(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PYM_JSObject *obj; - PYM_JSFunction *fun; - PyObject *funcArgs; - - if (!PyArg_ParseTuple(args, "O!O!O!", &PYM_JSObjectType, &obj, - &PYM_JSFunctionType, &fun, - &PyTuple_Type, &funcArgs)) - return NULL; - - PYM_ENSURE_RUNTIME_MATCH(self->runtime, obj->runtime); - PYM_ENSURE_RUNTIME_MATCH(self->runtime, fun->base.runtime); - - uintN argc = PyTuple_Size(funcArgs); - - jsval *argv = (jsval *) PyMem_Malloc(sizeof(jsval) * argc); - if (argv == NULL) - return PyErr_NoMemory(); - - jsval *currArg = argv; - - for (unsigned int i = 0; i < argc; i++) { - PyObject *item = PyTuple_GET_ITEM(funcArgs, i); - if (item == NULL || - PYM_pyObjectToJsval(self, item, currArg) == -1) { - PyMem_Free(argv); - return NULL; - } - currArg++; - } - - jsval rval; - - // TODO: This assumes that a JSFunction * is actually a subclass of - // a JSObject *, which may or may not be regarded as an implementation - // detail. - JSBool result; - Py_BEGIN_ALLOW_THREADS; - result = JS_CallFunction(self->cx, obj->obj, - (JSFunction *) fun->base.obj, - argc, argv, &rval); - Py_END_ALLOW_THREADS; - - PyMem_Free(argv); - - if (!result) { - PYM_jsExceptionToPython(self); - return NULL; - } - - return PYM_jsvalToPyObject(self, rval); -} - -static PyObject * -PYM_newFunction(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PyObject *callable; - const char *name; - - if (!PyArg_ParseTuple(args, "Os", &callable, &name)) - return NULL; - - return (PyObject *) PYM_newJSFunctionFromCallable(self, callable, name); -} - -static PyObject * -PYM_setOperationCallback(PYM_JSContextObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self->runtime); - PyObject *callable; - - if (!PyArg_ParseTuple(args, "O", &callable)) - return NULL; - - if (!PyCallable_Check(callable)) { - PyErr_SetString(PyExc_TypeError, "Callable must be callable"); - return NULL; - } - - JS_SetOperationCallback(self->cx, PYM_operationCallback); - - Py_INCREF(callable); - if (self->opCallback) - Py_DECREF(self->opCallback); - self->opCallback = callable; - - Py_RETURN_NONE; -} - -static PyObject * -PYM_triggerOperationCallback(PYM_JSContextObject *self, PyObject *args) -{ - JS_TriggerOperationCallback(self->cx); - Py_RETURN_NONE; -} - -static PyMethodDef PYM_JSContextMethods[] = { - {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, - "Get the JavaScript runtime associated with this context."}, - {"new_object", (PyCFunction) PYM_newObject, METH_VARARGS, - "Create a new JavaScript object."}, - {"init_standard_classes", - (PyCFunction) PYM_initStandardClasses, METH_VARARGS, - "Add standard classes and functions to the given object."}, - {"evaluate_script", - (PyCFunction) PYM_evaluateScript, METH_VARARGS, - "Evaluate the given JavaScript code in the context of the given " - "global object, using the given filename" - "and line number information."}, - {"call_function", - (PyCFunction) PYM_callFunction, METH_VARARGS, - "Calls a JS function."}, - {"new_function", - (PyCFunction) PYM_newFunction, METH_VARARGS, - "Creates a new function callable from JS."}, - {"define_property", - (PyCFunction) PYM_defineProperty, METH_VARARGS, - "Defines a property on an object."}, - {"get_property", (PyCFunction) PYM_getProperty, METH_VARARGS, - "Gets the given property for the given JavaScript object."}, - {"gc", (PyCFunction) PYM_gc, METH_VARARGS, - "Performs garbage collection on the context's runtime."}, - {"set_operation_callback", (PyCFunction) PYM_setOperationCallback, - METH_VARARGS, - "Sets the operation callback for the context."}, - {"trigger_operation_callback", (PyCFunction) PYM_triggerOperationCallback, - METH_VARARGS, - "Triggers the operation callback for the context."}, - {"get_object_private", (PyCFunction) PYM_getObjectPrivate, METH_VARARGS, - "Returns the private Python object stored in the JavaScript object."}, - {"clear_object_private", (PyCFunction) PYM_clearObjectPrivate, METH_VARARGS, - "Clears any private Python object stored in the JavaScript object."}, - {NULL, NULL, 0, NULL} -}; - -PyTypeObject PYM_JSContextType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pymonkey.Context", /*tp_name*/ - sizeof(PYM_JSContextObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /*tp_dealloc*/ - (destructor) PYM_JSContextDealloc, - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - /* tp_doc */ - "JavaScript Context.", - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PYM_JSContextMethods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -extern PYM_JSContextObject * -PYM_newJSContextObject(PYM_JSRuntimeObject *runtime, JSContext *cx) -{ - PYM_JSContextObject *context = PyObject_New(PYM_JSContextObject, - &PYM_JSContextType); - if (context == NULL) - return NULL; - - context->opCallback = NULL; - context->runtime = runtime; - Py_INCREF(runtime); - - context->cx = cx; - JS_SetContextPrivate(cx, context); - JS_SetErrorReporter(cx, PYM_reportError); - -#ifdef JS_GC_ZEAL - // TODO: Consider exposing JS_SetGCZeal() to Python instead of - // hard-coding it here. - JS_SetGCZeal(cx, 2); -#endif - - return context; -} diff -r 06269ca0b36c -r ac8ca0ee7760 context.h --- a/context.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_CONTEXT_H -#define PYM_CONTEXT_H - -#include "runtime.h" - -#include -#include - -typedef struct { - PyObject_HEAD - PYM_JSRuntimeObject *runtime; - JSContext *cx; - PyObject *opCallback; -} PYM_JSContextObject; - -extern PyTypeObject PYM_JSContextType; - -extern PYM_JSContextObject * -PYM_newJSContextObject(PYM_JSRuntimeObject *runtime, - JSContext *cx); - -#endif diff -r 06269ca0b36c -r ac8ca0ee7760 function.cpp --- a/function.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,224 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "function.h" -#include "utils.h" - -static void -PYM_JSFunctionDealloc(PYM_JSFunction *self) -{ - PYM_JSObjectType.tp_dealloc((PyObject *) self); -} - -static JSBool -PYM_dispatchJSFunctionToPython(JSContext *cx, - JSObject *obj, - uintN argc, - jsval *argv, - jsval *rval) -{ - PYM_PyAutoEnsureGIL gil; - jsval callee = JS_ARGV_CALLEE(argv); - jsval functionHolder; - if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(callee), 0, &functionHolder)) - return JS_FALSE; - - PyObject *callable; - if (!PYM_JS_getPrivatePyObject(cx, JSVAL_TO_OBJECT(functionHolder), - &callable)) - return JS_FALSE; - - if (callable == Py_None) { - JS_ReportError(cx, "Wrapped Python function no longer exists"); - return JS_FALSE; - } - - PYM_JSContextObject *context = (PYM_JSContextObject *) - JS_GetContextPrivate(cx); - - jsval thisArg = OBJECT_TO_JSVAL(obj); - PyObject *pyThisArg = PYM_jsvalToPyObject(context, thisArg); - if (pyThisArg == NULL) { - PYM_pythonExceptionToJs(context); - return JS_FALSE; - } - - PyObject *funcArgs = PyTuple_New(argc); - if (funcArgs == NULL) { - Py_DECREF(pyThisArg); - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - for (unsigned int i = 0; i < argc; i++) { - PyObject *arg = PYM_jsvalToPyObject(context, argv[i]); - if (arg == NULL || PyTuple_SetItem(funcArgs, i, arg)) { - if (arg) - Py_DECREF(arg); - Py_DECREF(funcArgs); - Py_DECREF(pyThisArg); - PYM_pythonExceptionToJs(context); - return JS_FALSE; - } - } - - PyObject *args = PyTuple_Pack(3, - (PyObject *) context, - pyThisArg, - funcArgs); - Py_DECREF(pyThisArg); - Py_DECREF(funcArgs); - if (args == NULL) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - - PyObject *result = PyObject_Call(callable, args, NULL); - Py_DECREF(args); - if (result == NULL) { - PYM_pythonExceptionToJs(context); - return JS_FALSE; - } - - int error = PYM_pyObjectToJsval(context, result, rval); - Py_DECREF(result); - - if (error) { - PYM_pythonExceptionToJs(context); - return JS_FALSE; - } - - return JS_TRUE; -} - -PyTypeObject PYM_JSFunctionType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pymonkey.Function", /*tp_name*/ - sizeof(PYM_JSFunction), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /*tp_dealloc*/ - (destructor) PYM_JSFunctionDealloc, - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - /*tp_flags*/ - Py_TPFLAGS_DEFAULT, - /* tp_doc */ - "JavaScript Function.", - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - 0, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -PYM_JSFunction * -PYM_newJSFunctionFromCallable(PYM_JSContextObject *context, - PyObject *callable, - const char *name) -{ - if (!PyCallable_Check(callable)) { - PyErr_SetString(PyExc_TypeError, "Callable must be callable"); - return NULL; - } - - JSFunction *func = JS_NewFunction(context->cx, - PYM_dispatchJSFunctionToPython, 0, - 0, NULL, name); - - if (func == NULL) { - PyErr_SetString(PYM_error, "JS_DefineFunction() failed"); - return NULL; - } - - JSObject *funcObj = JS_GetFunctionObject(func); - - if (funcObj == NULL) { - PyErr_SetString(PYM_error, "JS_GetFunctionObject() failed"); - return NULL; - } - - PYM_JSFunction *object = PyObject_New(PYM_JSFunction, - &PYM_JSFunctionType); - if (object == NULL) - return NULL; - - if (PYM_newJSObject(context, funcObj, - (PYM_JSObject *) object) == NULL) - // Note that our object's reference count will have already - // been decremented by PYM_newJSObject(). - return NULL; - - JSObject *functionHolder = PYM_JS_newObject(context->cx, callable); - if (functionHolder == NULL) { - Py_DECREF((PyObject *) object); - PyErr_SetString(PYM_error, "PYM_JS_newObject() failed"); - return NULL; - } - - if (!JS_SetReservedSlot(context->cx, funcObj, 0, - OBJECT_TO_JSVAL(functionHolder))) { - Py_DECREF((PyObject *) object); - PyErr_SetString(PYM_error, "JS_SetReservedSlot() failed"); - return NULL; - } - - return object; -} diff -r 06269ca0b36c -r ac8ca0ee7760 function.h --- a/function.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_FUNCTION_H -#define PYM_FUNCTION_H - -#include "object.h" -#include "context.h" - -#include -#include - -typedef struct { - PYM_JSObject base; -} PYM_JSFunction; - -extern PyTypeObject PYM_JSFunctionType; - -extern PYM_JSFunction * -PYM_newJSFunctionFromCallable(PYM_JSContextObject *context, - PyObject *callable, - const char *name); - -#endif diff -r 06269ca0b36c -r ac8ca0ee7760 object.cpp --- a/object.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,238 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "object.h" -#include "function.h" -#include "runtime.h" -#include "utils.h" - -JSObject * -PYM_JS_newObject(JSContext *cx, PyObject *pyObject) -{ - JSObject *obj = JS_NewObject(cx, &PYM_JS_ObjectClass, NULL, NULL); - if (obj) { - if (!JS_SetPrivate(cx, obj, pyObject)) - return NULL; - Py_XINCREF(pyObject); - } - return obj; -} - -JSBool -PYM_JS_setPrivatePyObject(JSContext *cx, JSObject *obj, PyObject *pyObject) -{ - PyObject *old; - if (!PYM_JS_getPrivatePyObject(cx, obj, &old)) - return JS_FALSE; - if (!JS_SetPrivate(cx, obj, pyObject)) - return JS_FALSE; - Py_INCREF(pyObject); - Py_XDECREF(old); - return JS_TRUE; -} - -JSBool -PYM_JS_getPrivatePyObject(JSContext *cx, JSObject *obj, PyObject **pyObject) -{ - JSClass *klass = JS_GET_CLASS(cx, obj); - if (klass != &PYM_JS_ObjectClass) { - JS_ReportError(cx, "Object is not an instance of PymonkeyObject"); - return JS_FALSE; - } - - *pyObject = (PyObject *) JS_GetPrivate(cx, obj); - return JS_TRUE; -} - -static void -PYM_JS_finalizeObject(JSContext *cx, JSObject *obj) -{ - PYM_PyAutoEnsureGIL gil; - PyObject *pyObject; - // TODO: What if this fails? - if (PYM_JS_getPrivatePyObject(cx, obj, &pyObject)) - Py_XDECREF(pyObject); -} - -// This one-size-fits-all JSClass is used for any JS objects created -// in Python. It can hold a reference to a Python object for as long as -// its parent JS object is accessible from JS-land. As soon as it's -// garbage collected by the JS interpreter, it releases its reference on -// the Python object. -JSClass PYM_JS_ObjectClass = { - "PymonkeyObject", JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PYM_JS_finalizeObject, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static void -PYM_JSObjectDealloc(PYM_JSObject *self) -{ - if (self->obj) { - JS_DHashTableOperate(&self->runtime->objects, - (void *) self->obj, - JS_DHASH_REMOVE); - - // JS_RemoveRoot() always returns JS_TRUE, so don't - // bother checking its return value. - JS_RemoveRootRT(self->runtime->rt, &self->obj); - self->obj = NULL; - } - - if (self->runtime) { - Py_DECREF(self->runtime); - self->runtime = NULL; - } - - self->ob_type->tp_free((PyObject *) self); -} - -static PyObject * -PYM_getRuntime(PYM_JSObject *self, PyObject *args) -{ - Py_INCREF(self->runtime); - return (PyObject *) self->runtime; -} - -static PyMethodDef PYM_JSObjectMethods[] = { - {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, - "Get the JavaScript runtime associated with this object."}, - {NULL, NULL, 0, NULL} -}; - -PyTypeObject PYM_JSObjectType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pymonkey.Object", /*tp_name*/ - sizeof(PYM_JSObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /*tp_dealloc*/ - (destructor) PYM_JSObjectDealloc, - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - /*tp_flags*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - /* tp_doc */ - "JavaScript Object.", - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PYM_JSObjectMethods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - 0, /* tp_new */ -}; - -PYM_JSObject *PYM_newJSObject(PYM_JSContextObject *context, - JSObject *obj, - PYM_JSObject *subclass) { - PYM_JSRuntimeObject *runtime = context->runtime; - PYM_HashEntry *cached = (PYM_HashEntry *) JS_DHashTableOperate( - &runtime->objects, - (void *) obj, - JS_DHASH_LOOKUP - ); - - if (JS_DHASH_ENTRY_IS_BUSY((JSDHashEntryHdr *) cached)) { - Py_INCREF((PyObject *) cached->value); - return (PYM_JSObject *) cached->value; - } - - PYM_JSObject *object; - - if (subclass) - object = subclass; - else { - if (JS_ObjectIsFunction(context->cx, obj)) { - PYM_JSFunction *func = PyObject_New(PYM_JSFunction, - &PYM_JSFunctionType); - object = (PYM_JSObject *) func; - } else - object = PyObject_New(PYM_JSObject, - &PYM_JSObjectType); - } - - if (object == NULL) - return NULL; - - object->runtime = NULL; - object->obj = NULL; - - cached = (PYM_HashEntry *) JS_DHashTableOperate(&runtime->objects, - (void *) obj, - JS_DHASH_ADD); - if (cached == NULL) { - Py_DECREF(object); - PyErr_SetString(PYM_error, "JS_DHashTableOperate() failed"); - return NULL; - } - - cached->base.key = (void *) obj; - cached->value = object; - - object->runtime = context->runtime; - Py_INCREF(object->runtime); - - object->obj = obj; - - JS_AddNamedRootRT(object->runtime->rt, &object->obj, - "Pymonkey-Generated Object"); - - return object; -} diff -r 06269ca0b36c -r ac8ca0ee7760 object.h --- a/object.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,68 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_OBJECT_H -#define PYM_OBJECT_H - -#include "context.h" - -#include -#include - -extern JSObject * -PYM_JS_newObject(JSContext *cx, PyObject *pyObject); - -extern JSBool -PYM_JS_setPrivatePyObject(JSContext *cx, JSObject *obj, PyObject *pyObject); - -extern JSBool -PYM_JS_getPrivatePyObject(JSContext *cx, JSObject *obj, PyObject **pyObject); - -extern JSClass PYM_JS_ObjectClass; - -typedef struct { - PyObject_HEAD - PYM_JSRuntimeObject *runtime; - JSObject *obj; -} PYM_JSObject; - -extern PyTypeObject PYM_JSObjectType; - -extern PYM_JSObject * -PYM_newJSObject(PYM_JSContextObject *context, JSObject *obj, - PYM_JSObject *subclass); - -#endif diff -r 06269ca0b36c -r ac8ca0ee7760 pymonkey.cpp --- a/pymonkey.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,94 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "undefined.h" -#include "runtime.h" -#include "context.h" -#include "object.h" -#include "function.h" -#include "utils.h" - -static PyMethodDef PYM_methods[] = { - {NULL, NULL, 0, NULL} -}; - -PyMODINIT_FUNC -initpymonkey(void) -{ - PyObject *module; - - module = Py_InitModule("pymonkey", PYM_methods); - if (module == NULL) - return; - - if (PyType_Ready(&PYM_undefinedType) < 0) - return; - - PYM_undefined = PyObject_New(PYM_undefinedObject, &PYM_undefinedType); - if (PYM_undefined == NULL) - return; - Py_INCREF(PYM_undefined); - PyModule_AddObject(module, "undefined", (PyObject *) PYM_undefined); - - PYM_error = PyErr_NewException("pymonkey.error", NULL, NULL); - Py_INCREF(PYM_error); - PyModule_AddObject(module, "error", PYM_error); - - if (!PyType_Ready(&PYM_JSRuntimeType) < 0) - return; - - Py_INCREF(&PYM_JSRuntimeType); - PyModule_AddObject(module, "Runtime", (PyObject *) &PYM_JSRuntimeType); - - if (!PyType_Ready(&PYM_JSContextType) < 0) - return; - - Py_INCREF(&PYM_JSContextType); - PyModule_AddObject(module, "Context", (PyObject *) &PYM_JSContextType); - - if (!PyType_Ready(&PYM_JSObjectType) < 0) - return; - - Py_INCREF(&PYM_JSObjectType); - PyModule_AddObject(module, "Object", (PyObject *) &PYM_JSObjectType); - - PYM_JSFunctionType.tp_base = &PYM_JSObjectType; - if (!PyType_Ready(&PYM_JSFunctionType) < 0) - return; - - Py_INCREF(&PYM_JSFunctionType); - PyModule_AddObject(module, "Function", (PyObject *) &PYM_JSFunctionType); -} diff -r 06269ca0b36c -r ac8ca0ee7760 runtime.cpp --- a/runtime.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,177 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "runtime.h" -#include "context.h" -#include "utils.h" - -static PyObject * -PYM_JSRuntimeNew(PyTypeObject *type, PyObject *args, - PyObject *kwds) -{ - PYM_JSRuntimeObject *self; - - self = (PYM_JSRuntimeObject *) type->tp_alloc(type, 0); - if (self != NULL) { - self->thread = PyThread_get_thread_ident(); - self->rt = NULL; - self->cx = NULL; - self->objects.ops = NULL; - - if (!JS_DHashTableInit(&self->objects, - JS_DHashGetStubOps(), - NULL, - sizeof(PYM_HashEntry), - JS_DHASH_DEFAULT_CAPACITY(100))) { - PyErr_SetString(PYM_error, "JS_DHashTableInit() failed"); - type->tp_dealloc((PyObject *) self); - self = NULL; - } - - if (self != NULL) { - self->rt = JS_NewRuntime(8L * 1024L * 1024L); - if (!self->rt) { - PyErr_SetString(PYM_error, "JS_NewRuntime() failed"); - type->tp_dealloc((PyObject *) self); - self = NULL; - } else { - self->cx = JS_NewContext(self->rt, 8192); - if (!self->cx) { - PyErr_SetString(PYM_error, "JS_NewContext() failed"); - type->tp_dealloc((PyObject *) self); - self = NULL; - } - } - } - } - - return (PyObject *) self; -} - -static void -PYM_JSRuntimeDealloc(PYM_JSRuntimeObject *self) -{ - if (self->objects.ops) { - JS_DHashTableFinish(&self->objects); - self->objects.ops = NULL; - } - - if (self->cx) { - // Note that this will also force GC of any remaining objects - // in the runtime. - JS_DestroyContext(self->cx); - self->cx = NULL; - } - - if (self->rt) { - JS_DestroyRuntime(self->rt); - self->rt = NULL; - } - - self->ob_type->tp_free((PyObject *) self); -} - -static PyObject * -PYM_newContext(PYM_JSRuntimeObject *self, PyObject *args) -{ - PYM_SANITY_CHECK(self); - JSContext *cx = JS_NewContext(self->rt, 8192); - if (cx == NULL) { - PyErr_SetString(PYM_error, "JS_NewContext() failed"); - return NULL; - } - - JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_DONT_REPORT_UNCAUGHT | - JSOPTION_ATLINE | JSOPTION_STRICT); - JS_SetVersion(cx, JSVERSION_LATEST); - - PyObject *retval = (PyObject *) PYM_newJSContextObject(self, cx); - - if (retval == NULL) - JS_DestroyContext(cx); - - return retval; -} - -static PyMethodDef PYM_JSRuntimeMethods[] = { - {"new_context", (PyCFunction) PYM_newContext, METH_VARARGS, - "Create a new JavaScript context."}, - {NULL, NULL, 0, NULL} -}; - -PyTypeObject PYM_JSRuntimeType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pymonkey.Runtime", /*tp_name*/ - sizeof(PYM_JSRuntimeObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - /*tp_dealloc*/ - (destructor) PYM_JSRuntimeDealloc, - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - 0, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - 0, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - 0, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - /* tp_doc */ - "JavaScript Runtime.", - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - PYM_JSRuntimeMethods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - PYM_JSRuntimeNew, /* tp_new */ -}; diff -r 06269ca0b36c -r ac8ca0ee7760 runtime.h --- a/runtime.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_RUNTIME_H -#define PYM_RUNTIME_H - -#include -#include -#include -#include - -#define PYM_SANITY_CHECK(runtime) \ - if (PyThread_get_thread_ident() != runtime->thread) { \ - PyErr_SetString(PYM_error, "Function called from wrong thread"); \ - return NULL; \ - } - -#define PYM_ENSURE_RUNTIME_MATCH(runtime1, runtime2) \ - if (runtime1 != runtime2) { \ - PyErr_SetString(PyExc_ValueError, "JS runtime mismatch"); \ - return NULL; \ - } - -typedef struct { - PyObject_HEAD - JSRuntime *rt; - JSContext *cx; - JSDHashTable objects; - long thread; -} PYM_JSRuntimeObject; - -extern PyTypeObject PYM_JSRuntimeType; - -#endif diff -r 06269ca0b36c -r ac8ca0ee7760 setup.py --- a/setup.py Mon Aug 17 03:52:51 2009 -0700 +++ b/setup.py Mon Aug 17 22:08:33 2009 -0700 @@ -92,7 +92,8 @@ setup_options['ext_modules'] = [ distutils.core.Extension('pymonkey', - SOURCE_FILES, + [os.path.join("src", filename) + for filename in SOURCE_FILES], **ext_options) ] @@ -228,7 +229,7 @@ result = subprocess.call( [sys.executable, - "test_pymonkey.py"], + os.path.join("tests", "test_pymonkey.py")], env = new_env ) diff -r 06269ca0b36c -r ac8ca0ee7760 src/context.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/context.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,553 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "context.h" +#include "object.h" +#include "function.h" +#include "utils.h" + +// This is the default JSOperationCallback for pymonkey-owned JS contexts, +// when they've defined one in Python. +static JSBool +PYM_operationCallback(JSContext *cx) +{ + PYM_PyAutoEnsureGIL gil; + PYM_JSContextObject *context = (PYM_JSContextObject *) + JS_GetContextPrivate(cx); + + PyObject *callable = context->opCallback; + PyObject *args = PyTuple_Pack(1, (PyObject *) context); + if (args == NULL) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + PyObject *result = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + if (result == NULL) { + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + + Py_DECREF(result); + return JS_TRUE; +} + +// This is the default JSErrorReporter for pymonkey-owned JS contexts. +static void +PYM_reportError(JSContext *cx, const char *message, JSErrorReport *report) +{ + PYM_PyAutoEnsureGIL gil; + + // Convert JS warnings into Python warnings. + if (JSREPORT_IS_WARNING(report->flags)) { + if (report->filename) + PyErr_WarnExplicit(NULL, message, report->filename, report->lineno, + NULL, NULL); + else + PyErr_Warn(NULL, message); + } else + // TODO: Not sure if this will ever get called, but we should know + // if it is. + PyErr_Warn(NULL, "A JS error was reported."); +} + +static void +PYM_JSContextDealloc(PYM_JSContextObject *self) +{ + if (self->opCallback) { + Py_DECREF(self->opCallback); + self->opCallback = NULL; + } + + if (self->cx) { + JS_DestroyContext(self->cx); + self->cx = NULL; + } + + Py_DECREF(self->runtime); + self->runtime = NULL; + + self->ob_type->tp_free((PyObject *) self); +} + +static PyObject * +PYM_getRuntime(PYM_JSContextObject *self, PyObject *args) +{ + Py_INCREF(self->runtime); + return (PyObject *) self->runtime; +} + +static PyObject * +PYM_getObjectPrivate(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + + if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) + return NULL; + + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + + JSObject *obj = object->obj; + + if (JS_ObjectIsFunction(self->cx, obj)) { + jsval functionHolder; + if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { + JS_ClearPendingException(self->cx); + Py_RETURN_NONE; + } + if (!JSVAL_IS_OBJECT(functionHolder)) + Py_RETURN_NONE; + obj = JSVAL_TO_OBJECT(functionHolder); + } + + JSClass *klass = JS_GET_CLASS(self->cx, obj); + if (klass != &PYM_JS_ObjectClass) + Py_RETURN_NONE; + + PyObject *pyObject; + + if (!PYM_JS_getPrivatePyObject(self->cx, obj, &pyObject)) { + PYM_jsExceptionToPython(self); + return NULL; + } + + if (pyObject == NULL) + pyObject = Py_None; + + Py_INCREF(pyObject); + return pyObject; +} + +static PyObject * +PYM_clearObjectPrivate(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + + if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) + return NULL; + + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + + JSObject *obj = object->obj; + + if (JS_ObjectIsFunction(self->cx, obj)) { + jsval functionHolder; + if (!JS_GetReservedSlot(self->cx, obj, 0, &functionHolder)) { + JS_ClearPendingException(self->cx); + Py_RETURN_NONE; + } + if (!JSVAL_IS_OBJECT(functionHolder)) + Py_RETURN_NONE; + obj = JSVAL_TO_OBJECT(functionHolder); + } + + JSClass *klass = JS_GET_CLASS(self->cx, obj); + if (klass != &PYM_JS_ObjectClass) + Py_RETURN_NONE; + + if (!PYM_JS_setPrivatePyObject(self->cx, obj, Py_None)) { + PYM_jsExceptionToPython(self); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +PYM_newObject(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PyObject *privateObj = NULL; + + if (!PyArg_ParseTuple(args, "|O", &privateObj)) + return NULL; + + JSObject *obj = PYM_JS_newObject(self->cx, privateObj); + if (obj == NULL) { + PyErr_SetString(PYM_error, "PYM_JS_newObject() failed"); + return NULL; + } + + // If this fails, we don't need to worry about cleaning up + // obj because it'll get cleaned up at the next GC. + return (PyObject *) PYM_newJSObject(self, obj, NULL); +} + +static PyObject * +PYM_getProperty(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + char *buffer = NULL; + int size; + + if (!PyArg_ParseTuple(args, "O!es#", &PYM_JSObjectType, &object, + "utf-16", &buffer, &size)) + return NULL; + + if (self->runtime != object->runtime) { + PyMem_Free(buffer); + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + } + + jsval val; + JSBool result; + Py_BEGIN_ALLOW_THREADS; + // Note that we're manipulating buffer and size here to get rid of + // the BOM. + result = JS_GetUCProperty(self->cx, object->obj, (jschar *) (buffer + 2), + (size / 2) - 1, &val); + Py_END_ALLOW_THREADS; + + PyMem_Free(buffer); + + if (!result) { + PYM_jsExceptionToPython(self); + return NULL; + } + + return PYM_jsvalToPyObject(self, val); +} + +static PyObject * +PYM_gc(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + JS_GC(self->cx); + Py_RETURN_NONE; +} + +static PyObject * +PYM_initStandardClasses(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + + if (!PyArg_ParseTuple(args, "O!", &PYM_JSObjectType, &object)) + return NULL; + + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + + if (!JS_InitStandardClasses(self->cx, object->obj)) { + PyErr_SetString(PYM_error, "JS_InitStandardClasses() failed"); + return NULL; + } + + Py_RETURN_NONE; +} + +static PyObject * +PYM_evaluateScript(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + const char *source; + int sourceLen; + const char *filename; + int lineNo; + + if (!PyArg_ParseTuple(args, "O!s#si", &PYM_JSObjectType, &object, + &source, &sourceLen, &filename, &lineNo)) + return NULL; + + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + + jsval rval; + JSBool result; + Py_BEGIN_ALLOW_THREADS; + result = JS_EvaluateScript(self->cx, object->obj, source, sourceLen, + filename, lineNo, &rval); + Py_END_ALLOW_THREADS; + + if (!result) { + PYM_jsExceptionToPython(self); + return NULL; + } + + PyObject *pyRval = PYM_jsvalToPyObject(self, rval); + return pyRval; +} + +static PyObject * +PYM_defineProperty(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *object; + PyObject *value; + char *name = NULL; + int namelen; + + if (!PyArg_ParseTuple(args, "O!es#O", &PYM_JSObjectType, &object, + "utf-16", &name, &namelen, &value)) + return NULL; + + if (self->runtime != object->runtime) { + PyMem_Free(name); + PYM_ENSURE_RUNTIME_MATCH(self->runtime, object->runtime); + } + + jsval jsValue; + + if (PYM_pyObjectToJsval(self, value, &jsValue) == -1) { + PyMem_Free(name); + return NULL; + } + + // Note that we're manipulating buffer and size here to get rid of + // the BOM. + if (!JS_DefineUCProperty(self->cx, object->obj, (jschar *) (name + 2), + (namelen / 2) - 1, jsValue, NULL, NULL, + JSPROP_ENUMERATE)) { + // TODO: There's probably an exception pending on self->cx, + // what should we do about it? + PyMem_Free(name); + PyErr_SetString(PYM_error, "JS_DefineProperty() failed"); + return NULL; + } + + PyMem_Free(name); + Py_RETURN_NONE; +} + +static PyObject * +PYM_callFunction(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PYM_JSObject *obj; + PYM_JSFunction *fun; + PyObject *funcArgs; + + if (!PyArg_ParseTuple(args, "O!O!O!", &PYM_JSObjectType, &obj, + &PYM_JSFunctionType, &fun, + &PyTuple_Type, &funcArgs)) + return NULL; + + PYM_ENSURE_RUNTIME_MATCH(self->runtime, obj->runtime); + PYM_ENSURE_RUNTIME_MATCH(self->runtime, fun->base.runtime); + + uintN argc = PyTuple_Size(funcArgs); + + jsval *argv = (jsval *) PyMem_Malloc(sizeof(jsval) * argc); + if (argv == NULL) + return PyErr_NoMemory(); + + jsval *currArg = argv; + + for (unsigned int i = 0; i < argc; i++) { + PyObject *item = PyTuple_GET_ITEM(funcArgs, i); + if (item == NULL || + PYM_pyObjectToJsval(self, item, currArg) == -1) { + PyMem_Free(argv); + return NULL; + } + currArg++; + } + + jsval rval; + + // TODO: This assumes that a JSFunction * is actually a subclass of + // a JSObject *, which may or may not be regarded as an implementation + // detail. + JSBool result; + Py_BEGIN_ALLOW_THREADS; + result = JS_CallFunction(self->cx, obj->obj, + (JSFunction *) fun->base.obj, + argc, argv, &rval); + Py_END_ALLOW_THREADS; + + PyMem_Free(argv); + + if (!result) { + PYM_jsExceptionToPython(self); + return NULL; + } + + return PYM_jsvalToPyObject(self, rval); +} + +static PyObject * +PYM_newFunction(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PyObject *callable; + const char *name; + + if (!PyArg_ParseTuple(args, "Os", &callable, &name)) + return NULL; + + return (PyObject *) PYM_newJSFunctionFromCallable(self, callable, name); +} + +static PyObject * +PYM_setOperationCallback(PYM_JSContextObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self->runtime); + PyObject *callable; + + if (!PyArg_ParseTuple(args, "O", &callable)) + return NULL; + + if (!PyCallable_Check(callable)) { + PyErr_SetString(PyExc_TypeError, "Callable must be callable"); + return NULL; + } + + JS_SetOperationCallback(self->cx, PYM_operationCallback); + + Py_INCREF(callable); + if (self->opCallback) + Py_DECREF(self->opCallback); + self->opCallback = callable; + + Py_RETURN_NONE; +} + +static PyObject * +PYM_triggerOperationCallback(PYM_JSContextObject *self, PyObject *args) +{ + JS_TriggerOperationCallback(self->cx); + Py_RETURN_NONE; +} + +static PyMethodDef PYM_JSContextMethods[] = { + {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, + "Get the JavaScript runtime associated with this context."}, + {"new_object", (PyCFunction) PYM_newObject, METH_VARARGS, + "Create a new JavaScript object."}, + {"init_standard_classes", + (PyCFunction) PYM_initStandardClasses, METH_VARARGS, + "Add standard classes and functions to the given object."}, + {"evaluate_script", + (PyCFunction) PYM_evaluateScript, METH_VARARGS, + "Evaluate the given JavaScript code in the context of the given " + "global object, using the given filename" + "and line number information."}, + {"call_function", + (PyCFunction) PYM_callFunction, METH_VARARGS, + "Calls a JS function."}, + {"new_function", + (PyCFunction) PYM_newFunction, METH_VARARGS, + "Creates a new function callable from JS."}, + {"define_property", + (PyCFunction) PYM_defineProperty, METH_VARARGS, + "Defines a property on an object."}, + {"get_property", (PyCFunction) PYM_getProperty, METH_VARARGS, + "Gets the given property for the given JavaScript object."}, + {"gc", (PyCFunction) PYM_gc, METH_VARARGS, + "Performs garbage collection on the context's runtime."}, + {"set_operation_callback", (PyCFunction) PYM_setOperationCallback, + METH_VARARGS, + "Sets the operation callback for the context."}, + {"trigger_operation_callback", (PyCFunction) PYM_triggerOperationCallback, + METH_VARARGS, + "Triggers the operation callback for the context."}, + {"get_object_private", (PyCFunction) PYM_getObjectPrivate, METH_VARARGS, + "Returns the private Python object stored in the JavaScript object."}, + {"clear_object_private", (PyCFunction) PYM_clearObjectPrivate, METH_VARARGS, + "Clears any private Python object stored in the JavaScript object."}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject PYM_JSContextType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pymonkey.Context", /*tp_name*/ + sizeof(PYM_JSContextObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /*tp_dealloc*/ + (destructor) PYM_JSContextDealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + /* tp_doc */ + "JavaScript Context.", + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYM_JSContextMethods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +extern PYM_JSContextObject * +PYM_newJSContextObject(PYM_JSRuntimeObject *runtime, JSContext *cx) +{ + PYM_JSContextObject *context = PyObject_New(PYM_JSContextObject, + &PYM_JSContextType); + if (context == NULL) + return NULL; + + context->opCallback = NULL; + context->runtime = runtime; + Py_INCREF(runtime); + + context->cx = cx; + JS_SetContextPrivate(cx, context); + JS_SetErrorReporter(cx, PYM_reportError); + +#ifdef JS_GC_ZEAL + // TODO: Consider exposing JS_SetGCZeal() to Python instead of + // hard-coding it here. + JS_SetGCZeal(cx, 2); +#endif + + return context; +} diff -r 06269ca0b36c -r ac8ca0ee7760 src/context.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/context.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,58 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_CONTEXT_H +#define PYM_CONTEXT_H + +#include "runtime.h" + +#include +#include + +typedef struct { + PyObject_HEAD + PYM_JSRuntimeObject *runtime; + JSContext *cx; + PyObject *opCallback; +} PYM_JSContextObject; + +extern PyTypeObject PYM_JSContextType; + +extern PYM_JSContextObject * +PYM_newJSContextObject(PYM_JSRuntimeObject *runtime, + JSContext *cx); + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 src/function.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/function.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,224 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "function.h" +#include "utils.h" + +static void +PYM_JSFunctionDealloc(PYM_JSFunction *self) +{ + PYM_JSObjectType.tp_dealloc((PyObject *) self); +} + +static JSBool +PYM_dispatchJSFunctionToPython(JSContext *cx, + JSObject *obj, + uintN argc, + jsval *argv, + jsval *rval) +{ + PYM_PyAutoEnsureGIL gil; + jsval callee = JS_ARGV_CALLEE(argv); + jsval functionHolder; + if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(callee), 0, &functionHolder)) + return JS_FALSE; + + PyObject *callable; + if (!PYM_JS_getPrivatePyObject(cx, JSVAL_TO_OBJECT(functionHolder), + &callable)) + return JS_FALSE; + + if (callable == Py_None) { + JS_ReportError(cx, "Wrapped Python function no longer exists"); + return JS_FALSE; + } + + PYM_JSContextObject *context = (PYM_JSContextObject *) + JS_GetContextPrivate(cx); + + jsval thisArg = OBJECT_TO_JSVAL(obj); + PyObject *pyThisArg = PYM_jsvalToPyObject(context, thisArg); + if (pyThisArg == NULL) { + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + + PyObject *funcArgs = PyTuple_New(argc); + if (funcArgs == NULL) { + Py_DECREF(pyThisArg); + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + for (unsigned int i = 0; i < argc; i++) { + PyObject *arg = PYM_jsvalToPyObject(context, argv[i]); + if (arg == NULL || PyTuple_SetItem(funcArgs, i, arg)) { + if (arg) + Py_DECREF(arg); + Py_DECREF(funcArgs); + Py_DECREF(pyThisArg); + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + } + + PyObject *args = PyTuple_Pack(3, + (PyObject *) context, + pyThisArg, + funcArgs); + Py_DECREF(pyThisArg); + Py_DECREF(funcArgs); + if (args == NULL) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + + PyObject *result = PyObject_Call(callable, args, NULL); + Py_DECREF(args); + if (result == NULL) { + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + + int error = PYM_pyObjectToJsval(context, result, rval); + Py_DECREF(result); + + if (error) { + PYM_pythonExceptionToJs(context); + return JS_FALSE; + } + + return JS_TRUE; +} + +PyTypeObject PYM_JSFunctionType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pymonkey.Function", /*tp_name*/ + sizeof(PYM_JSFunction), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /*tp_dealloc*/ + (destructor) PYM_JSFunctionDealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + /*tp_flags*/ + Py_TPFLAGS_DEFAULT, + /* tp_doc */ + "JavaScript Function.", + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + 0, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PYM_JSFunction * +PYM_newJSFunctionFromCallable(PYM_JSContextObject *context, + PyObject *callable, + const char *name) +{ + if (!PyCallable_Check(callable)) { + PyErr_SetString(PyExc_TypeError, "Callable must be callable"); + return NULL; + } + + JSFunction *func = JS_NewFunction(context->cx, + PYM_dispatchJSFunctionToPython, 0, + 0, NULL, name); + + if (func == NULL) { + PyErr_SetString(PYM_error, "JS_DefineFunction() failed"); + return NULL; + } + + JSObject *funcObj = JS_GetFunctionObject(func); + + if (funcObj == NULL) { + PyErr_SetString(PYM_error, "JS_GetFunctionObject() failed"); + return NULL; + } + + PYM_JSFunction *object = PyObject_New(PYM_JSFunction, + &PYM_JSFunctionType); + if (object == NULL) + return NULL; + + if (PYM_newJSObject(context, funcObj, + (PYM_JSObject *) object) == NULL) + // Note that our object's reference count will have already + // been decremented by PYM_newJSObject(). + return NULL; + + JSObject *functionHolder = PYM_JS_newObject(context->cx, callable); + if (functionHolder == NULL) { + Py_DECREF((PyObject *) object); + PyErr_SetString(PYM_error, "PYM_JS_newObject() failed"); + return NULL; + } + + if (!JS_SetReservedSlot(context->cx, funcObj, 0, + OBJECT_TO_JSVAL(functionHolder))) { + Py_DECREF((PyObject *) object); + PyErr_SetString(PYM_error, "JS_SetReservedSlot() failed"); + return NULL; + } + + return object; +} diff -r 06269ca0b36c -r ac8ca0ee7760 src/function.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/function.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,57 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_FUNCTION_H +#define PYM_FUNCTION_H + +#include "object.h" +#include "context.h" + +#include +#include + +typedef struct { + PYM_JSObject base; +} PYM_JSFunction; + +extern PyTypeObject PYM_JSFunctionType; + +extern PYM_JSFunction * +PYM_newJSFunctionFromCallable(PYM_JSContextObject *context, + PyObject *callable, + const char *name); + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 src/object.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,238 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "object.h" +#include "function.h" +#include "runtime.h" +#include "utils.h" + +JSObject * +PYM_JS_newObject(JSContext *cx, PyObject *pyObject) +{ + JSObject *obj = JS_NewObject(cx, &PYM_JS_ObjectClass, NULL, NULL); + if (obj) { + if (!JS_SetPrivate(cx, obj, pyObject)) + return NULL; + Py_XINCREF(pyObject); + } + return obj; +} + +JSBool +PYM_JS_setPrivatePyObject(JSContext *cx, JSObject *obj, PyObject *pyObject) +{ + PyObject *old; + if (!PYM_JS_getPrivatePyObject(cx, obj, &old)) + return JS_FALSE; + if (!JS_SetPrivate(cx, obj, pyObject)) + return JS_FALSE; + Py_INCREF(pyObject); + Py_XDECREF(old); + return JS_TRUE; +} + +JSBool +PYM_JS_getPrivatePyObject(JSContext *cx, JSObject *obj, PyObject **pyObject) +{ + JSClass *klass = JS_GET_CLASS(cx, obj); + if (klass != &PYM_JS_ObjectClass) { + JS_ReportError(cx, "Object is not an instance of PymonkeyObject"); + return JS_FALSE; + } + + *pyObject = (PyObject *) JS_GetPrivate(cx, obj); + return JS_TRUE; +} + +static void +PYM_JS_finalizeObject(JSContext *cx, JSObject *obj) +{ + PYM_PyAutoEnsureGIL gil; + PyObject *pyObject; + // TODO: What if this fails? + if (PYM_JS_getPrivatePyObject(cx, obj, &pyObject)) + Py_XDECREF(pyObject); +} + +// This one-size-fits-all JSClass is used for any JS objects created +// in Python. It can hold a reference to a Python object for as long as +// its parent JS object is accessible from JS-land. As soon as it's +// garbage collected by the JS interpreter, it releases its reference on +// the Python object. +JSClass PYM_JS_ObjectClass = { + "PymonkeyObject", JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PYM_JS_finalizeObject, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +static void +PYM_JSObjectDealloc(PYM_JSObject *self) +{ + if (self->obj) { + JS_DHashTableOperate(&self->runtime->objects, + (void *) self->obj, + JS_DHASH_REMOVE); + + // JS_RemoveRoot() always returns JS_TRUE, so don't + // bother checking its return value. + JS_RemoveRootRT(self->runtime->rt, &self->obj); + self->obj = NULL; + } + + if (self->runtime) { + Py_DECREF(self->runtime); + self->runtime = NULL; + } + + self->ob_type->tp_free((PyObject *) self); +} + +static PyObject * +PYM_getRuntime(PYM_JSObject *self, PyObject *args) +{ + Py_INCREF(self->runtime); + return (PyObject *) self->runtime; +} + +static PyMethodDef PYM_JSObjectMethods[] = { + {"get_runtime", (PyCFunction) PYM_getRuntime, METH_VARARGS, + "Get the JavaScript runtime associated with this object."}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject PYM_JSObjectType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pymonkey.Object", /*tp_name*/ + sizeof(PYM_JSObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /*tp_dealloc*/ + (destructor) PYM_JSObjectDealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + /*tp_flags*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + /* tp_doc */ + "JavaScript Object.", + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYM_JSObjectMethods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + 0, /* tp_new */ +}; + +PYM_JSObject *PYM_newJSObject(PYM_JSContextObject *context, + JSObject *obj, + PYM_JSObject *subclass) { + PYM_JSRuntimeObject *runtime = context->runtime; + PYM_HashEntry *cached = (PYM_HashEntry *) JS_DHashTableOperate( + &runtime->objects, + (void *) obj, + JS_DHASH_LOOKUP + ); + + if (JS_DHASH_ENTRY_IS_BUSY((JSDHashEntryHdr *) cached)) { + Py_INCREF((PyObject *) cached->value); + return (PYM_JSObject *) cached->value; + } + + PYM_JSObject *object; + + if (subclass) + object = subclass; + else { + if (JS_ObjectIsFunction(context->cx, obj)) { + PYM_JSFunction *func = PyObject_New(PYM_JSFunction, + &PYM_JSFunctionType); + object = (PYM_JSObject *) func; + } else + object = PyObject_New(PYM_JSObject, + &PYM_JSObjectType); + } + + if (object == NULL) + return NULL; + + object->runtime = NULL; + object->obj = NULL; + + cached = (PYM_HashEntry *) JS_DHashTableOperate(&runtime->objects, + (void *) obj, + JS_DHASH_ADD); + if (cached == NULL) { + Py_DECREF(object); + PyErr_SetString(PYM_error, "JS_DHashTableOperate() failed"); + return NULL; + } + + cached->base.key = (void *) obj; + cached->value = object; + + object->runtime = context->runtime; + Py_INCREF(object->runtime); + + object->obj = obj; + + JS_AddNamedRootRT(object->runtime->rt, &object->obj, + "Pymonkey-Generated Object"); + + return object; +} diff -r 06269ca0b36c -r ac8ca0ee7760 src/object.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/object.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,68 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_OBJECT_H +#define PYM_OBJECT_H + +#include "context.h" + +#include +#include + +extern JSObject * +PYM_JS_newObject(JSContext *cx, PyObject *pyObject); + +extern JSBool +PYM_JS_setPrivatePyObject(JSContext *cx, JSObject *obj, PyObject *pyObject); + +extern JSBool +PYM_JS_getPrivatePyObject(JSContext *cx, JSObject *obj, PyObject **pyObject); + +extern JSClass PYM_JS_ObjectClass; + +typedef struct { + PyObject_HEAD + PYM_JSRuntimeObject *runtime; + JSObject *obj; +} PYM_JSObject; + +extern PyTypeObject PYM_JSObjectType; + +extern PYM_JSObject * +PYM_newJSObject(PYM_JSContextObject *context, JSObject *obj, + PYM_JSObject *subclass); + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 src/pymonkey.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pymonkey.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,94 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "undefined.h" +#include "runtime.h" +#include "context.h" +#include "object.h" +#include "function.h" +#include "utils.h" + +static PyMethodDef PYM_methods[] = { + {NULL, NULL, 0, NULL} +}; + +PyMODINIT_FUNC +initpymonkey(void) +{ + PyObject *module; + + module = Py_InitModule("pymonkey", PYM_methods); + if (module == NULL) + return; + + if (PyType_Ready(&PYM_undefinedType) < 0) + return; + + PYM_undefined = PyObject_New(PYM_undefinedObject, &PYM_undefinedType); + if (PYM_undefined == NULL) + return; + Py_INCREF(PYM_undefined); + PyModule_AddObject(module, "undefined", (PyObject *) PYM_undefined); + + PYM_error = PyErr_NewException("pymonkey.error", NULL, NULL); + Py_INCREF(PYM_error); + PyModule_AddObject(module, "error", PYM_error); + + if (!PyType_Ready(&PYM_JSRuntimeType) < 0) + return; + + Py_INCREF(&PYM_JSRuntimeType); + PyModule_AddObject(module, "Runtime", (PyObject *) &PYM_JSRuntimeType); + + if (!PyType_Ready(&PYM_JSContextType) < 0) + return; + + Py_INCREF(&PYM_JSContextType); + PyModule_AddObject(module, "Context", (PyObject *) &PYM_JSContextType); + + if (!PyType_Ready(&PYM_JSObjectType) < 0) + return; + + Py_INCREF(&PYM_JSObjectType); + PyModule_AddObject(module, "Object", (PyObject *) &PYM_JSObjectType); + + PYM_JSFunctionType.tp_base = &PYM_JSObjectType; + if (!PyType_Ready(&PYM_JSFunctionType) < 0) + return; + + Py_INCREF(&PYM_JSFunctionType); + PyModule_AddObject(module, "Function", (PyObject *) &PYM_JSFunctionType); +} diff -r 06269ca0b36c -r ac8ca0ee7760 src/runtime.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/runtime.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,177 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "runtime.h" +#include "context.h" +#include "utils.h" + +static PyObject * +PYM_JSRuntimeNew(PyTypeObject *type, PyObject *args, + PyObject *kwds) +{ + PYM_JSRuntimeObject *self; + + self = (PYM_JSRuntimeObject *) type->tp_alloc(type, 0); + if (self != NULL) { + self->thread = PyThread_get_thread_ident(); + self->rt = NULL; + self->cx = NULL; + self->objects.ops = NULL; + + if (!JS_DHashTableInit(&self->objects, + JS_DHashGetStubOps(), + NULL, + sizeof(PYM_HashEntry), + JS_DHASH_DEFAULT_CAPACITY(100))) { + PyErr_SetString(PYM_error, "JS_DHashTableInit() failed"); + type->tp_dealloc((PyObject *) self); + self = NULL; + } + + if (self != NULL) { + self->rt = JS_NewRuntime(8L * 1024L * 1024L); + if (!self->rt) { + PyErr_SetString(PYM_error, "JS_NewRuntime() failed"); + type->tp_dealloc((PyObject *) self); + self = NULL; + } else { + self->cx = JS_NewContext(self->rt, 8192); + if (!self->cx) { + PyErr_SetString(PYM_error, "JS_NewContext() failed"); + type->tp_dealloc((PyObject *) self); + self = NULL; + } + } + } + } + + return (PyObject *) self; +} + +static void +PYM_JSRuntimeDealloc(PYM_JSRuntimeObject *self) +{ + if (self->objects.ops) { + JS_DHashTableFinish(&self->objects); + self->objects.ops = NULL; + } + + if (self->cx) { + // Note that this will also force GC of any remaining objects + // in the runtime. + JS_DestroyContext(self->cx); + self->cx = NULL; + } + + if (self->rt) { + JS_DestroyRuntime(self->rt); + self->rt = NULL; + } + + self->ob_type->tp_free((PyObject *) self); +} + +static PyObject * +PYM_newContext(PYM_JSRuntimeObject *self, PyObject *args) +{ + PYM_SANITY_CHECK(self); + JSContext *cx = JS_NewContext(self->rt, 8192); + if (cx == NULL) { + PyErr_SetString(PYM_error, "JS_NewContext() failed"); + return NULL; + } + + JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_DONT_REPORT_UNCAUGHT | + JSOPTION_ATLINE | JSOPTION_STRICT); + JS_SetVersion(cx, JSVERSION_LATEST); + + PyObject *retval = (PyObject *) PYM_newJSContextObject(self, cx); + + if (retval == NULL) + JS_DestroyContext(cx); + + return retval; +} + +static PyMethodDef PYM_JSRuntimeMethods[] = { + {"new_context", (PyCFunction) PYM_newContext, METH_VARARGS, + "Create a new JavaScript context."}, + {NULL, NULL, 0, NULL} +}; + +PyTypeObject PYM_JSRuntimeType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pymonkey.Runtime", /*tp_name*/ + sizeof(PYM_JSRuntimeObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /*tp_dealloc*/ + (destructor) PYM_JSRuntimeDealloc, + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + /* tp_doc */ + "JavaScript Runtime.", + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + PYM_JSRuntimeMethods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + PYM_JSRuntimeNew, /* tp_new */ +}; diff -r 06269ca0b36c -r ac8ca0ee7760 src/runtime.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/runtime.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,67 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_RUNTIME_H +#define PYM_RUNTIME_H + +#include +#include +#include +#include + +#define PYM_SANITY_CHECK(runtime) \ + if (PyThread_get_thread_ident() != runtime->thread) { \ + PyErr_SetString(PYM_error, "Function called from wrong thread"); \ + return NULL; \ + } + +#define PYM_ENSURE_RUNTIME_MATCH(runtime1, runtime2) \ + if (runtime1 != runtime2) { \ + PyErr_SetString(PyExc_ValueError, "JS runtime mismatch"); \ + return NULL; \ + } + +typedef struct { + PyObject_HEAD + JSRuntime *rt; + JSContext *cx; + JSDHashTable objects; + long thread; +} PYM_JSRuntimeObject; + +extern PyTypeObject PYM_JSRuntimeType; + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 src/undefined.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/undefined.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,82 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "undefined.h" + +static Py_ssize_t PYM_undefinedLength(PyObject *o) { + return 0; +}; + +static PyMappingMethods PYM_undefinedAsMapping = { + PYM_undefinedLength, /*mp_length*/ + 0, /*mp_subscript*/ + 0 /*mp_ass_subscript*/ +}; + +static PyObject *PYM_undefinedRepr(PyObject *o) { + return PyString_FromString("pymonkey.undefined"); +} + +// TODO: We should make this behave as much like JavaScript's +// "undefined" value as possible; e.g., its string value should +// be "undefined", the singleton should be falsy, etc. +PyTypeObject PYM_undefinedType = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "pymonkey.undefined", /*tp_name*/ + sizeof(PYM_undefinedObject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + 0, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + PYM_undefinedRepr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + &PYM_undefinedAsMapping, /*tp_as_mapping*/ + 0, /*tp_hash */ + 0, /*tp_call*/ + PYM_undefinedRepr, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + /* tp_doc */ + "Pythonic equivalent of JavaScript's 'undefined' value", +}; + +PYM_undefinedObject *PYM_undefined; diff -r 06269ca0b36c -r ac8ca0ee7760 src/undefined.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/undefined.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,53 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_UNDEFINED_H +#define PYM_UNDEFINED_H + +#include + +#define Py_RETURN_UNDEFINED { Py_INCREF(PYM_undefined); \ + return (PyObject *) PYM_undefined; } + +typedef struct { + PyObject_HEAD +} PYM_undefinedObject; + +extern PyTypeObject PYM_undefinedType; + +extern PYM_undefinedObject *PYM_undefined; + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 src/utils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/utils.cpp Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,257 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "utils.h" +#include "undefined.h" +#include "object.h" + +PyObject *PYM_error; + +static int +PYM_doubleToJsval(PYM_JSContextObject *context, + double number, + jsval *rval) +{ + jsdouble *numberAsJsdouble = JS_NewDouble(context->cx, number); + if (numberAsJsdouble == NULL) { + PyErr_SetString(PYM_error, "JS_NewDouble() failed"); + return -1; + } + *rval = DOUBLE_TO_JSVAL(numberAsJsdouble); + return 0; +} + +int +PYM_pyObjectToJsval(PYM_JSContextObject *context, + PyObject *object, + jsval *rval) +{ + if (PyString_Check(object) || PyUnicode_Check(object)) { + PyObject *unicode; + if (PyString_Check(object)) { + unicode = PyUnicode_FromObject(object); + if (unicode == NULL) + return -1; + } else { + unicode = object; + Py_INCREF(unicode); + } + + PyObject *string = PyUnicode_AsUTF16String(unicode); + Py_DECREF(unicode); + if (string == NULL) + return -1; + + char *buffer = PyString_AS_STRING(string); + Py_ssize_t size = PyString_GET_SIZE(string); + + // Note that we're manipulating buffer and size here to get rid of + // the BOM. + JSString *jsString = JS_NewUCStringCopyN(context->cx, + (const jschar *) (buffer + 2), + (size / 2) - 1); + Py_DECREF(string); + if (jsString == NULL) { + PyErr_SetString(PYM_error, "JS_NewUCStringCopyN() failed"); + return -1; + } + + *rval = STRING_TO_JSVAL(jsString); + return 0; + } + + if (PyInt_Check(object)) { + long number = PyInt_AS_LONG(object); + if (INT_FITS_IN_JSVAL(number)) { + *rval = INT_TO_JSVAL(number); + return 0; + } else + return PYM_doubleToJsval(context, number, rval); + } + + if (PyFloat_Check(object)) + return PYM_doubleToJsval(context, PyFloat_AS_DOUBLE(object), rval); + + if (PyObject_TypeCheck(object, &PYM_JSObjectType)) { + PYM_JSObject *jsObject = (PYM_JSObject *) object; + JSRuntime *rt = JS_GetRuntime(context->cx); + if (rt != jsObject->runtime->rt) { + PyErr_SetString(PyExc_ValueError, + "JS object and JS context are from different " + "JS runtimes"); + return -1; + } + *rval = OBJECT_TO_JSVAL(jsObject->obj); + return 0; + } + + if (object == Py_True) { + *rval = JSVAL_TRUE; + return 0; + } + + if (object == Py_False) { + *rval = JSVAL_FALSE; + return 0; + } + + if (object == Py_None) { + *rval = JSVAL_NULL; + return 0; + } + + // TODO: Support more types. + PyErr_SetString(PyExc_NotImplementedError, + "Data type conversion not implemented."); + return -1; +} + +PyObject * +PYM_jsvalToPyObject(PYM_JSContextObject *context, + jsval value) { + if (JSVAL_IS_INT(value)) + return PyInt_FromLong(JSVAL_TO_INT(value)); + + if (JSVAL_IS_DOUBLE(value)) { + jsdouble *doubleRef = JSVAL_TO_DOUBLE(value); + return PyFloat_FromDouble(*doubleRef); + } + + if (value == JSVAL_FALSE) + Py_RETURN_FALSE; + + if (value == JSVAL_TRUE) + Py_RETURN_TRUE; + + if (JSVAL_IS_NULL(value)) + Py_RETURN_NONE; + + if (JSVAL_IS_VOID(value)) + Py_RETURN_UNDEFINED; + + if (JSVAL_IS_STRING(value)) { + // Strings in JS are funky: think of them as 16-bit versions of + // Python 2.x's 'str' type. Whether or not they're valid UTF-16 + // is entirely up to the client code. + + // TODO: Instead of ignoring errors, consider actually treating + // the string as a raw character buffer. + JSString *str = JSVAL_TO_STRING(value); + const char *chars = (const char *) JS_GetStringChars(str); + size_t length = JS_GetStringLength(str); + + // We're multiplying length by two since Python wants the number + // of bytes, not the number of 16-bit characters. + return PyUnicode_DecodeUTF16(chars, length * 2, "ignore", NULL); + } + + if (JSVAL_IS_OBJECT(value)) + return (PyObject *) PYM_newJSObject(context, JSVAL_TO_OBJECT(value), + NULL); + + // TODO: Support more types. + PyErr_SetString(PyExc_NotImplementedError, + "Data type conversion not implemented."); + return NULL; +} + +void +PYM_pythonExceptionToJs(PYM_JSContextObject *context) +{ + PyObject *type; + PyObject *value; + PyObject *traceback; + + PyErr_Fetch(&type, &value, &traceback); + + if (type == PYM_error && value && + PyObject_HasAttrString(value, "message")) { + jsval val; + PyObject *message = PyObject_GetAttrString(value, "message"); + if (message && PYM_pyObjectToJsval(context, message, &val) == 0) { + JS_SetPendingException(context->cx, val); + } else + JS_ReportError(context->cx, + "Python exception occurred, but exception " + "couldn't be converted"); + Py_XDECREF(message); + } else { + if (value) { + JSObject *exception = PYM_JS_newObject(context->cx, value); + if (exception) + JS_SetPendingException(context->cx, OBJECT_TO_JSVAL(exception)); + else + JS_ReportOutOfMemory(context->cx); + } else + JS_ReportError(context->cx, "Python exception occurred"); + } + + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); +} + +void +PYM_jsExceptionToPython(PYM_JSContextObject *context) +{ + if (!JS_IsExceptionPending(context->cx) && + PyErr_Occurred()) + return; + + jsval val; + if (JS_GetPendingException(context->cx, &val)) { + PyObject *obj = PYM_jsvalToPyObject(context, val); + if (obj) { + PyErr_SetObject(PYM_error, obj); + Py_DECREF(obj); + } else { + PyErr_Clear(); + + JSString *str = NULL; + + Py_BEGIN_ALLOW_THREADS; + str = JS_ValueToString(context->cx, val); + Py_END_ALLOW_THREADS; + + if (str != NULL) { + const char *chars = JS_GetStringBytes(str); + PyErr_SetString(PYM_error, chars); + } else + PyErr_SetString(PYM_error, "JS exception occurred"); + } + } else + PyErr_SetString(PYM_error, "JS_GetPendingException() failed"); +} diff -r 06269ca0b36c -r ac8ca0ee7760 src/utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/utils.h Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,81 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Pymonkey. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef PYM_UTILS_H +#define PYM_UTILS_H + +#include "context.h" + +#include +#include +#include + +class PYM_PyAutoEnsureGIL { +public: + PYM_PyAutoEnsureGIL() { + state = PyGILState_Ensure(); + } + + ~PYM_PyAutoEnsureGIL() { + PyGILState_Release(state); + } + +protected: + PyGILState_STATE state; +}; + +typedef struct { + JSDHashEntryStub base; + void *value; +} PYM_HashEntry; + +extern PyObject *PYM_error; + +extern int +PYM_pyObjectToJsval(PYM_JSContextObject *context, + PyObject *object, + jsval *rval); + +extern PyObject * +PYM_jsvalToPyObject(PYM_JSContextObject *context, jsval value); + +extern void +PYM_pythonExceptionToJs(PYM_JSContextObject *context); + +void +PYM_jsExceptionToPython(PYM_JSContextObject *context); + +#endif diff -r 06269ca0b36c -r ac8ca0ee7760 test_pymonkey.py --- a/test_pymonkey.py Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,575 +0,0 @@ -import sys -import unittest -import weakref -import time -import threading - -import pymonkey - -class PymonkeyTests(unittest.TestCase): - def _evaljs(self, code): - rt = pymonkey.Runtime() - cx = rt.new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - return cx.evaluate_script(obj, code, '', 1) - - def _evalJsWrappedPyFunc(self, func, code): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - return cx.evaluate_script(obj, code, '', 1) - - def assertRaises(self, exctype, func, *args): - was_raised = False - try: - func(*args) - except exctype, e: - self.last_exception = e - was_raised = True - self.assertTrue(was_raised) - - def testThreadSafetyExceptionIsRaised(self): - stuff = {} - def make_runtime(): - stuff['rt'] = pymonkey.Runtime() - thread = threading.Thread(target = make_runtime) - thread.start() - thread.join() - self.assertRaises(pymonkey.error, - stuff['rt'].new_context) - self.assertEqual(self.last_exception.args[0], - 'Function called from wrong thread') - - def testClearObjectPrivateWorks(self): - class Foo(object): - pass - pyobj = Foo() - cx = pymonkey.Runtime().new_context() - obj = cx.new_object(pyobj) - pyobj = weakref.ref(pyobj) - self.assertEqual(pyobj(), cx.get_object_private(obj)) - cx.clear_object_private(obj) - self.assertEqual(cx.get_object_private(obj), None) - self.assertEqual(pyobj(), None) - - def testGetObjectPrivateWorks(self): - class Foo(object): - pass - pyobj = Foo() - cx = pymonkey.Runtime().new_context() - obj = cx.new_object(pyobj) - pyobj = weakref.ref(pyobj) - self.assertEqual(pyobj(), cx.get_object_private(obj)) - del obj - del cx - self.assertEqual(pyobj(), None) - - def testOperationCallbackIsCalled(self): - def opcb(cx): - raise Exception("stop eet!") - - cx = pymonkey.Runtime().new_context() - cx.set_operation_callback(opcb) - obj = cx.new_object() - cx.init_standard_classes(obj) - - def watchdog(): - time.sleep(0.1) - cx.trigger_operation_callback() - - thread = threading.Thread(target = watchdog) - thread.start() - - self.assertRaises( - pymonkey.error, - cx.evaluate_script, - obj, 'while (1) {}', '', 1 - ) - - def testUndefinedStrIsUndefined(self): - self.assertEqual(str(pymonkey.undefined), - "pymonkey.undefined") - - def testJsWrappedPythonFuncHasPrivate(self): - def foo(cx, this, args): - pass - - cx = pymonkey.Runtime().new_context() - jsfunc = cx.new_function(foo, foo.__name__) - self.assertEqual(cx.get_object_private(jsfunc), foo) - - def testJsWrappedPythonFuncIsNotGCd(self): - def define(cx, obj): - def func(cx, this, args): - return u'func was called' - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - return weakref.ref(func) - rt = pymonkey.Runtime() - cx = rt.new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - ref = define(cx, obj) - cx.gc() - self.assertNotEqual(ref(), None) - result = cx.evaluate_script(obj, 'func()', '', 1) - self.assertEqual(result, u'func was called') - - # Now ensure that the wrapped function is GC'd when it's - # no longer reachable from JS space. - cx.define_property(obj, 'func', 0) - cx.gc() - self.assertEqual(ref(), None) - - def testCircularJsWrappedPythonFuncIsGCdIfPrivateCleared(self): - def define(cx, obj): - rt = cx.get_runtime() - def func(cx, this, args): - # Oh noes, a circular reference is born! - rt - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - return (jsfunc, weakref.ref(func)) - rt = pymonkey.Runtime() - cx = rt.new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc, ref = define(cx, obj) - - # This will break the circular reference. - cx.clear_object_private(jsfunc) - - del jsfunc - del rt - del cx - del obj - self.assertEqual(ref(), None) - - def testJsWrappedPythonFuncIsGCdAtRuntimeDestruction(self): - def define(cx, obj): - def func(cx, this, args): - return u'func was called' - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - return weakref.ref(func) - rt = pymonkey.Runtime() - cx = rt.new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - ref = define(cx, obj) - del rt - del cx - del obj - self.assertEqual(ref(), None) - - def testJsWrappedPythonFuncThrowsExcIfPrivateCleared(self): - def func(cx, this, args): - return True - - code = "func()" - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - cx.clear_object_private(jsfunc) - self.assertRaises(pymonkey.error, - cx.evaluate_script, - obj, code, '', 1) - self.assertEqual( - self._tostring(cx, self.last_exception.args[0]), - "Error: Wrapped Python function no longer exists" - ) - - def testJsWrappedPythonFuncPassesContext(self): - contexts = [] - - def func(cx, this, args): - contexts.append(cx) - return True - - code = "func()" - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - cx.evaluate_script(obj, code, '', 1) - self.assertEqual(contexts[0], cx) - - def testJsWrappedPythonFuncPassesThisArg(self): - thisObjs = [] - - def func(cx, this, args): - thisObjs.append(this) - return True - - code = "func()" - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - cx.evaluate_script(obj, code, '', 1) - self.assertEqual(thisObjs[0], obj) - - def testJsWrappedPythonFuncPassesFuncArgs(self): - funcArgs = [] - - def func(cx, this, args): - funcArgs.append(args) - return True - - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - jsfunc = cx.new_function(func, func.__name__) - cx.define_property(obj, func.__name__, jsfunc) - - cx.evaluate_script(obj, "func()", '', 1) - self.assertEqual(len(funcArgs[0]), 0) - self.assertTrue(isinstance(funcArgs[0], tuple)) - - cx.evaluate_script(obj, "func(1, 'foo')", '', 1) - self.assertEqual(len(funcArgs[1]), 2) - self.assertEqual(funcArgs[1][0], 1) - self.assertEqual(funcArgs[1][1], u'foo') - - def testJsWrappedPythonFunctionReturnsUnicodeWithEmbeddedNULs(self): - def hai2u(cx, this, args): - return args[0] + u"o hai" - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, - 'hai2u("blah\x00 ")'), - u"blah\x00 o hai") - - def testJsWrappedPythonFunctionReturnsString(self): - def hai2u(cx, this, args): - return "o hai" - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - "o hai") - - def testJsWrappedPythonFunctionReturnsUnicode(self): - def hai2u(cx, this, args): - return u"o hai\u2026" - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - u"o hai\u2026") - - def testJsWrappedPythonFunctionThrowsJsException(self): - def hai2u(cx, this, args): - raise pymonkey.error(u"blarg") - self.assertRaises(pymonkey.error, - self._evalJsWrappedPyFunc, - hai2u, 'hai2u()') - self.assertEqual(self.last_exception.args[0], u"blarg") - - def testJsWrappedPythonFunctionThrowsPyException(self): - thecx = [] - def hai2u(cx, this, args): - thecx.append(cx) - raise Exception("hello") - self.assertRaises(pymonkey.error, - self._evalJsWrappedPyFunc, - hai2u, 'hai2u()') - exc = thecx[0].get_object_private(self.last_exception.args[0]) - self.assertEqual(exc.args[0], "hello") - - def testJsWrappedPythonFunctionReturnsNone(self): - def hai2u(cx, this, args): - pass - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - None) - - def testJsWrappedPythonFunctionReturnsTrue(self): - def hai2u(cx, this, args): - return True - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - True) - - def testJsWrappedPythonFunctionReturnsFalse(self): - def hai2u(cx, this, args): - return False - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - False) - - def testJsWrappedPythonFunctionReturnsSmallInt(self): - def hai2u(cx, this, args): - return 5 - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - 5) - - def testJsWrappedPythonFunctionReturnsFloat(self): - def hai2u(cx, this, args): - return 5.1 - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - 5.1) - - def testJsWrappedPythonFunctionReturnsNegativeInt(self): - def hai2u(cx, this, args): - return -5 - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - -5) - - def testJsWrappedPythonFunctionReturnsBigInt(self): - def hai2u(cx, this, args): - return 2147483647 - self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), - 2147483647) - - def testDefinePropertyWorksWithUnicodePropertyNames(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - foo = cx.new_object() - cx.define_property(obj, u"foo\u2026", foo) - self.assertEqual( - cx.get_property(obj, u"foo\u2026"), - foo - ) - - def testDefinePropertyWorksWithObject(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - foo = cx.new_object() - cx.define_property(obj, "foo", foo) - self.assertEqual( - cx.evaluate_script(obj, 'foo', '', 1), - foo - ) - - def testDefinePropertyWorksWithString(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - foo = cx.new_object() - cx.define_property(obj, "foo", u"hello") - self.assertEqual( - cx.evaluate_script(obj, 'foo', '', 1), - u"hello" - ) - - def testObjectIsIdentityPreserving(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - cx.evaluate_script(obj, 'var foo = {bar: 1}', '', 1) - self.assertTrue(isinstance(cx.get_property(obj, u"foo"), - pymonkey.Object)) - self.assertTrue(cx.get_property(obj, u"foo") is - cx.get_property(obj, "foo")) - - def testObjectGetattrThrowsException(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - result = cx.evaluate_script(obj, '({get foo() { throw "blah"; }})', - '', 1) - self.assertRaises(pymonkey.error, - cx.get_property, - result, - u"foo") - self.assertEqual(self.last_exception.args[0], u"blah") - - def testInfiniteRecursionRaisesError(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - self.assertRaises( - pymonkey.error, - cx.evaluate_script, - obj, '(function foo() { foo(); })();', '', 1 - ) - self.assertEqual( - self._tostring(cx, self.last_exception.args[0]), - "InternalError: too much recursion" - ) - - def testObjectGetattrWorks(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - cx.evaluate_script(obj, 'var boop = 5', '', 1) - cx.evaluate_script(obj, 'this["blarg\u2026"] = 5', '', 1) - self.assertEqual(cx.get_property(obj, u"beans"), - pymonkey.undefined) - self.assertEqual(cx.get_property(obj, u"blarg\u2026"), 5) - self.assertEqual(cx.get_property(obj, u"boop"), 5) - - def testContextIsInstance(self): - cx = pymonkey.Runtime().new_context() - self.assertTrue(isinstance(cx, pymonkey.Context)) - - def testContextTypeCannotBeInstantiated(self): - self.assertRaises(TypeError, pymonkey.Context) - - def testObjectIsInstance(self): - obj = pymonkey.Runtime().new_context().new_object() - self.assertTrue(isinstance(obj, pymonkey.Object)) - self.assertFalse(isinstance(obj, pymonkey.Function)) - - def testObjectTypeCannotBeInstantiated(self): - self.assertRaises(TypeError, pymonkey.Object) - - def testFunctionIsInstance(self): - def boop(): - pass - obj = pymonkey.Runtime().new_context().new_function(boop, "boop") - self.assertTrue(isinstance(obj, pymonkey.Object)) - self.assertTrue(isinstance(obj, pymonkey.Function)) - - def testFunctionTypeCannotBeInstantiated(self): - self.assertRaises(TypeError, pymonkey.Function) - - def testObjectGetRuntimeWorks(self): - rt = pymonkey.Runtime() - obj = rt.new_context().new_object() - self.assertEqual(obj.get_runtime(), rt) - - def testContextGetRuntimeWorks(self): - rt = pymonkey.Runtime() - cx = rt.new_context() - self.assertEqual(cx.get_runtime(), rt) - - def testUndefinedCannotBeInstantiated(self): - self.assertRaises(TypeError, pymonkey.undefined) - - def testEvaluateThrowsException(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - self.assertRaises(pymonkey.error, - cx.evaluate_script, - obj, 'hai2u()', '', 1) - self.assertEqual(self._tostring(cx, - self.last_exception.args[0]), - 'ReferenceError: hai2u is not defined') - - def testEvaluateReturnsUndefined(self): - retval = self._evaljs("") - self.assertTrue(retval is pymonkey.undefined) - - def testEvaludateReturnsUnicodeWithEmbeddedNULs(self): - retval = self._evaljs("'\x00hi'") - self.assertEqual(retval, u'\x00hi') - - def testEvaluateReturnsSMPUnicode(self): - # This is 'LINEAR B SYLLABLE B008 A', in the supplementary - # multilingual plane (SMP). - retval = self._evaljs("'\uD800\uDC00'") - self.assertEqual(retval, u'\U00010000') - self.assertEqual(retval.encode('utf-16'), - '\xff\xfe\x00\xd8\x00\xdc') - - def testEvaluateReturnsBMPUnicode(self): - retval = self._evaljs("'o hai\u2026'") - self.assertTrue(type(retval) == unicode) - self.assertEqual(retval, u'o hai\u2026') - - def testEvaluateReturnsObject(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - obj = cx.evaluate_script(obj, '({boop: 1})', '', 1) - self.assertTrue(isinstance(obj, pymonkey.Object)) - self.assertEqual(cx.get_property(obj, u"boop"), 1) - - def testEvaluateReturnsFunction(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - cx.init_standard_classes(obj) - obj = cx.evaluate_script(obj, '(function boop() { return 1; })', - '', 1) - self.assertTrue(isinstance(obj, pymonkey.Function)) - - def testJsExceptionStateIsClearedAfterExceptionIsCaught(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - self.assertRaises(pymonkey.error, - cx.evaluate_script, - obj, 'blah()', '', 1) - self.assertEqual(cx.evaluate_script(obj, '5+3', '', 1), - 8) - - def testCallFunctionRaisesErrorOnBadFuncArgs(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - obj = cx.evaluate_script( - obj, - '(function boop(a, b) { return a+b+this.c; })', - '', 1 - ) - self.assertRaises( - NotImplementedError, - cx.call_function, - obj, obj, (1, self) - ) - - def _tostring(self, cx, obj): - return cx.call_function(obj, - cx.get_property(obj, u"toString"), - ()) - - def testCallFunctionRaisesErrorFromJS(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - obj = cx.evaluate_script( - obj, - '(function boop(a, b) { blarg(); })', - '', 1 - ) - self.assertRaises(pymonkey.error, - cx.call_function, - obj, obj, (1,)) - self.assertEqual(self._tostring(cx, - self.last_exception.args[0]), - 'ReferenceError: blarg is not defined') - - def testInitStandardClassesRaisesExcOnRuntimeMismatch(self): - cx2 = pymonkey.Runtime().new_context() - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - self.assertRaises(ValueError, - cx2.init_standard_classes, - obj) - self.assertEqual(self.last_exception.args[0], - 'JS runtime mismatch') - - def testCallFunctionWorks(self): - cx = pymonkey.Runtime().new_context() - obj = cx.new_object() - thisArg = cx.new_object() - cx.define_property(thisArg, "c", 3) - cx.init_standard_classes(obj) - obj = cx.evaluate_script( - obj, - '(function boop(a, b) { return a+b+this.c; })', - '', 1 - ) - self.assertEqual(cx.call_function(thisArg, obj, (1,2)), 6) - - def testEvaluateReturnsTrue(self): - self.assertTrue(self._evaljs('true') is True) - - def testEvaluateReturnsFalse(self): - self.assertTrue(self._evaljs('false') is False) - - def testEvaluateReturnsNone(self): - self.assertTrue(self._evaljs('null') is None) - - def testEvaluateReturnsIntegers(self): - self.assertEqual(self._evaljs('1+3'), 4) - - def testEvaluateReturnsNegativeIntegers(self): - self.assertEqual(self._evaljs('-5'), -5) - - def testEvaluateReturnsBigIntegers(self): - self.assertEqual(self._evaljs('2147483647*2'), - 2147483647*2) - - def testEvaluateReturnsFloats(self): - self.assertEqual(self._evaljs('1.1+3'), 4.1) - -if __name__ == '__main__': - unittest.main() diff -r 06269ca0b36c -r ac8ca0ee7760 tests/test_pymonkey.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_pymonkey.py Mon Aug 17 22:08:33 2009 -0700 @@ -0,0 +1,575 @@ +import sys +import unittest +import weakref +import time +import threading + +import pymonkey + +class PymonkeyTests(unittest.TestCase): + def _evaljs(self, code): + rt = pymonkey.Runtime() + cx = rt.new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + return cx.evaluate_script(obj, code, '', 1) + + def _evalJsWrappedPyFunc(self, func, code): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + return cx.evaluate_script(obj, code, '', 1) + + def assertRaises(self, exctype, func, *args): + was_raised = False + try: + func(*args) + except exctype, e: + self.last_exception = e + was_raised = True + self.assertTrue(was_raised) + + def testThreadSafetyExceptionIsRaised(self): + stuff = {} + def make_runtime(): + stuff['rt'] = pymonkey.Runtime() + thread = threading.Thread(target = make_runtime) + thread.start() + thread.join() + self.assertRaises(pymonkey.error, + stuff['rt'].new_context) + self.assertEqual(self.last_exception.args[0], + 'Function called from wrong thread') + + def testClearObjectPrivateWorks(self): + class Foo(object): + pass + pyobj = Foo() + cx = pymonkey.Runtime().new_context() + obj = cx.new_object(pyobj) + pyobj = weakref.ref(pyobj) + self.assertEqual(pyobj(), cx.get_object_private(obj)) + cx.clear_object_private(obj) + self.assertEqual(cx.get_object_private(obj), None) + self.assertEqual(pyobj(), None) + + def testGetObjectPrivateWorks(self): + class Foo(object): + pass + pyobj = Foo() + cx = pymonkey.Runtime().new_context() + obj = cx.new_object(pyobj) + pyobj = weakref.ref(pyobj) + self.assertEqual(pyobj(), cx.get_object_private(obj)) + del obj + del cx + self.assertEqual(pyobj(), None) + + def testOperationCallbackIsCalled(self): + def opcb(cx): + raise Exception("stop eet!") + + cx = pymonkey.Runtime().new_context() + cx.set_operation_callback(opcb) + obj = cx.new_object() + cx.init_standard_classes(obj) + + def watchdog(): + time.sleep(0.1) + cx.trigger_operation_callback() + + thread = threading.Thread(target = watchdog) + thread.start() + + self.assertRaises( + pymonkey.error, + cx.evaluate_script, + obj, 'while (1) {}', '', 1 + ) + + def testUndefinedStrIsUndefined(self): + self.assertEqual(str(pymonkey.undefined), + "pymonkey.undefined") + + def testJsWrappedPythonFuncHasPrivate(self): + def foo(cx, this, args): + pass + + cx = pymonkey.Runtime().new_context() + jsfunc = cx.new_function(foo, foo.__name__) + self.assertEqual(cx.get_object_private(jsfunc), foo) + + def testJsWrappedPythonFuncIsNotGCd(self): + def define(cx, obj): + def func(cx, this, args): + return u'func was called' + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + return weakref.ref(func) + rt = pymonkey.Runtime() + cx = rt.new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + ref = define(cx, obj) + cx.gc() + self.assertNotEqual(ref(), None) + result = cx.evaluate_script(obj, 'func()', '', 1) + self.assertEqual(result, u'func was called') + + # Now ensure that the wrapped function is GC'd when it's + # no longer reachable from JS space. + cx.define_property(obj, 'func', 0) + cx.gc() + self.assertEqual(ref(), None) + + def testCircularJsWrappedPythonFuncIsGCdIfPrivateCleared(self): + def define(cx, obj): + rt = cx.get_runtime() + def func(cx, this, args): + # Oh noes, a circular reference is born! + rt + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + return (jsfunc, weakref.ref(func)) + rt = pymonkey.Runtime() + cx = rt.new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc, ref = define(cx, obj) + + # This will break the circular reference. + cx.clear_object_private(jsfunc) + + del jsfunc + del rt + del cx + del obj + self.assertEqual(ref(), None) + + def testJsWrappedPythonFuncIsGCdAtRuntimeDestruction(self): + def define(cx, obj): + def func(cx, this, args): + return u'func was called' + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + return weakref.ref(func) + rt = pymonkey.Runtime() + cx = rt.new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + ref = define(cx, obj) + del rt + del cx + del obj + self.assertEqual(ref(), None) + + def testJsWrappedPythonFuncThrowsExcIfPrivateCleared(self): + def func(cx, this, args): + return True + + code = "func()" + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + cx.clear_object_private(jsfunc) + self.assertRaises(pymonkey.error, + cx.evaluate_script, + obj, code, '', 1) + self.assertEqual( + self._tostring(cx, self.last_exception.args[0]), + "Error: Wrapped Python function no longer exists" + ) + + def testJsWrappedPythonFuncPassesContext(self): + contexts = [] + + def func(cx, this, args): + contexts.append(cx) + return True + + code = "func()" + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + cx.evaluate_script(obj, code, '', 1) + self.assertEqual(contexts[0], cx) + + def testJsWrappedPythonFuncPassesThisArg(self): + thisObjs = [] + + def func(cx, this, args): + thisObjs.append(this) + return True + + code = "func()" + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + cx.evaluate_script(obj, code, '', 1) + self.assertEqual(thisObjs[0], obj) + + def testJsWrappedPythonFuncPassesFuncArgs(self): + funcArgs = [] + + def func(cx, this, args): + funcArgs.append(args) + return True + + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + jsfunc = cx.new_function(func, func.__name__) + cx.define_property(obj, func.__name__, jsfunc) + + cx.evaluate_script(obj, "func()", '', 1) + self.assertEqual(len(funcArgs[0]), 0) + self.assertTrue(isinstance(funcArgs[0], tuple)) + + cx.evaluate_script(obj, "func(1, 'foo')", '', 1) + self.assertEqual(len(funcArgs[1]), 2) + self.assertEqual(funcArgs[1][0], 1) + self.assertEqual(funcArgs[1][1], u'foo') + + def testJsWrappedPythonFunctionReturnsUnicodeWithEmbeddedNULs(self): + def hai2u(cx, this, args): + return args[0] + u"o hai" + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, + 'hai2u("blah\x00 ")'), + u"blah\x00 o hai") + + def testJsWrappedPythonFunctionReturnsString(self): + def hai2u(cx, this, args): + return "o hai" + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + "o hai") + + def testJsWrappedPythonFunctionReturnsUnicode(self): + def hai2u(cx, this, args): + return u"o hai\u2026" + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + u"o hai\u2026") + + def testJsWrappedPythonFunctionThrowsJsException(self): + def hai2u(cx, this, args): + raise pymonkey.error(u"blarg") + self.assertRaises(pymonkey.error, + self._evalJsWrappedPyFunc, + hai2u, 'hai2u()') + self.assertEqual(self.last_exception.args[0], u"blarg") + + def testJsWrappedPythonFunctionThrowsPyException(self): + thecx = [] + def hai2u(cx, this, args): + thecx.append(cx) + raise Exception("hello") + self.assertRaises(pymonkey.error, + self._evalJsWrappedPyFunc, + hai2u, 'hai2u()') + exc = thecx[0].get_object_private(self.last_exception.args[0]) + self.assertEqual(exc.args[0], "hello") + + def testJsWrappedPythonFunctionReturnsNone(self): + def hai2u(cx, this, args): + pass + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + None) + + def testJsWrappedPythonFunctionReturnsTrue(self): + def hai2u(cx, this, args): + return True + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + True) + + def testJsWrappedPythonFunctionReturnsFalse(self): + def hai2u(cx, this, args): + return False + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + False) + + def testJsWrappedPythonFunctionReturnsSmallInt(self): + def hai2u(cx, this, args): + return 5 + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + 5) + + def testJsWrappedPythonFunctionReturnsFloat(self): + def hai2u(cx, this, args): + return 5.1 + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + 5.1) + + def testJsWrappedPythonFunctionReturnsNegativeInt(self): + def hai2u(cx, this, args): + return -5 + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + -5) + + def testJsWrappedPythonFunctionReturnsBigInt(self): + def hai2u(cx, this, args): + return 2147483647 + self.assertEqual(self._evalJsWrappedPyFunc(hai2u, 'hai2u()'), + 2147483647) + + def testDefinePropertyWorksWithUnicodePropertyNames(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + foo = cx.new_object() + cx.define_property(obj, u"foo\u2026", foo) + self.assertEqual( + cx.get_property(obj, u"foo\u2026"), + foo + ) + + def testDefinePropertyWorksWithObject(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + foo = cx.new_object() + cx.define_property(obj, "foo", foo) + self.assertEqual( + cx.evaluate_script(obj, 'foo', '', 1), + foo + ) + + def testDefinePropertyWorksWithString(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + foo = cx.new_object() + cx.define_property(obj, "foo", u"hello") + self.assertEqual( + cx.evaluate_script(obj, 'foo', '', 1), + u"hello" + ) + + def testObjectIsIdentityPreserving(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + cx.evaluate_script(obj, 'var foo = {bar: 1}', '', 1) + self.assertTrue(isinstance(cx.get_property(obj, u"foo"), + pymonkey.Object)) + self.assertTrue(cx.get_property(obj, u"foo") is + cx.get_property(obj, "foo")) + + def testObjectGetattrThrowsException(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + result = cx.evaluate_script(obj, '({get foo() { throw "blah"; }})', + '', 1) + self.assertRaises(pymonkey.error, + cx.get_property, + result, + u"foo") + self.assertEqual(self.last_exception.args[0], u"blah") + + def testInfiniteRecursionRaisesError(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + self.assertRaises( + pymonkey.error, + cx.evaluate_script, + obj, '(function foo() { foo(); })();', '', 1 + ) + self.assertEqual( + self._tostring(cx, self.last_exception.args[0]), + "InternalError: too much recursion" + ) + + def testObjectGetattrWorks(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + cx.evaluate_script(obj, 'var boop = 5', '', 1) + cx.evaluate_script(obj, 'this["blarg\u2026"] = 5', '', 1) + self.assertEqual(cx.get_property(obj, u"beans"), + pymonkey.undefined) + self.assertEqual(cx.get_property(obj, u"blarg\u2026"), 5) + self.assertEqual(cx.get_property(obj, u"boop"), 5) + + def testContextIsInstance(self): + cx = pymonkey.Runtime().new_context() + self.assertTrue(isinstance(cx, pymonkey.Context)) + + def testContextTypeCannotBeInstantiated(self): + self.assertRaises(TypeError, pymonkey.Context) + + def testObjectIsInstance(self): + obj = pymonkey.Runtime().new_context().new_object() + self.assertTrue(isinstance(obj, pymonkey.Object)) + self.assertFalse(isinstance(obj, pymonkey.Function)) + + def testObjectTypeCannotBeInstantiated(self): + self.assertRaises(TypeError, pymonkey.Object) + + def testFunctionIsInstance(self): + def boop(): + pass + obj = pymonkey.Runtime().new_context().new_function(boop, "boop") + self.assertTrue(isinstance(obj, pymonkey.Object)) + self.assertTrue(isinstance(obj, pymonkey.Function)) + + def testFunctionTypeCannotBeInstantiated(self): + self.assertRaises(TypeError, pymonkey.Function) + + def testObjectGetRuntimeWorks(self): + rt = pymonkey.Runtime() + obj = rt.new_context().new_object() + self.assertEqual(obj.get_runtime(), rt) + + def testContextGetRuntimeWorks(self): + rt = pymonkey.Runtime() + cx = rt.new_context() + self.assertEqual(cx.get_runtime(), rt) + + def testUndefinedCannotBeInstantiated(self): + self.assertRaises(TypeError, pymonkey.undefined) + + def testEvaluateThrowsException(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + self.assertRaises(pymonkey.error, + cx.evaluate_script, + obj, 'hai2u()', '', 1) + self.assertEqual(self._tostring(cx, + self.last_exception.args[0]), + 'ReferenceError: hai2u is not defined') + + def testEvaluateReturnsUndefined(self): + retval = self._evaljs("") + self.assertTrue(retval is pymonkey.undefined) + + def testEvaludateReturnsUnicodeWithEmbeddedNULs(self): + retval = self._evaljs("'\x00hi'") + self.assertEqual(retval, u'\x00hi') + + def testEvaluateReturnsSMPUnicode(self): + # This is 'LINEAR B SYLLABLE B008 A', in the supplementary + # multilingual plane (SMP). + retval = self._evaljs("'\uD800\uDC00'") + self.assertEqual(retval, u'\U00010000') + self.assertEqual(retval.encode('utf-16'), + '\xff\xfe\x00\xd8\x00\xdc') + + def testEvaluateReturnsBMPUnicode(self): + retval = self._evaljs("'o hai\u2026'") + self.assertTrue(type(retval) == unicode) + self.assertEqual(retval, u'o hai\u2026') + + def testEvaluateReturnsObject(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + obj = cx.evaluate_script(obj, '({boop: 1})', '', 1) + self.assertTrue(isinstance(obj, pymonkey.Object)) + self.assertEqual(cx.get_property(obj, u"boop"), 1) + + def testEvaluateReturnsFunction(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + cx.init_standard_classes(obj) + obj = cx.evaluate_script(obj, '(function boop() { return 1; })', + '', 1) + self.assertTrue(isinstance(obj, pymonkey.Function)) + + def testJsExceptionStateIsClearedAfterExceptionIsCaught(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + self.assertRaises(pymonkey.error, + cx.evaluate_script, + obj, 'blah()', '', 1) + self.assertEqual(cx.evaluate_script(obj, '5+3', '', 1), + 8) + + def testCallFunctionRaisesErrorOnBadFuncArgs(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + obj = cx.evaluate_script( + obj, + '(function boop(a, b) { return a+b+this.c; })', + '', 1 + ) + self.assertRaises( + NotImplementedError, + cx.call_function, + obj, obj, (1, self) + ) + + def _tostring(self, cx, obj): + return cx.call_function(obj, + cx.get_property(obj, u"toString"), + ()) + + def testCallFunctionRaisesErrorFromJS(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + obj = cx.evaluate_script( + obj, + '(function boop(a, b) { blarg(); })', + '', 1 + ) + self.assertRaises(pymonkey.error, + cx.call_function, + obj, obj, (1,)) + self.assertEqual(self._tostring(cx, + self.last_exception.args[0]), + 'ReferenceError: blarg is not defined') + + def testInitStandardClassesRaisesExcOnRuntimeMismatch(self): + cx2 = pymonkey.Runtime().new_context() + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + self.assertRaises(ValueError, + cx2.init_standard_classes, + obj) + self.assertEqual(self.last_exception.args[0], + 'JS runtime mismatch') + + def testCallFunctionWorks(self): + cx = pymonkey.Runtime().new_context() + obj = cx.new_object() + thisArg = cx.new_object() + cx.define_property(thisArg, "c", 3) + cx.init_standard_classes(obj) + obj = cx.evaluate_script( + obj, + '(function boop(a, b) { return a+b+this.c; })', + '', 1 + ) + self.assertEqual(cx.call_function(thisArg, obj, (1,2)), 6) + + def testEvaluateReturnsTrue(self): + self.assertTrue(self._evaljs('true') is True) + + def testEvaluateReturnsFalse(self): + self.assertTrue(self._evaljs('false') is False) + + def testEvaluateReturnsNone(self): + self.assertTrue(self._evaljs('null') is None) + + def testEvaluateReturnsIntegers(self): + self.assertEqual(self._evaljs('1+3'), 4) + + def testEvaluateReturnsNegativeIntegers(self): + self.assertEqual(self._evaljs('-5'), -5) + + def testEvaluateReturnsBigIntegers(self): + self.assertEqual(self._evaljs('2147483647*2'), + 2147483647*2) + + def testEvaluateReturnsFloats(self): + self.assertEqual(self._evaljs('1.1+3'), 4.1) + +if __name__ == '__main__': + unittest.main() diff -r 06269ca0b36c -r ac8ca0ee7760 undefined.cpp --- a/undefined.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "undefined.h" - -static Py_ssize_t PYM_undefinedLength(PyObject *o) { - return 0; -}; - -static PyMappingMethods PYM_undefinedAsMapping = { - PYM_undefinedLength, /*mp_length*/ - 0, /*mp_subscript*/ - 0 /*mp_ass_subscript*/ -}; - -static PyObject *PYM_undefinedRepr(PyObject *o) { - return PyString_FromString("pymonkey.undefined"); -} - -// TODO: We should make this behave as much like JavaScript's -// "undefined" value as possible; e.g., its string value should -// be "undefined", the singleton should be falsy, etc. -PyTypeObject PYM_undefinedType = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ - "pymonkey.undefined", /*tp_name*/ - sizeof(PYM_undefinedObject), /*tp_basicsize*/ - 0, /*tp_itemsize*/ - 0, /*tp_dealloc*/ - 0, /*tp_print*/ - 0, /*tp_getattr*/ - 0, /*tp_setattr*/ - 0, /*tp_compare*/ - PYM_undefinedRepr, /*tp_repr*/ - 0, /*tp_as_number*/ - 0, /*tp_as_sequence*/ - &PYM_undefinedAsMapping, /*tp_as_mapping*/ - 0, /*tp_hash */ - 0, /*tp_call*/ - PYM_undefinedRepr, /*tp_str*/ - 0, /*tp_getattro*/ - 0, /*tp_setattro*/ - 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT, /*tp_flags*/ - /* tp_doc */ - "Pythonic equivalent of JavaScript's 'undefined' value", -}; - -PYM_undefinedObject *PYM_undefined; diff -r 06269ca0b36c -r ac8ca0ee7760 undefined.h --- a/undefined.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_UNDEFINED_H -#define PYM_UNDEFINED_H - -#include - -#define Py_RETURN_UNDEFINED { Py_INCREF(PYM_undefined); \ - return (PyObject *) PYM_undefined; } - -typedef struct { - PyObject_HEAD -} PYM_undefinedObject; - -extern PyTypeObject PYM_undefinedType; - -extern PYM_undefinedObject *PYM_undefined; - -#endif diff -r 06269ca0b36c -r ac8ca0ee7760 utils.cpp --- a/utils.cpp Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,257 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "utils.h" -#include "undefined.h" -#include "object.h" - -PyObject *PYM_error; - -static int -PYM_doubleToJsval(PYM_JSContextObject *context, - double number, - jsval *rval) -{ - jsdouble *numberAsJsdouble = JS_NewDouble(context->cx, number); - if (numberAsJsdouble == NULL) { - PyErr_SetString(PYM_error, "JS_NewDouble() failed"); - return -1; - } - *rval = DOUBLE_TO_JSVAL(numberAsJsdouble); - return 0; -} - -int -PYM_pyObjectToJsval(PYM_JSContextObject *context, - PyObject *object, - jsval *rval) -{ - if (PyString_Check(object) || PyUnicode_Check(object)) { - PyObject *unicode; - if (PyString_Check(object)) { - unicode = PyUnicode_FromObject(object); - if (unicode == NULL) - return -1; - } else { - unicode = object; - Py_INCREF(unicode); - } - - PyObject *string = PyUnicode_AsUTF16String(unicode); - Py_DECREF(unicode); - if (string == NULL) - return -1; - - char *buffer = PyString_AS_STRING(string); - Py_ssize_t size = PyString_GET_SIZE(string); - - // Note that we're manipulating buffer and size here to get rid of - // the BOM. - JSString *jsString = JS_NewUCStringCopyN(context->cx, - (const jschar *) (buffer + 2), - (size / 2) - 1); - Py_DECREF(string); - if (jsString == NULL) { - PyErr_SetString(PYM_error, "JS_NewUCStringCopyN() failed"); - return -1; - } - - *rval = STRING_TO_JSVAL(jsString); - return 0; - } - - if (PyInt_Check(object)) { - long number = PyInt_AS_LONG(object); - if (INT_FITS_IN_JSVAL(number)) { - *rval = INT_TO_JSVAL(number); - return 0; - } else - return PYM_doubleToJsval(context, number, rval); - } - - if (PyFloat_Check(object)) - return PYM_doubleToJsval(context, PyFloat_AS_DOUBLE(object), rval); - - if (PyObject_TypeCheck(object, &PYM_JSObjectType)) { - PYM_JSObject *jsObject = (PYM_JSObject *) object; - JSRuntime *rt = JS_GetRuntime(context->cx); - if (rt != jsObject->runtime->rt) { - PyErr_SetString(PyExc_ValueError, - "JS object and JS context are from different " - "JS runtimes"); - return -1; - } - *rval = OBJECT_TO_JSVAL(jsObject->obj); - return 0; - } - - if (object == Py_True) { - *rval = JSVAL_TRUE; - return 0; - } - - if (object == Py_False) { - *rval = JSVAL_FALSE; - return 0; - } - - if (object == Py_None) { - *rval = JSVAL_NULL; - return 0; - } - - // TODO: Support more types. - PyErr_SetString(PyExc_NotImplementedError, - "Data type conversion not implemented."); - return -1; -} - -PyObject * -PYM_jsvalToPyObject(PYM_JSContextObject *context, - jsval value) { - if (JSVAL_IS_INT(value)) - return PyInt_FromLong(JSVAL_TO_INT(value)); - - if (JSVAL_IS_DOUBLE(value)) { - jsdouble *doubleRef = JSVAL_TO_DOUBLE(value); - return PyFloat_FromDouble(*doubleRef); - } - - if (value == JSVAL_FALSE) - Py_RETURN_FALSE; - - if (value == JSVAL_TRUE) - Py_RETURN_TRUE; - - if (JSVAL_IS_NULL(value)) - Py_RETURN_NONE; - - if (JSVAL_IS_VOID(value)) - Py_RETURN_UNDEFINED; - - if (JSVAL_IS_STRING(value)) { - // Strings in JS are funky: think of them as 16-bit versions of - // Python 2.x's 'str' type. Whether or not they're valid UTF-16 - // is entirely up to the client code. - - // TODO: Instead of ignoring errors, consider actually treating - // the string as a raw character buffer. - JSString *str = JSVAL_TO_STRING(value); - const char *chars = (const char *) JS_GetStringChars(str); - size_t length = JS_GetStringLength(str); - - // We're multiplying length by two since Python wants the number - // of bytes, not the number of 16-bit characters. - return PyUnicode_DecodeUTF16(chars, length * 2, "ignore", NULL); - } - - if (JSVAL_IS_OBJECT(value)) - return (PyObject *) PYM_newJSObject(context, JSVAL_TO_OBJECT(value), - NULL); - - // TODO: Support more types. - PyErr_SetString(PyExc_NotImplementedError, - "Data type conversion not implemented."); - return NULL; -} - -void -PYM_pythonExceptionToJs(PYM_JSContextObject *context) -{ - PyObject *type; - PyObject *value; - PyObject *traceback; - - PyErr_Fetch(&type, &value, &traceback); - - if (type == PYM_error && value && - PyObject_HasAttrString(value, "message")) { - jsval val; - PyObject *message = PyObject_GetAttrString(value, "message"); - if (message && PYM_pyObjectToJsval(context, message, &val) == 0) { - JS_SetPendingException(context->cx, val); - } else - JS_ReportError(context->cx, - "Python exception occurred, but exception " - "couldn't be converted"); - Py_XDECREF(message); - } else { - if (value) { - JSObject *exception = PYM_JS_newObject(context->cx, value); - if (exception) - JS_SetPendingException(context->cx, OBJECT_TO_JSVAL(exception)); - else - JS_ReportOutOfMemory(context->cx); - } else - JS_ReportError(context->cx, "Python exception occurred"); - } - - Py_XDECREF(type); - Py_XDECREF(value); - Py_XDECREF(traceback); -} - -void -PYM_jsExceptionToPython(PYM_JSContextObject *context) -{ - if (!JS_IsExceptionPending(context->cx) && - PyErr_Occurred()) - return; - - jsval val; - if (JS_GetPendingException(context->cx, &val)) { - PyObject *obj = PYM_jsvalToPyObject(context, val); - if (obj) { - PyErr_SetObject(PYM_error, obj); - Py_DECREF(obj); - } else { - PyErr_Clear(); - - JSString *str = NULL; - - Py_BEGIN_ALLOW_THREADS; - str = JS_ValueToString(context->cx, val); - Py_END_ALLOW_THREADS; - - if (str != NULL) { - const char *chars = JS_GetStringBytes(str); - PyErr_SetString(PYM_error, chars); - } else - PyErr_SetString(PYM_error, "JS exception occurred"); - } - } else - PyErr_SetString(PYM_error, "JS_GetPendingException() failed"); -} diff -r 06269ca0b36c -r ac8ca0ee7760 utils.h --- a/utils.h Mon Aug 17 03:52:51 2009 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Pymonkey. - * - * The Initial Developer of the Original Code is Mozilla. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Atul Varma - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#ifndef PYM_UTILS_H -#define PYM_UTILS_H - -#include "context.h" - -#include -#include -#include - -class PYM_PyAutoEnsureGIL { -public: - PYM_PyAutoEnsureGIL() { - state = PyGILState_Ensure(); - } - - ~PYM_PyAutoEnsureGIL() { - PyGILState_Release(state); - } - -protected: - PyGILState_STATE state; -}; - -typedef struct { - JSDHashEntryStub base; - void *value; -} PYM_HashEntry; - -extern PyObject *PYM_error; - -extern int -PYM_pyObjectToJsval(PYM_JSContextObject *context, - PyObject *object, - jsval *rval); - -extern PyObject * -PYM_jsvalToPyObject(PYM_JSContextObject *context, jsval value); - -extern void -PYM_pythonExceptionToJs(PYM_JSContextObject *context); - -void -PYM_jsExceptionToPython(PYM_JSContextObject *context); - -#endif