Commit 64ded33c authored by Michael Droettboom's avatar Michael Droettboom

Use a separate class for bound methods.

parent 54b55455
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
using emscripten::val; using emscripten::val;
PyObject *jsToPython(val x, val *parent, const char *name) { PyObject *jsToPython(val x) {
val xType = x.typeOf(); val xType = x.typeOf();
if (xType.equals(val("string"))) { if (xType.equals(val("string"))) {
...@@ -17,6 +17,6 @@ PyObject *jsToPython(val x, val *parent, const char *name) { ...@@ -17,6 +17,6 @@ PyObject *jsToPython(val x, val *parent, const char *name) {
Py_INCREF(Py_None); Py_INCREF(Py_None);
return Py_None; return Py_None;
} else { } else {
return JsProxy_cnew(x, parent, name); return JsProxy_cnew(x);
} }
} }
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
#include <emscripten/val.h> #include <emscripten/val.h>
#include <Python.h> #include <Python.h>
PyObject *jsToPython(emscripten::val x, PyObject *jsToPython(emscripten::val x);
emscripten::val *parent = NULL,
const char *name = NULL);
#endif /* JS2PYTHON_H */ #endif /* JS2PYTHON_H */
...@@ -8,6 +8,8 @@ using emscripten::val; ...@@ -8,6 +8,8 @@ using emscripten::val;
// TODO: Bound methods should probably have their own class, rather than using // TODO: Bound methods should probably have their own class, rather than using
// JsProxy for everything // JsProxy for everything
static PyObject *JsBoundMethod_cnew(val v, val this_, const char *name);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// JsProxy // JsProxy
// //
...@@ -16,18 +18,10 @@ using emscripten::val; ...@@ -16,18 +18,10 @@ using emscripten::val;
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
val *js; val *js;
val *parent;
char *name;
} JsProxy; } JsProxy;
static void JsProxy_dealloc(JsProxy *self) { static void JsProxy_dealloc(JsProxy *self) {
delete self->js; delete self->js;
if (self->parent) {
delete self->parent;
}
if (self->name) {
free(self->name);
}
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
...@@ -43,7 +37,11 @@ static PyObject *JsProxy_GetAttr(PyObject *o, PyObject *attr_name) { ...@@ -43,7 +37,11 @@ static PyObject *JsProxy_GetAttr(PyObject *o, PyObject *attr_name) {
val v = (*self->js)[s]; val v = (*self->js)[s];
Py_DECREF(str); Py_DECREF(str);
return jsToPython(v, self->js, s.c_str()); if (v.typeof().equals(val("function"))) {
return JsBoundMethod_cnew(v, *self->js, s.c_str());
}
return jsToPython(v);
} }
static int JsProxy_SetAttr(PyObject *o, PyObject *attr_name, PyObject *value) { static int JsProxy_SetAttr(PyObject *o, PyObject *attr_name, PyObject *value) {
...@@ -70,90 +68,150 @@ static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) { ...@@ -70,90 +68,150 @@ static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
// TODO: There's probably some way to not have to explicitly expand arguments // TODO: There's probably some way to not have to explicitly expand arguments
// here. // here.
if (self->parent) {
switch (nargs) { switch (nargs) {
case 0: case 0:
return jsToPython((*self->parent).call<val>(self->name)); return jsToPython((*self->js)());
case 1: case 1:
return jsToPython((*self->parent).call<val>( return jsToPython((*self->js)
self->name, (pythonToJs(PyTuple_GET_ITEM(args, 0))));
pythonToJs(PyTuple_GET_ITEM(args, 0))));
case 2: case 2:
return jsToPython((*self->parent).call<val>( return jsToPython((*self->js)
self->name, (pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)))); pythonToJs(PyTuple_GET_ITEM(args, 1))));
case 3: case 3:
return jsToPython((*self->parent).call<val>( return jsToPython((*self->js)
self->name, (pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)), pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)))); pythonToJs(PyTuple_GET_ITEM(args, 2))));
case 4: case 4:
return jsToPython((*self->parent).call<val>( return jsToPython((*self->js)
self->name, (pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)), pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)), pythonToJs(PyTuple_GET_ITEM(args, 2)),
pythonToJs(PyTuple_GET_ITEM(args, 3)))); pythonToJs(PyTuple_GET_ITEM(args, 3))));
case 5:
return jsToPython((*self->js)
(pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)),
pythonToJs(PyTuple_GET_ITEM(args, 3)),
pythonToJs(PyTuple_GET_ITEM(args, 4))));
} }
} else { PyErr_SetString(PyExc_TypeError, "Too many arguments to function");
return NULL;
}
static PyTypeObject JsProxyType = {
.tp_name = "JsProxy",
.tp_basicsize = sizeof(JsProxy),
.tp_dealloc = (destructor)JsProxy_dealloc,
.tp_call = JsProxy_Call,
.tp_getattro = JsProxy_GetAttr,
.tp_setattro = JsProxy_SetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "A proxy to make a Javascript object behave like a Python object"
};
PyObject *JsProxy_cnew(val v) {
JsProxy *self;
self = (JsProxy *)JsProxyType.tp_alloc(&JsProxyType, 0);
self->js = new val(v);
return (PyObject *)self;
}
////////////////////////////////////////////////////////////
// JsBoundMethod
//
// A special class for bound methods
typedef struct {
JsProxy head;
val *this_;
char *name;
} JsBoundMethod;
static void JsBoundMethod_dealloc(JsBoundMethod *self) {
delete self->head.js;
delete self->this_;
free(self->name);
Py_TYPE(self)->tp_free((PyObject *)self);
}
static PyObject* JsBoundMethod_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
JsBoundMethod *self = (JsBoundMethod *)o;
Py_ssize_t nargs = PyTuple_Size(args);
// TODO: There's probably some way to not have to explicitly expand arguments
// here.
switch (nargs) { switch (nargs) {
case 0: case 0:
return jsToPython((*self->js)()); return jsToPython((*self->this_).call<val>(self->name));
case 1: case 1:
return jsToPython((*self->js)( return jsToPython((*self->this_).call<val>
(self->name,
pythonToJs(PyTuple_GET_ITEM(args, 0)))); pythonToJs(PyTuple_GET_ITEM(args, 0))));
case 2: case 2:
return jsToPython((*self->js)( return jsToPython((*self->this_).call<val>
(self->name,
pythonToJs(PyTuple_GET_ITEM(args, 0)), pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)))); pythonToJs(PyTuple_GET_ITEM(args, 1))));
case 3: case 3:
return jsToPython((*self->js)( return jsToPython((*self->this_).call<val>
(self->name,
pythonToJs(PyTuple_GET_ITEM(args, 0)), pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)), pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)))); pythonToJs(PyTuple_GET_ITEM(args, 2))));
case 4: case 4:
return jsToPython((*self->js)( return jsToPython((*self->this_).call<val>
(self->name,
pythonToJs(PyTuple_GET_ITEM(args, 0)), pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)), pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)), pythonToJs(PyTuple_GET_ITEM(args, 2)),
pythonToJs(PyTuple_GET_ITEM(args, 3)))); pythonToJs(PyTuple_GET_ITEM(args, 3))));
case 5:
return jsToPython((*self->this_).call<val>
(self->name,
pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)),
pythonToJs(PyTuple_GET_ITEM(args, 3)),
pythonToJs(PyTuple_GET_ITEM(args, 4))));
} }
}
// TODO: Handle exception here PyErr_SetString(PyExc_TypeError, "Too many arguments to function");
return NULL; return NULL;
} }
static PyTypeObject JsProxyType = { static PyTypeObject JsBoundMethodType = {
.tp_name = "JsProxy", .tp_name = "JsBoundMethod",
.tp_basicsize = sizeof(JsProxy), .tp_basicsize = sizeof(JsBoundMethod),
.tp_dealloc = (destructor)JsProxy_dealloc, .tp_dealloc = (destructor)JsBoundMethod_dealloc,
.tp_call = JsProxy_Call, .tp_call = JsBoundMethod_Call,
.tp_getattro = JsProxy_GetAttr,
.tp_setattro = JsProxy_SetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT, .tp_flags = Py_TPFLAGS_DEFAULT,
.tp_doc = "A proxy to make a Javascript object behave like a Python object" .tp_doc = "A proxy to make it possible to call Javascript bound methods from Python."
}; };
PyObject *JsProxy_cnew(val v, val *parent, const char *name) { static PyObject *JsBoundMethod_cnew(val v, val this_, const char *name) {
JsProxy *self; JsBoundMethod *self;
self = (JsProxy *)JsProxyType.tp_alloc(&JsProxyType, 0); self = (JsBoundMethod *)JsBoundMethodType.tp_alloc(&JsBoundMethodType, 0);
self->js = new val(v); self->head.js = new val(v);
if (parent) { self->this_ = new val(this_);
self->parent = new val(*parent);
size_t n = strnlen(name, 256); size_t n = strnlen(name, 256);
char *copy = (char *)malloc(n + 1); char *copy = (char *)malloc(n + 1);
strncpy(copy, name, n + 1); strncpy(copy, name, n + 1);
self->name = copy; self->name = copy;
} else {
self->parent = NULL;
}
return (PyObject *)self; return (PyObject *)self;
} }
////////////////////////////////////////////////////////////
// Public functions
int JsProxy_Check(PyObject *x) { int JsProxy_Check(PyObject *x) {
return PyObject_TypeCheck(x, &JsProxyType); return (PyObject_TypeCheck(x, &JsProxyType) ||
PyObject_TypeCheck(x, &JsBoundMethodType));
} }
val JsProxy_AsVal(PyObject *x) { val JsProxy_AsVal(PyObject *x) {
...@@ -162,5 +220,6 @@ val JsProxy_AsVal(PyObject *x) { ...@@ -162,5 +220,6 @@ val JsProxy_AsVal(PyObject *x) {
} }
int JsProxy_Ready() { int JsProxy_Ready() {
return PyType_Ready(&JsProxyType); return (PyType_Ready(&JsProxyType) ||
PyType_Ready(&JsBoundMethodType));
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
#include <emscripten/bind.h> #include <emscripten/bind.h>
#include <emscripten/val.h> #include <emscripten/val.h>
PyObject *JsProxy_cnew(emscripten::val v, emscripten::val *parent, const char *name); PyObject *JsProxy_cnew(emscripten::val v);
int JsProxy_Check(PyObject *x); int JsProxy_Check(PyObject *x);
emscripten::val JsProxy_AsVal(PyObject *x); emscripten::val JsProxy_AsVal(PyObject *x);
int JsProxy_Ready(); int JsProxy_Ready();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment