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+ (instancetype)enumeratorWithWrappedDictionary:(OC_PythonDictionary*)value;
23- (id)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+(instancetype)enumeratorWithWrappedDictionary:(OC_PythonDictionary*)v
35{
36	return [[[self alloc] initWithWrappedDictionary:v] autorelease];
37}
38
39-(id)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+ (OC_PythonDictionary*)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_PythonDictionary dictionaryWithPythonObject:object];
90	}
91
92	return NULL;
93}
94
95+ (id)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+(OC_PythonDictionary*)dictionaryWithPythonObject:(PyObject*)v
118{
119	OC_PythonDictionary* res =
120		[[OC_PythonDictionary alloc] initWithPythonObject:v];
121	[res autorelease];
122	return res;
123}
124
125-(OC_PythonDictionary*)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-(BOOL)supportsWeakPointers { return YES; }
137
138-(oneway void)release
139{
140	/* See comment in OC_PythonUnicode */
141	PyObjC_BEGIN_WITH_GIL
142		[super release];
143	PyObjC_END_WITH_GIL
144}
145
146
147
148-(void)dealloc
149{
150	PyObjC_BEGIN_WITH_GIL
151		PyObjC_UnregisterObjCProxy(value, self);
152		Py_XDECREF(value);
153
154	PyObjC_END_WITH_GIL
155
156	[super dealloc];
157}
158
159-(PyObject*)__pyobjc_PythonObject__
160{
161	Py_INCREF(value);
162	return value;
163}
164-(PyObject*)__pyobjc_PythonTransient__:(int*)cookie
165{
166	*cookie = 0;
167	Py_INCREF(value);
168	return value;
169}
170
171-(NSUInteger)count
172{
173	Py_ssize_t result;
174
175	PyObjC_BEGIN_WITH_GIL
176		if (likely(PyDict_CheckExact(value))) {
177			result = PyDict_Size(value);
178		} else {
179			result = PyObject_Length(value);
180		}
181
182	PyObjC_END_WITH_GIL
183
184	if (sizeof(Py_ssize_t) > sizeof(NSUInteger)) {
185		if (result > (Py_ssize_t)NSUIntegerMax) {
186			return NSUIntegerMax;
187		}
188	}
189	return result;
190}
191
192-(int)depythonify:(PyObject*)v toId:(id*)datum
193{
194	if (unlikely(depythonify_c_value(@encode(id), v, datum) == -1)) {
195		return -1;
196	}
197	if (unlikely(*datum == nil)) {
198		*datum = [NSNull null];
199	}
200	return 0;
201}
202
203-(id)objectForKey:key
204{
205	PyObject* v;
206	PyObject* k;
207	id result;
208
209	PyObjC_BEGIN_WITH_GIL
210
211		if (unlikely(key == [NSNull null])) {
212			Py_INCREF(Py_None);
213			k = Py_None;
214		} else {
215			k = PyObjC_IdToPython(key);
216			if (k == NULL) {
217				PyObjC_GIL_FORWARD_EXC();
218			}
219		}
220
221		if (likely(PyDict_CheckExact(value))) {
222			v = PyDict_GetItem(value, k);
223			Py_XINCREF(v);
224		} else {
225			v = PyObject_GetItem(value, k);
226		}
227		Py_DECREF(k);
228
229		if (unlikely(v == NULL)) {
230			PyErr_Clear();
231			PyObjC_GIL_RETURN(nil);
232		}
233
234		if (unlikely([self depythonify:v toId:&result] == -1)) {
235			Py_DECREF(v);
236			PyObjC_GIL_FORWARD_EXC();
237		}
238		Py_DECREF(v);
239
240	PyObjC_END_WITH_GIL
241
242	return result;
243}
244
245
246-(void)setObject:val forKey:key
247{
248	PyObject* v = NULL;
249	PyObject* k = NULL;
250	id null = [NSNull null];
251
252	PyObjC_BEGIN_WITH_GIL
253		if (unlikely(val == null)) {
254			Py_INCREF(Py_None);
255			v = Py_None;
256		} else {
257			v = PyObjC_IdToPython(val);
258			if (unlikely(v == NULL)) {
259				PyObjC_GIL_FORWARD_EXC();
260			}
261		}
262
263		if (unlikely(key == nil)) {
264			Py_INCREF(Py_None);
265			k = Py_None;
266		} else {
267			k = PyObjC_IdToPython(key);
268			if (k == NULL) {
269				Py_XDECREF(v);
270				PyObjC_GIL_FORWARD_EXC();
271			}
272		}
273
274		if (likely(PyDict_CheckExact(value))) {
275			if (unlikely(PyDict_SetItem(value, k, v) < 0)) {
276				Py_XDECREF(v);
277				Py_XDECREF(k);
278				PyObjC_GIL_FORWARD_EXC();
279			}
280
281		} else {
282			if (unlikely(PyObject_SetItem(value, k, v) < 0)) {
283				Py_XDECREF(v);
284				Py_XDECREF(k);
285				PyObjC_GIL_FORWARD_EXC();
286			}
287
288		}
289
290		Py_DECREF(v);
291		Py_DECREF(k);
292
293	PyObjC_END_WITH_GIL
294}
295
296-(BOOL)wrappedKey:(id*)keyPtr value:(id*)valuePtr atPosition:(Py_ssize_t*)positionPtr
297{
298	PyObject *pykey = NULL;
299	PyObject *pyvalue = NULL;
300	PyObject **pykeyptr = (keyPtr == nil) ? NULL : &pykey;
301	PyObject **pyvalueptr = (valuePtr == nil) ? NULL : &pyvalue;
302
303	PyObjC_BEGIN_WITH_GIL
304		if (unlikely(!PyDict_Next(value, positionPtr, pykeyptr, pyvalueptr))) {
305			PyObjC_GIL_RETURN(NO);
306		}
307		if (keyPtr) {
308			if (unlikely([self depythonify:pykey toId:keyPtr] == -1)) {
309				PyObjC_GIL_FORWARD_EXC();
310			}
311		}
312		if (likely(valuePtr)) {
313			if (unlikely([self depythonify:pyvalue toId:valuePtr] == -1)) {
314				PyObjC_GIL_FORWARD_EXC();
315			}
316		}
317	PyObjC_END_WITH_GIL
318	return YES;
319}
320
321-(void)removeObjectForKey:key
322{
323	PyObject* k;
324
325	PyObjC_BEGIN_WITH_GIL
326		if (unlikely(key == [NSNull null])) {
327			Py_INCREF(Py_None);
328			k = Py_None;
329		} else {
330			k = PyObjC_IdToPython(key);
331			if (unlikely(k == NULL)) {
332				PyObjC_GIL_FORWARD_EXC();
333			}
334		}
335
336		if (PyDict_CheckExact(value)) {
337			if (unlikely(PyDict_DelItem(value, k) < 0)) {
338				Py_DECREF(k);
339				PyObjC_GIL_FORWARD_EXC();
340			}
341		} else {
342			if (unlikely(PyObject_DelItem(value, k) < 0)) {
343				Py_DECREF(k);
344				PyObjC_GIL_FORWARD_EXC();
345			}
346		}
347		Py_DECREF(k);
348
349	PyObjC_END_WITH_GIL
350}
351
352-(NSEnumerator *)keyEnumerator
353{
354	if (PyDict_CheckExact(value)) {
355		return [OC_PythonDictionaryEnumerator enumeratorWithWrappedDictionary:self];
356	} else {
357		PyObjC_BEGIN_WITH_GIL
358			PyObject* keys = PyObject_CallMethod(value, "keys", NULL);
359			if (keys == NULL) {
360				PyObjC_GIL_FORWARD_EXC();
361			}
362
363			PyObject* iter = PyObject_GetIter(keys);
364			Py_DECREF(keys);
365			if (iter == NULL) {
366				PyObjC_GIL_FORWARD_EXC();
367			}
368
369			NSEnumerator* result = [OC_PythonEnumerator enumeratorWithPythonObject:iter];
370			PyObjC_GIL_RETURN(result);
371
372		PyObjC_END_WITH_GIL
373	}
374}
375
376
377- (id)initWithObjects:(NSObject**)objects
378	  forKeys:(NSObject**)keys
379	    count:(NSUInteger)count
380{
381	/* This implementation is needed for our support for the NSCoding
382	 * protocol, NSDictionary's initWithCoder: will call this method.
383	 */
384	NSUInteger i;
385
386	PyObjC_BEGIN_WITH_GIL
387		for  (i = 0; i < count; i++) {
388			PyObject* k;
389			PyObject* v;
390			int r;
391
392			if (objects[i] == [NSNull null]) {
393				v = Py_None;
394				Py_INCREF(Py_None);
395			} else {
396				v = PyObjC_IdToPython(objects[i]);
397				if (v == NULL) {
398					PyObjC_GIL_FORWARD_EXC();
399				}
400			}
401
402			if (keys[i] == [NSNull null]) {
403				k = Py_None;
404				Py_INCREF(Py_None);
405			} else {
406				k = PyObjC_IdToPython(keys[i]);
407				if (k == NULL) {
408					PyObjC_GIL_FORWARD_EXC();
409				}
410			}
411
412			r = PyDict_SetItem(value, k, v);
413			Py_DECREF(k); Py_DECREF(v);
414
415			if (r == -1) {
416				PyObjC_GIL_FORWARD_EXC();
417			}
418		}
419	PyObjC_END_WITH_GIL
420	return self;
421}
422
423/*
424 * Helper method for initWithCoder, needed to deal with
425 * recursive objects (e.g. o.value = o)
426 */
427-(void)pyobjcSetValue:(NSObject*)other
428{
429	PyObjC_BEGIN_WITH_GIL
430		PyObject* v = PyObjC_IdToPython(other);
431		Py_XDECREF(value);
432		value = v;
433	PyObjC_END_WITH_GIL
434}
435
436- (id)initWithCoder:(NSCoder*)coder
437{
438	int code;
439	if ([coder allowsKeyedCoding]) {
440		code = [coder decodeInt32ForKey:@"pytype"];
441	} else {
442		[coder decodeValueOfObjCType:@encode(int) at:&code];
443	}
444	switch (code) {
445	case 1:
446		PyObjC_BEGIN_WITH_GIL
447			value = PyDict_New();
448			if (value == NULL) {
449				PyObjC_GIL_FORWARD_EXC();
450			}
451		PyObjC_END_WITH_GIL
452
453		self = [super initWithCoder:coder];
454		return self;
455
456	case 2:
457		if (PyObjC_Decoder != NULL) {
458			PyObjC_BEGIN_WITH_GIL
459				PyObject* cdr = PyObjC_IdToPython(coder);
460				if (cdr == NULL) {
461					PyObjC_GIL_FORWARD_EXC();
462				}
463
464				PyObject* setValue;
465				PyObject* selfAsPython = PyObjCObject_New(self, 0, YES);
466				setValue = PyObject_GetAttrString(selfAsPython, "pyobjcSetValue_");
467
468				PyObject* v = PyObject_CallFunction(PyObjC_Decoder, "OO", cdr, setValue);
469				Py_DECREF(cdr);
470				Py_DECREF(setValue);
471				Py_DECREF(selfAsPython);
472
473				if (v == NULL) {
474					PyObjC_GIL_FORWARD_EXC();
475				}
476
477				Py_XDECREF(value);
478				value = v;
479
480				NSObject* proxy = PyObjC_FindObjCProxy(value);
481				if (proxy == NULL) {
482					PyObjC_RegisterObjCProxy(value, self);
483				} else {
484					[self release];
485					[proxy retain];
486					self = (OC_PythonDictionary*)proxy;
487				}
488
489
490			PyObjC_END_WITH_GIL
491
492			return self;
493
494		} else {
495			[NSException raise:NSInvalidArgumentException
496					format:@"decoding Python objects is not supported"];
497			return nil;
498
499		}
500	}
501	[NSException raise:NSInvalidArgumentException
502			format:@"decoding Python objects is not supported"];
503	[self release];
504	return nil;
505}
506
507-(Class)classForCoder
508{
509	return [OC_PythonDictionary class];
510}
511
512
513- (void)encodeWithCoder:(NSCoder*)coder
514{
515	if (PyDict_CheckExact(value)) {
516		if ([coder allowsKeyedCoding]) {
517			[coder encodeInt32:1 forKey:@"pytype"];
518		} else {
519			int v = 1;
520			[coder encodeValueOfObjCType:@encode(int) at:&v];
521		}
522		[super encodeWithCoder:coder];
523
524	} else {
525		if ([coder allowsKeyedCoding]) {
526			[coder encodeInt32:2 forKey:@"pytype"];
527		} else {
528			int v = 2;
529			[coder encodeValueOfObjCType:@encode(int) at:&v];
530		}
531		PyObjC_encodeWithCoder(value, coder);
532
533	}
534}
535
536-(id)copyWithZone:(NSZone*)zone
537{
538	if (PyObjC_CopyFunc) {
539		PyObjC_BEGIN_WITH_GIL
540			PyObject* copy = PyObject_CallFunctionObjArgs(PyObjC_CopyFunc,
541					value, NULL);
542			if (copy == NULL) {
543				PyObjC_GIL_FORWARD_EXC();
544			}
545
546			NSObject* result = PyObjC_PythonToId(copy);
547			Py_DECREF(copy);
548
549			if (PyErr_Occurred()) {
550				PyObjC_GIL_FORWARD_EXC();
551			}
552
553			[result retain];
554
555			PyObjC_GIL_RETURN(result);
556
557		PyObjC_END_WITH_GIL
558	} else {
559		return [super copyWithZone:zone];
560	}
561}
562
563-(id)mutableCopyWithZone:(NSZone*)zone
564{
565	if (PyObjC_CopyFunc) {
566		PyObjC_BEGIN_WITH_GIL
567			PyObject* copy = PyDict_New();
568			if (copy == NULL) {
569				PyObjC_GIL_FORWARD_EXC();
570			}
571
572			int r = PyDict_Update(copy, value);
573			if (r == -1) {
574				PyObjC_GIL_FORWARD_EXC();
575			}
576
577			NSObject* result = PyObjC_PythonToId(copy);
578			Py_DECREF(copy);
579
580			if (PyErr_Occurred()) {
581				PyObjC_GIL_FORWARD_EXC();
582			}
583
584			[result retain];
585
586			PyObjC_GIL_RETURN(result);
587
588		PyObjC_END_WITH_GIL
589	} else {
590		return [super mutableCopyWithZone:zone];
591	}
592}
593
594
595@end  // interface OC_PythonDictionary
596