File: frames.c

package info (click to toggle)
psyco 1.5.1-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 1,864 kB
  • ctags: 3,295
  • sloc: ansic: 24,491; python: 5,573; perl: 1,309; makefile: 166; sh: 1
file content (738 lines) | stat: -rw-r--r-- 21,366 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
#include "frames.h"
#include "pyver.h"
#include "../codemanager.h"
#include "../stats.h"
#include "../vcompiler.h"
#include "../Objects/pobject.h"

#include <opcode.h>


 /***************************************************************/

#if HAVE_DYN_COMPILE

/* turn a running frame into its Psyco equivalent, a PsycoObject.
   Return Py_None if the frame cannot be turned into a PsycoObject.
   Never sets an exception. */
PSY_INLINE PyObject* PsycoObject_FromFrame(PyFrameObject* f, int recursion)
{
	int i, extras, module;
	vinfo_t* v;
	PsycoObject* po;
	RunTimeSource rsrc;
	source_known_t* sk;
	PyCodeObject* co = f->f_code;
	PyObject* merge_points;

	if (f->f_stacktop == NULL) {
          /*#if version >= 2.2.2*/
		/* cannot patch a frame other than the top (running) one */
          /*#else*/
		/* feature requires Python version 2.2.2 or later */
          /*#endif*/
		goto fail;
	}
        module = f->f_globals == f->f_locals;
	merge_points = PyCodeStats_MergePoints(PyCodeStats_Get(co), module);
	if (merge_points == Py_None) {
		/* unsupported bytecode instructions */
		goto fail;
	}
	if (psyco_mp_flags(merge_points) & MP_FLAGS_HAS_FINALLY) {
		/* incompatible handling of 'finally' blocks */
		goto fail;
	}

	/* the local variables are assumed to be stored as 'fast' variables,
	   not in the f_locals dictionary.  This is currently asserted by
	   the fact that LOAD_NAME and STORE_NAME opcodes are not supported
	   at all.  XXX support LOAD_NAME / STORE_NAME / DELETE_NAME */
	extras = (f->f_valuestack - f->f_localsplus) + co->co_stacksize;

	po = PsycoObject_New(INDEX_LOC_LOCALS_PLUS + extras);
	po->stack_depth = INITIAL_STACK_DEPTH;
	po->vlocals.count = INDEX_LOC_LOCALS_PLUS + extras;
	INIT_PROCESSOR_PSYCOOBJECT(po);
	po->pr.auto_recursion = AUTO_RECURSION(recursion);

	/* initialize po->vlocals */
	Py_INCREF(f->f_globals);
	sk = sk_new((long) f->f_globals, SkFlagPyObj);
	LOC_GLOBALS = vinfo_new(CompileTime_NewSk(sk));
	
	/* move the current arguments, locals, and object stack into
	   their target place */
	for (i = f->f_stacktop - f->f_localsplus; i--; ) {
		PyObject* o = f->f_localsplus[i];
		po->stack_depth += sizeof(long);
		if (o == NULL) {
			/* uninitialized local variable,
			   the corresponding stack position is not used */
			v = psyco_vi_Zero();
		}
		else {
			/* XXX do something more intelligent for cell and
			       free vars */
			/* arguments get borrowed references */
			rsrc = RunTime_NewStack(po->stack_depth, false, false);
			v = vinfo_new(rsrc);
		}
		LOC_LOCALS_PLUS[i] = v;
	}
	/* the rest of the stack in LOC_LOCALS_PLUS is
	   initialized to NULL by PsycoObject_New() */

	/* store the code object */
	po->pr.co = co;
	Py_INCREF(co);  /* XXX never freed */
	po->pr.next_instr = f->f_lasti;
	pyc_data_build(po, merge_points);
	if (f->f_iblock) {
		po->pr.iblock = f->f_iblock;
		memcpy(po->pr.blockstack, f->f_blockstack,
		       sizeof(PyTryBlock)*po->pr.iblock);
	}

	/* set up the CALL return address */
	po->stack_depth += sizeof(long);
	rsrc = RunTime_NewStack(po->stack_depth, false, false);
	LOC_CONTINUATION = vinfo_new(rsrc);
	psyco_assert_coherent(po);
	return (PyObject*) po;

 fail:
	Py_INCREF(Py_None);
	return Py_None;
}

/* same as PsycoObject_FromFrame() on any not-yet-started frame with the
   given code object */
