diff --git a/Makefile b/Makefile index a3e6f24db..e2774f926 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ LDFLAGS=$(OPTFLAGS) \ all: build/pyodide.asm.html build/pyodide.js -build/pyodide.asm.html: src/main.bc src/jsproxy.bc src/js2python.bc src/pylocals.bc \ +build/pyodide.asm.html: src/main.bc src/jsimport.bc src/jsproxy.bc src/js2python.bc \ src/pyproxy.bc src/python2js.bc src/runpython.bc root $(CC) -s EXPORT_NAME="'pyodide'" --bind -o $@ $(filter %.bc,$^) $(LDFLAGS) \ $(foreach d,$(wildcard root/*),--preload-file $d@/$(notdir $d)) diff --git a/src/jsimport.cpp b/src/jsimport.cpp new file mode 100644 index 000000000..a5ccade18 --- /dev/null +++ b/src/jsimport.cpp @@ -0,0 +1,104 @@ +#include "jsimport.hpp" + +#include "js2python.hpp" + +using emscripten::val; + +static PyObject *original__import__; +PyObject *globals = NULL; +PyObject *original_globals = NULL; + +typedef struct { + PyObject_HEAD +} JsImport; + +static PyObject *JsImport_Call(PyObject *self, PyObject *args, PyObject *kwargs) { + PyObject *name = PyTuple_GET_ITEM(args, 0); + if (PyUnicode_CompareWithASCIIString(name, "js") == 0) { + PyObject *locals = PyTuple_GET_ITEM(args, 2); + PyObject *fromlist = PyTuple_GET_ITEM(args, 3); + Py_ssize_t n = PySequence_Size(fromlist); + PyObject *jsmod = PyModule_New("js"); + PyObject *d = PyModule_GetDict(jsmod); + for (Py_ssize_t i = 0; i < n; ++i) { + PyObject *key = PySequence_GetItem(fromlist, i); + if (key == NULL) { + return NULL; + } + char *c = PyUnicode_AsUTF8(key); + if (c == NULL) { + Py_DECREF(key); + return NULL; + } + + val jsval = val::global(c); + PyObject *pyval = jsToPython(jsval); + if (PyDict_SetItem(d, key, pyval)) { + Py_DECREF(key); + Py_DECREF(pyval); + return NULL; + } + Py_DECREF(key); + Py_DECREF(pyval); + } + + return jsmod; + } else { + return PyObject_Call(original__import__, args, kwargs); + } +} + +static PyTypeObject JsImportType = { + .tp_name = "JsImport", + .tp_basicsize = sizeof(JsImport), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_call = JsImport_Call, + .tp_doc = "An import hook that imports things from Javascript." +}; + +static PyObject *JsImport_New() { + JsImport *self; + self = (JsImport *)JsImportType.tp_alloc(&JsImportType, 0); + return (PyObject *)self; +} + +int JsImport_Ready() { + if (PyType_Ready(&JsImportType)) { + return 1; + } + + PyObject *m = PyImport_AddModule("builtins"); + if (m == NULL) { + return 1; + } + PyObject *d = PyModule_GetDict(m); + if (d == NULL) { + return 1; + } + original__import__ = PyDict_GetItemString(d, "__import__"); + if (original__import__ == NULL) { + return 1; + } + Py_INCREF(original__import__); + + PyObject *importer = JsImport_New(); + if (importer == NULL) { + return 1; + } + + if (PyDict_SetItemString(d, "__import__", importer)) { + return 1; + } + + m = PyImport_AddModule("__main__"); + if (m == NULL) + return 1; + globals = PyModule_GetDict(m); + + m = PyImport_AddModule("builtins"); + PyDict_Update(globals, PyModule_GetDict(m)); + + original_globals = PyDict_Copy(globals); + + return 0; +} diff --git a/src/jsimport.hpp b/src/jsimport.hpp new file mode 100644 index 000000000..682408e68 --- /dev/null +++ b/src/jsimport.hpp @@ -0,0 +1,10 @@ +#ifndef JSIMPORT_H +#define JSIMPORT_H + +#include + +int JsImport_Ready(); +extern PyObject *globals; +extern PyObject *original_globals; + +#endif /* JSIMPORT_H */ diff --git a/src/main.cpp b/src/main.cpp index 908c92f5e..e69f70cc1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,9 +4,9 @@ #include #include // from CPython +#include "jsimport.hpp" #include "jsproxy.hpp" #include "js2python.hpp" -#include "pylocals.hpp" #include "pyproxy.hpp" #include "python2js.hpp" #include "runpython.hpp" @@ -39,7 +39,7 @@ extern "C" { if (JsProxy_Ready() || jsToPython_Ready() || pythonToJs_Ready() || - PyLocals_Ready()) { + JsImport_Ready()) { return 1; } diff --git a/src/pylocals.cpp b/src/pylocals.cpp deleted file mode 100644 index 98c6b07a7..000000000 --- a/src/pylocals.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "pylocals.hpp" -#include "js2python.hpp" - -#include -#include -#include - -using emscripten::val; - -//////////////////////////////////////////////////////////// -// PyLocals -// -// This is an object designed to be used as a "locals" namespace dictionary. -// It first looks for things in its own internal dictionary, and failing that, -// looks in the Javascript global namespace. This is a way of merging the -// Python and Javascript namespaces without fullying copying either one. - -PyObject *locals = NULL; -PyObject *globals = NULL; -PyObject *original_globals = NULL; - -typedef struct { - PyObject_HEAD - PyObject *locals; -} PyLocals; - -static void PyLocals_dealloc(PyLocals *self) { - Py_DECREF(self->locals); - Py_TYPE(self)->tp_free((PyObject *)self); -} - -static Py_ssize_t PyLocals_length(PyObject *o) { - PyLocals *self = (PyLocals *)o; - - return PyDict_Size(self->locals); -} - -PyObject* PyLocals_get(PyObject *o, PyObject *key) { - PyLocals *self = (PyLocals *)o; - - { - PyObject *str = PyObject_Str(key); - if (str == NULL) { - return NULL; - } - char *c = PyUnicode_AsUTF8(str); - Py_DECREF(str); - } - - PyObject *py_val = PyDict_GetItem(self->locals, key); - if (py_val != NULL) { - Py_INCREF(py_val); - return py_val; - } - - PyObject *str = PyObject_Str(key); - if (str == NULL) { - return NULL; - } - char *c = PyUnicode_AsUTF8(str); - val v = val::global(c); - Py_DECREF(str); - return jsToPython(v); -} - -int PyLocals_set(PyObject *o, PyObject *k, PyObject *v) { - PyLocals *self = (PyLocals *)o; - - if (v == NULL) { - // TODO: This might not actually be here to delete... - return PyDict_DelItem(self->locals, k); - } else { - return PyDict_SetItem(self->locals, k, v); - } -} - -static PyMappingMethods PyLocals_as_mapping = { - PyLocals_length, - PyLocals_get, - PyLocals_set -}; - -static PyTypeObject PyLocalsType = { - .tp_name = "PyLocals", - .tp_basicsize = sizeof(PyLocals), - .tp_dealloc = (destructor)PyLocals_dealloc, - .tp_as_mapping = &PyLocals_as_mapping, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = "A proxy that looks in a dict first, otherwise in the global JS namespace" -}; - -static PyObject *PyLocals_cnew(PyObject *d) -{ - PyLocals *self; - self = (PyLocals *)PyLocalsType.tp_alloc(&PyLocalsType, 0); - if (self != NULL) { - Py_INCREF(d); - self->locals = d; - } - - return (PyObject *)self; -} - -int PyLocals_Ready() { - if (PyType_Ready(&PyLocalsType) < 0) - return 1; - - PyObject *m = PyImport_AddModule("__main__"); - if (m == NULL) - return 1; - globals = PyModule_GetDict(m); - - m = PyImport_AddModule("builtins"); - PyDict_Update(globals, PyModule_GetDict(m)); - - original_globals = PyDict_Copy(globals); - - locals = PyLocals_cnew(globals); - if (locals == NULL) - return 1; - - return 0; -} diff --git a/src/pylocals.hpp b/src/pylocals.hpp deleted file mode 100644 index 51f31e788..000000000 --- a/src/pylocals.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef PYLOCALS_H -#define PYLOCALS_H - -#include - -int PyLocals_Ready(); - -extern PyObject *locals; -extern PyObject *globals; -extern PyObject *original_globals; - -#endif /* PYLOCALS_H */ diff --git a/src/runpython.cpp b/src/runpython.cpp index 17973c19b..b122f59c2 100644 --- a/src/runpython.cpp +++ b/src/runpython.cpp @@ -6,7 +6,7 @@ #include #include // from Python -#include "pylocals.hpp" +#include "jsimport.hpp" #include "python2js.hpp" using emscripten::val; @@ -57,7 +57,7 @@ val runPython(std::wstring code) { *last_line = 0; last_line++; } - ret = PyRun_StringFlags(&*code_utf8.begin(), Py_file_input, globals, locals, &cf); + ret = PyRun_StringFlags(&*code_utf8.begin(), Py_file_input, globals, globals, &cf); if (ret == NULL) { return pythonExcToJs(); } @@ -70,10 +70,10 @@ val runPython(std::wstring code) { ret = Py_None; break; case 1: - ret = PyRun_StringFlags(&*last_line, Py_eval_input, globals, locals, &cf); + ret = PyRun_StringFlags(&*last_line, Py_eval_input, globals, globals, &cf); break; case 2: - ret = PyRun_StringFlags(&*last_line, Py_file_input, globals, locals, &cf); + ret = PyRun_StringFlags(&*last_line, Py_file_input, globals, globals, &cf); break; } @@ -81,19 +81,6 @@ val runPython(std::wstring code) { return pythonExcToJs(); } - // Now copy all the variables over to the Javascript side - { - val js_globals = val::global("window"); - PyObject *k, *v; - Py_ssize_t pos = 0; - - while (PyDict_Next(globals, &pos, &k, &v)) { - if (!PyDict_Contains(original_globals, k)) { - js_globals.set(pythonToJs(k), pythonToJs(v)); - } - } - } - val result = pythonToJs(ret); Py_DECREF(ret); return result;