@@ -1549,10 +1549,77 @@ static PyType_Spec attrgetter_type_spec = {
1549
1549
typedef struct {
1550
1550
PyObject_HEAD
1551
1551
PyObject * name ;
1552
- PyObject * args ;
1552
+ PyObject * xargs ; // reference to arguments passed in constructor
1553
1553
PyObject * kwds ;
1554
+ PyObject * * vectorcall_args ; /* Borrowed references */
1555
+ PyObject * vectorcall_kwnames ;
1556
+ vectorcallfunc vectorcall ;
1554
1557
} methodcallerobject ;
1555
1558
1559
+ static int _methodcaller_initialize_vectorcall (methodcallerobject * mc )
1560
+ {
1561
+ PyObject * args = mc -> xargs ;
1562
+ PyObject * kwds = mc -> kwds ;
1563
+
1564
+ Py_ssize_t nargs = PyTuple_GET_SIZE (args );
1565
+ assert (nargs > 0 );
1566
+ mc -> vectorcall_args = PyMem_Calloc (
1567
+ nargs + (kwds ? PyDict_Size (kwds ) : 0 ),
1568
+ sizeof (PyObject * ));
1569
+ if (!mc -> vectorcall_args ) {
1570
+ PyErr_NoMemory ();
1571
+ return -1 ;
1572
+ }
1573
+ /* The first item of vectorcall_args will be filled with obj later */
1574
+ if (nargs > 1 ) {
1575
+ memcpy (mc -> vectorcall_args , PySequence_Fast_ITEMS (args ),
1576
+ nargs * sizeof (PyObject * ));
1577
+ }
1578
+ if (kwds ) {
1579
+ const Py_ssize_t nkwds = PyDict_Size (kwds );
1580
+
1581
+ mc -> vectorcall_kwnames = PyTuple_New (nkwds );
1582
+ if (!mc -> vectorcall_kwnames ) {
1583
+ return -1 ;
1584
+ }
1585
+ Py_ssize_t i = 0 , ppos = 0 ;
1586
+ PyObject * key , * value ;
1587
+ while (PyDict_Next (kwds , & ppos , & key , & value )) {
1588
+ PyTuple_SET_ITEM (mc -> vectorcall_kwnames , i , Py_NewRef (key ));
1589
+ mc -> vectorcall_args [nargs + i ] = value ; // borrowed reference
1590
+ ++ i ;
1591
+ }
1592
+ }
1593
+ else {
1594
+ mc -> vectorcall_kwnames = NULL ;
1595
+ }
1596
+ return 1 ;
1597
+ }
1598
+
1599
+
1600
+ static PyObject *
1601
+ methodcaller_vectorcall (
1602
+ methodcallerobject * mc , PyObject * const * args , size_t nargsf , PyObject * kwnames )
1603
+ {
1604
+ if (!_PyArg_CheckPositional ("methodcaller" , PyVectorcall_NARGS (nargsf ), 1 , 1 )
1605
+ || !_PyArg_NoKwnames ("methodcaller" , kwnames )) {
1606
+ return NULL ;
1607
+ }
1608
+ if (mc -> vectorcall_args == NULL ) {
1609
+ if (_methodcaller_initialize_vectorcall (mc ) < 0 ) {
1610
+ return NULL ;
1611
+ }
1612
+ }
1613
+
1614
+ assert (mc -> vectorcall_args != 0 );
1615
+ mc
67ED
-> vectorcall_args [0 ] = args [0 ];
1616
+ return PyObject_VectorcallMethod (
1617
+ mc -> name , mc -> vectorcall_args ,
1618
+ (PyTuple_GET_SIZE (mc -> xargs )) | PY_VECTORCALL_ARGUMENTS_OFFSET ,
1619
+ mc -> vectorcall_kwnames );
1620
+ }
1621
+
1622
+
1556
1623
/* AC 3.5: variable number of arguments, not currently support by AC */
1557
1624
static PyObject *
1558
1625
methodcaller_new (PyTypeObject * type , PyObject * args , PyObject * kwds )
@@ -1580,38 +1647,40 @@ methodcaller_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
1580
1647
return NULL ;
1581
1648
}
1582
1649
1583
- name = PyTuple_GET_ITEM (args , 0 );
1584
1650
Py_INCREF (name );
1585
1651
PyUnicode_InternInPlace (& name );
1586
1652
mc -> name = name ;
1587
1653
1654
+ mc -> xargs = Py_XNewRef (args ); // allows us to use borrowed references
1588
1655
mc -> kwds = Py_XNewRef (kwds );
1656
+ mc -> vectorcall_args = 0 ;
1589
1657
1590
- mc -> args = PyTuple_GetSlice (args , 1 , PyTuple_GET_SIZE (args ));
1591
- if (mc -> args == NULL ) {
1592
- Py_DECREF (mc );
1593
- return NULL ;
1594
- }
1658
+
1659
+ mc -> vectorcall = (vectorcallfunc )methodcaller_vectorcall ;
1595
1660
1596
1661
PyObject_GC_Track (mc );
1597
1662
return (PyObject * )mc ;
1598
1663
}
1599
1664
1600
- static int
1665
+ static void
1601
1666
methodcaller_clear (methodcallerobject * mc )
1602
1667
{
1603
1668
Py_CLEAR (mc -> name );
1604
- Py_CLEAR (mc -> args );
1669
+ Py_CLEAR (mc -> xargs );
1605
1670
Py_CLEAR (mc -> kwds );
1606
- return 0 ;
1671
+ if (mc -> vectorcall_args != NULL ) {
1672
+ PyMem_Free (mc -> vectorcall_args );
1673
<
F438
code class="diff-text syntax-highlighted-line addition">+ mc -> vectorcall_args = 0 ;
1674
+ Py_CLEAR (mc -> vectorcall_kwnames );
1675
+ }
1607
1676
}
1608
1677
1609
1678
static void
1610
1679
methodcaller_dealloc (methodcallerobject * mc )
1611
1680
{
1612
1681
PyTypeObject * tp = Py_TYPE (mc );
1613
1682
PyObject_GC_UnTrack (mc );
1614
- ( void ) methodcaller_clear (mc );
1683
+ methodcaller_clear (mc );
1615
1684
tp -> tp_free (mc );
1616
1685
Py_DECREF (tp );
1617
1686
}
@@ -1620,7 +1689,7 @@ static int
1620
1689
methodcaller_traverse (methodcallerobject * mc , visitproc visit , void * arg )
1621
1690
{
1622
1691
Py_VISIT (mc -> name );
1623
- Py_VISIT (mc -> args );
1692
+ Py_VISIT (mc -> xargs );
1624
1693
Py_VISIT (mc -> kwds );
1625
1694
Py_VISIT (Py_TYPE (mc ));
1626
1695
return 0 ;
@@ -1639,7 +1708,16 @@ methodcaller_call(methodcallerobject *mc, PyObject *args, PyObject *kw)
1639
1708
method = PyObject_GetAttr (obj , mc -> name );
1640
1709
if (method == NULL )
1641
1710
return NULL ;
1642
- result = PyObject_Call (method , mc -> args , mc -> kwds );
1711
+
1712
+
1713
+ PyObject * cargs = PyTuple_GetSlice (mc -> xargs , 1 , PyTuple_GET_SIZE (mc -> xargs ));
1714
+ if (cargs == NULL ) {
1715
+ Py_DECREF (method );
1716
+ return NULL ;
1717
+ }
1718
+
1719
+ result = PyObject_Call (method , cargs , mc -> kwds );
1720
+ Py_DECREF (cargs );
1643
1721
Py_DECREF (method );
1644
1722
return result ;
1645
1723
}
@@ -1657,7 +1735,7 @@ methodcaller_repr(methodcallerobject *mc)
1657
1735
}
1658
1736
1659
1737
numkwdargs = mc -> kwds != NULL ? PyDict_GET_SIZE (mc -> kwds ) : 0 ;
1660
- numposargs = PyTuple_GET_SIZE (mc -> args ) ;
1738
+ numposargs = PyTuple_GET_SIZE (mc -> xargs ) - 1 ;
1661
1739
numtotalargs = numposargs + numkwdargs ;
1662
1740
1663
1741
if (numtotalargs == 0 ) {
@@ -1673,7 +1751,7 @@ methodcaller_repr(methodcallerobject *mc)
1673
1751
}
1674
1752
1675
1753
for (i = 0 ; i < numposargs ; ++ i ) {
1676
- PyObject * onerepr = PyObject_Repr (PyTuple_GET_ITEM (mc -> args , i ));
1754
+ PyObject * onerepr = PyObject_Repr (PyTuple_GET_ITEM (mc -> xargs , i + 1 ));
1677
1755
if (onerepr == NULL )
1678
1756
goto done ;
1679
1757
PyTuple_SET_ITEM (argreprs , i , onerepr );
@@ -1723,17 +1801,16 @@ methodcaller_repr(methodcallerobject *mc)
1723
1801
static PyObject *
1724
1802
methodcaller_reduce (methodcallerobject * mc , PyObject * Py_UNUSED (ignored ))
1725
1803
{
1726
- PyObject * newargs ;
1727
1804
if (!mc -> kwds || PyDict_GET_SIZE (mc -> kwds ) == 0 ) {
1728
1805
Py_ssize_t i ;
1729
- Py_ssize_t callargcount = PyTuple_GET_SIZE (mc -> args );
1730
- newargs = PyTuple_New (1 + callargcount );
1806
+ Py_ssize_t newarg_size = PyTuple_GET_SIZE (mc -> xargs );
1807
+ PyObject * newargs = PyTuple_New (newarg_size );
1731
1808
if (newargs == NULL )
1732
1809
return NULL ;
1733
1810
PyTuple_SET_ITEM (newargs , 0 , Py_NewRef (mc -> name ));
1734
- for (i = 0 ; i < callargcount ; ++ i ) {
1735
- PyObject * arg = PyTuple_GET_ITEM (mc -> args , i );
1736
- PyTuple_SET_ITEM (newargs , i + 1 , Py_NewRef (arg ));
1811
+ for (i = 1 ; i < newarg_size ; ++ i ) {
1812
+ PyObject * arg = PyTuple_GET_ITEM (mc -> xargs , i );
1813
+ PyTuple_SET_ITEM (newargs , i , Py_NewRef (arg ));
1737
1814
}
1738
1815
return Py_BuildValue ("ON" , Py_TYPE (mc ), newargs );
1739
1816
}
@@ -1751,7 +1828,12 @@ methodcaller_reduce(methodcallerobject *mc, PyObject *Py_UNUSED(ignored))
1751
1828
constructor = PyObject_VectorcallDict (partial , newargs , 2 , mc -> kwds );
1752
1829
1753
1830
Py_DECREF (partial );
1754
- return Py_BuildValue ("NO" , constructor , mc -> args );
1831
+ PyObject * args = PyTuple_GetSlice (mc -> xargs , 1 , PyTuple_GET_SIZE (mc -> xargs ));
1832
+ if (!args ) {
1833
+ Py_DECREF (constructor );
1834
+ return NULL ;
1835
+ }
1836
+ return Py_BuildValue ("NO" , constructor , args );
1755
1837
}
1756
1838
}
1757
1839
@@ -1760,6 +1842,12 @@ static PyMethodDef methodcaller_methods[] = {
1760
1842
reduce_doc },
1761
1843
{NULL }
1762
1844
};
1845
+
1846
+ static PyMemberDef methodcaller_members [] = {
1847
+ {"__vectorcalloffset__" , Py_T_PYSSIZET , offsetof(methodcallerobject , vectorcall ), Py_READONLY },
1848
+ {NULL }
1849
+ };
1850
+
1763
1851
PyDoc_STRVAR (methodcaller_doc ,
1764
1852
"methodcaller(name, /, *args, **kwargs)\n--\n\n\
1765
1853
Return a callable object that calls the given method on its operand.\n\
@@ -1774,6 +1862,7 @@ static PyType_Slot methodcaller_type_slots[] = {
1774
1862
{Py_tp_traverse , methodcaller_traverse },
1775
1863
{Py_tp_clear , methodcaller_clear },
1776
1864
{Py_tp_methods , methodcaller_methods },
1865
+ {Py_tp_members , methodcaller_members },
1777
1866
{Py_tp_new , methodcaller_new },
1778
1867
{Py_tp_getattro , PyObject_GenericGetAttr },
1779
1868
{Py_tp_repr , methodcaller_repr },
@@ -1785,7 +1874,7 @@ static PyType_Spec methodcaller_type_spec = {
1785
1874
.basicsize = sizeof (methodcallerobject ),
1786
1875
.itemsize = 0 ,
1787
1876
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
1788
- Py_TPFLAGS_IMMUTABLETYPE ),
1877
+ Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_IMMUTABLETYPE ),
1789
1878
.slots = methodcaller_type_slots ,
1790
1879
};
1791
1880
0 commit comments