PSY_INLINE PyObject* PsycoObject_FromCode(PyCodeObject* co,
                                      PyObject* globals,
                                      int recursion,
                                      int module)
{
	int i, argc, ncells, nfrees, extras;
	PyObject* merge_points;
	PsycoObject* po;
	Source rsrc;
	source_known_t* sk;
	vinfo_t* v;
	
	merge_points = PyCodeStats_MergePoints(PyCodeStats_Get(co), module);
	if (merge_points == Py_None) {
		/* unsupported bytecode instructions */
		goto fail;
	}

	ncells = PyTuple_GET_SIZE(co->co_cellvars);
	nfrees = PyTuple_GET_SIZE(co->co_freevars);
	extras = co->co_stacksize + co->co_nlocals + ncells + nfrees;

	po = PsycoObject_New(INDEX_LOC_LOCALS_PLUS + extras);
	po->stack_depth = INITIAL_STACK_DEPTH;
	po->vlocals.count = INDEX_LOC_LOCALS_PLUS + extras;
	INIT_PROCESSOR_PSYCOOBJECT(po);
	po->pr.auto_recursion = AUTO_RECURSION(recursion);

	/* initialize po->vlocals */
	Py_INCREF(globals);
	sk = sk_new((long) globals, SkFlagPyObj);
	LOC_GLOBALS = vinfo_new(CompileTime_NewSk(sk));

	argc = co->co_argcount;
	if (co->co_flags & CO_VARARGS)
		argc++;
	if (co->co_flags & CO_VARKEYWORDS)
		argc++;

	/* initialize the free and cell vars */
	i = co->co_nlocals + ncells + nfrees;
	if (ncells || nfrees) {
		while (i > co->co_nlocals) {
			po->stack_depth += sizeof(long);
			/* borrowed references from the frame object */
			rsrc = RunTime_NewStack(po->stack_depth, false, false);
			v = vinfo_new(rsrc);
			LOC_LOCALS_PLUS[--i] = v;
		}
		/* skip the unbound local variables */
		po->stack_depth += sizeof(long) * (i-argc);
	}
	/* initialize the local variables to zero (unbound) */
	while (i > argc) {
		v = psyco_vi_Zero();
		LOC_LOCALS_PLUS[--i] = v;
	}
	/* initialize the keyword arguments dict */
	if (co->co_flags & CO_VARKEYWORDS) {
		po->stack_depth += sizeof(long);
		rsrc = RunTime_NewStack(po->stack_depth, false, false);
		v = vinfo_new(rsrc);
		/* known to be a dict */
                /*Psyco_AssertType(NULL, v, &PyDict_Type);*/
		rsrc = CompileTime_New((long) &PyDict_Type);
                v->array = array_new(FIELDS_TOTAL(OB_type));
                v->array->items[iOB_TYPE] = vinfo_new(rsrc);
		LOC_LOCALS_PLUS[--i] = v;
	}
	/* initialize the extra arguments tuple */
	if (co->co_flags & CO_VARARGS) {
		po->stack_depth += sizeof(long);
		rsrc = RunTime_NewStack(po->stack_depth, false, false);
		v = vinfo_new(rsrc);
		/* known to be a tuple */
		rsrc = CompileTime_New((long) &PyTuple_Type);
		v->array = array_new(iOB_TYPE+1);
		v->array->items[iOB_TYPE] = vinfo_new(rsrc);
		LOC_LOCALS_PLUS[--i] = v;
	}
	/* initialize the regular arguments */
	while (i > 0) {
		/* XXX do something more intelligent for cell and
		       free vars */
		po->stack_depth += sizeof(long);
		/* arguments get borrowed references */
		rsrc = RunTime_NewStack(po->stack_depth, false, false);
		v = vinfo_new(rsrc);
		LOC_LOCALS_PLUS[--i] = v;
	}
	/* the rest of the stack in LOC_LOCALS_PLUS is
	   initialized to NULL by PsycoObject_New() */

	/* store the code object */
	po->pr.co = co;
	Py_INCREF(co);  /* XXX never freed */
	pyc_data_build(po, merge_points);

	/* set up the CALL return address */
	po->stack_depth += sizeof(long);
	rsrc = RunTime_NewStack(po->stack_depth, false, false);
	LOC_CONTINUATION = vinfo_new(rsrc);
	return (PyObject*) po;

 fail:
	Py_INCREF(Py_None);
	return Py_None;
}

