diff src/utils.cpp @ 117:ac8ca0ee7760

Moved all .cpp/.h files into 'src' dir and test suite into 'tests' dir.
author Atul Varma <varmaa@toolness.com>
date Mon, 17 Aug 2009 22:08:33 -0700
parents utils.cpp@3570ab12747b
children 856ca7a139e4
line wrap: on
line diff
--- /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 <atul@mozilla.com>
+ *
+ * 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");
+}