Index: Include/classobject.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/classobject.h,v retrieving revision 2.43 diff -c -r2.43 classobject.h *** Include/classobject.h 8 Apr 2003 18:47:21 -0000 2.43 --- Include/classobject.h 18 Apr 2003 21:37:45 -0000 *************** *** 74,79 **** --- 74,80 ---- PyAPI_FUNC(int) PyClass_IsSubclass(PyObject *, PyObject *); + PyAPI_FUNC(PyObject *) _Py_instance_getmethod(PyInstanceObject *, PyObject *); #ifdef __cplusplus } Index: Include/object.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/object.h,v retrieving revision 2.117 diff -c -r2.117 object.h *** Include/object.h 23 Mar 2003 17:52:28 -0000 2.117 --- Include/object.h 18 Apr 2003 21:37:47 -0000 *************** *** 387,392 **** --- 387,394 ---- PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *); PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); + PyAPI_FUNC(int) _PyObject_GenericGetAttr_raw(PyObject *, PyObject *, + PyObject **); PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(long) PyObject_Hash(PyObject *); Index: Include/opcode.h =================================================================== RCS file: /cvsroot/python/python/dist/src/Include/opcode.h,v retrieving revision 2.41 diff -c -r2.41 opcode.h *** Include/opcode.h 30 Aug 2002 13:09:49 -0000 2.41 --- Include/opcode.h 18 Apr 2003 21:37:47 -0000 *************** *** 138,143 **** --- 138,144 ---- /* Support for opargs more than 16 bits long */ #define EXTENDED_ARG 143 + #define CALL_ATTR 144 enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE, Index: Lib/opcode.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/opcode.py,v retrieving revision 1.1 diff -c -r1.1 opcode.py *** Lib/opcode.py 27 Feb 2003 21:27:52 -0000 1.1 --- Lib/opcode.py 18 Apr 2003 21:37:48 -0000 *************** *** 184,188 **** --- 184,189 ---- def_op('EXTENDED_ARG', 143) EXTENDED_ARG = 143 + def_op('CALL_ATTR', 144) del def_op, name_op, jrel_op, jabs_op Index: Objects/classobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/classobject.c,v retrieving revision 2.170 diff -c -r2.170 classobject.c *** Objects/classobject.c 9 Apr 2003 19:35:08 -0000 2.170 --- Objects/classobject.c 18 Apr 2003 21:37:55 -0000 *************** *** 759,764 **** --- 759,782 ---- return res; } + PyObject * + _Py_instance_getmethod(PyInstanceObject *inst, PyObject *name) + { + PyObject *v; + PyClassObject *class; + descrgetfunc f; + + v = PyDict_GetItem(inst->in_dict, name); + if (v != NULL) + return NULL; + v = class_lookup(inst->in_class, name, &class); + if (v != NULL && PyFunction_Check(v)) { + Py_INCREF(v); + return v; + } + return NULL; + } + /* See classobject.h comments: this only does dict lookups, and is always * safe to call. */ Index: Objects/object.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/object.c,v retrieving revision 2.207 diff -c -r2.207 object.c *** Objects/object.c 15 Apr 2003 15:10:51 -0000 2.207 --- Objects/object.c 18 Apr 2003 21:38:00 -0000 *************** *** 1351,1356 **** --- 1351,1531 ---- return obj; } + /* + * Multiple return values in C, sigh. + * The _PyObject_GenericGetAttr_raw function is the backend for both + * the PyObject_GenericGetAttr function, and the CALL_ATTR opcode + * handling in ceval.c. It needs to return two values, the found + * object and a status indicator, because in the case of descriptors, + * where the descriptor was found indicates whether we should call its + * descr_get method. + * + * *res should not contain an object; it will be initialized to NULL. + * When status was successful, *res always contains a new reference. + * The status flag means: + * + * -1 : an exception was raised (which could be an AttributeError); + * *res should not be used. + * 0 : a regular attribute was found, and put into *res. Its + * descr_get method should *not* be called even if the object is a + * descr. + * 1 : a descr was found in the right context, and put into *res. If + * desired, its descr_get method can be called. (CALL_ATTR, in + * specific, will avoid this if at all possible.) + */ + + int + _PyObject_GenericGetAttr_raw(PyObject *obj, PyObject *name, PyObject **res) + { + PyTypeObject *tp = obj->ob_type; + PyObject *descr = NULL; + long dictoffset; + PyObject **dictptr; + int status = -1; + + *res = NULL; + + if (!PyString_Check(name)){ + #ifdef Py_USING_UNICODE + /* The Unicode to string conversion is done here because the + existing tp_setattro slots expect a string object as name + and we wouldn't want to break those. */ + if (PyUnicode_Check(name)) { + name = PyUnicode_AsEncodedString(name, NULL, NULL); + if (name == NULL) + return -1; + } + else + #endif + { + PyErr_SetString(PyExc_TypeError, + "attribute name must be string"); + return -1; + } + } + else + Py_INCREF(name); + + if (tp->tp_dict == NULL) { + if (PyType_Ready(tp) < 0) { + status = -1; + goto done; + } + } + + /* Inline _PyType_Lookup */ + { + int i, n; + PyObject *mro, *base, *dict; + + /* Look in tp_dict of types in MRO */ + mro = tp->tp_mro; + assert(mro != NULL); + assert(PyTuple_Check(mro)); + n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + base = PyTuple_GET_ITEM(mro, i); + if (PyClass_Check(base)) + dict = ((PyClassObject *)base)->cl_dict; + else { + assert(PyType_Check(base)); + dict = ((PyTypeObject *)base)->tp_dict; + } + assert(dict && PyDict_Check(dict)); + descr = PyDict_GetItem(dict, name); + if (descr != NULL) + break; + } + } + + if (descr != NULL && + PyType_HasFeature(descr->ob_type, Py_TPFLAGS_HAVE_CLASS)) { + if (descr->ob_type->tp_descr_get != NULL && + PyDescr_IsData(descr)) { + Py_INCREF(descr); + *res = descr; + status = 1; + goto done; + } + } + + /* Inline _PyObject_GetDictPtr */ + dictoffset = tp->tp_dictoffset; + if (dictoffset != 0) { + PyObject *dict; + if (dictoffset < 0) { + int tsize; + size_t size; + + tsize = ((PyVarObject *)obj)->ob_size; + if (tsize < 0) + tsize = -tsize; + size = _PyObject_VAR_SIZE(tp, tsize); + + dictoffset += (long)size; + assert(dictoffset > 0); + assert(dictoffset % SIZEOF_VOID_P == 0); + } + dictptr = (PyObject **) ((char *)obj + dictoffset); + dict = *dictptr; + if (dict != NULL) { + *res = PyDict_GetItem(dict, name); + if (*res != NULL) { + Py_INCREF(*res); + status = 0; + goto done; + } + } + } + + + if (descr != NULL) { + Py_INCREF(descr); + if (descr->ob_type->tp_descr_get != NULL) + status = 1; + else + status = 0; + *res = descr; + goto done; + } + + PyErr_Format(PyExc_AttributeError, + "'%.50s' object has no attribute '%.400s'", + tp->tp_name, PyString_AS_STRING(name)); + *res = NULL; + status = -1; + done: + Py_DECREF(name); + return status; + } + + #if CALL_ATTR_SLOW_BUT_PRETTY_PATH + + /* If not for speed, we would be using this PyObject_GenericGetAttr + * implementation + */ + PyObject * + PyObject_GenericGetAttr(PyObject *obj, PyObject *name) + { + PyObject *res = NULL; + int status; + + status = _PyObject_GenericGetAttr_raw(obj, name, &res); + if (status < 0) + return NULL; + if (status > 0) { + descrgetfunc f; + PyObject *old = res; + f = res->ob_type->tp_descr_get; + assert(f); + res = f(old, obj, (PyObject *)obj->ob_type); + Py_DECREF(old); + } + return res; + } + + #else + PyObject * PyObject_GenericGetAttr(PyObject *obj, PyObject *name) { *************** *** 1468,1473 **** --- 1643,1650 ---- Py_DECREF(name); return res; } + + #endif int PyObject_GenericSetAttr(PyObject *obj, PyObject *name, PyObject *value) Index: Python/ceval.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/ceval.c,v retrieving revision 2.358 diff -c -r2.358 ceval.c *** Python/ceval.c 9 Apr 2003 19:06:17 -0000 2.358 --- Python/ceval.c 18 Apr 2003 21:38:11 -0000 *************** *** 34,39 **** --- 34,40 ---- /* Forward declarations */ static PyObject *eval_frame(PyFrameObject *); static PyObject *call_function(PyObject ***, int); + static PyObject *call_attr(PyObject ***, PyObject *, int); static PyObject *fast_function(PyObject *, PyObject ***, int, int, int); static PyObject *do_call(PyObject *, PyObject ***, int, int); static PyObject *ext_do_call(PyObject *, PyObject ***, int, int, int); *************** *** 2086,2091 **** --- 2087,2100 ---- STACK_LEVEL()); continue; + case CALL_ATTR: + PCALL(PCALL_ALL); + x = call_attr(&stack_pointer, names, oparg); + PUSH(x); + if (x != NULL) + continue; + break; + case CALL_FUNCTION: PCALL(PCALL_ALL); x = call_function(&stack_pointer, oparg); *************** *** 3377,3382 **** --- 3386,3608 ---- nargs); } + #if CALL_ATTR_SLOW_BUT_PRETTY_PATH + + static PyObject * + call_callable(PyObject ***pp_stack, PyObject *func, PyObject **top_obj, + int n, int na, int nk) + { + PyObject * result, * tmpo; + + /* Always dispatch PyCFunction first, because these are + presumed to be the most frequent callable object. + */ + if (PyCFunction_Check(func) && nk == 0) { + int flags = PyCFunction_GET_FLAGS(func); + PCALL(PCALL_CFUNCTION); + if (flags & (METH_NOARGS | METH_O)) { + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + if (flags & METH_NOARGS && na == 0) + result = (*meth)(self, NULL); + else if (flags & METH_O && na == 1) { + PyObject *arg = EXT_POP(*pp_stack); + result = (*meth)(self, arg); + Py_DECREF(arg); + } + else { + err_args(func, flags, na); + result = NULL; + } + } + else { + PyObject *callargs; + callargs = load_args(pp_stack, na); + result = PyCFunction_Call(func, callargs, NULL); + Py_XDECREF(callargs); + } + } + else { + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + /* optimize access to bound methods */ + PyObject *self = PyMethod_GET_SELF(func); + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + Py_INCREF(self); + tmpo = func; + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(tmpo); + Py_DECREF(*top_obj); + *top_obj = self; + na++; + n++; + } + if (PyFunction_Check(func)) + result = fast_function(func, pp_stack, n, na, nk); + else + result = do_call(func, pp_stack, na, nk); + } + Py_DECREF(func); + /* We need to pop off the stack all arguments plus the function + * object, but the PyMethod case modifies the stack and the number + * of arguments, so we can't just pop 'n' times. */ + while ((*pp_stack) > top_obj) { + tmpo = EXT_POP(*pp_stack); + Py_DECREF(tmpo); + PCALL(PCALL_POP); + } + return result; + } + + #endif + + /* Helper function for call_attr to call python functions. Eats a + * reference to func. + */ + static PyObject * + call_fast_pymeth(PyObject *func, PyObject ***pp_stack, PyObject **inst, + int n, int na, int nk) + { + PyObject *w, *result; + + n++; + na++; + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + result = fast_function(func, pp_stack, n, na, nk); + Py_DECREF(func); + while ((*pp_stack) > inst) { + w = EXT_POP(*pp_stack); + Py_DECREF(w); + PCALL(PCALL_POP); + } + return result; + } + + static PyObject * + call_attr(PyObject ***pp_stack, PyObject *names, int oparg) + { + int na = oparg & 0xFF; + int attri = (oparg >> 8) & 0xFFFF; + int nk = oparg >> 24; + int n = na + 2 * nk; + + PyObject **inst = (*pp_stack) - n - 1; + PyObject *funcname = GETITEM(names, attri); + PyObject *func = NULL, *result, *tmpo; + + if ((*inst)->ob_type->tp_getattro == PyObject_GenericGetAttr) { + int status = _PyObject_GenericGetAttr_raw(*inst, funcname, + &func); + if (status < 0) + return NULL; + if (status == 1 && PyFunction_Check(func)) + return call_fast_pymeth(func, pp_stack, inst, + n, na, nk); + else if (status == 1) { + descrgetfunc f = func->ob_type->tp_descr_get; + tmpo = func; + assert(f); + func = f(tmpo, *inst, (PyObject *)(*inst)->ob_type); + Py_DECREF(tmpo); + } + } else { + if (PyInstance_Check(*inst)) { + func = _Py_instance_getmethod( + (PyInstanceObject *)*inst, funcname); + if (func != NULL) + return call_fast_pymeth(func, pp_stack, inst, + n, na, nk); + } + func = PyObject_GetAttr(*inst, funcname); + if (func == NULL) { + return NULL; + } + } + #if CALL_ATTR_SLOW_BUT_PRETTY_PATH + result = call_callable(pp_stack, func, inst, n, na, nk); + return result; + #else + + /* Always dispatch PyCFunction first, because these are + presumed to be the most frequent callable object. + */ + if (PyCFunction_Check(func) && nk == 0) { + int flags = PyCFunction_GET_FLAGS(func); + PCALL(PCALL_CFUNCTION); + if (flags & (METH_NOARGS | METH_O)) { + PyCFunction meth = PyCFunction_GET_FUNCTION(func); + PyObject *self = PyCFunction_GET_SELF(func); + if (flags & METH_NOARGS && na == 0) + result = (*meth)(self, NULL); + else if (flags & METH_O && na == 1) { + PyObject *arg = EXT_POP(*pp_stack); + result = (*meth)(self, arg); + Py_DECREF(arg); + } + else { + err_args(func, flags, na); + result = NULL; + } + } + else { + PyObject *callargs; + callargs = load_args(pp_stack, na); + result = PyCFunction_Call(func, callargs, NULL); + Py_XDECREF(callargs); + } + } + else { + if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) { + /* optimize access to bound methods */ + PyObject *self = PyMethod_GET_SELF(func); + PCALL(PCALL_METHOD); + PCALL(PCALL_BOUND_METHOD); + Py_INCREF(self); + tmpo = func; + func = PyMethod_GET_FUNCTION(func); + Py_INCREF(func); + Py_DECREF(tmpo); + Py_DECREF(*inst); + *inst = self; + na++; + n++; + } + if (PyFunction_Check(func)) + result = fast_function(func, pp_stack, n, na, nk); + else + result = do_call(func, pp_stack, na, nk); + } + Py_DECREF(func); + /* We need to pop off the stack all arguments plus the function + * object, but the PyMethod case modifies the stack and the number + * of arguments, so we can't just pop 'n' times. */ + while ((*pp_stack) > inst) { + tmpo = EXT_POP(*pp_stack); + Py_DECREF(tmpo); + PCALL(PCALL_POP); + } + return result; + #endif + } + + #if CALL_ATTR_SLOW_BUT_PRETTY_PATH + + inline static PyObject * + call_function(PyObject ***pp_stack, int oparg) + { + int na = oparg & 0xff; + int nk = (oparg>>8) & 0xff; + int n = na + 2 * nk; + PyObject **pfunc = (*pp_stack) - n - 1; + Py_INCREF(*pfunc); + + return call_callable(pp_stack, *pfunc, pfunc, n, na, nk); + } + + #else + static PyObject * call_function(PyObject ***pp_stack, int oparg) { *************** *** 3444,3449 **** --- 3670,3677 ---- } return x; } + + #endif /* The fast_function() function optimize calls for which no argument tuple is necessary; the objects are passed directly from the stack. Index: Python/compile.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/compile.c,v retrieving revision 2.279 diff -c -r2.279 compile.c *** Python/compile.c 15 Apr 2003 10:35:07 -0000 2.279 --- Python/compile.c 18 Apr 2003 21:38:19 -0000 *************** *** 1850,1855 **** --- 1850,1903 ---- com_node(c, CHILD(n, 2)); } + #define STARFLAG 0x1 + #define STARSTARFLAG 0x2 + + static int + com_call_arglist(struct compiling *c, node *n, int *na, int *nk) + { + PyObject *keywords = NULL; + int i; + int lineno = n->n_lineno; + int flags = 0; + REQ(n, arglist); + for (i = 0; i < NCH(n); i += 2) { + node *ch = CHILD(n, i); + if (TYPE(ch) == STAR || + TYPE(ch) == DOUBLESTAR) + break; + if (ch->n_lineno != lineno) { + lineno = ch->n_lineno; + com_set_lineno(c, lineno); + } + com_argument(c, ch, &keywords); + if (keywords == NULL) + (*na)++; + else + (*nk)++; + } + Py_XDECREF(keywords); + while (i < NCH(n)) { + node *tok = CHILD(n, i); + node *ch = CHILD(n, i+1); + i += 3; + switch (TYPE(tok)) { + case STAR: + flags |= STARFLAG; + break; + case DOUBLESTAR: + flags |= STARSTARFLAG; + break; + } + com_node(c, ch); + } + if (*na > 255 || *nk > 255) { + com_error(c, PyExc_SyntaxError, + "more than 255 arguments"); + } + return flags; + } + static void com_call_function(struct compiling *c, node *n) { *************** *** 1857,1911 **** com_addoparg(c, CALL_FUNCTION, 0); } else { ! PyObject *keywords = NULL; ! int i, na, nk; ! int lineno = n->n_lineno; ! int star_flag = 0; ! int starstar_flag = 0; ! int opcode; ! REQ(n, arglist); ! na = 0; ! nk = 0; ! for (i = 0; i < NCH(n); i += 2) { ! node *ch = CHILD(n, i); ! if (TYPE(ch) == STAR || ! TYPE(ch) == DOUBLESTAR) ! break; ! if (ch->n_lineno != lineno) { ! lineno = ch->n_lineno; ! com_set_lineno(c, lineno); ! } ! com_argument(c, ch, &keywords); ! if (keywords == NULL) ! na++; ! else ! nk++; ! } ! Py_XDECREF(keywords); ! while (i < NCH(n)) { ! node *tok = CHILD(n, i); ! node *ch = CHILD(n, i+1); ! i += 3; ! switch (TYPE(tok)) { ! case STAR: star_flag = 1; break; ! case DOUBLESTAR: starstar_flag = 1; break; ! } ! com_node(c, ch); ! } ! if (na > 255 || nk > 255) { ! com_error(c, PyExc_SyntaxError, ! "more than 255 arguments"); ! } ! if (star_flag || starstar_flag) ! opcode = CALL_FUNCTION_VAR - 1 + ! star_flag + (starstar_flag << 1); else ! opcode = CALL_FUNCTION; com_addoparg(c, opcode, na | (nk << 8)); ! com_pop(c, na + 2*nk + star_flag + starstar_flag); } } static void com_select_member(struct compiling *c, node *n) { --- 1905,1924 ---- com_addoparg(c, CALL_FUNCTION, 0); } else { ! int na = 0, nk = 0, opcode; ! int flags = com_call_arglist(c, n, &na, &nk); ! if (flags & STARFLAG || flags & STARSTARFLAG) ! opcode = CALL_FUNCTION_VAR - 1 + flags; else ! opcode = CALL_FUNCTION; com_addoparg(c, opcode, na | (nk << 8)); ! com_pop(c, na + 2*nk + flags); } } + #undef STARSTARFLAG + #undef STARFLAG + static void com_select_member(struct compiling *c, node *n) { *************** *** 2072,2077 **** --- 2085,2145 ---- } } + static int + com_callattr_check(struct compiling *c, node *n, int child) + { + node *n1, *n2; + int na = 0, nk = 0; + int flags = 0, index; + PyObject *name; + char buffer[MANGLE_LEN]; + char * sname; + + if (child+1 >= NCH(n)) + return 0; + n1 = CHILD(n, child); + n2 = CHILD(n, child+1); + if (TYPE(n1) != trailer || TYPE(n2) != trailer) + return 0; + if (TYPE(CHILD(n1, 0)) != DOT) + return 0; + if (TYPE(CHILD(n2, 0)) != LPAR) + return 0; + if (TYPE(CHILD(n2, 1)) != RPAR) { + node *arglistnode = CHILD(n2, 1); + int i = NCH(arglistnode); + if (i > 1 && + (TYPE(CHILD(arglistnode, i - 2)) == STAR || + TYPE(CHILD(arglistnode, i - 2)) == DOUBLESTAR)) + return 0; + if (com_call_arglist(c, arglistnode, &na, &nk)) { + com_error(c, PyExc_SystemError, + "com_callattr_check failed to detect *args or **kwargs"); + return 0; + } + } + + REQ(CHILD(n1, 1), NAME); + sname = STR(CHILD(n1, 1)); + if (_Py_Mangle(c->c_private, sname, buffer, sizeof(buffer))) + sname = buffer; + if (sname == NULL || + (name = PyString_InternFromString(sname)) == NULL) { + c->c_errors++; + index = 255; + } else { + index = com_addname(c, name); + Py_DECREF(name); + } + if (index >> 16) { + com_error(c, PyExc_SystemError, "Too many constants"); + return 0; + } + flags = (nk << 24) + (index << 8) + na; + com_addoparg(c, CALL_ATTR, flags); + return 1; + } + static void com_power(struct compiling *c, node *n) { *************** *** 2085,2092 **** com_pop(c, 1); break; } ! else com_apply_trailer(c, CHILD(n, i)); } } --- 2153,2166 ---- com_pop(c, 1); break; } ! else { ! int j = com_callattr_check(c, n, i); ! if (j) { ! i += j; ! continue; ! } com_apply_trailer(c, CHILD(n, i)); + } } } Index: Python/import.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Python/import.c,v retrieving revision 2.220 diff -c -r2.220 import.c *** Python/import.c 23 Mar 2003 14:31:01 -0000 2.220 --- Python/import.c 18 Apr 2003 21:38:25 -0000 *************** *** 55,60 **** --- 55,63 ---- GvR, 2002-08-31: Because MWH changed the bytecode again, moved the magic number *back* to 62011. This should get the snake-farm to throw away its old .pyc files, amongst others. + + ThW, 2003-03-25: Changed it back to MWH's version for the CALL_ATTR + opcode. Known values: Python 1.5: 20121 *************** *** 69,76 **** Python 2.3a0: 62011 Python 2.3a0: 62021 Python 2.3a0: 62011 (!) */ ! #define MAGIC (62011 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the --- 72,80 ---- Python 2.3a0: 62011 Python 2.3a0: 62021 Python 2.3a0: 62011 (!) + Python 2.3a0: 62021 (!) */ ! #define MAGIC (62021 | ((long)'\r'<<16) | ((long)'\n'<<24)) /* Magic word as global; note that _PyImport_Init() can change the value of this global to accommodate for alterations of how the