DEFINEFN
PyObject* PsycoCode_CompileCode(PyCodeObject* co,
                                PyObject* globals,
                                int recursion,
                                int module)
{
	mergepoint_t* mp;
	PsycoObject* po;
	PyObject* o = PsycoObject_FromCode(co, globals, recursion, module);
	if (o == Py_None)
		return o;

	/* compile the function */
	po = (PsycoObject*) o;
	mp = PsycoObject_Ready(po);
	return (PyObject*) psyco_compile_code(po, mp);
}

DEFINEFN
PyObject* PsycoCode_CompileFrame(PyFrameObject* f, int recursion)
{
	mergepoint_t* mp;
	PsycoObject* po;
	PyObject* o = PsycoObject_FromFrame(f, recursion);
	if (o == Py_None)
		return o;

	/* compile the function */
	po = (PsycoObject*) o;
	mp = psyco_exact_merge_point(po->pr.merge_points, po->pr.next_instr);
	if (mp != NULL)
		psyco_delete_unused_vars(po, &mp->entries);
	return (PyObject*) psyco_compile_code(po, mp);
}

DEFINEFN
bool PsycoCode_Run(PyObject* codebuf, PyFrameObject* f, bool entering)
{
	PyObject* tdict;
	PyFrameRuntime* fruntime;
	stack_frame_info_t** finfo;
	int err;
	long* initial_stack;
	PyObject* result;
        PyCodeObject* co = f->f_code;

	extra_assert(codebuf != NULL);
	extra_assert(CodeBuffer_Check(codebuf));
	
	/* over the current Python frame, a lightweight chained list of
	   Psyco frames will be built. Mark the current Python frame as
	   the starting point of this chained list. */
	tdict = psyco_thread_dict();
	if (tdict==NULL) return false;
	fruntime = PyCStruct_NEW(PyFrameRuntime, PyFrameRuntime_dealloc);
        Py_INCREF(f);
        fruntime->cs_key = (PyObject*) f;
        fruntime->psy_frames_start = &finfo;
        fruntime->psy_code = co;
        fruntime->psy_globals = f->f_globals;
	extra_assert(PyDict_GetItem(tdict, (PyObject*) f) == NULL);
	err = PyDict_SetItem(tdict, (PyObject*) f, (PyObject*) fruntime);
	Py_DECREF(fruntime);
	if (err) return false;
	/* Warning, no 'return' between this point and the PyDict_DelItem()
	   below */
        
	/* get the actual arguments */
	initial_stack = (long*) f->f_localsplus;

	/* run! */
        Py_INCREF(codebuf);
	result = psyco_processor_run((CodeBufferObject*) codebuf,
				     initial_stack, &finfo, tdict);
	Py_DECREF(codebuf);
	psyco_trash_object(NULL);  /* free any trashed object now */

#if CODE_DUMP >= 2
        psyco_dump_code_buffers();
#endif
	if (PyDict_DelItem(tdict, (PyObject*) f)) {
		Py_XDECREF(result);
		result = NULL;
	}
	if (result == NULL) {
		PyObject *exc, *value, *tb;
		if (entering) {
			/* Nothing special to worry in this case,
			   eval_frame() will return right away */
			extra_assert(PyErr_Occurred());  /* exception */
			return false;
		}
		
		/* Attention: the maybe_call_line_trace() call of ceval.c:822
		   (Python 2.3b1) will *not* reload f->f_lasti and
		   f->f_stacktop if these get modified in case of exception!
		   We definitely cannot modify the stack top. We *must*
		   however empty the block stack to prevent exception
		   handlers to be entered --- they have already been run by
		   Psyco! */
		PyErr_Fetch(&exc, &value, &tb);
		extra_assert(exc != NULL);      /* exception */
                f->f_iblock = 0;
		
		/* We cannot prevent Python from calling PyTraceBack_Here()
		   when this function returns, althought Psyco has already
		   recorded a traceback. We remove Psyco's traceback and
		   make sure Python will re-insert an equivalent one. */
		if (tb != NULL) {  /* should normally never be NULL */
			/* no C interface to tb_lasti; call it via Python */
			PyObject *tb_next, *tb_lasti;
			tb_lasti = PyObject_GetAttrString(tb, "tb_lasti");
			extra_assert(tb_lasti != NULL);
			extra_assert(PyInt_Check(tb_lasti));
			f->f_lasti = PyInt_AS_LONG(tb_lasti);
			Py_DECREF(tb_lasti);

			tb_next = PyObject_GetAttrString(tb, "tb_next");
			extra_assert(tb_next != NULL);
			Py_DECREF(tb);
			tb = tb_next;
		}
		PyErr_Restore(exc, value, tb);
		return false;
	}
	else {
		/* to emulate the return, move the current position to
		   the end of the function code.  We assume that the
		   last instruction of any code object is a RETURN_VALUE. */
		PyObject** p;
		int new_i = PyString_GET_SIZE(co->co_code) - 1;
		psyco_assert(PyString_AS_STRING(co->co_code)[new_i]
                             == RETURN_VALUE);
#if PY_VERSION_HEX >= 0x02030000   /* 2.3 */
		/* dubious compatibility hack for Python 2.3, in which f_lasti
		   no longer always refer to the instruction that will be
		   executed just after the current trace hook returns */
	        new_i -= entering;
#endif
		f->f_lasti = new_i;
		f->f_iblock = 0;

		/* free the stack */
		for (p=f->f_stacktop; --p >= f->f_valuestack; ) {
			Py_XDECREF(*p);
			*p = NULL;
		}
		/* push the result alone on the stack */
		p = f->f_valuestack;
		*p++ = result;  /* consume a ref */
		f->f_stacktop = p;

		extra_assert(!PyErr_Occurred());
		return true;
	}
}

