Permalink
Browse files

first public commit

  • Loading branch information...
0 parents commit 0da35388a81f241f530a4abc47c117919cce9ebd @unbit committed Oct 8, 2011
Showing with 358 additions and 0 deletions.
  1. +35 −0 LICENSE
  2. +269 −0 pydrone.c
  3. +11 −0 setup.py
  4. +43 −0 tests/droneme.py
35 LICENSE
@@ -0,0 +1,35 @@
+/* ***** 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 __________________________________________.
+ *
+ * The Initial Developer of the Original Code is
+ * ____________________________________________.
+ * Portions created by the Initial Developer are Copyright (C) 2___
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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 ***** */
269 pydrone.c
@@ -0,0 +1,269 @@
+/* ***** 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 pydrone.
+ *
+ * The Initial Developer of the Original Code is
+ * Unbit S.a.s. (09244720018, Italy).
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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 <Python.h>
+#include <jsapi.h>
+
+
+// define the global class
+static JSClass global_class = {
+ .name = "global",
+ .flags = JSCLASS_GLOBAL_FLAGS,
+ .addProperty = JS_PropertyStub,
+ .delProperty = JS_PropertyStub,
+ .getProperty = JS_PropertyStub,
+ .setProperty = JS_StrictPropertyStub,
+ .enumerate = JS_EnumerateStub,
+ .resolve = JS_ResolveStub,
+ .convert = JS_ConvertStub,
+ .finalize = JS_FinalizeStub,
+ JSCLASS_NO_OPTIONAL_MEMBERS
+};
+
+// map js exception to python's one
+void js_raise_exc(JSContext *context, const char *message, JSErrorReport *report) {
+
+ PyErr_Format(PyExc_ValueError, "%s:%u:%s", report->filename,
+ (unsigned int) report->lineno, message);
+
+ int *error = JS_GetContextPrivate(context);
+
+ *error = 1;
+}
+
+// convert js object to py
+static PyObject *js_to_py(JSContext *context, jsval rval) {
+
+
+ if (JSVAL_IS_STRING(rval)) {
+ char *str = JS_EncodeString(context, JSVAL_TO_STRING(rval));
+ return PyUnicode_FromString(str);
+ }
+ else if (JSVAL_IS_INT(rval)) {
+ return PyInt_FromLong(JSVAL_TO_INT(rval));
+ }
+ else if (JSVAL_IS_DOUBLE(rval)) {
+ return PyFloat_FromDouble(JSVAL_TO_DOUBLE(rval));
+ }
+ else if (JSVAL_IS_OBJECT(rval)) {
+ JSObject *obj = JSVAL_TO_OBJECT(rval);
+ if (JS_IsArrayObject(context, obj)) {
+ PyObject *list = PyList_New(0);
+ size_t len,i;
+ jsval arrayval;
+ if (JS_GetArrayLength(context, obj, &len)) {
+ for(i=0;i<len;i++) {
+ if (JS_GetElement(context, obj, i, &arrayval)) {
+ PyObject *list_item = js_to_py(context, arrayval);
+ PyList_Append(list, list_item );
+ Py_DECREF(list_item);
+ }
+ }
+ }
+ return list;
+ }
+ else {
+ JSObject *iter = JS_NewPropertyIterator(context, obj);
+ if (iter) {
+ PyObject *dict = PyDict_New();
+ jsid p_id;
+ while(JS_NextProperty(context, iter, &p_id) == JS_TRUE) {
+ if (p_id == JSID_VOID ) break;
+ jsval js_key;
+ jsval js_value;
+ if (JS_IdToValue(context, p_id, &js_key)) {
+ PyObject *py_key = js_to_py(context, js_key);
+ if (py_key != Py_None) {
+ if (JS_GetPropertyById(context, obj, p_id, &js_value)) {
+ PyObject *py_value = js_to_py(context, js_value);
+ PyDict_SetItem(dict, py_key, py_value);
+ Py_DECREF(py_key);
+ Py_DECREF(py_value);
+ }
+ }
+ }
+ }
+ return dict;
+ }
+ }
+ }
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static jsval py_to_js(JSContext *context, PyObject *data) {
+
+ if (PyString_Check(data)) {
+ JSString *js_string = JS_NewStringCopyN(context, PyString_AsString(data), PyString_Size(data));
+ return STRING_TO_JSVAL(js_string);
+ }
+ else if (PyUnicode_Check(data)) {
+ PyObject *py_str = PyUnicode_AsUTF8String(data);
+ JSString *js_string = JS_NewStringCopyN(context, PyString_AsString(py_str), PyString_Size(py_str));
+ Py_DECREF(py_str);
+ return STRING_TO_JSVAL(js_string);
+ }
+ else if (PyFloat_Check(data)) {
+ return DOUBLE_TO_JSVAL( PyFloat_AsDouble(data) );
+ }
+ else if (PyInt_Check(data)) {
+ return INT_TO_JSVAL( PyInt_AsLong(data) );
+ }
+ else if (PyLong_Check(data)) {
+ return INT_TO_JSVAL( PyLong_AsLong(data) );
+ }
+ else if (PyList_Check(data)) {
+ int i;
+ JSObject *js_array = JS_NewArrayObject(context, 0, NULL);
+ for(i=0;i<PyList_Size(data);i++) {
+ jsval js_py_obj = py_to_js(context, PyList_GetItem(data, i) );
+ JS_SetElement(context, js_array, i, &js_py_obj);
+ }
+ return OBJECT_TO_JSVAL(js_array);
+ }
+ else if (PyTuple_Check(data)) {
+ int i;
+ JSObject *js_array = JS_NewArrayObject(context, 0, NULL);
+ for(i=0;i<PyTuple_Size(data);i++) {
+ jsval js_py_obj = py_to_js(context, PyTuple_GetItem(data, i) );
+ JS_SetElement(context, js_array, i, &js_py_obj);
+ }
+ return OBJECT_TO_JSVAL(js_array);
+ }
+ else if (PyDict_Check(data)) {
+ PyObject *key, *value;
+ char *prop_name = NULL;
+ Py_ssize_t pos = 0;
+ JSObject *js_dict = JS_NewObject(context, NULL, NULL, NULL);
+ while (PyDict_Next(data, &pos, &key, &value)) {
+ if (PyString_Check(key)) {
+ prop_name = PyString_AsString(key);
+ }
+ else if (PyUnicode_Check(key)) {
+ PyObject *py_str = PyUnicode_AsUTF8String(data);
+ prop_name = PyString_AsString(py_str);
+ Py_DECREF(py_str);
+ }
+
+ if (prop_name) {
+ jsval js_dict_item = py_to_js(context, value);
+ JS_SetProperty(context, js_dict, prop_name, &js_dict_item);
+ }
+ }
+ return OBJECT_TO_JSVAL(js_dict);
+ }
+
+ return JSVAL_NULL;
+}
+
+// the core of the module: create a js runtime/context run code and return a python object
+static PyObject *pydrone_js(PyObject *self, PyObject *args) {
+
+ // spidermonkey main pointers
+ JSRuntime *runtime;
+ JSContext *context;
+ JSObject *global;
+
+ // args
+ char *script;
+ Py_ssize_t script_len;
+ PyObject *py_data;
+
+ // script evaluation return value
+ jsval rval;
+
+ int error = 0;
+
+ if (!PyArg_ParseTuple(args, "s#O:js", &script, &script_len, &py_data))
+ return NULL;
+
+ runtime = JS_NewRuntime (1024L*1024L);
+ context = JS_NewContext (runtime, 8192);
+
+
+ // add error as private data in the context
+ JS_SetContextPrivate(context, &error);
+
+ // is JIT worthy in pydrone ?
+ //JS_SetOptions(context, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
+ JS_SetOptions(context, JSOPTION_VAROBJFIX);
+ JS_SetVersion(context, JSVERSION_LATEST);
+ // map the exception handler
+ JS_SetErrorReporter(context, js_raise_exc);
+
+ // initialize the global object
+ global = JS_NewCompartmentAndGlobalObject(context, &global_class, NULL);
+
+ // add standard classes (like Array)
+ JS_InitStandardClasses(context, global);
+
+ // convert the python data to js and map it as "var data"
+ jsval js_data = py_to_js(context, py_data);
+ JS_SetProperty(context, global, "data", &js_data);
+
+ // evaluate the script
+ JS_EvaluateScript(context, global, script, script_len, "pydrone", 1, &rval);
+
+ // check for exception
+ if (error == 1) {
+ JS_DestroyContext(context);
+ JS_DestroyRuntime(runtime);
+ JS_ShutDown();
+ return NULL;
+ }
+
+
+ PyObject *ret = js_to_py(context, rval);
+
+ JS_DestroyContext(context);
+ JS_DestroyRuntime(runtime);
+ JS_ShutDown();
+
+ return ret;
+}
+
+// methods
+static PyMethodDef pydrone_methods[] = {
+ {"js", pydrone_js, METH_VARARGS, "run JavaScript code with SpiderMonkey engine"},
+ {NULL, NULL, 0, NULL}
+};
+
+// init
+PyMODINIT_FUNC
+initpydrone(void) {
+ (void) Py_InitModule("pydrone", pydrone_methods);
+}
@@ -0,0 +1,11 @@
+from distutils.core import setup, Extension
+
+module = Extension('pydrone',
+ libraries = ['mozjs'],
+ include_dirs = ['/usr/local/include/mozjs', '/usr/include/mozjs'],
+ sources = ['pydrone.c'])
+
+setup (name = 'pydrone',
+ version = '0.1',
+ description = 'pydrone',
+ ext_modules = [module])
@@ -0,0 +1,43 @@
+import sys
+import time
+sys.path.insert(0, '.')
+
+code = """
+function calc(a,b, c) {
+ return data+' '+(a+b+c);
+}
+a = 1;
+b = 2;
+c = 3;
+
+a = {'lista': ['roberta','serena', 'alessandro', calc(a,b,c), 17, 30, 26, ['unbit'], 17.30], 'foo':'bar'};
+"""
+
+code2 = """
+data
+"""
+
+code3 = """
+Object.keys(data)
+"""
+
+code4 = """
+data['values'][5][0].toUpperCase();
+"""
+
+import pydrone
+
+data = {'values': [u'foobar', (17.0,30.0), 'test', (1,2,3,4,5), str(time.time()), ['one', 'two']], 'foo':'bar'}
+
+for i in xrange(1,100):
+ print pydrone.js(code, data)
+ print pydrone.js(code2, data)
+ print pydrone.js(code3, data)
+ print pydrone.js(code4, data)
+ try:
+ print "refcnt:",sys.gettotalrefcount()
+ with open('/proc/self/stat') as f:
+ stat = f.read().split()
+ print "vsz",stat[22],"rss",stat[23]
+ except:
+ pass

0 comments on commit 0da3538

Please sign in to comment.