You can look at the source code for the objects (all located in pypy/objspace/std) and find the method implementations there.
def append(self, w_item):
"""L.append(object) -- append object to end"""
self.strategy.append(self, w_item)
So it's just appending to an RPython list. If you want to see the source for that, look in rpython/rtyper. append's is in rpython/rtyper/rlist.py:
def rtype_method_append(self, hop):
v_lst, v_value = hop.inputargs(self, self.item_repr)
hop.exception_cannot_occur()
hop.gendirectcall(ll_append, v_lst, v_value)
This ends up calling ll_append in the end (I think the other stuff is for the JIT?), which is defined in the same file:
def ll_append(l, newitem):
length = l.ll_length()
l._ll_resize_ge(length+1) # see "a note about overflows" above
l.ll_setitem_fast(length, newitem)
Now, these ll_* functions are defined in the corresponding file inside rpython/rtyper/lltypesystem; in this case, it's rpython/rtyper/lltypesystem/rlist.py:
self.LIST.become(GcStruct("list", ("length", Signed),
("items", Ptr(ITEMARRAY)),
adtmeths = ADTIList({
"ll_newlist": ll_newlist,
"ll_newlist_hint": ll_newlist_hint,
"ll_newemptylist": ll_newemptylist,
"ll_length": ll_length,
"ll_items": ll_items,
"ITEM": ITEM,
"ll_getitem_fast": ll_getitem_fast,
"ll_setitem_fast": ll_setitem_fast,
"_ll_resize_ge": _ll_list_resize_ge,
"_ll_resize_le": _ll_list_resize_le,
"_ll_resize": _ll_list_resize,
"_ll_resize_hint": _ll_list_resize_hint,
}),
hints = {'list': True})
)
It's signaling to RPython all the different methods on the low-level list representation. Here, you want ll_setitem_fast and _ll_list_resize_ge (I also copy-pasted the functions they call):
@jit.look_inside_iff(lambda l, newsize, overallocate: jit.isconstant(len(l.items)) and jit.isconstant(newsize))
@signature(types.any(),
types.int(), types.bool(), returns=types.none())
def _ll_list_resize_hint_really(l, newsize, overallocate):
"""
Ensure l.items has room for at least newsize elements. Note that
l.items may change, and even if newsize is less than l.length on
entry.
"""
# This over-allocates proportional to the list size, making room
# for additional growth. The over-allocation is mild, but is
# enough to give linear-time amortized behavior over a long
# sequence of appends() in the presence of a poorly-performing
# system malloc().
# The growth pattern is: 0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
if newsize <= 0:
ll_assert(newsize == 0, "negative list length")
l.length = 0
l.items = _ll_new_empty_item_array(typeOf(l).TO)
return
elif overallocate:
if newsize < 9:
some = 3
else:
some = 6
some += newsize >> 3
new_allocated = newsize + some
else:
new_allocated = newsize
# new_allocated is a bit more than newsize, enough to ensure an amortized
# linear complexity for e.g. repeated usage of l.append(). In case
# it overflows sys.maxint, it is guaranteed negative, and the following
# malloc() will fail.
items = l.items
newitems = malloc(typeOf(l).
TO.items.TO, new_allocated)
before_len = l.length
if before_len: # avoids copying GC flags from the prebuilt_empty_array
if before_len < newsize:
p = before_len
else:
p = newsize
rgc.ll_arraycopy(items, newitems, 0, 0, p)
l.items = newitems
def _ll_list_resize_ge(l, newsize):
"""This is called with 'newsize' larger than the current length of the
list. If the list storage doesn't have enough space, then really perform
a realloc(). In the common case where we already overallocated enough,
then this is a very fast operation.
"""
cond = len(l.items) < newsize
if jit.isconstant(len(l.items)) and jit.isconstant(newsize):
if cond:
_ll_list_resize_hint_really(l, newsize, True)
else:
jit.conditional_call(cond,
_ll_list_resize_hint_really, l, newsize, True)
l.length = newsize
def ll_items(l):
return l.items
def ll_setitem_fast(l, index, item):
ll_assert(index < l.length, "setitem out of bounds")
l.ll_items()[index] = item
ll_setitem_fast.oopspec = 'list.setitem(l, index, item)'