diff --git a/Makefile b/Makefile index abcbcfb76..afd86b414 100644 --- a/Makefile +++ b/Makefile @@ -210,6 +210,7 @@ dist/test.tar: $(CPYTHONLIB) node_modules/.installed for testname in $(TEST_EXTENSIONS); do \ cd $(CPYTHONBUILD) && \ emcc Modules/$${testname%.*}.o -o $$testname $(SIDE_MODULE_LDFLAGS) && \ + rm -f $(CPYTHONLIB)/$$testname && \ ln -s $(CPYTHONBUILD)/$$testname $(CPYTHONLIB)/$$testname ; \ done diff --git a/docs/project/changelog.md b/docs/project/changelog.md index e7ac35afb..964d8faaa 100644 --- a/docs/project/changelog.md +++ b/docs/project/changelog.md @@ -61,6 +61,9 @@ substitutions: translated to negative Python ints. {pr}`2484` +- {{ Fix }} Pyodide now correctly handles JavaScript objects with `null` constructor. + {pr}`2520` + - {{ Fix }} Fix garbage collection of `once_callable` {pr}`2401` - {{ Enhancement }} `run_in_pyodide` now has support for pytest assertion diff --git a/src/core/error_handling.ts b/src/core/error_handling.ts index 9c30acbca..5f1f758a6 100644 --- a/src/core/error_handling.ts +++ b/src/core/error_handling.ts @@ -194,6 +194,9 @@ function convertCppException(ptr: number): CppException { Tests.convertCppException = convertCppException; function isPyodideFrame(frame: ErrorStackParser.StackFrame): boolean { + if (!frame) { + return false; + } const fileName = frame.fileName || ""; if (fileName.includes("wasm-function")) { return true; diff --git a/src/core/hiwire.c b/src/core/hiwire.c index 5c84c632b..7af917aa8 100644 --- a/src/core/hiwire.c +++ b/src/core/hiwire.c @@ -712,7 +712,7 @@ EM_JS_REF(JsRef, hiwire_to_string, (JsRef idobj), { return Hiwire.new_value(Hiwire.get_value(idobj).toString()); }); -EM_JS_REF(JsRef, hiwire_typeof, (JsRef idobj), { +EM_JS(JsRef, hiwire_typeof, (JsRef idobj), { return Hiwire.new_value(typeof Hiwire.get_value(idobj)); }); @@ -781,7 +781,7 @@ EM_JS_REF(JsRef, JsObject_Values, (JsRef idobj), { EM_JS(bool, hiwire_is_typedarray, (JsRef idobj), { let jsobj = Hiwire.get_value(idobj); // clang-format off - return ArrayBuffer.isView(jsobj) || jsobj.constructor.name === "ArrayBuffer"; + return ArrayBuffer.isView(jsobj) || (jsobj.constructor && jsobj.constructor.name === "ArrayBuffer"); // clang-format on }); diff --git a/src/core/jsproxy.c b/src/core/jsproxy.c index 2db3d3e85..5221df4bc 100644 --- a/src/core/jsproxy.c +++ b/src/core/jsproxy.c @@ -119,6 +119,12 @@ static PyObject* JsProxy_Repr(PyObject* self) { JsRef idrepr = hiwire_to_string(JsProxy_REF(self)); + if (idrepr == NULL) { + PyErr_Format(PyExc_TypeError, + "Pyodide cannot generate a repr for this Javascript object " + "because it has no 'toString' method"); + return NULL; + } PyObject* pyrepr = js2python(idrepr); hiwire_decref(idrepr); return pyrepr; diff --git a/src/tests/test_typeconversions.py b/src/tests/test_typeconversions.py index 8ce811886..6012e8135 100644 --- a/src/tests/test_typeconversions.py +++ b/src/tests/test_typeconversions.py @@ -1425,6 +1425,17 @@ def test_buffer_format_string(selenium): assert array_name == expected_array_name +@run_in_pyodide +def test_object_with_null_constructor(): + from unittest import TestCase + + from js import eval as run_js + + o = run_js("Object.create(null)") + with TestCase().assertRaises(TypeError): + repr(o) + + def test_dict_converter_cache(selenium): selenium.run_js( """