File: callmethod.py

package info (click to toggle)
pypy3 7.0.0%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 111,848 kB
  • sloc: python: 1,291,746; ansic: 74,281; asm: 5,187; cpp: 3,017; sh: 2,533; makefile: 544; xml: 243; lisp: 45; csh: 21; awk: 4
file content (143 lines) | stat: -rw-r--r-- 5,661 bytes parent folder | download | duplicates (3)
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
"""
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           LOOKUP_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 LOOKUP_METHOD_mapdict, \
    LOOKUP_METHOD_mapdict_fill_cache_method


# This module exports two extra methods for StdObjSpaceFrame implementing
# the LOOKUP_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 LOOKUP_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 LOOKUP_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
                        LOOKUP_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 & 0xff
    n_kwargs = (oparg >> 8) & 0xff
    w_self = f.peekvalue_maybe_none(n_args + (2 * n_kwargs))
    n = n_args + (w_self is not None)

    if not n_kwargs:
        w_callable = f.peekvalue(n_args + (2 * n_kwargs) + 1)
        try:
            w_result = f.space.call_valuestack(
                    w_callable, n, f, methodcall=w_self is not None)
        finally:
            f.dropvalues(n_args + 2)
    else:
        keywords = [None] * n_kwargs
        keywords_w = [None] * n_kwargs
        while True:
            n_kwargs -= 1
            if n_kwargs < 0:
                break
            w_value = f.popvalue()
            w_key = f.popvalue()
            key = f.space.text_w(w_key)
            keywords[n_kwargs] = key
            keywords_w[n_kwargs] = w_value

        arguments = f.popvalues(n)    # includes w_self if it is not None
        args = f.argument_factory(
                arguments, keywords, keywords_w, None, None,
                methodcall=w_self is not None)
        if w_self is None:
            f.popvalue_maybe_none()    # removes w_self, which is None
        w_callable = f.popvalue()
        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)