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

Fix jsproxy

parent 47b2d515
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
using emscripten::val; using emscripten::val;
// TODO: Bound methods should probably have their own class, rather than using static PyObject *JsBoundMethod_cnew(val this_, const char *name);
// JsProxy for everything
static PyObject *JsBoundMethod_cnew(val v, val this_, const char *name);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
// JsProxy // JsProxy
...@@ -34,11 +31,17 @@ static PyObject *JsProxy_GetAttr(PyObject *o, PyObject *attr_name) { ...@@ -34,11 +31,17 @@ static PyObject *JsProxy_GetAttr(PyObject *o, PyObject *attr_name) {
} }
std::string s(PyUnicode_AsUTF8(str)); std::string s(PyUnicode_AsUTF8(str));
if (s == "new") {
Py_DECREF(str);
return PyObject_GenericGetAttr(o, attr_name);
}
val v = (*self->js)[s]; val v = (*self->js)[s];
Py_DECREF(str); Py_DECREF(str);
if (v.typeof().equals(val("function"))) { 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); return jsToPython(v);
...@@ -60,6 +63,7 @@ static int JsProxy_SetAttr(PyObject *o, PyObject *attr_name, PyObject *value) { ...@@ -60,6 +63,7 @@ static int JsProxy_SetAttr(PyObject *o, PyObject *attr_name, PyObject *value) {
return 0; return 0;
} }
static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) { static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
JsProxy *self = (JsProxy *)o; JsProxy *self = (JsProxy *)o;
...@@ -101,6 +105,88 @@ static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) { ...@@ -101,6 +105,88 @@ static PyObject* JsProxy_Call(PyObject *o, PyObject *args, PyObject *kwargs) {
return NULL; 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 = { static PyTypeObject JsProxyType = {
.tp_name = "JsProxy", .tp_name = "JsProxy",
.tp_basicsize = sizeof(JsProxy), .tp_basicsize = sizeof(JsProxy),
...@@ -109,7 +195,9 @@ static PyTypeObject JsProxyType = { ...@@ -109,7 +195,9 @@ static PyTypeObject JsProxyType = {
.tp_getattro = JsProxy_GetAttr, .tp_getattro = JsProxy_GetAttr,
.tp_setattro = JsProxy_SetAttr, .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 a Javascript object behave like a Python object",
.tp_methods = JsProxy_Methods,
.tp_as_sequence = &JsProxy_SequenceMethods
}; };
PyObject *JsProxy_cnew(val v) { PyObject *JsProxy_cnew(val v) {
...@@ -125,16 +213,16 @@ PyObject *JsProxy_cnew(val v) { ...@@ -125,16 +213,16 @@ PyObject *JsProxy_cnew(val v) {
// //
// A special class for bound methods // A special class for bound methods
const size_t BOUND_METHOD_NAME_SIZE = 256;
typedef struct { typedef struct {
JsProxy head; PyObject_HEAD
val *this_; val *this_;
char *name; char name[BOUND_METHOD_NAME_SIZE];
} JsBoundMethod; } JsBoundMethod;
static void JsBoundMethod_dealloc(JsBoundMethod *self) { static void JsBoundMethod_dealloc(JsBoundMethod *self) {
delete self->head.js;
delete self->this_; delete self->this_;
free(self->name);
Py_TYPE(self)->tp_free((PyObject *)self); Py_TYPE(self)->tp_free((PyObject *)self);
} }
...@@ -194,15 +282,11 @@ static PyTypeObject JsBoundMethodType = { ...@@ -194,15 +282,11 @@ static PyTypeObject JsBoundMethodType = {
.tp_doc = "A proxy to make it possible to call Javascript bound methods from Python." .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; JsBoundMethod *self;
self = (JsBoundMethod *)JsBoundMethodType.tp_alloc(&JsBoundMethodType, 0); self = (JsBoundMethod *)JsBoundMethodType.tp_alloc(&JsBoundMethodType, 0);
self->head.js = new val(v);
self->this_ = new val(this_); self->this_ = new val(this_);
size_t n = strnlen(name, 256); strncpy(self->name, name, BOUND_METHOD_NAME_SIZE);
char *copy = (char *)malloc(n + 1);
strncpy(copy, name, n + 1);
self->name = copy;
return (PyObject *)self; return (PyObject *)self;
} }
...@@ -216,7 +300,7 @@ int JsProxy_Check(PyObject *x) { ...@@ -216,7 +300,7 @@ int JsProxy_Check(PyObject *x) {
val JsProxy_AsVal(PyObject *x) { val JsProxy_AsVal(PyObject *x) {
JsProxy *js_proxy = (JsProxy *)x; JsProxy *js_proxy = (JsProxy *)x;
return val(*(js_proxy->js)); return *(js_proxy->js);
} }
int JsProxy_Ready() { int JsProxy_Ready() {
......
...@@ -69,15 +69,10 @@ var languagePluginLoader = new Promise((resolve, reject) => { ...@@ -69,15 +69,10 @@ var languagePluginLoader = new Promise((resolve, reject) => {
let makeCallableProxy = (obj) => { let makeCallableProxy = (obj) => {
var clone = obj.clone(); var clone = obj.clone();
return (args, kwargs) => { function callProxy(args) {
if (args === undefined) { return clone.call(Array.from(arguments), {});
args = [];
}
if (kwargs === undefined) {
kwargs = {};
}
return clone.call(args, kwargs);
}; };
return callProxy;
}; };
let wasmURL = `${baseURL}pyodide.asm.wasm`; let wasmURL = `${baseURL}pyodide.asm.wasm`;
......
...@@ -52,6 +52,16 @@ def test_py_proxy(selenium): ...@@ -52,6 +52,16 @@ def test_py_proxy(selenium):
assert selenium.run("hasattr(f, 'baz')") == False 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): def test_open_url(selenium):
assert selenium.run( 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