1/*
2 * NOTE: the implementation uses PyDict_* APIs whenever possible and falls
3 * back to the generic PyObject_* APIs otherwise. We don't use the PyMapping_*
4 * APIs because those are incomplete(!).
5 */
6#include "pyobjc.h"
7#import "OC_PythonDictionary.h"
8
9static PyObject* mapTypes = NULL;
10
11/*
12 * OC_PythonDictionaryEnumerator - Enumerator for Python dictionaries
13 *
14 * This class implements an NSEnumerator for proxied Python dictionaries.
15 */
16@interface OC_PythonDictionaryEnumerator : NSEnumerator
17{
18	OC_PythonDictionary* value;
19	BOOL valid;
20	Py_ssize_t pos;
21}
22+ enumeratorWithWrappedDictionary:(OC_PythonDictionary*)value;
23- initWithWrappedDictionary:(OC_PythonDictionary*)value;
24-(void)dealloc;
25
26-(id)nextObject;
27
28@end /* interface OC_PythonDictionaryEnumerator */
29
30
31
32@implementation OC_PythonDictionaryEnumerator
33
34+enumeratorWithWrappedDictionary:(OC_PythonDictionary*)v;
35{
36	return [[[self alloc] initWithWrappedDictionary:v] autorelease];
37}
38
39-initWithWrappedDictionary:(OC_PythonDictionary*)v;
40{
41	self = [super init];
42	if (unlikely(self == nil)) return nil;
43
44	value = [v retain];
45	valid = YES;
46	pos = 0;
47	return self;
48}
49
50-(void)dealloc
51{
52	[value release];
53	[super dealloc];
54}
55
56-(id)nextObject
57{
58	id key = nil;
59
60	if (valid) {
61		valid = [value wrappedKey:&key value:nil atPosition:&pos];
62	}
63	return key;
64}
65
66@end // implementation OC_PythonDictionaryEnumerator
67
68
69@implementation OC_PythonDictionary
70
71+ depythonifyObject:(PyObject*)object
72{
73	Py_ssize_t i, len;
74
75	if (mapTypes == NULL) return NULL;
76
77	len = PyList_GET_SIZE(mapTypes);
78
79	for (i = 0; i < len; i++) {
80		PyObject* tp = PyList_GET_ITEM(mapTypes, i);
81		int r = PyObject_IsInstance(object, tp);
82		if (r == -1) {
83			return NULL;
84		}
85
86		if (!r) continue;
87
88		/* Instance of this type should be pythonifyed as a sequence */
89		return [OC_PythonArray arrayWithPythonObject:object];
90	}
91
92	return NULL;
93}
94
95+ depythonifyTable
96{
97	NSObject* result;
98
99	PyObjC_BEGIN_WITH_GIL
100
101		if (mapTypes == NULL) {
102			mapTypes = PyList_New(0);
103			if (mapTypes == NULL) {
104				PyObjC_GIL_FORWARD_EXC();
105			}
106		}
107		result = PyObjC_PythonToId(mapTypes);
108		if (result == NULL) {
109			PyObjC_GIL_FORWARD_EXC();
110		}
111
112	PyObjC_END_WITH_GIL
113
114	return result;
115}
116
117+dictionaryWithPythonObject:(PyObject*)v;
118{
119	OC_PythonDictionary* res =
120		[[OC_PythonDictionary alloc] initWithPythonObject:v];
121	[res autorelease];
122	return res;
123}
124
125-initWithPythonObject:(PyObject*)v;
126{
127	self = [super init];
128	if (unlikely(self == nil)) return nil;
129
130	Py_INCREF(v);
131	Py_XDECREF(value);
132	value = v;
133	return self;
134}
135
136-(void)release
137{
138	/* See comment in OC_PythonUnicode */
139	PyObjC_BEGIN_WITH_GIL
140		[super release];
141	PyObjC_END_WITH_GIL
142}
143
144
145
146-(void)dealloc
147{
148	PyObjC_BEGIN_WITH_GIL
149		PyObjC_UnregisterObjCProxy(value, self);
150		Py_XDECREF(value);
151
152	PyObjC_END_WITH_GIL
153
154	[super dealloc];
155}
156
157-(PyObject*)__pyobjc_PythonObject__
158{
159	Py_INCREF(value);
160	return value;
161}
162-(PyObject*)__pyobjc_PythonTransient__:(int*)cookie
163{
164	*cookie = 0;
165	Py_INCREF(value);
166	return value;
167}
168
169-(NSUInteger)count
170{
171	Py_ssize_t result;
172
173	PyObjC_BEGIN_WITH_GIL
174		if (likely(PyDict_CheckExact(value))) {
175			result = PyDict_Size(value);
176		} else {
177			result = PyObject_Length(value);
178		}
179
180	PyObjC_END_WITH_GIL
181
182	if (sizeof(Py_ssize_t) > sizeof(NSUInteger)) {
183		if (result > (Py_ssize_t)NSUIntegerMax) {
184			return NSUIntegerMax;
185		}
186	}
187	return result;
188}
189
190-(int)depythonify:(PyObject*)v toId:(id*)datum
191{
192	if (unlikely(depythonify_c_value(@encode(id), v, datum) == -1)) {
193		return -1;
194	}
195	if (unlikely(*datum == nil)) {
196		*datum = [NSNull null];
197	}
198	return 0;
199}
200
201-objectForKey:key
202{
203	PyObject* v;
204	PyObject* k;
205	id result;
206
207	PyObjC_BEGIN_WITH_GIL
208
209		if (unlikely(key == [NSNull null])) {
210			Py_INCREF(Py_None);
211			k = Py_None;
212		} else {
213			k = PyObjC_IdToPython(key);
214			if (k == NULL) {
215				PyObjC_GIL_FORWARD_EXC();
216			}
217		}
218
219		if (likely(PyDict_CheckExact(value))) {
220			v = PyDict_GetItem(value, k);
221			Py_XINCREF(v);
222		} else {
223			v = PyObject_GetItem(value, k);
224		}
225		Py_DECREF(k);
226
227		if (unlikely(v == NULL)) {
228			PyErr_Clear();
229			PyObjC_GIL_RETURN(nil);
230		}
231
232		if (unlikely([self depythonify:v toId:&result] == -1)) {
233			Py_DECREF(v);
234			PyObjC_GIL_FORWARD_EXC();
235		}
236		Py_DECREF(v);
237
238	PyObjC_END_WITH_GIL
239
240	return result;
241}
242
243
244-(void)setObject:val forKey:key
245{
246	PyObject* v = NULL;
247	PyObject* k = NULL;
248	id null = [NSNull null];
249
250	PyObjC_BEGIN_WITH_GIL
251		if (unlikely(val == null)) {
252			Py_INCREF(Py_None);
253			v = Py_None;
254		} else {
255			v = PyObjC_IdToPython(val);
256			if (unlikely(v == NULL)) {
257				PyObjC_GIL_FORWARD_EXC();
258			}
259		}
260
261		if (unlikely(key == nil)) {
262			Py_INCREF(Py_None);
263			k = Py_None;
264		} else {
265			k = PyObjC_IdToPython(key);
266			if (k == NULL) {
267				Py_XDECREF(v);
268				PyObjC_GIL_FORWARD_EXC();
269			}
270		}
271
272		if (likely(PyDict_CheckExact(value))) {
273			if (unlikely(PyDict_SetItem(value, k, v) < 0)) {
274				Py_XDECREF(v);
275				Py_XDECREF(k);
276				PyObjC_GIL_FORWARD_EXC();
277			}
278
279		} else {
280			if (unlikely(PyObject_SetItem(value, k, v) < 0)) {
281				Py_XDECREF(v);
282				Py_XDECREF(k);
283				PyObjC_GIL_FORWARD_EXC();
284			}
285
286		}
287
288		Py_DECREF(v);
289		Py_DECREF(k);
290
291	PyObjC_END_WITH_GIL
292}
293
294-(BOOL)wrappedKey:(id*)keyPtr value:(id*)valuePtr atPosition:(Py_ssize_t*)positionPtr
295{
296	PyObject *pykey = NULL;
297	PyObject *pyvalue = NULL;
298	PyObject **pykeyptr = (keyPtr == nil) ? NULL : &pykey;
299	PyObject **pyvalueptr = (valuePtr == nil) ? NULL : &pyvalue;
300
301	PyObjC_BEGIN_WITH_GIL
302		if (unlikely(!PyDict_Next(value, positionPtr, pykeyptr, pyvalueptr))) {
303			PyObjC_GIL_RETURN(NO);
304		}
305		if (keyPtr) {
306			if (unlikely([self depythonify:pykey toId:keyPtr] == -1)) {
307				PyObjC_GIL_FORWARD_EXC();
308			}
309		}
310		if (likely(valuePtr)) {
311			if (unlikely([self depythonify:pyvalue toId:valuePtr] == -1)) {
312				PyObjC_GIL_FORWARD_EXC();
313			}
314		}
315	PyObjC_END_WITH_GIL
316	return YES;
317}
318
319-(void)removeObjectForKey:key
320{
321	PyObject* k;
322
323	PyObjC_BEGIN_WITH_GIL
324		if (unlikely(key == [NSNull null])) {
325			Py_INCREF(Py_None);
326			k = Py_None;
327		} else {
328			k = PyObjC_IdToPython(key);
329			if (unlikely(k == NULL)) {
330				PyObjC_GIL_FORWARD_EXC();
331			}
332		}
333
334		if (PyDict_CheckExact(value)) {
335			if (unlikely(PyDict_DelItem(value, k) < 0)) {
336				Py_DECREF(k);
337				PyObjC_GIL_FORWARD_EXC();
338			}
339		} else {
340			if (unlikely(PyObject_DelItem(value, k) < 0)) {
341				Py_DECREF(k);
342				PyObjC_GIL_FORWARD_EXC();
343			}
344		}
345		Py_DECREF(k);
346
347	PyObjC_END_WITH_GIL
348}
349
350-(NSEnumerator *)keyEnumerator
351{
352	if (PyDict_CheckExact(value)) {
353		return [OC_PythonDictionaryEnumerator enumeratorWithWrappedDictionary:self];
354	} else {
355		PyObjC_BEGIN_WITH_GIL
356			PyObject* keys = PyObject_CallMethod(value, "keys", NULL);
357			if (keys == NULL) {
358				PyObjC_GIL_FORWARD_EXC();
359			}
360
361			PyObject* iter = PyObject_GetIter(keys);
362			Py_DECREF(keys);
363			if (iter == NULL) {
364				PyObjC_GIL_FORWARD_EXC();
365			}
366
367			NSEnumerator* result = [OC_PythonEnumerator enumeratorWithPythonObject:iter];
368			PyObjC_GIL_RETURN(result);
369
370		PyObjC_END_WITH_GIL
371	}
372}
373
374
375- initWithObjects:(NSObject**)objects
376	  forKeys:(NSObject**)keys
377	    count:(NSUInteger)count
378{
379	/* This implementation is needed for our support for the NSCoding
380	 * protocol, NSDictionary's initWithCoder: will call this method.
381	 */
382	NSUInteger i;
383
384	PyObjC_BEGIN_WITH_GIL
385		for  (i = 0; i < count; i++) {
386			PyObject* k;
387			PyObject* v;
388			int r;
389
390			if (objects[i] == [NSNull null]) {
391				v = Py_None;
392				Py_INCREF(Py_None);
393			} else {
394				v = PyObjC_IdToPython(objects[i]);
395				if (v == NULL) {
396					PyObjC_GIL_FORWARD_EXC();
397				}
398			}
399
400			if (keys[i] == [NSNull null]) {
401				k = Py_None;
402				Py_INCREF(Py_None);
403			} else {
404				k = PyObjC_IdToPython(keys[i]);
405				if (k == NULL) {
406					PyObjC_GIL_FORWARD_EXC();
407				}
408			}
409
410			r = PyDict_SetItem(value, k, v);
411			Py_DECREF(k); Py_DECREF(v);
412
413			if (r == -1) {
414				PyObjC_GIL_FORWARD_EXC();
415			}
416		}
417	PyObjC_END_WITH_GIL
418	return self;
419}
420
421/*
422 * Helper method for initWithCoder, needed to deal with
423 * recursive objects (e.g. o.value = o)
424 */
425-(void)pyobjcSetValue:(NSObject*)other
426{
427	PyObject* v = PyObjC_IdToPython(other);
428	Py_XDECREF(value);
429	value = v;
430}
431
432- initWithCoder:(NSCoder*)coder
433{
434	int code;
435	if ([coder allowsKeyedCoding]) {
436		code = [coder decodeInt32ForKey:@"pytype"];
437	} else {
438		[coder decodeValueOfObjCType:@encode(int) at:&code];
439	}
440	switch (code) {
441	case 1:
442		PyObjC_BEGIN_WITH_GIL
443			value = PyDict_New();
444			if (value == NULL) {
445				PyObjC_GIL_FORWARD_EXC();
446			}
447		PyObjC_END_WITH_GIL
448
449		self = [super initWithCoder:coder];
450		return self;
451
452	case 2:
453		if (PyObjC_Decoder != NULL) {
454			PyObjC_BEGIN_WITH_GIL
455				PyObject* cdr = PyObjC_IdToPython(coder);
456				if (cdr == NULL) {
457					PyObjC_GIL_FORWARD_EXC();
458				}
459
460				PyObject* setValue;
461				PyObject* selfAsPython = PyObjCObject_New(self, 0, YES);
462				setValue = PyObject_GetAttrString(selfAsPython, "pyobjcSetValue_");
463
464				PyObject* v = PyObject_CallFunction(PyObjC_Decoder, "OO", cdr, setValue);
465				Py_DECREF(cdr);
466				Py_DECREF(setValue);
467				Py_DECREF(selfAsPython);
468
469				if (v == NULL) {
470					PyObjC_GIL_FORWARD_EXC();
471				}
472
473				Py_XDECREF(value);
474				value = v;
475
476				NSObject* proxy = PyObjC_FindObjCProxy(value);
477				if (proxy == NULL) {
478					PyObjC_RegisterObjCProxy(value, self);
479				} else {
480					[self release];
481					[proxy retain];
482					self = (OC_PythonDictionary*)proxy;
483				}
484
485
486			PyObjC_END_WITH_GIL
487
488			return self;
489
490		} else {
491			[NSException raise:NSInvalidArgumentException
492					format:@"decoding Python objects is not supported"];
493			return nil;
494
495		}
496	}
497	[NSException raise:NSInvalidArgumentException
498			format:@"decoding Python objects is not supported"];
499	[self release];
500	return nil;
501}
502
503-(Class)classForCoder
504{
505	return [OC_PythonDictionary class];
506}
507
508
509- (void)encodeWithCoder:(NSCoder*)coder
510{
511	if (1 && PyDict_CheckExact(value)) {
512		if ([coder allowsKeyedCoding]) {
513			[coder encodeInt32:1 forKey:@"pytype"];
514		} else {
515			int v = 1;
516			[coder encodeValueOfObjCType:@encode(int) at:&v];
517		}
518		[super encodeWithCoder:coder];
519
520	} else {
521		if ([coder allowsKeyedCoding]) {
522			[coder encodeInt32:2 forKey:@"pytype"];
523		} else {
524			int v = 2;
525			[coder encodeValueOfObjCType:@encode(int) at:&v];
526		}
527		PyObjC_encodeWithCoder(value, coder);
528
529	}
530}
531
532-(id)copyWithZone:(NSZone*)zone
533{
534	if (PyObjC_CopyFunc) {
535		PyObjC_BEGIN_WITH_GIL
536			PyObject* copy = PyObject_CallFunctionObjArgs(PyObjC_CopyFunc,
537					value, NULL);
538			if (copy == NULL) {
539				PyObjC_GIL_FORWARD_EXC();
540			}
541
542			NSObject* result = PyObjC_PythonToId(copy);
543			Py_DECREF(copy);
544
545			if (PyErr_Occurred()) {
546				PyObjC_GIL_FORWARD_EXC();
547			}
548
549			[result retain];
550
551			PyObjC_GIL_RETURN(result);
552
553		PyObjC_END_WITH_GIL
554	} else {
555		return [super copyWithZone:zone];
556	}
557}
558
559-(id)mutableCopyWithZone:(NSZone*)zone
560{
561	if (PyObjC_CopyFunc) {
562		PyObjC_BEGIN_WITH_GIL
563			PyObject* copy = PyDict_New();
564			if (copy == NULL) {
565				PyObjC_GIL_FORWARD_EXC();
566			}
567
568			int r = PyDict_Update(copy, value);
569			if (r == -1) {
570				PyObjC_GIL_FORWARD_EXC();
571			}
572
573			NSObject* result = PyObjC_PythonToId(copy);
574			Py_DECREF(copy);
575
576			if (PyErr_Occurred()) {
577				PyObjC_GIL_FORWARD_EXC();
578			}
579
580			[result retain];
581
582			PyObjC_GIL_RETURN(result);
583
584		PyObjC_END_WITH_GIL
585	} else {
586		return [super mutableCopyWithZone:zone];
587	}
588}
589
590
591@end  // interface OC_PythonDictionary
592