#endif /* HAVE_DYN_COMPILE */


 /***************************************************************/

#define FRAME_STACK_ALLOC_BY	83   /* about 1KB */

DEFINEFN
stack_frame_info_t* psyco_finfo(PsycoObject* caller, PsycoObject* callee)
{
	static stack_frame_info_t* current = NULL;
	static stack_frame_info_t* end = NULL;
	
	Source sglobals;
	stack_frame_info_t* p;
	int inlining = caller != NULL && caller->pr.is_inlining;
	
	if (end - current <= inlining) {
		psyco_memory_usage += sizeof(stack_frame_info_t) *
			FRAME_STACK_ALLOC_BY;
		current = PyMem_NEW(stack_frame_info_t, FRAME_STACK_ALLOC_BY);
		if (current == NULL)
			OUT_OF_MEMORY();
		end = current + FRAME_STACK_ALLOC_BY;
	}
	p = current;
	current += inlining + 1;
#if NEED_STACK_FRAME_HACK
	p->link_stack_depth = -inlining;
#endif
	p->co = callee->pr.co;
	sglobals = callee->vlocals.items[INDEX_LOC_GLOBALS]->source;
	if (is_compiletime(sglobals))
		p->globals = (PyObject*) CompileTime_Get(sglobals)->value;
	else
		p->globals = NULL;  /* uncommon */
	if (inlining) {
		(p+1)->co = caller->pr.co;
		sglobals = caller->vlocals.items[INDEX_LOC_GLOBALS]->source;
		if (is_compiletime(sglobals))
			(p+1)->globals = (PyObject*)
				CompileTime_Get(sglobals)->value;
		else
			(p+1)->globals = NULL;  /* uncommon */
	}
	return p;
}

DEFINEFN
void PyFrameRuntime_dealloc(PyFrameRuntime* self)
{
	/* nothing */
}

PSY_INLINE PyFrameObject* psyco_build_pyframe(PyObject* co, PyObject* globals)
{
	PyFrameObject* back;
	PyFrameObject* result;
	PyThreadState* tstate = PyThreadState_GET();
	
	/* frame objects are not created in stack order
	   with Psyco, so it's probably better not to
	   create plain wrong chained lists */
	back = tstate->frame;
	tstate->frame = NULL;
	result = PyFrame_New(tstate, (PyCodeObject*) co, globals, NULL);
	if (result == NULL)
		OUT_OF_MEMORY();
        result->f_lasti = -1;  /* can be used to identify emulated frames */
	tstate->frame = back;
	return result;
}

DEFINEFN
PyFrameObject* psyco_emulate_frame(PyObject* o)
{
	if (PyFrame_Check(o)) {
		/* a real Python frame */
		Py_INCREF(o);
		return (PyFrameObject*) o;
	}
	else {
		/* a Psyco frame: emulate it */
		PyObject* co = PyTuple_GetItem(o, 0);
		PyObject* globals = PyTuple_GetItem(o, 1);
		extra_assert(co != NULL);
		extra_assert(globals != NULL);
		return psyco_build_pyframe(co, globals);
	}
}

