micropython

docs/invocation.txt

137:f660fe1aac5c
2008-09-01 Paul Boddie Added notes about calling initialisers and instantiators, adopting a strategy where instantiation detected at compile-time is performed using an initialiser directly, whereas that detected at run-time is done using an instantiator whose code is now generated in the image. Added a finalise method to the Importer in order to set attribute locations before code generation, since some code (use of initialisers) requires details of a different program unit's locals (although this is actually unnecessary, but done because Attr instances are employed in the generated code). Changed class invocation at compile-time to acquire the new object reference from the frame of an already invoked initialiser just before dropping the frame. Added some support for raw image encoding of classes and functions. Changed JumpWithFrame usage to involve the current callable, not the current value. Added RecoverFrame and AdjustFrame instructions. Improved the tests around instantiation.
     1 Invocations in classic Python:
     2 
     3   f(1, 2, 3)      # positional
     4   f(1, 2)         # positional with defaults
     5   f(1, 2, c=3)    # keywords
     6   f(1, c=3)       # keywords with defaults
     7   f(1, 2, 3, 4)   # extra positional arguments
     8   f(1, 2, 3, d=4) # extra keyword arguments
     9   f(1, 2, *args)  # positional bundles (possibly with defaults)
    10   f(1, 2, **kw)   # keyword bundles (possibly with defaults)
    11 
    12   Note that f is never fixed before run-time in Python.
    13 
    14 Comparison to invocations in C:
    15 
    16   f(1, 2, 3)      # positional, f known at compile-time
    17   f(1, 2, 3)      # positional, f is appropriate function pointer
    18                   # ie. (*f)(A, B, C)
    19 
    20 Least expensive cases:
    21 
    22   f(1, 2, 3)      # put arguments in frame
    23                   # if f is not known, add arguments vs. parameters check
    24   f(1, 2)         # to handle defaults, introduce default "filling" where
    25                   # not enough arguments are given
    26                   # if f is not known, this is obviously done at run-time
    27 
    28 More expensive cases:
    29 
    30   f(1, 2, c=3)    # prepare frame using parameter details
    31                   # (provided c is a known parameter)
    32                   # if f is not known, this is obviously done at run-time
    33   f(1, c=3)       # as with the previous case, with default "filling" done
    34                   # where not enough arguments are given
    35                   # if f is not known, this is obviously done at run-time
    36                   # but with all defaults copied in before keywords are
    37                   # assigned (since their positions and thus the positions
    38                   # of missing parameters cannot be known)
    39 
    40 Awkward cases:
    41 
    42   f(1, 2, 3, 4)   # extra positional arguments
    43   f(1, 2, 3, d=4) # extra keyword arguments
    44   f(1, 2, *args)  # positional bundles (possibly with defaults)
    45   f(1, 2, **kw)   # keyword bundles (possibly with defaults)
    46 
    47   These cases require additional structures to be created, potentially at
    48   run-time.
    49 
    50 Methods vs. functions:
    51 
    52   f(obj, 1, 2)    # f known as function at compile-time:
    53                   #   f(obj, 1, 2)
    54                   # f known as C.m at compile-time:
    55                   #   m(obj "assert isinstance(obj, C)", 1, 2)
    56                   # f not known at compile-time:
    57                   #   f(<context>, obj, 1, 2) for instance-accessed methods
    58                   #   f(obj, 1, 2) for class-accessed methods
    59                   #   f(obj, 1, 2) for functions
    60 
    61   (Could either have universal context usage even for functions, which would
    62    ignore them, or attempt to remove contexts when functions are called.)
    63 
    64 Argument lists for functions:
    65 
    66   f(obj, 1, 2)    # f known as function at compile-time
    67 
    68     f   -> don't get any context information
    69     obj -> argument #1
    70     1   -> argument #2
    71     2   -> argument #3
    72 
    73 Argument lists for methods:
    74 
    75   f(obj, 1, 2)    # f known as C.m at compile-time (context is C)
    76 
    77     f   -> C.m - don't get any context information
    78     obj -> argument #1
    79     1   -> argument #2
    80     2   -> argument #3
    81 
    82 Argument lists for methods:
    83 
    84   f(obj, 1, 2)    # f known as C.m at compile-time (context is an instance)
    85 
    86     f   -> C.m
    87         -> context is argument #1
    88     obj -> argument #2
    89     1   -> argument #3
    90     2   -> argument #4
    91 
    92 Argument lists for classes:
    93 
    94   f(obj, 1, 2)    # f known as C at compile-time
    95 
    96     f   -> C.__init__
    97         -> new instance is argument #1
    98     obj -> argument #2
    99     1   -> argument #3
   100     2   -> argument #4
   101 
   102   The new instance must be manually provided as the result after the call.
   103 
   104 Argument lists for unknown callables:
   105 
   106   f(obj, 1, 2)    # f not known at compile-time
   107 
   108     f   -> f
   109         -> load context for argument #1
   110     obj -> argument #2
   111     1   -> argument #3
   112     2   -> argument #4
   113 
   114   Then, check the context and shift the frame if necessary:
   115 
   116     <context> is module or class:
   117       (<context>, obj, 1, 2) -> (obj, 1, 2)
   118 
   119     <context> is instance: no change
   120 
   121 Argument lists in instantiators:
   122 
   123   f(obj, 1, 2)    # f not known at compile-time
   124 
   125     f   -> C.__new__ (known and called at run-time)
   126 
   127   Need to call C.__init__(<instance>, obj, 1, 2), preferably with the existing
   128   frame:
   129 
   130     <insert instance before received arguments>
   131     obj -> argument #1
   132     1   -> argument #2
   133     2   -> argument #3
   134 
   135   Then jump without switching frames.
   136   It should be possible to replace the old, tentative context information in the
   137   frame.
   138 
   139 Defaults for unknown callables:
   140 
   141   f(obj)          # f not known at compile-time
   142 
   143     f   -> f
   144         -> load context for argument #1
   145     obj -> argument #2
   146 
   147   Then, check the number of arguments and the availability of defaults against
   148   the details provided by the callable's structure.
   149 
   150 Checking defaults for unknown callables:
   151 
   152   Approach #1 - pre-fill defaults, add arguments, check frame
   153 
   154   Approach #2 - add arguments, add defaults while checking frame
   155 
   156 Functions as methods:
   157 
   158   def f(x, y, z): ...
   159   class C:
   160       m = f
   161   c = C()
   162   ...
   163   f(obj, 1, 2)    # no restrictions on obj
   164   obj.m(1, 2)     # f(obj, 1, 2)
   165   C.m(obj, 1, 2)  # f(obj "assert isinstance(obj, C)", 1, 2)
   166 
   167 Context propagation:
   168 
   169   fn = C.m        # has context C
   170   fn(obj, 1, 2)   # non-instance context -> explicit context required
   171                   # must perform isinstance(obj, C)
   172   fn = c.m        # table entry for m on C -> replace context
   173                   # gives context c
   174   fn(1, 2)        # instance context -> no explicit context required
   175                   # context c inserted in call