diff options
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ast.c | 2 | ||||
-rw-r--r-- | Python/bltinmodule.c | 4 | ||||
-rw-r--r-- | Python/ceval.c | 24 | ||||
-rw-r--r-- | Python/compile.c | 609 | ||||
-rw-r--r-- | Python/import.c | 7 | ||||
-rw-r--r-- | Python/marshal.c | 8 | ||||
-rw-r--r-- | Python/peephole.c | 610 | ||||
-rw-r--r-- | Python/pystate.c | 39 | ||||
-rw-r--r-- | Python/pythonrun.c | 4 | ||||
-rw-r--r-- | Python/symtable.c | 24 |
10 files changed, 693 insertions, 638 deletions
diff --git a/Python/ast.c b/Python/ast.c index ca832aa..b356192 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -2197,6 +2197,8 @@ alias_for_import_name(struct compiling *c, const node *n) } else { alias_ty a = alias_for_import_name(c, CHILD(n, 0)); + if (!a) + return NULL; if (strcmp(STR(CHILD(n, 1)), "as") != 0) { ast_error(n, "must use 'as' in import"); return NULL; diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 8b92c87..9616394 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -691,7 +691,7 @@ builtin_globals(PyObject *self) PyObject *d; d = PyEval_GetGlobals(); - Py_INCREF(d); + Py_XINCREF(d); return d; } @@ -1084,7 +1084,7 @@ builtin_locals(PyObject *self) PyObject *d; d = PyEval_GetLocals(); - Py_INCREF(d); + Py_XINCREF(d); return d; } diff --git a/Python/ceval.c b/Python/ceval.c index 07dfdc2..b33c16b 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -2597,6 +2597,7 @@ PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals, return NULL; } + assert(tstate != NULL); assert(globals != NULL); f = PyFrame_New(tstate, co, globals, locals); if (f == NULL) @@ -3589,6 +3590,7 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) PyFrame_New() that doesn't take locals, but does take builtins without sanity checking them. */ + assert(tstate != NULL); f = PyFrame_New(tstate, co, globals, NULL); if (f == NULL) return NULL; @@ -3601,7 +3603,6 @@ fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk) fastlocals[i] = *stack++; } retval = PyEval_EvalFrameEx(f,0); - assert(tstate != NULL); ++tstate->recursion_depth; Py_DECREF(f); --tstate->recursion_depth; @@ -3819,11 +3820,14 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) if (v != NULL) { Py_ssize_t x; if (PyInt_Check(v)) { - x = PyInt_AsSsize_t(v); + /* XXX(nnorwitz): I think PyInt_AS_LONG is correct, + however, it looks like it should be AsSsize_t. + There should be a comment here explaining why. + */ + x = PyInt_AS_LONG(v); } - else if (v->ob_type->tp_as_number && - v->ob_type->tp_as_number->nb_index) { - x = v->ob_type->tp_as_number->nb_index(v); + else if (PyIndex_Check(v)) { + x = PyNumber_AsSsize_t(v, NULL); if (x == -1 && PyErr_Occurred()) return 0; } @@ -3839,9 +3843,8 @@ _PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi) } #undef ISINDEX -#define ISINDEX(x) ((x) == NULL || PyInt_Check(x) || PyLong_Check(x) || \ - ((x)->ob_type->tp_as_number && \ - (x)->ob_type->tp_as_number->nb_index)) +#define ISINDEX(x) ((x) == NULL || \ + PyInt_Check(x) || PyLong_Check(x) || PyIndex_Check(x)) static PyObject * apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */ @@ -4081,6 +4084,11 @@ exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals, locals = PyEval_GetLocals(); plain = 1; } + if (!globals || !locals) { + PyErr_SetString(PyExc_SystemError, + "globals and locals cannot be NULL"); + return -1; + } } else if (locals == Py_None) locals = globals; diff --git a/Python/compile.c b/Python/compile.c index 4464882..678a4de 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -204,8 +204,8 @@ _Py_Mangle(PyObject *privateobj, PyObject *ident) const char *p, *name = PyString_AsString(ident); char *buffer; size_t nlen, plen; - if (privateobj == NULL || name == NULL || name[0] != '_' || - name[1] != '_') { + if (privateobj == NULL || !PyString_Check(privateobj) || + name == NULL || name[0] != '_' || name[1] != '_') { Py_INCREF(ident); return ident; } @@ -394,608 +394,6 @@ dictbytype(PyObject *src, int scope_type, int flag, int offset) return dest; } -/* Begin: Peephole optimizations ----------------------------------------- */ - -#define GETARG(arr, i) ((int)((arr[i+2]<<8) + arr[i+1])) -#define UNCONDITIONAL_JUMP(op) (op==JUMP_ABSOLUTE || op==JUMP_FORWARD) -#define ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) -#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, int 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_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); - 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, int 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: - case JUMP_IF_TRUE: - case JUMP_ABSOLUTE: - case CONTINUE_LOOP: - case SETUP_LOOP: - case SETUP_EXCEPT: - case SETUP_FINALLY: - 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. */ - -static PyObject * -optimize_code(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 exitUnchanged; - - /* 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_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 exitUnchanged; - 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 exitUnchanged; - - blocks = markblocks(codestr, codelen); - if (blocks == NULL) - goto exitUnchanged; - assert(PyList_Check(consts)); - - for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) { - opcode = codestr[i]; - - lastlc = cumlc; - cumlc = 0; - - switch (opcode) { - - /* Replace UNARY_NOT JUMP_IF_FALSE POP_TOP with - with JUMP_IF_TRUE POP_TOP */ - case UNARY_NOT: - if (codestr[i+1] != JUMP_IF_FALSE || - codestr[i+4] != POP_TOP || - !ISBASICBLOCK(blocks,i,5)) - continue; - tgt = GETJUMPTGT(codestr, (i+1)); - if (codestr[tgt] != POP_TOP) - continue; - j = GETARG(codestr, i+1) + 1; - codestr[i] = JUMP_IF_TRUE; - SETARG(codestr, i, j); - codestr[i+3] = POP_TOP; - codestr[i+4] = NOP; - break; - - /* 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) { - codestr[i] = LOAD_CONST; - SETARG(codestr, i, j); - cumlc = lastlc + 1; - break; - } - } - break; - - /* Skip over LOAD_CONST trueconst - JUMP_IF_FALSE xx POP_TOP */ - case LOAD_CONST: - cumlc = lastlc + 1; - j = GETARG(codestr, i); - if (codestr[i+3] != JUMP_IF_FALSE || - codestr[i+6] != POP_TOP || - !ISBASICBLOCK(blocks,i,7) || - !PyObject_IsTrue(PyList_GET_ITEM(consts, j))) - continue; - memset(codestr+i, NOP, 7); - 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 y y:JUMP_IF_FALSE z --> x:JUMP_IF_FALSE z - x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z --> x:JUMP_IF_FALSE y+3 - where y+3 is the instruction following the second test. - */ - case JUMP_IF_FALSE: - case JUMP_IF_TRUE: - tgt = GETJUMPTGT(codestr, i); - j = codestr[tgt]; - if (j == JUMP_IF_FALSE || j == JUMP_IF_TRUE) { - if (j == opcode) { - tgttgt = GETJUMPTGT(codestr, tgt) - i - 3; - SETARG(codestr, i, tgttgt); - } else { - tgt -= i; - SETARG(codestr, i, tgt); - } - break; - } - /* Intentional fallthrough */ - - /* Replace jumps to unconditional jumps */ - case FOR_ITER: - case JUMP_FORWARD: - case JUMP_ABSOLUTE: - case CONTINUE_LOOP: - case SETUP_LOOP: - case SETUP_EXCEPT: - case SETUP_FINALLY: - tgt = GETJUMPTGT(codestr, i); - 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 */ - case RETURN_VALUE: - if (i+4 >= codelen || - codestr[i+4] != RETURN_VALUE || - !ISBASICBLOCK(blocks,i,5)) - continue; - memset(codestr+i+1, NOP, 4); - 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: - j = addrmap[GETARG(codestr, i)]; - SETARG(codestr, i, j); - break; - - case FOR_ITER: - case JUMP_FORWARD: - case JUMP_IF_FALSE: - case JUMP_IF_TRUE: - case SETUP_LOOP: - case SETUP_EXCEPT: - case SETUP_FINALLY: - 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; - - exitUnchanged: - if (blocks != NULL) - PyMem_Free(blocks); - if (addrmap != NULL) - PyMem_Free(addrmap); - if (codestr != NULL) - PyMem_Free(codestr); - Py_INCREF(code); - return code; -} - -/* End: Peephole optimizations ----------------------------------------- */ - /* Leave this debugging code for just a little longer. @@ -1170,6 +568,7 @@ compiler_exit_scope(struct compiler *c) if (n >= 0) { wrapper = PyList_GET_ITEM(c->c_stack, n); c->u = (struct compiler_unit *)PyCObject_AsVoidPtr(wrapper); + assert(c->u); /* we are deleting from a list so this really shouldn't fail */ if (PySequence_DelItem(c->c_stack, n) < 0) Py_FatalError("compiler_exit_scope()"); @@ -4400,7 +3799,7 @@ makecode(struct compiler *c, struct assembler *a) if (flags < 0) goto error; - bytecode = optimize_code(a->a_bytecode, consts, names, a->a_lnotab); + bytecode = PyCode_Optimize(a->a_bytecode, consts, names, a->a_lnotab); if (!bytecode) goto error; diff --git a/Python/import.c b/Python/import.c index ef37e8b..a05078e 100644 --- a/Python/import.c +++ b/Python/import.c @@ -934,7 +934,8 @@ load_source_module(char *name, char *pathname, FILE *fp) if (Py_VerboseFlag) PySys_WriteStderr("import %s # from %s\n", name, pathname); - write_compiled_module(co, cpathname, mtime); + if (cpathname) + write_compiled_module(co, cpathname, mtime); } m = PyImport_ExecCodeModuleEx(name, (PyObject *)co, pathname); Py_DECREF(co); @@ -1233,6 +1234,8 @@ find_module(char *fullname, char *subname, PyObject *path, char *buf, for (i = 0; i < npath; i++) { PyObject *copy = NULL; PyObject *v = PyList_GetItem(path, i); + if (!v) + return NULL; #ifdef Py_USING_UNICODE if (PyUnicode_Check(v)) { copy = PyUnicode_Encode(PyUnicode_AS_UNICODE(v), @@ -3045,6 +3048,8 @@ initimp(void) if (m == NULL) goto failure; d = PyModule_GetDict(m); + if (d == NULL) + goto failure; if (setint(d, "SEARCH_ERROR", SEARCH_ERROR) < 0) goto failure; if (setint(d, "PY_SOURCE", PY_SOURCE) < 0) goto failure; diff --git a/Python/marshal.c b/Python/marshal.c index 10a6c0c..c3bc87f 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -213,6 +213,10 @@ w_object(PyObject *v, WFILE *p) w_byte(TYPE_COMPLEX, p); temp = (PyFloatObject*)PyFloat_FromDouble( PyComplex_RealAsDouble(v)); + if (!temp) { + p->error = 1; + return; + } PyFloat_AsReprString(buf, temp); Py_DECREF(temp); n = strlen(buf); @@ -220,6 +224,10 @@ w_object(PyObject *v, WFILE *p) w_string(buf, (int)n, p); temp = (PyFloatObject*)PyFloat_FromDouble( PyComplex_ImagAsDouble(v)); + if (!temp) { + p->error = 1; + return; + } PyFloat_AsReprString(buf, temp); Py_DECREF(temp); n = strlen(buf); diff --git a/Python/peephole.c b/Python/peephole.c new file mode 100644 index 0000000..836d398 --- /dev/null +++ b/Python/peephole.c @@ -0,0 +1,610 @@ +/* Peehole 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 ABSOLUTE_JUMP(op) (op==JUMP_ABSOLUTE || op==CONTINUE_LOOP) +#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, int 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_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); + 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, int 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: + case JUMP_IF_TRUE: + case JUMP_ABSOLUTE: + case CONTINUE_LOOP: + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + 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 exitUnchanged; + + /* 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_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 exitUnchanged; + 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 exitUnchanged; + + blocks = markblocks(codestr, codelen); + if (blocks == NULL) + goto exitUnchanged; + assert(PyList_Check(consts)); + + for (i=0 ; i<codelen ; i += CODESIZE(codestr[i])) { + opcode = codestr[i]; + + lastlc = cumlc; + cumlc = 0; + + switch (opcode) { + + /* Replace UNARY_NOT JUMP_IF_FALSE POP_TOP with + with JUMP_IF_TRUE POP_TOP */ + case UNARY_NOT: + if (codestr[i+1] != JUMP_IF_FALSE || + codestr[i+4] != POP_TOP || + !ISBASICBLOCK(blocks,i,5)) + continue; + tgt = GETJUMPTGT(codestr, (i+1)); + if (codestr[tgt] != POP_TOP) + continue; + j = GETARG(codestr, i+1) + 1; + codestr[i] = JUMP_IF_TRUE; + SETARG(codestr, i, j); + codestr[i+3] = POP_TOP; + codestr[i+4] = NOP; + break; + + /* 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) { + codestr[i] = LOAD_CONST; + SETARG(codestr, i, j); + cumlc = lastlc + 1; + break; + } + } + break; + + /* Skip over LOAD_CONST trueconst + JUMP_IF_FALSE xx POP_TOP */ + case LOAD_CONST: + cumlc = lastlc + 1; + j = GETARG(codestr, i); + if (codestr[i+3] != JUMP_IF_FALSE || + codestr[i+6] != POP_TOP || + !ISBASICBLOCK(blocks,i,7) || + !PyObject_IsTrue(PyList_GET_ITEM(consts, j))) + continue; + memset(codestr+i, NOP, 7); + 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 y y:JUMP_IF_FALSE z --> x:JUMP_IF_FALSE z + x:JUMP_IF_FALSE y y:JUMP_IF_TRUE z --> x:JUMP_IF_FALSE y+3 + where y+3 is the instruction following the second test. + */ + case JUMP_IF_FALSE: + case JUMP_IF_TRUE: + tgt = GETJUMPTGT(codestr, i); + j = codestr[tgt]; + if (j == JUMP_IF_FALSE || j == JUMP_IF_TRUE) { + if (j == opcode) { + tgttgt = GETJUMPTGT(codestr, tgt) - i - 3; + SETARG(codestr, i, tgttgt); + } else { + tgt -= i; + SETARG(codestr, i, tgt); + } + break; + } + /* Intentional fallthrough */ + + /* Replace jumps to unconditional jumps */ + case FOR_ITER: + case JUMP_FORWARD: + case JUMP_ABSOLUTE: + case CONTINUE_LOOP: + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + tgt = GETJUMPTGT(codestr, i); + 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 */ + case RETURN_VALUE: + if (i+4 >= codelen || + codestr[i+4] != RETURN_VALUE || + !ISBASICBLOCK(blocks,i,5)) + continue; + memset(codestr+i+1, NOP, 4); + 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: + j = addrmap[GETARG(codestr, i)]; + SETARG(codestr, i, j); + break; + + case FOR_ITER: + case JUMP_FORWARD: + case JUMP_IF_FALSE: + case JUMP_IF_TRUE: + case SETUP_LOOP: + case SETUP_EXCEPT: + case SETUP_FINALLY: + 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; + + exitUnchanged: + if (blocks != NULL) + PyMem_Free(blocks); + if (addrmap != NULL) + PyMem_Free(addrmap); + if (codestr != NULL) + PyMem_Free(codestr); + Py_INCREF(code); + return code; +} diff --git a/Python/pystate.c b/Python/pystate.c index 3fae85b..f591a59 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -342,28 +342,43 @@ PyThreadState_GetDict(void) /* Asynchronously raise an exception in a thread. Requested by Just van Rossum and Alex Martelli. To prevent naive misuse, you must write your own extension - to call this. Must be called with the GIL held. - Returns the number of tstates modified; if it returns a number - greater than one, you're in trouble, and you should call it again - with exc=NULL to revert the effect. This raises no exceptions. */ + to call this, or use ctypes. Must be called with the GIL held. + Returns the number of tstates modified (normally 1, but 0 if `id` didn't + match any known thread id). Can be called with exc=NULL to clear an + existing async exception. This raises no exceptions. */ int PyThreadState_SetAsyncExc(long id, PyObject *exc) { PyThreadState *tstate = PyThreadState_GET(); PyInterpreterState *interp = tstate->interp; PyThreadState *p; - int count = 0; + + /* Although the GIL is held, a few C API functions can be called + * without the GIL held, and in particular some that create and + * destroy thread and interpreter states. Those can mutate the + * list of thread states we're traversing, so to prevent that we lock + * head_mutex for the duration. + */ HEAD_LOCK(); for (p = interp->tstate_head; p != NULL; p = p->next) { - if (p->thread_id != id) - continue; - Py_CLEAR(p->async_exc); - Py_XINCREF(exc); - p->async_exc = exc; - count += 1; + if (p->thread_id == id) { + /* Tricky: we need to decref the current value + * (if any) in p->async_exc, but that can in turn + * allow arbitrary Python code to run, including + * perhaps calls to this function. To prevent + * deadlock, we need to release head_mutex before + * the decref. + */ + PyObject *old_exc = p->async_exc; + Py_XINCREF(exc); + p->async_exc = exc; + HEAD_UNLOCK(); + Py_XDECREF(old_exc); + return 1; + } } HEAD_UNLOCK(); - return count; + return 0; } diff --git a/Python/pythonrun.c b/Python/pythonrun.c index 555c39d..e170090 100644 --- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -199,12 +199,16 @@ Py_InitializeEx(int install_sigs) if (bimod == NULL) Py_FatalError("Py_Initialize: can't initialize __builtin__"); interp->builtins = PyModule_GetDict(bimod); + if (interp->builtins == NULL) + Py_FatalError("Py_Initialize: can't initialize builtins dict"); Py_INCREF(interp->builtins); sysmod = _PySys_Init(); if (sysmod == NULL) Py_FatalError("Py_Initialize: can't initialize sys"); interp->sysdict = PyModule_GetDict(sysmod); + if (interp->sysdict == NULL) + Py_FatalError("Py_Initialize: can't initialize sys dict"); Py_INCREF(interp->sysdict); _PyImport_FixupExtension("sys", "sys"); PySys_SetPath(Py_GetPath()); diff --git a/Python/symtable.c b/Python/symtable.c index 439a243..05d504c 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -221,8 +221,8 @@ PySymtable_Build(mod_ty mod, const char *filename, PyFutureFeatures *future) return st; st->st_filename = filename; st->st_future = future; - if (!symtable_enter_block(st, GET_IDENTIFIER(top), ModuleBlock, - (void *)mod, 0)) { + if (!GET_IDENTIFIER(top) || + !symtable_enter_block(st, top, ModuleBlock, (void *)mod, 0)) { PySymtable_Free(st); return NULL; } @@ -915,6 +915,8 @@ symtable_new_tmpname(struct symtable *st) PyOS_snprintf(tmpname, sizeof(tmpname), "_[%d]", ++st->st_cur->ste_tmpname); tmp = PyString_InternFromString(tmpname); + if (!tmp) + return 0; if (!symtable_add_def(st, tmp, DEF_LOCAL)) return 0; Py_DECREF(tmp); @@ -1121,12 +1123,13 @@ symtable_visit_expr(struct symtable *st, expr_ty e) VISIT(st, expr, e->v.UnaryOp.operand); break; case Lambda_kind: { - if (!symtable_add_def(st, GET_IDENTIFIER(lambda), DEF_LOCAL)) + if (!GET_IDENTIFIER(lambda) || + !symtable_add_def(st, lambda, DEF_LOCAL)) return 0; if (e->v.Lambda.args->defaults) VISIT_SEQ(st, expr, e->v.Lambda.args->defaults); /* XXX how to get line numbers for expressions */ - if (!symtable_enter_block(st, GET_IDENTIFIER(lambda), + if (!symtable_enter_block(st, lambda, FunctionBlock, (void *)e, 0)) return 0; VISIT_IN_BLOCK(st, arguments, e->v.Lambda.args, (void*)e); @@ -1323,8 +1326,11 @@ symtable_visit_alias(struct symtable *st, alias_ty a) PyObject *name = (a->asname == NULL) ? a->name : a->asname; const char *base = PyString_AS_STRING(name); char *dot = strchr(base, '.'); - if (dot) + if (dot) { store_name = PyString_FromStringAndSize(base, dot - base); + if (!store_name) + return 0; + } else { store_name = name; Py_INCREF(store_name); @@ -1399,8 +1405,8 @@ symtable_visit_genexp(struct symtable *st, expr_ty e) /* Outermost iterator is evaluated in current scope */ VISIT(st, expr, outermost->iter); /* Create generator scope for the rest */ - if (!symtable_enter_block(st, GET_IDENTIFIER(genexpr), - FunctionBlock, (void *)e, 0)) { + if (!GET_IDENTIFIER(genexpr) || + !symtable_enter_block(st, genexpr, FunctionBlock, (void *)e, 0)) { return 0; } st->st_cur->ste_generator = 1; @@ -1414,7 +1420,5 @@ symtable_visit_genexp(struct symtable *st, expr_ty e) VISIT_SEQ_TAIL_IN_BLOCK(st, comprehension, e->v.GeneratorExp.generators, 1, (void*)e); VISIT_IN_BLOCK(st, expr, e->v.GeneratorExp.elt, (void*)e); - if (!symtable_exit_block(st, (void *)e)) - return 0; - return 1; + return symtable_exit_block(st, (void *)e); } |