summaryrefslogtreecommitdiffstats
path: root/Python/peephole.c
blob: ae84efa996e8ce7ff6d7a6e9e26369789860f619 (plain)
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
/* Peephole optimizations for bytecode compiler. */

#include "Python.h"

#include "Python-ast.h"
#include "node.h"
#include "pyarena.h"
#include "ast.h"
#include "code.h"
#include "compile.h"
#include "symtable.h"
#include "opcode.h"

#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1]))
#define UNCONDITIONAL_JUMP(op)  (op==JUMP_ABSOLUTE || op==JUMP_FORWARD)
#define CONDITIONAL_JUMP(op) (op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
    || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP \
    || op==POP_JUMP_IF_FALSE || op==POP_JUMP_IF_TRUE \
    || op==JUMP_IF_FALSE_OR_POP || op==JUMP_IF_TRUE_OR_POP)
#define JUMPS_ON_TRUE(op) (op==POP_JUMP_IF_TRUE || op==JUMP_IF_TRUE_OR_POP)
#define GETJUMPTGT(arr, i) (GETARG(arr,i) + (ABSOLUTE_JUMP(arr[i]) ? 0 : i+3))
#define SETARG(arr, i, val) arr[i+2] = val>>8; arr[i+1] = val & 255
#define CODESIZE(op)  (HAS_ARG(op) ? 3 : 1)
#define ISBASICBLOCK(blocks, start, bytes) \
    (blocks[start]==blocks[start+bytes-1])

/* Replace LOAD_CONST c1. LOAD_CONST c2 ... LOAD_CONST cn BUILD_TUPLE n
   with    LOAD_CONST (c1, c2, ... cn).
   The consts table must still be in list form so that the
   new constant (c1, c2, ... cn) can be appended.
   Called with codestr pointing to the first LOAD_CONST.
   Bails out with no change if one or more of the LOAD_CONSTs is missing.
   Also works for BUILD_LIST when followed by an "in" or "not in" test.
*/
static int
tuple_of_constants(unsigned char *codestr, Py_ssize_t n, PyObject *consts)
{
    PyObject *newconst, *constant;
    Py_ssize_t i, arg, len_consts;

    /* Pre-conditions */
    assert(PyList_CheckExact(consts));
    assert(codestr[n*3] == BUILD_TUPLE || codestr[n*3] == BUILD_LIST);
    assert(GETARG(codestr, (n*3)) == n);
    for (i=0 ; i<n ; i++)
        assert(codestr[i*3] == LOAD_CONST);

    /* Buildup new tuple of constants */
    newconst = PyTuple_New(n);
    if (newconst == NULL)
        return 0;
    len_consts = PyList_GET_SIZE(consts);
    for (i=0 ; i<n ; i++) {
        arg = GETARG(codestr, (i*3));
        assert(arg < len_consts);
        constant = PyList_GET_ITEM(consts, arg);
        Py_INCREF(constant);
        PyTuple_SET_ITEM(newconst, i, constant);
    }

    /* Append folded constant onto consts */
    if (PyList_Append(consts, newconst)) {
        Py_DECREF(newconst);
        return 0;
    }
    Py_DECREF(newconst);

    /* Write NOPs over old LOAD_CONSTS and
       add a new LOAD_CONST newconst on top of the BUILD_TUPLE n */
    memset(codestr, NOP, n*3);
    codestr[n*3] = LOAD_CONST;
    SETARG(codestr, (n*3), len_consts);
    return 1;
}

