File: callmethod.py

package info (click to toggle)
pypy3 7.3.19%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 212,236 kB
  • sloc: python: 2,098,316; ansic: 540,565; sh: 21,462; asm: 14,419; cpp: 4,451; makefile: 4,209; objc: 761; xml: 530; exp: 499; javascript: 314; pascal: 244; lisp: 45; csh: 12; awk: 4
file content (147 lines) | stat: -rw-r--r-- 6,000 bytes parent folder | download | duplicates (2)
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
"""
Two bytecodes to speed up method calls.  Here is how a method call looks
like: (on the left, without the new bytecodes; on the right, with them)

    <push self>                    <push self>
    LOAD_ATTR       name           LOAD_METHOD   name
    <push arg 0>                   <push arg 0>
    ...                            ...
    <push arg n-1>                 <push arg n-1>
    CALL_FUNCTION   n              CALL_METHOD     n
"""

from pypy.interpreter import function
from rpython.rlib import jit
from pypy.objspace.std.mapdict import LOAD_METHOD_mapdict, \
    LOAD_METHOD_mapdict_fill_cache_method


# This module exports two extra methods for StdObjSpaceFrame implementing
# the LOAD_METHOD and CALL_METHOD opcodes in an efficient way, as well
# as a version of space.call_method() that uses the same approach.
# See pypy.objspace.std.objspace for where these functions are used from.


def LOAD_METHOD(f, nameindex, *ignored):
    from pypy.objspace.std.typeobject import MutableCell
    #   stack before                 after
    #  --------------    --fast-method----fallback-case------------
    #
    #                      w_object       None
    #    w_object    =>    w_function     w_boundmethod_or_whatever
    #   (more stuff)      (more stuff)   (more stuff)
    #
    space = f.space
    w_obj = f.popvalue()

    if not jit.we_are_jitted():
        # mapdict has an extra-fast version of this function
        if LOAD_METHOD_mapdict(f, nameindex, w_obj):
            return

    w_name = f.getname_w(nameindex)
    w_value = None

    w_type = space.type(w_obj)
    if w_type.has_object_getattribute():
        name = space.text_w(w_name)
        # bit of a mess to use these internal functions, but it allows the
        # mapdict caching below to work without an additional lookup
        version_tag = w_type.version_tag()
        if version_tag is None:
            _, w_descr = w_type._lookup_where(name)
            w_descr_cell = None
        else:
            _, w_descr_cell = w_type._pure_lookup_where_with_method_cache(
                name, version_tag)
            w_descr = w_descr_cell
            if isinstance(w_descr, MutableCell):
                w_descr = w_descr.unwrap_cell(space)
        if w_descr is None:
            # this handles directly the common case
            #   module.function(args..)
            w_value = w_obj.getdictvalue(space, name)
            # xxx we could also use the mapdict cache in that case, probably
        else:
            typ = type(w_descr)
            if typ is function.Function or typ is function.FunctionWithFixedCode:
                w_value = w_obj.getdictvalue(space, name)
                if w_value is None:
                    # fast method path: a function object in the class,
                    # nothing in the instance
                    f.pushvalue(w_descr)
                    f.pushvalue(w_obj)
                    if not jit.we_are_jitted():
                        # let mapdict cache stuff
                        LOAD_METHOD_mapdict_fill_cache_method(
                            space, f.getcode(), name, nameindex, w_obj, w_type,
                            w_descr_cell)
                    return
    if w_value is None:
        w_value = space.getattr(w_obj, w_name)
    f.pushvalue(w_value)
    f.pushvalue_none()

@jit.unroll_safe
def CALL_METHOD(f, oparg, *ignored):
    # opargs contains the arg, and kwarg count, excluding the implicit 'self'
    n_args = oparg
    w_self = f.peekvalue_maybe_none(n_args)
    n = n_args + (w_self is not None)

    w_callable = f.peekvalue(n_args + 1)
    w_result = f.space.call_valuestack(
            w_callable, n, f, methodcall=w_self is not None, dropvalues=n_args+2)
    f.pushvalue(w_result)

@jit.unroll_safe
def CALL_METHOD_KW(f, n_arguments, *ignored):
    from pypy.objspace.std.tupleobject import W_AbstractTupleObject
    # opargs contains the arg + kwarg count, excluding the implicit 'self'
    w_self = f.peekvalue_maybe_none(n_arguments + 1)

    space = f.space
    # like in BUILD_CONST_KEY_MAP we can't use space.fixedview because then
    # the immutability of the tuple is lost
    w_tup_varnames = space.interp_w(W_AbstractTupleObject, f.popvalue())
    n_keywords = space.len_w(w_tup_varnames)
    keyword_names_w = [None] * n_keywords
    keywords_w = [None] * n_keywords
    for i in range(n_keywords):
        keyword_names_w[i] = w_tup_varnames.getitem(space, i)
        w_value = f.peekvalue(n_keywords - 1 - i)
        keywords_w[i] = w_value
    f.dropvalues(n_keywords)
    n_arguments -= n_keywords
    n = n_arguments + (w_self is not None)
    arguments = f.popvalues(n)    # includes w_self if it is not None
    if w_self is None:
        f.popvalue_maybe_none()    # removes w_self, which is None
    w_callable = f.popvalue()
    args = f.argument_factory(
            arguments, keyword_names_w, keywords_w, None, None,
            methodcall=w_self is not None, w_function=w_callable)
    if f.get_is_being_profiled() and function.is_builtin_code(w_callable):
        w_result = f.space.call_args_and_c_profile(f, w_callable, args)
    else:
        w_result = f.space.call_args(w_callable, args)
    f.pushvalue(w_result)


def call_method_opt(space, w_obj, methname, *arg_w):
    """An optimized version of space.call_method()
    based on the same principle as above.
    """
    w_type = space.type(w_obj)
    if w_type.has_object_getattribute():
        w_descr = space.lookup(w_obj, methname)
        typ = type(w_descr)
        if typ is function.Function or typ is function.FunctionWithFixedCode:
            w_value = w_obj.getdictvalue(space, methname)
            if w_value is None:
                # fast method path: a function object in the class,
                # nothing in the instance
                return space.call_function(w_descr, w_obj, *arg_w)
    w_name = space.newtext(methname)
    w_meth = space.getattr(w_obj, w_name)
    return space.call_function(w_meth, *arg_w)