@@ -49,46 +49,64 @@ static PyObjectProxyHandler pyObjectProxyHandler;
4949static PyListProxyHandler pyListProxyHandler;
5050static PyIterableProxyHandler pyIterableProxyHandler;
5151
52- std::unordered_map<const char16_t *, PyObject *> ucs2ToPyObjectMap; // a map of char16_t (UCS-2) buffers to their corresponding PyObjects, used when finalizing JSExternalStrings
53- std::unordered_map<const JS::Latin1Char *, PyObject *> latin1ToPyObjectMap; // a map of Latin-1 char buffers to their corresponding PyObjects, used when finalizing JSExternalStrings
52+ std::unordered_map<PyObject *, size_t > jsExternalStringPyObjects;// a map of python string objects to the number of JSExternalStrings that depend on it, used when finalizing JSExternalStrings
5453
5554PyObject *PythonExternalString::getPyString (const char16_t *chars)
5655{
57- return ucs2ToPyObjectMap[chars];
56+ for (auto it: jsExternalStringPyObjects) {
57+ if (PyUnicode_DATA (it.first ) == (void *)chars) {
58+ return it.first ;
59+ }
60+ }
61+
62+ return NULL ; // this shouldn't be reachable
5863}
5964
6065PyObject *PythonExternalString::getPyString (const JS::Latin1Char *chars)
6166{
62- return latin1ToPyObjectMap[chars];
67+
68+ return PythonExternalString::getPyString ((const char16_t *)chars);
6369}
6470
6571void PythonExternalString::finalize (char16_t *chars) const
6672{
6773 // We cannot call Py_DECREF here when shutting down as the thread state is gone.
6874 // Then, when shutting down, there is only on reference left, and we don't need
6975 // to free the object since the entire process memory is being released.
70- PyObject *object = ucs2ToPyObjectMap[chars];
71- if (Py_REFCNT (object) > 1 ) {
72- Py_DECREF (object);
76+ if (_Py_IsFinalizing ()) { return ; }
77+
78+ for (auto it = jsExternalStringPyObjects.cbegin (), next_it = it; it != jsExternalStringPyObjects.cend (); it = next_it) {
79+ next_it++;
80+ if (PyUnicode_DATA (it->first ) == (void *)chars) {
81+ Py_DECREF (it->first );
82+ jsExternalStringPyObjects[it->first ] = jsExternalStringPyObjects[it->first ] - 1 ;
83+
84+ if (jsExternalStringPyObjects[it->first ] == 0 ) {
85+ jsExternalStringPyObjects.erase (it);
86+ }
87+ }
7388 }
7489}
7590
7691void PythonExternalString::finalize (JS::Latin1Char *chars) const
7792{
78- PyObject *object = latin1ToPyObjectMap[chars];
79- if (Py_REFCNT (object) > 1 ) {
80- Py_DECREF (object);
81- }
93+ PythonExternalString::finalize ((char16_t *)chars);
8294}
8395
8496size_t PythonExternalString::sizeOfBuffer (const char16_t *chars, mozilla::MallocSizeOf mallocSizeOf) const
8597{
86- return PyUnicode_GetLength (ucs2ToPyObjectMap[chars]);
98+ for (auto it: jsExternalStringPyObjects) {
99+ if (PyUnicode_DATA (it.first ) == (void *)chars) {
100+ return PyUnicode_GetLength (it.first );
101+ }
102+ }
103+
104+ return 0 ; // // this shouldn't be reachable
87105}
88106
89107size_t PythonExternalString::sizeOfBuffer (const JS::Latin1Char *chars, mozilla::MallocSizeOf mallocSizeOf) const
90108{
91- return PyUnicode_GetLength (latin1ToPyObjectMap[ chars] );
109+ return PythonExternalString::sizeOfBuffer (( const char16_t *) chars, mallocSizeOf );
92110}
93111
94112PythonExternalString PythonExternalStringCallbacks = {};
@@ -151,21 +169,22 @@ JS::Value jsTypeFactory(JSContext *cx, PyObject *object) {
151169 break ;
152170 }
153171 case (PyUnicode_2BYTE_KIND): {
154- ucs2ToPyObjectMap[(char16_t *)PyUnicode_2BYTE_DATA (object)] = object;
172+ jsExternalStringPyObjects[object] = jsExternalStringPyObjects[object] + 1 ;
173+ Py_INCREF (object);
155174 JSString *str = JS_NewExternalUCString (cx, (char16_t *)PyUnicode_2BYTE_DATA (object), PyUnicode_GET_LENGTH (object), &PythonExternalStringCallbacks);
156175 returnType.setString (str);
157176 break ;
158177 }
159178 case (PyUnicode_1BYTE_KIND): {
160- latin1ToPyObjectMap[(JS::Latin1Char *)PyUnicode_1BYTE_DATA (object)] = object;
179+ jsExternalStringPyObjects[object] = jsExternalStringPyObjects[object] + 1 ;
180+ Py_INCREF (object);
161181 JSString *str = JS_NewExternalStringLatin1 (cx, (JS::Latin1Char *)PyUnicode_1BYTE_DATA (object), PyUnicode_GET_LENGTH (object), &PythonExternalStringCallbacks);
162182 // JSExternalString can now be properly treated as either one-byte or two-byte strings when GCed
163183 // see https://hg.mozilla.org/releases/mozilla-esr128/file/tip/js/src/vm/StringType-inl.h#l785
164184 returnType.setString (str);
165185 break ;
166186 }
167187 }
168- Py_INCREF (object);
169188 }
170189 else if (PyMethod_Check (object) || PyFunction_Check (object) || PyCFunction_Check (object)) {
171190 // can't determine number of arguments for PyCFunctions, so just assume potentially unbounded
0 commit comments