pythonpython-3.xvirtual-machinebytecode

Understanding what a python 3.12 bytecode does -- CALL 0 after GET_ITER


I have this python function and the bytecode it translates to:

Text code:

x = "-".join(str(z) for z in range(5))
assert x == "0-1-2-3-4"
print("Assert test case for generator_expression_in_join")


Disassembled code:

  0           0 RESUME                   0

  2           2 LOAD_CONST               0 ('-')
              4 LOAD_ATTR                1 (NULL|self + join)
             24 LOAD_CONST               1 (<code object <genexpr> at 0x1026abe30, file "<dis>", line 2>)
             26 MAKE_FUNCTION            0
             28 PUSH_NULL
             30 LOAD_NAME                1 (range)
             32 LOAD_CONST               2 (5)
             34 CALL                     1
             42 GET_ITER
             44 CALL                     0
             52 CALL                     1
             60 STORE_NAME               2 (x)

  3          62 LOAD_NAME                2 (x)
             64 LOAD_CONST               3 ('0-1-2-3-4')
             66 COMPARE_OP              40 (==)
             70 POP_JUMP_IF_TRUE         2 (to 76)
             72 LOAD_ASSERTION_ERROR
             74 RAISE_VARARGS            1

  4     >>   76 PUSH_NULL
             78 LOAD_NAME                3 (print)
             80 LOAD_CONST               4 ('Assert test case for generator_expression_in_join')
             82 CALL                     1
             90 POP_TOP
             92 RETURN_CONST             5 (None)

Disassembly of <code object <genexpr> at 0x1026abe30, file "<dis>", line 2>:
  2           0 RETURN_GENERATOR
              2 POP_TOP
              4 RESUME                   0
              6 LOAD_FAST                0 (.0)
        >>    8 FOR_ITER                15 (to 42)
             12 STORE_FAST               1 (z)
             14 LOAD_GLOBAL              1 (NULL + str)
             24 LOAD_FAST                1 (z)
             26 CALL                     1
             34 YIELD_VALUE              1
             36 RESUME                   1
             38 POP_TOP
             40 JUMP_BACKWARD           17 (to 8)
        >>   42 END_FOR
             44 RETURN_CONST             0 (None)
        >>   46 CALL_INTRINSIC_1         3 (INTRINSIC_STOPITERATION_ERROR)
             48 RERAISE                  1

I have trouble understanding what the instruction with label 44 stands for. I understand that I have a range iterator on the top of my stack after instruction 42 (iter(range(5))), but I don't know why one would call an iterator.

I'm trying to implement a python virtual machine, and am struggling to implement the CALL opcode correctly. I don't see any help in the provided spec.

What is the logic behind doing CALL 0 on an iterator since an iterator isn't even callable?


Solution

  • argc is the total of the positional and named arguments, excluding self when a NULL is not present.

    Here’s one call:

     28 PUSH_NULL
     30 LOAD_NAME                1 (range)
     32 LOAD_CONST               2 (5)
     34 CALL                     1
    

    And here’s another:

     24 LOAD_CONST               1 (<code object <genexpr> at 0x1026abe30, file "<dis>", line 2>)
     26 MAKE_FUNCTION            0
        ⋮
     42 GET_ITER
     44 CALL                     0
    

    That is, you’re calling the generator expression with the iterator as self.

    Note that this instruction is different again in Python 3.13.