1 /*****************************************************************************
2 
3   Copyright (c) 2001, 2002 Zope Foundation and Contributors.
4   All Rights Reserved.
5 
6   This software is subject to the provisions of the Zope Public License,
7   Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9   WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10   WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11   FOR A PARTICULAR PURPOSE
12 
13 ****************************************************************************/
14 
15 /* Strings initialized by init_strings() below. */
16 static PyObject *py___slotnames__, *copy_reg_slotnames, *__newobj__;
17 static PyObject *py___getnewargs__, *py___getstate__;
18 
19 
20 static int
pickle_setup(void)21 pickle_setup(void)
22 {
23     PyObject* copy_reg;
24 
25 #define INIT_STRING(S) if (!(py_ ## S = INTERN(#S))) return -1;
26     INIT_STRING(__slotnames__);
27     INIT_STRING(__getnewargs__);
28     INIT_STRING(__getstate__);
29 #undef INIT_STRING
30 
31 #ifdef PY3K
32     copy_reg = PyImport_ImportModule("copyreg");
33 #else
34     copy_reg = PyImport_ImportModule("copy_reg");
35 #endif
36 
37     if (!copy_reg) {
38         return -1;
39     }
40 
41     copy_reg_slotnames = PyObject_GetAttrString(copy_reg, "_slotnames");
42     if (!copy_reg_slotnames)
43     {
44       Py_DECREF(copy_reg);
45       return -1;
46     }
47 
48     __newobj__ = PyObject_GetAttrString(copy_reg, "__newobj__");
49     if (!__newobj__)
50     {
51       Py_DECREF(copy_reg);
52       return -1;
53     }
54 
55   return 0;
56 }
57 
58 static PyObject * pickle_slotnames(PyTypeObject *cls);
59 static PyObject * convert_name(PyObject *name);
60 
61 
62 
63 /****************************************************************************/
64 
65 
66 static PyObject *
pickle_slotnames(PyTypeObject * cls)67 pickle_slotnames(PyTypeObject *cls)
68 {
69     PyObject *slotnames;
70 
71     slotnames = PyDict_GetItem(cls->tp_dict, py___slotnames__);
72     if (slotnames)
73     {
74         int n = PyObject_Not(slotnames);
75         if (n < 0)
76             return NULL;
77         if (n)
78             slotnames = Py_None;
79 
80         Py_INCREF(slotnames);
81         return slotnames;
82     }
83 
84     slotnames = PyObject_CallFunctionObjArgs(copy_reg_slotnames,
85                                             (PyObject*)cls, NULL);
86     if (slotnames && !(slotnames == Py_None || PyList_Check(slotnames)))
87     {
88         PyErr_SetString(PyExc_TypeError,
89                         "copy_reg._slotnames didn't return a list or None");
90         Py_DECREF(slotnames);
91         return NULL;
92     }
93 
94     return slotnames;
95 }
96 
97 static PyObject *
pickle_copy_dict(PyObject * state)98 pickle_copy_dict(PyObject *state)
99 {
100     PyObject *copy, *key, *value;
101     char *ckey;
102     Py_ssize_t pos = 0;
103 
104     copy = PyDict_New();
105     if (!copy)
106         return NULL;
107 
108     if (!state)
109         return copy;
110 
111     while (PyDict_Next(state, &pos, &key, &value))
112     {
113         int is_special;
114 #ifdef PY3K
115         if (key && PyUnicode_Check(key))
116         {
117             PyObject *converted = convert_name(key);
118             ckey = PyBytes_AS_STRING(converted);
119 #else
120         if (key && PyBytes_Check(key))
121         {
122             ckey = PyBytes_AS_STRING(key);
123 #endif
124             is_special = (*ckey == '_' &&
125                           (ckey[1] == 'v' || ckey[1] == 'p') &&
126                            ckey[2] == '_');
127 #ifdef PY3K
128             Py_DECREF(converted);
129 #endif
130             if (is_special) /* skip volatile and persistent */
131                 continue;
132         }
133 
134         if (PyObject_SetItem(copy, key, value) < 0)
135             goto err;
136     }
137 
138     return copy;
139 err:
140     Py_DECREF(copy);
141     return NULL;
142 }
143 
144 
145 static char pickle___getstate__doc[] =
146   "Get the object serialization state\n"
147   "\n"
148   "If the object has no assigned slots and has no instance dictionary, then \n"
149   "None is returned.\n"
150   "\n"
151   "If the object has no assigned slots and has an instance dictionary, then \n"
152   "the a copy of the instance dictionary is returned. The copy has any items \n"
153   "with names starting with '_v_' or '_p_' ommitted.\n"
154   "\n"
155   "If the object has assigned slots, then a two-element tuple is returned.  \n"
156   "The first element is either None or a copy of the instance dictionary, \n"
157   "as described above. The second element is a dictionary with items \n"
158   "for each of the assigned slots.\n"
159   ;
160 
161 static PyObject *
162 pickle___getstate__(PyObject *self)
163 {
164     PyObject *slotnames=NULL, *slots=NULL, *state=NULL;
165     PyObject **dictp;
166     int n=0;
167 
168     slotnames = pickle_slotnames(Py_TYPE(self));
169     if (!slotnames)
170         return NULL;
171 
172     dictp = _PyObject_GetDictPtr(self);
173     if (dictp)
174         state = pickle_copy_dict(*dictp);
175     else
176     {
177         state = Py_None;
178         Py_INCREF(state);
179     }
180 
181     if (slotnames != Py_None)
182     {
183         int i;
184 
185         slots = PyDict_New();
186         if (!slots)
187             goto end;
188 
189         for (i = 0; i < PyList_GET_SIZE(slotnames); i++)
190         {
191             PyObject *name, *value;
192             char *cname;
193             int is_special;
194 
195             name = PyList_GET_ITEM(slotnames, i);
196 #ifdef PY3K
197             if (PyUnicode_Check(name))
198             {
199                 PyObject *converted = convert_name(name);
200                 cname = PyBytes_AS_STRING(converted);
201 #else
202             if (PyBytes_Check(name))
203             {
204                 cname = PyBytes_AS_STRING(name);
205 #endif
206                 is_special = (*cname == '_' &&
207                               (cname[1] == 'v' || cname[1] == 'p') &&
208                                cname[2] == '_');
209 #ifdef PY3K
210                 Py_DECREF(converted);
211 #endif
212                 if (is_special) /* skip volatile and persistent */
213                 {
214                     continue;
215                 }
216             }
217 
218             /* Unclear:  Will this go through our getattr hook? */
219             value = PyObject_GetAttr(self, name);
220             if (value == NULL)
221                 PyErr_Clear();
222             else
223             {
224                 int err = PyDict_SetItem(slots, name, value);
225                 Py_DECREF(value);
226                 if (err < 0)
227                     goto end;
228                 n++;
229             }
230         }
231     }
232 
233     if (n)
234         state = Py_BuildValue("(NO)", state, slots);
235 
236 end:
237     Py_XDECREF(slotnames);
238     Py_XDECREF(slots);
239 
240     return state;
241 }
242 
243 static int
244 pickle_setattrs_from_dict(PyObject *self, PyObject *dict)
245 {
246     PyObject *key, *value;
247     Py_ssize_t pos = 0;
248 
249     if (!PyDict_Check(dict))
250     {
251         PyErr_SetString(PyExc_TypeError, "Expected dictionary");
252         return -1;
253     }
254 
255     while (PyDict_Next(dict, &pos, &key, &value))
256     {
257         if (PyObject_SetAttr(self, key, value) < 0)
258             return -1;
259     }
260     return 0;
261 }
262 
263 static char pickle___setstate__doc[] =
264   "Set the object serialization state\n\n"
265   "The state should be in one of 3 forms:\n\n"
266   "- None\n\n"
267   "  Ignored\n\n"
268   "- A dictionary\n\n"
269   "  In this case, the object's instance dictionary will be cleared and \n"
270   "  updated with the new state.\n\n"
271   "- A two-tuple with a string as the first element. \n\n"
272   "  In this case, the method named by the string in the first element will\n"
273   "  be called with the second element.\n\n"
274   "  This form supports migration of data formats.\n\n"
275   "- A two-tuple with None or a Dictionary as the first element and\n"
276   "  with a dictionary as the second element.\n\n"
277   "  If the first element is not None, then the object's instance dictionary \n"
278   "  will be cleared and updated with the value.\n\n"
279   "  The items in the second element will be assigned as attributes.\n"
280   ;
281 
282 static PyObject *
283 pickle___setstate__(PyObject *self, PyObject *state)
284 {
285     PyObject *slots=NULL;
286 
287     if (PyTuple_Check(state))
288     {
289         if (!PyArg_ParseTuple(state, "OO:__setstate__", &state, &slots))
290             return NULL;
291     }
292 
293     if (state != Py_None)
294     {
295         PyObject **dict;
296         PyObject *d_key, *d_value;
297         Py_ssize_t i;
298 
299         dict = _PyObject_GetDictPtr(self);
300 
301         if (!dict)
302         {
303             PyErr_SetString(PyExc_TypeError,
304                             "this object has no instance dictionary");
305             return NULL;
306         }
307 
308         if (!*dict)
309         {
310             *dict = PyDict_New();
311             if (!*dict)
312                 return NULL;
313         }
314 
315         PyDict_Clear(*dict);
316 
317         i = 0;
318         while (PyDict_Next(state, &i, &d_key, &d_value)) {
319             /* normally the keys for instance attributes are
320                interned.  we should try to do that here. */
321             if (NATIVE_CHECK_EXACT(d_key)) {
322                 Py_INCREF(d_key);
323                 INTERN_INPLACE(&d_key);
324                 Py_DECREF(d_key);
325             }
326             if (PyObject_SetItem(*dict, d_key, d_value) < 0)
327                 return NULL;
328         }
329     }
330 
331     if (slots && pickle_setattrs_from_dict(self, slots) < 0)
332         return NULL;
333 
334     Py_INCREF(Py_None);
335     return Py_None;
336 }
337 
338 static char pickle___reduce__doc[] =
339   "Reduce an object to contituent parts for serialization\n"
340   ;
341 
342 static PyObject *
343 pickle___reduce__(PyObject *self)
344 {
345     PyObject *args=NULL, *bargs=NULL, *state=NULL, *getnewargs=NULL;
346     int l, i;
347 
348     getnewargs = PyObject_GetAttr(self, py___getnewargs__);
349     if (getnewargs)
350     {
351         bargs = PyObject_CallFunctionObjArgs(getnewargs, NULL);
352         Py_DECREF(getnewargs);
353         if (!bargs)
354             return NULL;
355         l = PyTuple_Size(bargs);
356         if (l < 0)
357             goto end;
358     }
359     else
360     {
361         PyErr_Clear();
362         l = 0;
363     }
364 
365     args = PyTuple_New(l+1);
366     if (args == NULL)
367         goto end;
368 
369     Py_INCREF(Py_TYPE(self));
370     PyTuple_SET_ITEM(args, 0, (PyObject*)(Py_TYPE(self)));
371     for (i = 0; i < l; i++)
372     {
373         Py_INCREF(PyTuple_GET_ITEM(bargs, i));
374         PyTuple_SET_ITEM(args, i+1, PyTuple_GET_ITEM(bargs, i));
375     }
376 
377     state = PyObject_CallMethodObjArgs(self, py___getstate__, NULL);
378     if (!state)
379         goto end;
380 
381     state = Py_BuildValue("(OON)", __newobj__, args, state);
382 
383 end:
384     Py_XDECREF(bargs);
385     Py_XDECREF(args);
386 
387     return state;
388 }
389 
390 /* convert_name() returns a new reference to a string name
391    or sets an exception and returns NULL.
392 */
393 
394 static PyObject *
395 convert_name(PyObject *name)
396 {
397 #ifdef Py_USING_UNICODE
398   /* The Unicode to string conversion is done here because the
399      existing tp_setattro slots expect a string object as name
400      and we wouldn't want to break those. */
401     if (PyUnicode_Check(name))
402     {
403         name = PyUnicode_AsEncodedString(name, NULL, NULL);
404     }
405     else
406 #endif
407         if (!PyBytes_Check(name))
408         {
409             PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
410             return NULL;
411         }
412         else
413             Py_INCREF(name);
414     return name;
415 }
416 
417 #define PICKLE_GETSTATE_DEF \
418 {"__getstate__", (PyCFunction)pickle___getstate__, METH_NOARGS,      \
419    pickle___getstate__doc},
420 
421 #define PICKLE_SETSTATE_DEF \
422 {"__setstate__", (PyCFunction)pickle___setstate__, METH_O,           \
423    pickle___setstate__doc},
424 
425 #define PICKLE_GETNEWARGS_DEF
426 
427 #define PICKLE_REDUCE_DEF \
428 {"__reduce__", (PyCFunction)pickle___reduce__, METH_NOARGS,          \
429    pickle___reduce__doc},
430 
431 #define PICKLE_METHODS PICKLE_GETSTATE_DEF PICKLE_SETSTATE_DEF \
432                        PICKLE_GETNEWARGS_DEF PICKLE_REDUCE_DEF
433