struct sfitmp_s {
	stack_frame_info_t** fi;
	struct sfitmp_s* next;
};

static PyObject* pvisitframes(PyObject*(*callback)(PyObject*,void*),
			      void* data)
{
        /* Whenever we run Psyco-produced machine code, we mark the current
           Python frame as the starting point of a chained list of Psyco
           frames. The machine code will update this chained list so that
           psyco_next_stack_frame() can be used to visit the list from
           the outermost to the innermost frames. Note that the list does
           not contain the first Psyco frame, the one directly run by a
           call to psyco_processor_run(). This still gives the expected
           result, because PsycoFunctionObjects are only supposed to be
           called by proxy codes (see psyco_proxycode()). This proxy
           code itself has a frame. It replaces the missing Psyco frame.
           XXX this would no longer work if we filled the emulated frames
               with more information, like local variables */

	PyObject* result = NULL;
	PyFrameRuntime* fstart;
	PyObject* tdict = psyco_thread_dict();
	PyFrameObject* f = PyThreadState_Get()->frame;
	
	while (f != NULL) {
		/* is this Python frame the starting point of a chained
		   list of Psyco frames ? */
		fstart = (PyFrameRuntime*) PyDict_GetItem(tdict, (PyObject*) f);
		if (fstart != NULL) {
			/* Yes. Get the list start. */
			struct sfitmp_s* revlist;
			struct sfitmp_s* p;
			PyObject* o;
			PyObject* g;
			long tag;
			stack_frame_info_t** f1;
			stack_frame_info_t** finfo;
			stack_frame_info_t* fdata;
			stack_frame_info_t* flimit;
			finfo = *(fstart->psy_frames_start);

			/* Enumerate the frames and store them in a
			   last-in first-out linked list. The end is marked by
			   a pointer with an odd integer value (actually with
                           i386 the least significant byte of the integer value
                           is -1, and with ivm the end pointer's value is
                           exactly 1; but real pointers cannot be odd at all
                           because they are aligned anyway). */
			revlist = NULL;
			for (f1 = finfo; (((long)(*f1)) & 1) == 0;
			     f1 = psyco_next_stack_frame(f1)) {
				p = (struct sfitmp_s*)
					PyMem_MALLOC(sizeof(struct sfitmp_s));
				if (p == NULL)
					OUT_OF_MEMORY();
				p->fi = f1;
				p->next = revlist;
				revlist = p;
#if NEED_STACK_FRAME_HACK
				if ((*f1)->link_stack_depth == 0)
					break; /* stack top is an inline frame */
#endif
			}

			/* now actually visit them in the correct order */
			while (revlist) {
				p = revlist;
				/* a Psyco frame is represented as
				   (co, globals, address_of(*fi)) */
				if (result == NULL) {
					tag = (long)(p->fi);
					fdata = *p->fi;
					flimit = finfo_last(fdata);
					while (1) {
						g = fdata->globals;
						if (g == NULL)
							g = f->f_globals;
						o = Py_BuildValue("OOl",
								  fdata->co, g,
								  tag);
						if (o == NULL)
							OUT_OF_MEMORY();
						result = callback(o, data);
						Py_DECREF(o);
						if (result != NULL)
							break;
						if (fdata == flimit)
							break;
						fdata++, tag--;
					}
				}
				revlist = p->next;
				PyMem_FREE(p);
			}
			if (result != NULL)
				return result;

			/* there is still the real Python frame
			   which is shadowed by a Psyco frame, i.e. a
			   proxy function. Represented as
			   (co, globals, f) */
			o = Py_BuildValue("OOO",
					  fstart->psy_code,
					  fstart->psy_globals,
					  f);
			if (o == NULL)
				OUT_OF_MEMORY();
			result = callback(o, data);
			Py_DECREF(o);
		}
		else {
			/* a real unshadowed Python frame */
			result = callback((PyObject*) f, data);
		}
		if (result != NULL)
			return result;
		f = f->f_back;
	}
	return NULL;
}



static PyObject* visit_nth_frame(PyObject* o, void* n)
{
	/* count the calls to the function and return 'o' when
	   the counter reaches zero */
	if (!--*(int*)n) {
		Py_INCREF(o);
		return o;
	}
	return NULL;
}

