bpo-43563 : Introduce dedicated opcodes for super calls by vladima · Pull Request #24936 · python/cpython · GitHub
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Include/cpython/object.h
2 changes: 2 additions & 0 deletions Include/opcode.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Lib/importlib/_bootstrap_external.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ def _write_atomic(path, data, mode=0o666):
# Python 3.10a2 3432 (Function annotation for MAKE_FUNCTION is changed from dict to tuple bpo-42202)
# Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0)
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
# Python 3.10a6 3435 (LOAD_*_SUPER opcodes)

#
# MAGIC must change whenever the bytecode emitted by the compiler may no
Expand All @@ -323,7 +324,7 @@ def _write_atomic(path, data, mode=0o666):
# Whenever MAGIC_NUMBER is changed, the ranges in the magic_values array
# in PC/launcher.c must also be updated.

MAGIC_NUMBER = (3434).to_bytes(2, 'little') + b'\r\n'
MAGIC_NUMBER = (3435).to_bytes(2, 'little') + b'\r\n'
_RAW_MAGIC_NUMBER = int.from_bytes(MAGIC_NUMBER, 'little') # For import.c

_PYCACHE = '__pycache__'
Expand Down
4 changes: 4 additions & 0 deletions Lib/opcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,5 +211,9 @@ def jabs_op(name, op):
def_op('SET_UPDATE', 163)
def_op('DICT_MERGE', 164)
def_op('DICT_UPDATE', 165)
name_op('LOAD_METHOD_SUPER', 166)
hasconst.append(166)
name_op('LOAD_ATTR_SUPER', 167)
hasconst.append(167)

del def_op, name_op, jrel_op, jabs_op
309 changes: 309 additions & 0 deletions Lib/test/test_dis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from test.support import captured_stdout
from test.support.bytecode_helper import BytecodeTestCase
from textwrap import dedent
import unittest
import sys
import dis
Expand Down Expand Up @@ -672,6 +673,314 @@ def check(expected, **kwargs):
check(dis_nested_2)


def test_super_zero_args(self):
src = """
class C:
def f(self): super().f1()
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 LOAD_DEREF 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_METHOD_SUPER 1 ((1, True))
8 CALL_METHOD 0
10 POP_TOP
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
"""

g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

def test_super_zero_args_load_attr(self):
src = """
class C:
def f(self): super().f1(a=1)
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 LOAD_DEREF 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR_SUPER 1 ((1, True))
8 LOAD_CONST 2 (1)
10 LOAD_CONST 3 (('a',))
12 CALL_FUNCTION_KW 1
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

def test_super_two_args(self):
src = """
class C:
def f(self): super(C, self).f1()
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 LOAD_GLOBAL 1 (C)
4 LOAD_FAST 0 (self)
6 LOAD_METHOD_SUPER 1 ((2, False))
8 CALL_METHOD 0
10 POP_TOP
12 LOAD_CONST 0 (None)
14 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)


def test_super_zero_method_args(self):
src = """
class C:
def f(): super().f1()
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 CALL_FUNCTION 0
4 LOAD_METHOD 1 (f1)
6 CALL_METHOD 0
8 POP_TOP
10 LOAD_CONST 0 (None)
12 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

def test_super_two_args_attr(self):
src = """
class C:
def f(self): super(C, self).f1(a=1)
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 LOAD_GLOBAL 1 (C)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR_SUPER 1 ((2, False))
8 LOAD_CONST 2 (1)
10 LOAD_CONST 3 (('a',))
12 CALL_FUNCTION_KW 1
14 POP_TOP
16 LOAD_CONST 0 (None)
18 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

def test_super_attr_load(self):
src = """
class C:
def f(self): return super().x
"""
expected = """\
3 0 LOAD_GLOBAL 0 (super)
2 LOAD_DEREF 0 (__class__)
4 LOAD_FAST 0 (self)
6 LOAD_ATTR_SUPER 1 ((1, True))
8 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)
expected_dis_info = """\
Name: f
Filename: <string>
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 3
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: (1, True)
Names:
0: super
1: x
Variable names:
0: self
Free variables:
0: __class__"""
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)

def test_super_attr_load_self_cell(self):
src = """
class C:
def f(self):
lambda: self
return super().x
"""
expected = """\
4 0 LOAD_CLOSURE 0 (self)
2 BUILD_TUPLE 1
4 LOAD_CONST 1 (<code object <lambda> at 0x..., file "<string>", line 4>)
6 LOAD_CONST 2 ('C.f.<locals>.<lambda>')
8 MAKE_FUNCTION 8 (closure)
10 POP_TOP

5 12 LOAD_GLOBAL 0 (super)
14 LOAD_DEREF 1 (__class__)
16 LOAD_DEREF 0 (self)
18 LOAD_ATTR_SUPER 3 ((1, True))
20 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

def test_super_attr_store(self):
src = """
class C:
def f(self): super().x = 1
"""
expected = """\
3 0 LOAD_CONST 1 (1)
2 LOAD_GLOBAL 0 (super)
4 CALL_FUNCTION 0
6 STORE_ATTR 1 (x)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)
expected_dis_info = """\
Name: f
Filename: <string>
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 2
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
Names:
0: super
1: x
Variable names:
0: self
Free variables:
0: __class__"""
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)

def test_super_as_global_1(self):
src = """
super = 1
class C:
def f(self): super().f1(a=1)
"""
expected = """\
4 0 LOAD_GLOBAL 0 (super)
2 CALL_FUNCTION 0
4 LOAD_ATTR 1 (f1)
6 LOAD_CONST 1 (1)
8 LOAD_CONST 2 (('a',))
10 CALL_FUNCTION_KW 1
12 POP_TOP
14 LOAD_CONST 0 (None)
16 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)
expected_dis_info = """\
Name: f
Filename: <string>
Argument count: 1
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: 3
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
2: ('a',)
Names:
0: super
1: f1
Variable names:
0: self
Free variables:
0: __class__"""
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)


def test_super_as_local_1(self):
src = """
class C:
def f(self, super):
super().f1(a=1)
"""
expected = """\
4 0 LOAD_FAST 1 (super)
2 CALL_FUNCTION 0
4 LOAD_ATTR 0 (f1)
6 LOAD_CONST 1 (1)
8 LOAD_CONST 2 (('a',))
10 CALL_FUNCTION_KW 1
12 POP_TOP
14 LOAD_CONST 0 (None)
16 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)
expected_dis_info = """\
Name: f
Filename: <string>
Argument count: 2
Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 2
Stack size: 3
Flags: OPTIMIZED, NEWLOCALS
Constants:
0: None
1: 1
2: ('a',)
Names:
0: f1
Variable names:
0: self
1: super
Free variables:
0: __class__"""
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)

def test_super_as_local_2(self):
src = """
def f():
super = lambda: 1
class C:
def f(self):
super().f1(a=1)
return C
C = f()
"""
expected = """\
6 0 LOAD_DEREF 1 (super)
2 CALL_FUNCTION 0
4 LOAD_ATTR 0 (f1)
6 LOAD_CONST 1 (1)
8 LOAD_CONST 2 (('a',))
10 CALL_FUNCTION_KW 1
12 POP_TOP
14 LOAD_CONST 0 (None)
16 RETURN_VALUE
"""
g = {}
exec(dedent(src), g)
self.do_disassembly_test(g["C"].f, expected)

class DisWithFileTests(DisTests):

# Run the tests again, using the file arg instead of print
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_gdb.py
Loading