/* Replace LOAD_CONST c1. LOAD_CONST c2 BINOP
   with    LOAD_CONST binop(c1,c2)
   The consts table must still be in list form so that the
   new constant can be appended.
   Called with codestr pointing to the first LOAD_CONST.
   Abandons the transformation if the folding fails (i.e.  1+'a').
   If the new constant is a sequence, only folds when the size
   is below a threshold value.  That keeps pyc files from
   becoming large in the presence of code like:  (None,)*1000.
*/
static int
fold_binops_on_constants(unsigned char *codestr, PyObject *consts)
{
    PyObject *newconst, *v, *w;
    Py_ssize_t len_consts, size;
    int opcode;

    /* Pre-conditions */
    assert(PyList_CheckExact(consts));
    assert(codestr[0] == LOAD_CONST);
    assert(codestr[3] == LOAD_CONST);

    /* Create new constant */
    v = PyList_GET_ITEM(consts, GETARG(codestr, 0));
    w = PyList_GET_ITEM(consts, GETARG(codestr, 3));
    opcode = codestr[6];
    switch (opcode) {
        case BINARY_POWER:
            newconst = PyNumber_Power(v, w, Py_None);
            break;
        case BINARY_MULTIPLY:
            newconst = PyNumber_Multiply(v, w);
            break;
        case BINARY_DIVIDE:
            /* Cannot fold this operation statically since
               the result can depend on the run-time presence
               of the -Qnew flag */
            return 0;
        case BINARY_TRUE_DIVIDE:
            newconst = PyNumber_TrueDivide(v, w);
            break;
        case BINARY_FLOOR_DIVIDE:
            newconst = PyNumber_FloorDivide(v, w);
            break;
        case BINARY_MODULO:
            newconst = PyNumber_Remainder(v, w);
            break;
        case BINARY_ADD:
            newconst = PyNumber_Add(v, w);
            break;
        case BINARY_SUBTRACT:
            newconst = PyNumber_Subtract(v, w);
            break;
        case BINARY_SUBSCR:
            newconst = PyObject_GetItem(v, w);
            /* #5057: if v is unicode, there might be differences between
               wide and narrow builds in cases like u'\U00012345'[0].
               Wide builds will return a non-BMP char, whereas narrow builds
               will return a surrogate.  In both the cases skip the
               optimization in order to produce compatible pycs.
             */
#ifdef Py_USING_UNICODE
            if (newconst != NULL &&
                PyUnicode_Check(v) && PyUnicode_Check(newconst)) {
                Py_UNICODE ch = PyUnicode_AS_UNICODE(newconst)[0];
#ifdef Py_UNICODE_WIDE
                if (ch > 0xFFFF) {
#else
                if (ch >= 0xD800 && ch <= 0xDFFF) {
#endif
                    Py_DECREF(newconst);
                    return 0;
                }
            }
#endif
            break;
        case BINARY_LSHIFT:
            newconst = PyNumber_Lshift(v, w);
            break;
        case BINARY_RSHIFT:
            newconst = PyNumber_Rshift(v, w);
            break;
        case BINARY_AND:
            newconst = PyNumber_And(v, w);
            break;
        case BINARY_XOR:
            newconst = PyNumber_Xor(v, w);
            break;
        case BINARY_OR:
            newconst = PyNumber_Or(v, w);
            break;
        default:
            /* Called with an unknown opcode */
            PyErr_Format(PyExc_SystemError,
                 "unexpected binary operation %d on a constant",
                     opcode);
            return 0;
    }
    if (newconst == NULL) {
        PyErr_Clear();
        return 0;
    }
    size = PyObject_Size(newconst);
    if (size == -1)
        PyErr_Clear();
    else if (size > 20) {
        Py_DECREF(newconst);
        return 0;
    }

    /* Append folded constant into consts table */
    len_consts = PyList_GET_SIZE(consts);
    if (PyList_Append(consts, newconst)) {
        Py_DECREF(newconst);
        return 0;
    }
    Py_DECREF(newconst);

    /* Write NOP NOP NOP NOP LOAD_CONST newconst */
    memset(codestr, NOP, 4);
    codestr[4] = LOAD_CONST;
    SETARG(codestr, 4, len_consts);
    return 1;
}

static int
fold_unaryops_on_constants(unsigned char *codestr, PyObject *consts)
{
    PyObject *newconst=NULL, *v;
    Py_ssize_t len_consts;
    int opcode;

    /* Pre-conditions */
    assert(PyList_CheckExact(consts));
    assert(codestr[0] == LOAD_CONST);

    /* Create new constant */
    v = PyList_GET_ITEM(consts, GETARG(codestr, 0));
    opcode = codestr[3];
    switch (opcode) {
        case UNARY_NEGATIVE:
            /* Preserve the sign of -0.0 */
            if (PyObject_IsTrue(v) == 1)
                newconst = PyNumber_Negative(v);
            break;
        case UNARY_CONVERT:
            newconst = PyObject_Repr(v);
            break;
        case UNARY_INVERT:
            newconst = PyNumber_Invert(v);
            break;
        default:
            /* Called with an unknown opcode */
            PyErr_Format(PyExc_SystemError,
                 "unexpected unary operation %d on a constant",
                     opcode);
            return 0;
    }
    if (newconst == NULL) {
        PyErr_Clear();
        return 0;
    }

    /* Append folded constant into consts table */
    len_consts = PyList_GET_SIZE(consts);
    if (PyList_Append(consts, newconst)) {
        Py_DECREF(newconst);
        return 0;
    }
    Py_DECREF(newconst);

    /* Write NOP LOAD_CONST newconst */
    codestr[0] = NOP;
    codestr[1] = LOAD_CONST;
    SETARG(codestr, 1, len_consts);
    return 1;
}

static unsigned int *
markblocks(unsigned char *code, Py_ssize_t len)
{
    unsigned int *blocks = (unsigned int *)PyMem_Malloc(len*sizeof(int));
    int i,j, opcode, blockcnt = 0;

    if (blocks == NULL) {
        PyErr_NoMemory();
        return NULL;
    }
    memset(blocks, 0, len*sizeof(int));

    /* Mark labels in the first pass */
    for (i=0 ; i<len ; i+=CODESIZE(opcode)) {
        opcode = code[i];
        switch (opcode) {
            case FOR_ITER:
            case JUMP_FORWARD:
            case JUMP_IF_FALSE_OR_POP:
            case JUMP_IF_TRUE_OR_POP:
            case POP_JUMP_IF_FALSE:
            case POP_JUMP_IF_TRUE:
            case JUMP_ABSOLUTE:
            case CONTINUE_LOOP:
            case SETUP_LOOP:
            case SETUP_EXCEPT:
            case SETUP_FINALLY:
            case SETUP_WITH:
                j = GETJUMPTGT(code, i);
                blocks[j] = 1;
                break;
        }
    }
    /* Build block numbers in the second pass */
    for (i=0 ; i<len ; i++) {
        blockcnt += blocks[i];          /* increment blockcnt over labels */
        blocks[i] = blockcnt;
    }
    return blocks;
}

/* Perform basic peephole optimizations to components of a code object.
   The consts object should still be in list form to allow new constants
   to be appended.

   To keep the optimizer simple, it bails out (does nothing) for code
   containing extended arguments or that has a length over 32,700.  That
   allows us to avoid overflow and sign issues.  Likewise, it bails when
   the lineno table has complex encoding for gaps >= 255.

   Optimizations are restricted to simple transformations occuring within a
   single basic block.  All transformations keep the code size the same or
   smaller.  For those that reduce size, the gaps are initially filled with
   NOPs.  Later those NOPs are removed and the jump addresses retargeted in
   a single pass.  Line numbering is adjusted accordingly. */

PyObject *
PyCode_Optimize(PyObject *code, PyObject* consts, PyObject *names,
                PyObject *lineno_obj)
{
    Py_ssize_t i, j, codelen;
    int nops, h, adj;
    int tgt, tgttgt, opcode;
    unsigned char *codestr = NULL;
    unsigned char *lineno;
    int *addrmap = NULL;
    int new_line, cum_orig_line, last_line, tabsiz;
    int cumlc=0, lastlc=0;      /* Count runs of consecutive LOAD_CONSTs */
    unsigned int *blocks = NULL;
    char *name;

    /* Bail out if an exception is set */
    if (PyErr_Occurred())
        goto exitError;

    /* Bypass optimization when the lineno table is too complex */
    assert(PyString_Check(lineno_obj));
    lineno = (unsigned char*)PyString_AS_STRING(lineno_obj);
    tabsiz = PyString_GET_SIZE(lineno_obj);
    if (memchr(lineno, 255, tabsiz) != NULL)
        goto exitUnchanged;

    /* Avoid situations where jump retargeting could overflow */
    assert(PyString_Check(code));
    codelen = PyString_GET_SIZE(code);
    if (codelen > 32700)
        goto exitUnchanged;

    /* Make a modifiable copy of the code string */
    codestr = (unsigned char *)PyMem_Malloc(codelen);
    if (codestr == NULL)
        goto exitError;
    codestr = (unsigned char *)memcpy(codestr,
                                      PyString_AS_STRING(code), codelen);

    /* Verify that RETURN_VALUE terminates the codestring. This allows
       the various transformation patterns to look ahead several
       instructions without additional checks to make sure they are not
       looking beyond the end of the code string.
    */
    if (codestr[codelen-1] != RETURN_VALUE)
        goto exitUnchanged;

    /* Mapping to new jump targets after NOPs are removed */
    addrmap = (int *)PyMem_Malloc(codelen * sizeof(int));
    if (addrmap == NULL)
        goto exitError;

    blocks = markblocks(codestr, codelen);
    if (blocks == NULL)
        goto exitError;
    assert(PyList_Check(consts));

    for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
      reoptimize_current:
        opcode = codestr[i];

        lastlc = cumlc;
        cumlc = 0;

        switch (opcode) {
            /* Replace UNARY_NOT POP_JUMP_IF_FALSE
               with    POP_JUMP_IF_TRUE */
            case UNARY_NOT:
                if (codestr[i+1] != POP_JUMP_IF_FALSE
                    || !ISBASICBLOCK(blocks,i,4))
                    continue;
                j = GETARG(codestr, i+1);
                codestr[i] = POP_JUMP_IF_TRUE;
                SETARG(codestr, i, j);
                codestr[i+3] = NOP;
                goto reoptimize_current;

                /* not a is b -->  a is not b
                   not a in b -->  a not in b
                   not a is not b -->  a is b
                   not a not in b -->  a in b
                */
            case COMPARE_OP:
                j = GETARG(codestr, i);
                if (j < 6  ||  j > 9  ||
                    codestr[i+3] != UNARY_NOT  ||
                    !ISBASICBLOCK(blocks,i,4))
                    continue;
                SETARG(codestr, i, (j^1));
                codestr[i+3] = NOP;
                break;

                /* Replace LOAD_GLOBAL/LOAD_NAME None
                   with LOAD_CONST None */
            case LOAD_NAME:
            case LOAD_GLOBAL:
                j = GETARG(codestr, i);
                name = PyString_AsString(PyTuple_GET_ITEM(names, j));
                if (name == NULL  ||  strcmp(name, "None") != 0)
                    continue;
                for (j=0 ; j < PyList_GET_SIZE(consts) ; j++) {
                    if (PyList_GET_ITEM(consts, j) == Py_None)
                        break;
                }
                if (j == PyList_GET_SIZE(consts)) {
                    if (PyList_Append(consts, Py_None) == -1)
                        goto exitError;
                }
                assert(PyList_GET_ITEM(consts, j) == Py_None);
                codestr[i] = LOAD_CONST;
                SETARG(codestr, i, j);
                cumlc = lastlc + 1;
                break;

                /* Skip over LOAD_CONST trueconst
                   POP_JUMP_IF_FALSE xx. This improves
                   "while 1" performance. */
            case LOAD_CONST:
                cumlc = lastlc + 1;
                j = GETARG(codestr, i);
                if (codestr[i+3] != POP_JUMP_IF_FALSE  ||
                    !ISBASICBLOCK(blocks,i,6)  ||
                    !PyObject_IsTrue(PyList_GET_ITEM(consts, j)))
                    continue;
                memset(codestr+i, NOP, 6);
                cumlc = 0;
                break;

                /* Try to fold tuples of constants (includes a case for lists
                   which are only used for "in" and "not in" tests).
                   Skip over BUILD_SEQN 1 UNPACK_SEQN 1.
                   Replace BUILD_SEQN 2 UNPACK_SEQN 2 with ROT2.
                   Replace BUILD_SEQN 3 UNPACK_SEQN 3 with ROT3 ROT2. */
            case BUILD_TUPLE:
            case BUILD_LIST:
                j = GETARG(codestr, i);
                h = i - 3 * j;
                if (h >= 0 &&
                    j <= lastlc &&
                    ((opcode == BUILD_TUPLE &&
                      ISBASICBLOCK(blocks, h, 3*(j+1))) ||
                     (opcode == BUILD_LIST &&
                      codestr[i+3]==COMPARE_OP &&
                      ISBASICBLOCK(blocks, h, 3*(j+2)) &&
                      (GETARG(codestr,i+3)==6 ||
                       GETARG(codestr,i+3)==7))) &&
                    tuple_of_constants(&codestr[h], j, consts)) {
                    assert(codestr[i] == LOAD_CONST);
                    cumlc = 1;
                    break;
                }
                if (codestr[i+3] != UNPACK_SEQUENCE  ||
                    !ISBASICBLOCK(blocks,i,6) ||
                    j != GETARG(codestr, i+3))
                    continue;
                if (j == 1) {
                    memset(codestr+i, NOP, 6);
                } else if (j == 2) {
                    codestr[i] = ROT_TWO;
                    memset(codestr+i+1, NOP, 5);
                } else if (j == 3) {
                    codestr[i] = ROT_THREE;
                    codestr[i+1] = ROT_TWO;
                    memset(codestr+i+2, NOP, 4);
                }
                break;

                /* Fold binary ops on constants.
                   LOAD_CONST c1 LOAD_CONST c2 BINOP -->  LOAD_CONST binop(c1,c2) */
            case BINARY_POWER:
            case BINARY_MULTIPLY:
            case BINARY_TRUE_DIVIDE:
            case BINARY_FLOOR_DIVIDE:
            case BINARY_MODULO:
            case BINARY_ADD:
            case BINARY_SUBTRACT:
            case BINARY_SUBSCR:
            case BINARY_LSHIFT:
            case BINARY_RSHIFT:
            case BINARY_AND:
            case BINARY_XOR:
            case BINARY_OR:
                if (lastlc >= 2 &&
                    ISBASICBLOCK(blocks, i-6, 7) &&
                    fold_binops_on_constants(&codestr[i-6], consts)) {
                    i -= 2;
                    assert(codestr[i] == LOAD_CONST);
                    cumlc = 1;
                }
                break;

                /* Fold unary ops on constants.
                   LOAD_CONST c1  UNARY_OP --> LOAD_CONST unary_op(c) */
            case UNARY_NEGATIVE:
            case UNARY_CONVERT:
            case UNARY_INVERT:
                if (lastlc >= 1 &&
                    ISBASICBLOCK(blocks, i-3, 4) &&
                    fold_unaryops_on_constants(&codestr[i-3], consts)) {
                    i -= 2;
                    assert(codestr[i] == LOAD_CONST);
                    cumlc = 1;
                }
                break;

                /* Simplify conditional jump to conditional jump where the
                   result of the first test implies the success of a similar
                   test or the failure of the opposite test.
                   Arises in code like:
                   "if a and b:"
                   "if a or b:"
                   "a and b or c"
                   "(a and b) and c"
                   x:JUMP_IF_FALSE_OR_POP y   y:JUMP_IF_FALSE_OR_POP z
                      -->  x:JUMP_IF_FALSE_OR_POP z
                   x:JUMP_IF_FALSE_OR_POP y   y:JUMP_IF_TRUE_OR_POP z
                      -->  x:POP_JUMP_IF_FALSE y+3
                   where y+3 is the instruction following the second test.
                */
            case JUMP_IF_FALSE_OR_POP:
            case JUMP_IF_TRUE_OR_POP:
                tgt = GETJUMPTGT(codestr, i);
                j = codestr[tgt];
                if (CONDITIONAL_JUMP(j)) {
                    /* NOTE: all possible jumps here are absolute! */
                    if (JUMPS_ON_TRUE(j) == JUMPS_ON_TRUE(opcode)) {
                        /* The second jump will be
                           taken iff the first is. */
                        tgttgt = GETJUMPTGT(codestr, tgt);
                        /* The current opcode inherits
                           its target's stack behaviour */
                        codestr[i] = j;
                        SETARG(codestr, i, tgttgt);
                        goto reoptimize_current;
                    } else {
                        /* The second jump is not taken if the first is (so
                           jump past it), and all conditional jumps pop their
                           argument when they're not taken (so change the
                           first jump to pop its argument when it's taken). */
                        if (JUMPS_ON_TRUE(opcode))
                            codestr[i] = POP_JUMP_IF_TRUE;
                        else
                            codestr[i] = POP_JUMP_IF_FALSE;
                        SETARG(codestr, i, (tgt + 3));
                        goto reoptimize_current;
                    }
                }
                /* Intentional fallthrough */

                /* Replace jumps to unconditional jumps */
            case POP_JUMP_IF_FALSE:
            case POP_JUMP_IF_TRUE:
            case FOR_ITER:
            case JUMP_FORWARD:
            case JUMP_ABSOLUTE:
            case CONTINUE_LOOP:
            case SETUP_LOOP:
            case SETUP_EXCEPT:
            case SETUP_FINALLY:
            case SETUP_WITH:
                tgt = GETJUMPTGT(codestr, i);
                /* Replace JUMP_* to a RETURN into just a RETURN */
                if (UNCONDITIONAL_JUMP(opcode) &&
                    codestr[tgt] == RETURN_VALUE) {
                    codestr[i] = RETURN_VALUE;
                    memset(codestr+i+1, NOP, 2);
                    continue;
                }
                if (!UNCONDITIONAL_JUMP(codestr[tgt]))
                    continue;
                tgttgt = GETJUMPTGT(codestr, tgt);
                if (opcode == JUMP_FORWARD) /* JMP_ABS can go backwards */
                    opcode = JUMP_ABSOLUTE;
                if (!ABSOLUTE_JUMP(opcode))
                    tgttgt -= i + 3;        /* Calc relative jump addr */
                if (tgttgt < 0)             /* No backward relative jumps */
                    continue;
                codestr[i] = opcode;
                SETARG(codestr, i, tgttgt);
                break;

            case EXTENDED_ARG:
                goto exitUnchanged;

                /* Replace RETURN LOAD_CONST None RETURN with just RETURN */
                /* Remove unreachable JUMPs after RETURN */
            case RETURN_VALUE:
                if (i+4 >= codelen)
                    continue;
                if (codestr[i+4] == RETURN_VALUE &&
                    ISBASICBLOCK(blocks,i,5))
                    memset(codestr+i+1, NOP, 4);
                else if (UNCONDITIONAL_JUMP(codestr[i+1]) &&
                         ISBASICBLOCK(blocks,i,4))
                    memset(codestr+i+1, NOP, 3);
                break;
        }
    }

    /* Fixup linenotab */
    for (i=0, nops=0 ; i<codelen ; i += CODESIZE(codestr[i])) {
        addrmap[i] = i - nops;
        if (codestr[i] == NOP)
            nops++;
    }
    cum_orig_line = 0;
    last_line = 0;
    for (i=0 ; i < tabsiz ; i+=2) {
        cum_orig_line += lineno[i];
        new_line = addrmap[cum_orig_line];
        assert (new_line - last_line < 255);
        lineno[i] =((unsigned char)(new_line - last_line));
        last_line = new_line;
    }

    /* Remove NOPs and fixup jump targets */
    for (i=0, h=0 ; i<codelen ; ) {
        opcode = codestr[i];
        switch (opcode) {
            case NOP:
                i++;
                continue;

            case JUMP_ABSOLUTE:
            case CONTINUE_LOOP:
            case POP_JUMP_IF_FALSE:
            case POP_JUMP_IF_TRUE:
            case JUMP_IF_FALSE_OR_POP:
            case JUMP_IF_TRUE_OR_POP:
                j = addrmap[GETARG(codestr, i)];
                SETARG(codestr, i, j);
                break;

            case FOR_ITER:
            case JUMP_FORWARD:
            case SETUP_LOOP:
            case SETUP_EXCEPT:
            case SETUP_FINALLY:
            case SETUP_WITH:
                j = addrmap[GETARG(codestr, i) + i + 3] - addrmap[i] - 3;
                SETARG(codestr, i, j);
                break;
        }
        adj = CODESIZE(opcode);
        while (adj--)
            codestr[h++] = codestr[i++];
    }
    assert(h + nops == codelen);

    code = PyString_FromStringAndSize((char *)codestr, h);
    PyMem_Free(addrmap);
    PyMem_Free(codestr);
    PyMem_Free(blocks);
    return code;

 exitError:
    code = NULL;

 exitUnchanged:
    if (blocks != NULL)
        PyMem_Free(blocks);
    if (addrmap != NULL)
        PyMem_Free(addrmap);
    if (codestr != NULL)
        PyMem_Free(codestr);
    Py_XINCREF(code);
    return code;
}
span>op->cl_name == NULL || !PyString_Check(op->cl_name)) name = "?"; else name = PyString_AsString(op->cl_name); if (mod == NULL || !PyString_Check(mod)) sprintf(buf, "<class ?.%.100s at %lx>", name, (long)op); else sprintf(buf, "<class %.50s.%.50s at %lx>", PyString_AsString(mod), name, (long)op); return PyString_FromString(buf); } static PyObject * class_str(op) PyClassObject *op; { PyObject *mod = PyDict_GetItemString(op->cl_dict, "__module__"); PyObject *name = op->cl_name; PyObject *res; int m, n; if (name == NULL || !PyString_Check(name)) return class_repr(op); if (mod == NULL || !PyString_Check(mod)) { Py_INCREF(name); return name; } m = PyString_Size(mod); n = PyString_Size(name); res = PyString_FromStringAndSize((char *)NULL, m+1+n); if (res != NULL) { char *s = PyString_AsString(res); memcpy(s, PyString_AsString(mod), m); s += m; *s++ = '.'; memcpy(s, PyString_AsString(name), n); } return res; } static int class_traverse(PyClassObject *o, visitproc visit, void *arg) { int err; if (o->cl_bases) { err = visit(o->cl_bases, arg); if (err) return err; } if (o->cl_dict) { err = visit(o->cl_dict, arg); if (err) return err; } if (o->cl_name) { err = visit(o->cl_name, arg); if (err) return err; } if (o->cl_getattr) { err = visit(o->cl_getattr, arg); if (err) return err; } if (o->cl_setattr) { err = visit(o->cl_setattr, arg); if (err) return err; } if (o->cl_delattr) { err = visit(o->cl_delattr, arg); if (err) return err; } return 0; } PyTypeObject PyClass_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "class", sizeof(PyClassObject) + PyGC_INFO_SIZE, 0, (destructor)class_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ (reprfunc)class_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call*/ (reprfunc)class_str, /*tp_str*/ (getattrofunc)class_getattr, /*tp_getattro*/ (setattrofunc)class_setattr, /*tp_setattro*/ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ 0, /* tp_doc */ (traverseproc)class_traverse, /* tp_traverse */ }; int PyClass_IsSubclass(class, base) PyObject *class; PyObject *base; { int i, n; PyClassObject *cp; if (class == base) return 1; if (class == NULL || !PyClass_Check(class)) return 0; cp = (PyClassObject *)class; n = PyTuple_Size(cp->cl_bases); for (i = 0; i < n; i++) { if (PyClass_IsSubclass(PyTuple_GetItem(cp->cl_bases, i), base)) return 1; } return 0; } /* Instance objects */ PyObject * PyInstance_New(class, arg, kw) PyObject *class; PyObject *arg; PyObject *kw; { register PyInstanceObject *inst; PyObject *init; static PyObject *initstr; if (!PyClass_Check(class)) { PyErr_BadInternalCall(); return NULL; } inst = PyObject_NEW(PyInstanceObject, &PyInstance_Type); if (inst == NULL) return NULL; inst->in_dict = PyDict_New(); if (inst->in_dict == NULL) { PyObject_DEL(inst); return NULL; } Py_INCREF(class); inst->in_class = (PyClassObject *)class; if (initstr == NULL) initstr = PyString_InternFromString("__init__"); init = instance_getattr2(inst, initstr); if (init == NULL) { if ((arg != NULL && (!PyTuple_Check(arg) || PyTuple_Size(arg) != 0)) || (kw != NULL && (!PyDict_Check(kw) || PyDict_Size(kw) != 0))) { PyErr_SetString(PyExc_TypeError, "this constructor takes no arguments"); Py_DECREF(inst); inst = NULL; } } else { PyObject *res = PyEval_CallObjectWithKeywords(init, arg, kw); Py_DECREF(init); if (res == NULL) { Py_DECREF(inst); inst = NULL; } else { if (res != Py_None) { PyErr_SetString(PyExc_TypeError, "__init__() should return None"); Py_DECREF(inst); inst = NULL; } Py_DECREF(res); } } return (PyObject *)inst; } /* Instance methods */ static void instance_dealloc(inst) register PyInstanceObject *inst; { PyObject *error_type, *error_value, *error_traceback; PyObject *del; static PyObject *delstr; /* Call the __del__ method if it exists. First temporarily revive the object and save the current exception, if any. */ #ifdef Py_TRACE_REFS /* much too complicated if Py_TRACE_REFS defined */ extern long _Py_RefTotal; inst->ob_type = &PyInstance_Type; _Py_NewReference((PyObject *)inst); _Py_RefTotal--; /* compensate for increment in NEWREF */ #ifdef COUNT_ALLOCS inst->ob_type->tp_alloc--; /* ditto */ #endif #else /* !Py_TRACE_REFS */ Py_INCREF(inst); #endif /* !Py_TRACE_REFS */ PyErr_Fetch(&error_type, &error_value, &error_traceback); if (delstr == NULL) delstr = PyString_InternFromString("__del__"); if ((del = instance_getattr2(inst, delstr)) != NULL) { PyObject *res = PyEval_CallObject(del, (PyObject *)NULL); if (res == NULL) { PyObject *f, *t, *v, *tb; PyErr_Fetch(&t, &v, &tb); f = PySys_GetObject("stderr"); if (f != NULL) { PyFile_WriteString("Exception ", f); if (t) { PyFile_WriteObject(t, f, Py_PRINT_RAW); if (v && v != Py_None) { PyFile_WriteString(": ", f); PyFile_WriteObject(v, f, 0); } } PyFile_WriteString(" in ", f); PyFile_WriteObject(del, f, 0); PyFile_WriteString(" ignored\n", f); PyErr_Clear(); /* Just in case */ } Py_XDECREF(t); Py_XDECREF(v); Py_XDECREF(tb); } else Py_DECREF(res); Py_DECREF(del); } /* Restore the saved exception and undo the temporary revival */ PyErr_Restore(error_type, error_value, error_traceback); /* Can't use DECREF here, it would cause a recursive call */ if (--inst->ob_refcnt > 0) { #ifdef COUNT_ALLOCS inst->ob_type->tp_free--; #endif return; /* __del__ added a reference; don't delete now */ } #ifdef Py_TRACE_REFS #ifdef COUNT_ALLOCS inst->ob_type->tp_free--; /* compensate for increment in UNREF */ #endif _Py_ForgetReference((PyObject *)inst); inst->ob_type = NULL; #endif /* Py_TRACE_REFS */ Py_DECREF(inst->in_class); Py_XDECREF(inst->in_dict); PyObject_DEL(inst); } static PyObject * instance_getattr1(inst, name) register PyInstanceObject *inst; PyObject *name; { register PyObject *v; register char *sname = PyString_AsString(name); if (sname[0] == '_' && sname[1] == '_') { if (strcmp(sname, "__dict__") == 0) { if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "instance.__dict__ not accessible in restricted mode"); return NULL; } Py_INCREF(inst->in_dict); return inst->in_dict; } if (strcmp(sname, "__class__") == 0) { Py_INCREF(inst->in_class); return (PyObject *)inst->in_class; } } v = instance_getattr2(inst, name); if (v == NULL) { PyErr_Format(PyExc_AttributeError,"'%.50s' instance has no attribute '%.400s'", PyString_AS_STRING(inst->in_class->cl_name), sname); } return v; } static PyObject * instance_getattr2(inst, name) register PyInstanceObject *inst; PyObject *name; { register PyObject *v; PyClassObject *class; class = NULL; v = PyDict_GetItem(inst->in_dict, name); if (v == NULL) { v = class_lookup(inst->in_class, name, &class); if (v == NULL) return v; } Py_INCREF(v); if (class != NULL) { if (PyFunction_Check(v)) { PyObject *w = PyMethod_New(v, (PyObject *)inst, (PyObject *)class); Py_DECREF(v); v = w; } else if (PyMethod_Check(v)) { PyObject *im_class = PyMethod_Class(v); /* Only if classes are compatible */ if (PyClass_IsSubclass((PyObject *)class, im_class)) { PyObject *im_func = PyMethod_Function(v); PyObject *w = PyMethod_New(im_func, (PyObject *)inst, im_class); Py_DECREF(v); v = w; } } } return v; } static PyObject * instance_getattr(inst, name) register PyInstanceObject *inst; PyObject *name; { register PyObject *func, *res; res = instance_getattr1(inst, name); if (res == NULL && (func = inst->in_class->cl_getattr) != NULL) { PyObject *args; PyErr_Clear(); args = Py_BuildValue("(OO)", inst, name); if (args == NULL) return NULL; res = PyEval_CallObject(func, args); Py_DECREF(args); } return res; } static int instance_setattr1(inst, name, v) PyInstanceObject *inst; PyObject *name; PyObject *v; { if (v == NULL) { int rv = PyDict_DelItem(inst->in_dict, name); if (rv < 0) PyErr_SetString(PyExc_AttributeError, "delete non-existing instance attribute"); return rv; } else return PyDict_SetItem(inst->in_dict, name, v); } static int instance_setattr(inst, name, v) PyInstanceObject *inst; PyObject *name; PyObject *v; { PyObject *func, *args, *res, *tmp; char *sname = PyString_AsString(name); if (sname[0] == '_' && sname[1] == '_') { int n = PyString_Size(name); if (sname[n-1] == '_' && sname[n-2] == '_') { if (strcmp(sname, "__dict__") == 0) { if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "__dict__ not accessible in restricted mode"); return -1; } if (v == NULL || !PyDict_Check(v)) { PyErr_SetString(PyExc_TypeError, "__dict__ must be set to a dictionary"); return -1; } tmp = inst->in_dict; Py_INCREF(v); inst->in_dict = v; Py_DECREF(tmp); return 0; } if (strcmp(sname, "__class__") == 0) { if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "__class__ not accessible in restricted mode"); return -1; } if (v == NULL || !PyClass_Check(v)) { PyErr_SetString(PyExc_TypeError, "__class__ must be set to a class"); return -1; } tmp = (PyObject *)(inst->in_class); Py_INCREF(v); inst->in_class = (PyClassObject *)v; Py_DECREF(tmp); return 0; } } } if (v == NULL) func = inst->in_class->cl_delattr; else func = inst->in_class->cl_setattr; if (func == NULL) return instance_setattr1(inst, name, v); if (v == NULL) args = Py_BuildValue("(OO)", inst, name); else args = Py_BuildValue("(OOO)", inst, name, v); if (args == NULL) return -1; res = PyEval_CallObject(func, args); Py_DECREF(args); if (res == NULL) return -1; Py_DECREF(res); return 0; } static PyObject * instance_repr(inst) PyInstanceObject *inst; { PyObject *func; PyObject *res; static PyObject *reprstr; if (reprstr == NULL) reprstr = PyString_InternFromString("__repr__"); func = instance_getattr(inst, reprstr); if (func == NULL) { char buf[140]; PyObject *classname = inst->in_class->cl_name; PyObject *mod = PyDict_GetItemString( inst->in_class->cl_dict, "__module__"); char *cname; if (classname != NULL && PyString_Check(classname)) cname = PyString_AsString(classname); else cname = "?"; PyErr_Clear(); if (mod == NULL || !PyString_Check(mod)) sprintf(buf, "<?.%.100s instance at %lx>", cname, (long)inst); else sprintf(buf, "<%.50s.%.50s instance at %lx>", PyString_AsString(mod), cname, (long)inst); return PyString_FromString(buf); } res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); return res; } static PyObject * instance_compare1(inst, other) PyObject *inst, *other; { return PyInstance_DoBinOp(inst, other, "__cmp__", "__rcmp__", instance_compare1); } static int instance_compare(inst, other) PyObject *inst, *other; { PyObject *result; long outcome; result = instance_compare1(inst, other); if (result == NULL) return -1; if (!PyInt_Check(result)) { Py_DECREF(result); PyErr_SetString(PyExc_TypeError, "comparison did not return an int"); return -1; } outcome = PyInt_AsLong(result); Py_DECREF(result); if (outcome < 0) return -1; else if (outcome > 0) return 1; return 0; } static long instance_hash(inst) PyInstanceObject *inst; { PyObject *func; PyObject *res; long outcome; static PyObject *hashstr, *cmpstr; if (hashstr == NULL) hashstr = PyString_InternFromString("__hash__"); func = instance_getattr(inst, hashstr); if (func == NULL) { /* If there is no __cmp__ method, we hash on the address. If a __cmp__ method exists, there must be a __hash__. */ PyErr_Clear(); if (cmpstr == NULL) cmpstr = PyString_InternFromString("__cmp__"); func = instance_getattr(inst, cmpstr); if (func == NULL) { PyErr_Clear(); return _Py_HashPointer(inst); } PyErr_SetString(PyExc_TypeError, "unhashable instance"); return -1; } res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); if (res == NULL) return -1; if (PyInt_Check(res)) { outcome = PyInt_AsLong(res); if (outcome == -1) outcome = -2; } else { PyErr_SetString(PyExc_TypeError, "__hash__() should return an int"); outcome = -1; } Py_DECREF(res); return outcome; } static int instance_traverse(PyInstanceObject *o, visitproc visit, void *arg) { int err; if (o->in_class) { err = visit((PyObject *)(o->in_class), arg); if (err) return err; } if (o->in_dict) { err = visit(o->in_dict, arg); if (err) return err; } return 0; } static PyObject *getitemstr, *setitemstr, *delitemstr, *lenstr; static int instance_length(inst) PyInstanceObject *inst; { PyObject *func; PyObject *res; int outcome; if (lenstr == NULL) lenstr = PyString_InternFromString("__len__"); func = instance_getattr(inst, lenstr); if (func == NULL) return -1; res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); if (res == NULL) return -1; if (PyInt_Check(res)) { outcome = PyInt_AsLong(res); if (outcome < 0) PyErr_SetString(PyExc_ValueError, "__len__() should return >= 0"); } else { PyErr_SetString(PyExc_TypeError, "__len__() should return an int"); outcome = -1; } Py_DECREF(res); return outcome; } static PyObject * instance_subscript(inst, key) PyInstanceObject *inst; PyObject *key; { PyObject *func; PyObject *arg; PyObject *res; if (getitemstr == NULL) getitemstr = PyString_InternFromString("__getitem__"); func = instance_getattr(inst, getitemstr); if (func == NULL) return NULL; arg = Py_BuildValue("(O)", key); if (arg == NULL) { Py_DECREF(func); return NULL; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); return res; } static int instance_ass_subscript(inst, key, value) PyInstanceObject*inst; PyObject *key; PyObject *value; { PyObject *func; PyObject *arg; PyObject *res; if (value == NULL) { if (delitemstr == NULL) delitemstr = PyString_InternFromString("__delitem__"); func = instance_getattr(inst, delitemstr); } else { if (setitemstr == NULL) setitemstr = PyString_InternFromString("__setitem__"); func = instance_getattr(inst, setitemstr); } if (func == NULL) return -1; if (value == NULL) arg = Py_BuildValue("(O)", key); else arg = Py_BuildValue("(OO)", key, value); if (arg == NULL) { Py_DECREF(func); return -1; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); if (res == NULL) return -1; Py_DECREF(res); return 0; } static PyMappingMethods instance_as_mapping = { (inquiry)instance_length, /*mp_length*/ (binaryfunc)instance_subscript, /*mp_subscript*/ (objobjargproc)instance_ass_subscript, /*mp_ass_subscript*/ }; static PyObject * instance_item(inst, i) PyInstanceObject *inst; int i; { PyObject *func, *arg, *res; if (getitemstr == NULL) getitemstr = PyString_InternFromString("__getitem__"); func = instance_getattr(inst, getitemstr); if (func == NULL) return NULL; arg = Py_BuildValue("(i)", i); if (arg == NULL) { Py_DECREF(func); return NULL; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); return res; } static PyObject * instance_slice(inst, i, j) PyInstanceObject *inst; int i, j; { PyObject *func, *arg, *res; static PyObject *getslicestr; if (getslicestr == NULL) getslicestr = PyString_InternFromString("__getslice__"); func = instance_getattr(inst, getslicestr); if (func == NULL) return NULL; arg = Py_BuildValue("(ii)", i, j); if (arg == NULL) { Py_DECREF(func); return NULL; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); return res; } static int instance_ass_item(inst, i, item) PyInstanceObject *inst; int i; PyObject *item; { PyObject *func, *arg, *res; if (item == NULL) { if (delitemstr == NULL) delitemstr = PyString_InternFromString("__delitem__"); func = instance_getattr(inst, delitemstr); } else { if (setitemstr == NULL) setitemstr = PyString_InternFromString("__setitem__"); func = instance_getattr(inst, setitemstr); } if (func == NULL) return -1; if (item == NULL) arg = Py_BuildValue("i", i); else arg = Py_BuildValue("(iO)", i, item); if (arg == NULL) { Py_DECREF(func); return -1; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); if (res == NULL) return -1; Py_DECREF(res); return 0; } static int instance_ass_slice(inst, i, j, value) PyInstanceObject *inst; int i, j; PyObject *value; { PyObject *func, *arg, *res; static PyObject *setslicestr, *delslicestr; if (value == NULL) { if (delslicestr == NULL) delslicestr = PyString_InternFromString("__delslice__"); func = instance_getattr(inst, delslicestr); } else { if (setslicestr == NULL) setslicestr = PyString_InternFromString("__setslice__"); func = instance_getattr(inst, setslicestr); } if (func == NULL) return -1; if (value == NULL) arg = Py_BuildValue("(ii)", i, j); else arg = Py_BuildValue("(iiO)", i, j, value); if (arg == NULL) { Py_DECREF(func); return -1; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); if (res == NULL) return -1; Py_DECREF(res); return 0; } static int instance_contains(PyInstanceObject *inst, PyObject *member) { static PyObject *__contains__; PyObject *func, *arg, *res; int ret; if(__contains__ == NULL) { __contains__ = PyString_InternFromString("__contains__"); if(__contains__ == NULL) return -1; } func = instance_getattr(inst, __contains__); if(func == NULL) { /* fall back to previous behaviour */ int i, cmp_res; if(!PyErr_ExceptionMatches(PyExc_AttributeError)) return -1; PyErr_Clear(); for(i=0;;i++) { PyObject *obj = instance_item(inst, i); int ret = 0; if(obj == NULL) { if(!PyErr_ExceptionMatches(PyExc_IndexError)) return -1; PyErr_Clear(); return 0; } if(PyObject_Cmp(obj, member, &cmp_res) == -1) ret = -1; if(cmp_res == 0) ret = 1; Py_DECREF(obj); if(ret) return ret; } } arg = Py_BuildValue("(O)", member); if(arg == NULL) { Py_DECREF(func); return -1; } res = PyEval_CallObject(func, arg); Py_DECREF(func); Py_DECREF(arg); if(res == NULL) return -1; ret = PyObject_IsTrue(res); Py_DECREF(res); return ret; } static PySequenceMethods instance_as_sequence = { (inquiry)instance_length, /*sq_length*/ 0, /*sq_concat*/ 0, /*sq_repeat*/ (intargfunc)instance_item, /*sq_item*/ (intintargfunc)instance_slice, /*sq_slice*/ (intobjargproc)instance_ass_item, /*sq_ass_item*/ (intintobjargproc)instance_ass_slice, /*sq_ass_slice*/ (objobjproc)instance_contains, /* sq_contains */ }; static PyObject * generic_unary_op(self, methodname) PyInstanceObject *self; PyObject *methodname; { PyObject *func, *res; if ((func = instance_getattr(self, methodname)) == NULL) return NULL; res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); return res; } /* Forward */ static int halfbinop Py_PROTO((PyObject *, PyObject *, char *, PyObject **, PyObject * (*) Py_PROTO((PyObject *, PyObject *)), int )); /* Implement a binary operator involving at least one class instance. */ PyObject * PyInstance_DoBinOp(v, w, opname, ropname, thisfunc) PyObject *v; PyObject *w; char *opname; char *ropname; PyObject * (*thisfunc) Py_PROTO((PyObject *, PyObject *)); { char buf[256]; PyObject *result = NULL; if (halfbinop(v, w, opname, &result, thisfunc, 0) <= 0) return result; if (halfbinop(w, v, ropname, &result, thisfunc, 1) <= 0) return result; /* Sigh -- special case for comnparisons */ if (strcmp(opname, "__cmp__") == 0) { long c = (v < w) ? -1 : (v > w) ? 1 : 0; return PyInt_FromLong(c); } sprintf(buf, "%s nor %s defined for these operands", opname, ropname); PyErr_SetString(PyExc_TypeError, buf); return NULL; } /* Try one half of a binary operator involving a class instance. Return value: -1 if an exception is to be reported right away 0 if we have a valid result 1 if we could try another operation */ static PyObject *coerce_obj; static int halfbinop(v, w, opname, r_result, thisfunc, swapped) PyObject *v; PyObject *w; char *opname; PyObject **r_result; PyObject * (*thisfunc) Py_PROTO((PyObject *, PyObject *)); int swapped; { PyObject *func; PyObject *args; PyObject *coercefunc; PyObject *coerced = NULL; PyObject *v1; if (!PyInstance_Check(v)) return 1; if (coerce_obj == NULL) { coerce_obj = PyString_InternFromString("__coerce__"); if (coerce_obj == NULL) return -1; } coercefunc = PyObject_GetAttr(v, coerce_obj); if (coercefunc == NULL) { PyErr_Clear(); } else { args = Py_BuildValue("(O)", w); if (args == NULL) { return -1; } coerced = PyEval_CallObject(coercefunc, args); Py_DECREF(args); Py_DECREF(coercefunc); if (coerced == NULL) { return -1; } if (coerced == Py_None) { Py_DECREF(coerced); return 1; } if (!PyTuple_Check(coerced) || PyTuple_Size(coerced) != 2) { Py_DECREF(coerced); PyErr_SetString(PyExc_TypeError, "coercion should return None or 2-tuple"); return -1; } v1 = PyTuple_GetItem(coerced, 0); w = PyTuple_GetItem(coerced, 1); if (v1 != v) { v = v1; if (!PyInstance_Check(v) && !PyInstance_Check(w)) { if (swapped) *r_result = (*thisfunc)(w, v); else *r_result = (*thisfunc)(v, w); Py_DECREF(coerced); return *r_result == NULL ? -1 : 0; } } } func = PyObject_GetAttrString(v, opname); if (func == NULL) { Py_XDECREF(coerced); if (!PyErr_ExceptionMatches(PyExc_AttributeError)) return -1; PyErr_Clear(); return 1; } args = Py_BuildValue("(O)", w); if (args == NULL) { Py_DECREF(func); Py_XDECREF(coerced); return -1; } *r_result = PyEval_CallObject(func, args); Py_DECREF(args); Py_DECREF(func); Py_XDECREF(coerced); return *r_result == NULL ? -1 : 0; } static int instance_coerce(pv, pw) PyObject **pv; PyObject **pw; { PyObject *v = *pv; PyObject *w = *pw; PyObject *coercefunc; PyObject *args; PyObject *coerced; if (coerce_obj == NULL) { coerce_obj = PyString_InternFromString("__coerce__"); if (coerce_obj == NULL) return -1; } coercefunc = PyObject_GetAttr(v, coerce_obj); if (coercefunc == NULL) { /* No __coerce__ method: always OK */ PyErr_Clear(); Py_INCREF(v); Py_INCREF(w); return 0; } /* Has __coerce__ method: call it */ args = Py_BuildValue("(O)", w); if (args == NULL) { return -1; } coerced = PyEval_CallObject(coercefunc, args); Py_DECREF(args); Py_DECREF(coercefunc); if (coerced == NULL) { /* __coerce__ call raised an exception */ return -1; } if (coerced == Py_None) { /* __coerce__ says "I can't do it" */ Py_DECREF(coerced); return 1; } if (!PyTuple_Check(coerced) || PyTuple_Size(coerced) != 2) { /* __coerce__ return value is malformed */ Py_DECREF(coerced); PyErr_SetString(PyExc_TypeError, "coercion should return None or 2-tuple"); return -1; } /* __coerce__ returned two new values */ *pv = PyTuple_GetItem(coerced, 0); *pw = PyTuple_GetItem(coerced, 1); Py_INCREF(*pv); Py_INCREF(*pw); Py_DECREF(coerced); return 0; } #define UNARY(funcname, methodname) \ static PyObject *funcname(self) PyInstanceObject *self; { \ static PyObject *o; \ if (o == NULL) o = PyString_InternFromString(methodname); \ return generic_unary_op(self, o); \ } UNARY(instance_neg, "__neg__") UNARY(instance_pos, "__pos__") UNARY(instance_abs, "__abs__") static int instance_nonzero(self) PyInstanceObject *self; { PyObject *func, *res; long outcome; static PyObject *nonzerostr; if (nonzerostr == NULL) nonzerostr = PyString_InternFromString("__nonzero__"); if ((func = instance_getattr(self, nonzerostr)) == NULL) { PyErr_Clear(); if (lenstr == NULL) lenstr = PyString_InternFromString("__len__"); if ((func = instance_getattr(self, lenstr)) == NULL) { PyErr_Clear(); /* Fall back to the default behavior: all instances are nonzero */ return 1; } } res = PyEval_CallObject(func, (PyObject *)NULL); Py_DECREF(func); if (res == NULL) return -1; if (!PyInt_Check(res)) { Py_DECREF(res); PyErr_SetString(PyExc_TypeError, "__nonzero__ should return an int"); return -1; } outcome = PyInt_AsLong(res); Py_DECREF(res); if (outcome < 0) { PyErr_SetString(PyExc_ValueError, "__nonzero__ should return >= 0"); return -1; } return outcome > 0; } UNARY(instance_invert, "__invert__") UNARY(instance_int, "__int__") UNARY(instance_long, "__long__") UNARY(instance_float, "__float__") UNARY(instance_oct, "__oct__") UNARY(instance_hex, "__hex__") /* This version is for ternary calls only (z != None) */ static PyObject * instance_pow(v, w, z) PyObject *v; PyObject *w; PyObject *z; { /* XXX Doesn't do coercions... */ PyObject *func; PyObject *args; PyObject *result; static PyObject *powstr; if (powstr == NULL) powstr = PyString_InternFromString("__pow__"); func = PyObject_GetAttr(v, powstr); if (func == NULL) return NULL; args = Py_BuildValue("(OO)", w, z); if (args == NULL) { Py_DECREF(func); return NULL; } result = PyEval_CallObject(func, args); Py_DECREF(func); Py_DECREF(args); return result; } static PyNumberMethods instance_as_number = { 0, /*nb_add*/ 0, /*nb_subtract*/ 0, /*nb_multiply*/ 0, /*nb_divide*/ 0, /*nb_remainder*/ 0, /*nb_divmod*/ (ternaryfunc)instance_pow, /*nb_power*/ (unaryfunc)instance_neg, /*nb_negative*/ (unaryfunc)instance_pos, /*nb_positive*/ (unaryfunc)instance_abs, /*nb_absolute*/ (inquiry)instance_nonzero, /*nb_nonzero*/ (unaryfunc)instance_invert, /*nb_invert*/ 0, /*nb_lshift*/ 0, /*nb_rshift*/ 0, /*nb_and*/ 0, /*nb_xor*/ 0, /*nb_or*/ (coercion)instance_coerce, /*nb_coerce*/ (unaryfunc)instance_int, /*nb_int*/ (unaryfunc)instance_long, /*nb_long*/ (unaryfunc)instance_float, /*nb_float*/ (unaryfunc)instance_oct, /*nb_oct*/ (unaryfunc)instance_hex, /*nb_hex*/ }; PyTypeObject PyInstance_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "instance", sizeof(PyInstanceObject) + PyGC_INFO_SIZE, 0, (destructor)instance_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ instance_compare, /*tp_compare*/ (reprfunc)instance_repr, /*tp_repr*/ &instance_as_number, /*tp_as_number*/ &instance_as_sequence, /*tp_as_sequence*/ &instance_as_mapping, /*tp_as_mapping*/ (hashfunc)instance_hash, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ (getattrofunc)instance_getattr, /*tp_getattro*/ (setattrofunc)instance_setattr, /*tp_setattro*/ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ 0, /* tp_doc */ (traverseproc)instance_traverse, /* tp_traverse */ }; /* Instance method objects are used for two purposes: (a) as bound instance methods (returned by instancename.methodname) (b) as unbound methods (returned by ClassName.methodname) In case (b), im_self is NULL */ static PyMethodObject *free_list; PyObject * PyMethod_New(func, self, class) PyObject *func; PyObject *self; PyObject *class; { register PyMethodObject *im; if (!PyCallable_Check(func)) { PyErr_BadInternalCall(); return NULL; } im = free_list; if (im != NULL) { free_list = (PyMethodObject *)(im->im_self); PyObject_INIT(im, &PyMethod_Type); } else { im = PyObject_NEW(PyMethodObject, &PyMethod_Type); if (im == NULL) return NULL; } Py_INCREF(func); im->im_func = func; Py_XINCREF(self); im->im_self = self; Py_INCREF(class); im->im_class = class; return (PyObject *)im; } PyObject * PyMethod_Function(im) register PyObject *im; { if (!PyMethod_Check(im)) { PyErr_BadInternalCall(); return NULL; } return ((PyMethodObject *)im)->im_func; } PyObject * PyMethod_Self(im) register PyObject *im; { if (!PyMethod_Check(im)) { PyErr_BadInternalCall(); return NULL; } return ((PyMethodObject *)im)->im_self; } PyObject * PyMethod_Class(im) register PyObject *im; { if (!PyMethod_Check(im)) { PyErr_BadInternalCall(); return NULL; } return ((PyMethodObject *)im)->im_class; } /* Class method methods */ #define OFF(x) offsetof(PyMethodObject, x) static struct memberlist instancemethod_memberlist[] = { {"im_func", T_OBJECT, OFF(im_func)}, {"im_self", T_OBJECT, OFF(im_self)}, {"im_class", T_OBJECT, OFF(im_class)}, /* Dummies that are not handled by getattr() except for __members__ */ {"__doc__", T_INT, 0}, {"__name__", T_INT, 0}, {NULL} /* Sentinel */ }; static PyObject * instancemethod_getattr(im, name) register PyMethodObject *im; PyObject *name; { char *sname = PyString_AsString(name); if (sname[0] == '_') { /* Inherit __name__ and __doc__ from the callable object implementing the method */ if (strcmp(sname, "__name__") == 0 || strcmp(sname, "__doc__") == 0) return PyObject_GetAttr(im->im_func, name); } if (PyEval_GetRestricted()) { PyErr_SetString(PyExc_RuntimeError, "instance-method attributes not accessible in restricted mode"); return NULL; } return PyMember_Get((char *)im, instancemethod_memberlist, sname); } static void instancemethod_dealloc(im) register PyMethodObject *im; { Py_DECREF(im->im_func); Py_XDECREF(im->im_self); Py_DECREF(im->im_class); im->im_self = (PyObject *)free_list; free_list = im; } static int instancemethod_compare(a, b) PyMethodObject *a, *b; { if (a->im_self != b->im_self) return (a->im_self < b->im_self) ? -1 : 1; return PyObject_Compare(a->im_func, b->im_func); } static PyObject * instancemethod_repr(a) PyMethodObject *a; { char buf[240]; PyInstanceObject *self = (PyInstanceObject *)(a->im_self); PyObject *func = a->im_func; PyClassObject *class = (PyClassObject *)(a->im_class); PyObject *fclassname, *iclassname, *funcname; char *fcname, *icname, *fname; fclassname = class->cl_name; if (PyFunction_Check(func)) { funcname = ((PyFunctionObject *)func)->func_name; Py_INCREF(funcname); } else { funcname = PyObject_GetAttrString(func,"__name__"); if (funcname == NULL) PyErr_Clear(); } if (funcname != NULL && PyString_Check(funcname)) fname = PyString_AS_STRING(funcname); else fname = "?"; if (fclassname != NULL && PyString_Check(fclassname)) fcname = PyString_AsString(fclassname); else fcname = "?"; if (self == NULL) sprintf(buf, "<unbound method %.100s.%.100s>", fcname, fname); else { iclassname = self->in_class->cl_name; if (iclassname != NULL && PyString_Check(iclassname)) icname = PyString_AsString(iclassname); else icname = "?"; sprintf(buf, "<method %.60s.%.60s of %.60s instance at %lx>", fcname, fname, icname, (long)self); } Py_XDECREF(funcname); return PyString_FromString(buf); } static long instancemethod_hash(a) PyMethodObject *a; { long x, y; if (a->im_self == NULL) x = PyObject_Hash(Py_None); else x = PyObject_Hash(a->im_self); if (x == -1) return -1; y = PyObject_Hash(a->im_func); if (y == -1) return -1; return x ^ y; } static int instancemethod_traverse(PyMethodObject *im, visitproc visit, void *arg) { int err; if (im->im_func) { err = visit(im->im_func, arg); if (err) return err; } if (im->im_self) { err = visit(im->im_self, arg); if (err) return err; } if (im->im_class) { err = visit(im->im_class, arg); if (err) return err; } return 0; } PyTypeObject PyMethod_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, "instance method", sizeof(PyMethodObject) + PyGC_INFO_SIZE, 0, (destructor)instancemethod_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ (cmpfunc)instancemethod_compare, /*tp_compare*/ (reprfunc)instancemethod_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ (hashfunc)instancemethod_hash, /*tp_hash*/ 0, /*tp_call*/ 0, /*tp_str*/ (getattrofunc)instancemethod_getattr, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_GC, /*tp_flags*/ 0, /* tp_doc */ (traverseproc)instancemethod_traverse, /* tp_traverse */ }; /* Clear out the free list */ void PyMethod_Fini() { while (free_list) { PyMethodObject *im = free_list; free_list = (PyMethodObject *)(im->im_self); PyObject_DEL(im); } }