static PyObject* visit_prev_frame(PyObject* o, void* data)
{
	PyObject* cmp = *(PyObject**) data;

	if (cmp != NULL) {
		/* still searching */
		if (PyFrame_Check(o) || PyFrame_Check(cmp)) {
			if (o != cmp) return NULL;
		}
		else {
			PyObject* p1;
			PyObject* p2;

			p1 = PyTuple_GetItem(o,   2);  /* tag */
			p2 = PyTuple_GetItem(cmp, 2);
			if (PyObject_Compare(p1, p2) != 0) return NULL;

			p1 = PyTuple_GetItem(o,   0);  /* code */
			p2 = PyTuple_GetItem(cmp, 0);
			if (p1 != p2) return NULL;

			p1 = PyTuple_GetItem(o,   1);  /* globals */
			p2 = PyTuple_GetItem(cmp, 1);
			if (p1 != p2) return NULL;
		}
		/* found it ! We will succeed the next time
		   visit_find_frame() is called. */
		*(PyObject**) data = NULL;
		return NULL;
	}
	else {
		/* found it the previous time, now return this next 'o' */
		Py_INCREF(o);
		return o;
	}
}

DEFINEFN
PyObject* psyco_find_frame(PyObject* o)
{
	void* result;
	if (PyInt_Check(o)) {
		int depth = PyInt_AsLong(o) + 1;
		if (depth <= 0)
			depth = 1;
		result = pvisitframes(visit_nth_frame, &depth);
	}
	else {
		result = pvisitframes(visit_prev_frame, (void*) &o);
		if (result == NULL && !PyErr_Occurred() && o != NULL)
			PyErr_SetString(PyExc_PsycoError,
					"f_back is invalid when frames are no longer active");
	}
	if (result == NULL && !PyErr_Occurred())
		PyErr_SetString(PyExc_ValueError,
				"call stack is not deep enough");
	return (PyObject*) result;
}

static PyObject* visit_get_globals(PyObject* o, void* ignored)
{
	if (PyFrame_Check(o))
		return ((PyFrameObject*) o)->f_globals;
	else
		return PyTuple_GetItem(o, 1);
}
DEFINEFN
PyObject* psyco_get_globals(void)
{
	PyObject* result = pvisitframes(visit_get_globals, NULL);
	if (result == NULL)
		psyco_fatal_msg("sorry, don't know what to do with no globals");
	return result;
}

#if HAVE_PYTHON_SUPPORT
static PyFrameObject* cached_frame = NULL;
static PyObject* visit_first_frame(PyObject* o, void* ignored)
{
	if (PyFrame_Check(o)) {
		/* a real Python frame: don't return a new reference */
		return (PyObject*) o;
	}
	else {
		/* a Psyco frame: emulate it */
		/* we can't return a new reference, so we have to remember
		   the last frame we emulated and free it now.  This is
		   not too bad since we can use this as a cache and avoid
		   rebuilding the new emulated frame all the time. */
		PyFrameObject* f;
		PyFrameObject* newf;
		PyObject* co = PyTuple_GetItem(o, 0);
		PyObject* globals = PyTuple_GetItem(o, 1);
		extra_assert(co != NULL);
		extra_assert(globals != NULL);
		while (cached_frame != NULL) {
			f = cached_frame;
			if ((PyObject*) f->f_code == co
			    && f->f_globals == globals)
				return (PyObject*) f;  /* reuse it */
			cached_frame = NULL;
			Py_DECREF(f);  /* might set cached_frame again
					  XXX could this loop never end? */
		}
		newf = psyco_build_pyframe(co, globals);
		while (cached_frame != NULL) {
			/* worst-case safe...  this is unlikely */
			f = cached_frame;
			cached_frame = NULL;
			Py_DECREF(f);
		}
		cached_frame = newf;   /* transfer ownership */
		return (PyObject*) newf;
	}
}
static PyFrameObject* psyco_threadstate_getframe(PyThreadState* self)
{
	return (PyFrameObject*) pvisitframes(visit_first_frame, NULL);
}
#endif


 /***************************************************************/

INITIALIZATIONFN
void psyco_frames_init(void)
{
#if HAVE_PYTHON_SUPPORT
	_PyThreadState_GetFrame =
#  if PYTHON_API_VERSION < 1012
		(unaryfunc)
#  endif
		psyco_threadstate_getframe;
#endif
}