Commit 527a2e79 authored by Michael Droettboom's avatar Michael Droettboom

Fix jsproxy

parent 47b2d515
......@@ -5,10 +5,7 @@
using emscripten::val;
// TODO: Bound methods should probably have their own class, rather than using
// JsProxy for everything
static PyObject *JsBoundMethod_cnew(val v, val this_, const char *name);
static PyObject *JsBoundMethod_cnew(val this_, const char *name);
////////////////////////////////////////////////////////////
// JsProxy
......@@ -34,11 +31,17 @@ static PyObject *JsProxy_GetAttr(PyObject *o, PyObject *attr_name) {
}
std::string s(PyUnicode_AsUTF8(str));
if (s == "new") {
Py_DECREF(str);
return PyObject_GenericGetAttr(o, attr_name);
}
val v = (*self->js)[s];
Py_DECREF(str);
if (v.typeof().equals(val("function"))) {
return JsBoundMethod_cnew(v, *self->js, s.c_str());
return JsBoundMethod_cnew(*self->js, s.c_str());
}
return jsToPython(v);
......@@ -60,6 +63,7 @@ static int JsProxy_SetAttr(PyObject *o, PyObject *attr_name, PyObject *value) {
return 0;
}
static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
JsProxy *self = (JsProxy *)o;
......@@ -101,6 +105,88 @@ static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
return NULL;
}
static PyObject* JsProxy_New(PyObject *o, PyObject *args, PyObject *kwargs) {
JsProxy *self = (JsProxy *)o;
Py_ssize_t nargs = PyTuple_Size(args);
// TODO: There's probably some way to not have to explicitly expand arguments
// here.
switch (nargs) {
case 0:
return jsToPython((*self->js).new_());
case 1:
return jsToPython((*self->js)
.new_(pythonToJs(PyTuple_GET_ITEM(args, 0))));
case 2:
return jsToPython((*self->js)
.new_(pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1))));
case 3:
return jsToPython((*self->js)
.new_(pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2))));
case 4:
return jsToPython((*self->js)
.new_(pythonToJs(PyTuple_GET_ITEM(args, 0)),
pythonToJs(PyTuple_GET_ITEM(args, 1)),
pythonToJs(PyTuple_GET_ITEM(args, 2)),
pythonToJs(PyTuple_GET_ITEM(args, 3))));
case 5:
return jsToPython((*self->js)
.new_(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))));
}
PyErr_SetString(PyExc_TypeError, "Too many arguments to function");
return NULL;
}
Py_ssize_t JsProxy_length(PyObject *o) {
JsProxy *self = (JsProxy *)o;
return (*self->js)["length"].as<Py_ssize_t>();
}
PyObject* JsProxy_item(PyObject *o, Py_ssize_t idx) {
JsProxy *self = (JsProxy *)o;
val v = (*self->js)[idx];
return jsToPython(v);
}
int JsProxy_ass_item(PyObject *o, Py_ssize_t idx, PyObject *value) {
JsProxy *self = (JsProxy *)o;
val js_value = pythonToJs(value);
(*self->js).set(idx, js_value);
return 0;
}
static PySequenceMethods JsProxy_SequenceMethods = {
JsProxy_length,
NULL,
NULL,
JsProxy_item,
NULL,
JsProxy_ass_item,
NULL,
NULL,
NULL,
NULL
};
static PyMethodDef JsProxy_Methods[] = {
{"new", (PyCFunction)JsProxy_New, METH_VARARGS|METH_KEYWORDS, "Construct a new instance"},
{ NULL }
};
static PyTypeObject JsProxyType = {
.tp_name = "JsProxy",
.tp_basicsize = sizeof(JsProxy),
......@@ -109,7 +195,9 @@ static PyTypeObject JsProxyType = {
.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"
.tp_doc = "A proxy to make a Javascript object behave like a Python object",
.tp_methods = JsProxy_Methods,
.tp_as_sequence = &JsProxy_SequenceMethods
};
PyObject *JsProxy_cnew(val v) {
......@@ -125,16 +213,16 @@ PyObject *JsProxy_cnew(val v) {
//
// A special class for bound methods
const size_t BOUND_METHOD_NAME_SIZE = 256;
typedef struct {
JsProxy head;
PyObject_HEAD
val *this_;
char *name;
char name[BOUND_METHOD_NAME_SIZE];
} JsBoundMethod;
static void JsBoundMethod_dealloc(JsBoundMethod *self) {
delete self->head.js;
delete self->this_;
free(self->name);
Py_TYPE(self)->tp_free((PyObject *)self);
}
......@@ -194,15 +282,11 @@ static PyTypeObject JsBoundMethodType = {
.tp_doc = "A proxy to make it possible to call Javascript bound methods from Python."
};
static PyObject *JsBoundMethod_cnew(val v, val this_, const char *name) {
static PyObject *JsBoundMethod_cnew(val this_, const char *name) {
JsBoundMethod *self;
self = (JsBoundMethod *)JsBoundMethodType.tp_alloc(&JsBoundMethodType, 0);
self->head.js = new val(v);
self->this_ = new val(this_);
size_t n = strnlen(name, 256);
char *copy = (char *)malloc(n + 1);
strncpy(copy, name, n + 1);
self->name = copy;
strncpy(self->name, name, BOUND_METHOD_NAME_SIZE);
return (PyObject *)self;
}
......@@ -216,7 +300,7 @@ int JsProxy_Check(PyObject *x) {
val JsProxy_AsVal(PyObject *x) {
JsProxy *js_proxy = (JsProxy *)x;
return val(*(js_proxy->js));
return *(js_proxy->js);
}
int JsProxy_Ready() {
......
......@@ -69,15 +69,10 @@ var languagePluginLoader = new Promise((resolve, reject) => {
let makeCallableProxy = (obj) => {
var clone = obj.clone();
return (args, kwargs) => {
if (args === undefined) {
args = [];
}
if (kwargs === undefined) {
kwargs = {};
}
return clone.call(args, kwargs);
function callProxy(args) {
return clone.call(Array.from(arguments), {});
};
return callProxy;
};
let wasmURL = `${baseURL}pyodide.asm.wasm`;
......
......@@ -52,6 +52,16 @@ def test_py_proxy(selenium):
assert selenium.run("hasattr(f, 'baz')") == False
def test_js_proxy(selenium):
assert selenium.run(
"from js import document\n"
"el = document.createElement('div')\n"
"document.body.appendChild(el)\n"
"document.body.children.length\n"
) == 1
assert selenium.run(
"document.body.children[0].tagName") == 'DIV'
def test_open_url(selenium):
assert selenium.run(
......
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