Skip to content

Commit ccbb515

Browse files
committed
Introduce dedicated opcodes for super calls
1 parent f6c6b58 commit ccbb515

File tree

11 files changed

+1897
-1244
lines changed

11 files changed

+1897
-1244
lines changed

Include/cpython/object.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ PyAPI_FUNC(int) _PyObject_LookupAttrId(PyObject *, struct _Py_Identifier *, PyOb
327327

328328
PyAPI_FUNC(int) _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method);
329329

330+
PyAPI_FUNC(PyObject *) _PySuper_Lookup(PyTypeObject *type, PyObject *obj,
331+
PyObject *name, PyObject *super_instance,
332+
int *meth_found);
333+
330334
PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
331335
PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *);
332336
PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);

Include/opcode.h

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/importlib/_bootstrap_external.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ def _write_atomic(path, data, mode=0o666):
314314
# Python 3.10a2 3432 (Function annotation for MAKE_FUNCTION is changed from dict to tuple bpo-42202)
315315
# Python 3.10a2 3433 (RERAISE restores f_lasti if oparg != 0)
316316
# Python 3.10a6 3434 (PEP 634: Structural Pattern Matching)
317+
# Python 3.10a6 3435 (LOAD_*_SUPER opcodes)
317318

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

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

329330
_PYCACHE = '__pycache__'

Lib/opcode.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,9 @@ def jabs_op(name, op):
211211
def_op('SET_UPDATE', 163)
212212
def_op('DICT_MERGE', 164)
213213
def_op('DICT_UPDATE', 165)
214+
name_op('LOAD_METHOD_SUPER', 166)
215+
hasconst.append(166)
216+
name_op('LOAD_ATTR_SUPER', 167)
217+
hasconst.append(167)
214218

215219
del def_op, name_op, jrel_op, jabs_op

Lib/test/test_dis.py

Lines changed: 284 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from test.support import captured_stdout
44
from test.support.bytecode_helper import BytecodeTestCase
5+
from textwrap import dedent
56
import unittest
67
import sys
78
import dis
@@ -672,6 +673,289 @@ def check(expected, **kwargs):
672673
check(dis_nested_2)
673674

674675

676+
def test_super_zero_args(self):
677+
src = """
678+
class C:
679+
def f(self): super().f1()
680+
"""
681+
expected = """\
682+
3 0 LOAD_GLOBAL 0 (super)
683+
2 LOAD_DEREF 0 (__class__)
684+
4 LOAD_FAST 0 (self)
685+
6 LOAD_METHOD_SUPER 1 ((1, True))
686+
8 CALL_METHOD 0
687+
10 POP_TOP
688+
12 LOAD_CONST 0 (None)
689+
14 RETURN_VALUE
690+
"""
691+
692+
g = {}
693+
exec(dedent(src), g)
694+
self.do_disassembly_test(g["C"].f, expected)
695+
696+
def test_super_zero_args_load_attr(self):
697+
src = """
698+
class C:
699+
def f(self): super().f1(a=1)
700+
"""
701+
expected = """\
702+
3 0 LOAD_GLOBAL 0 (super)
703+
2 LOAD_DEREF 0 (__class__)
704+
4 LOAD_FAST 0 (self)
705+
6 LOAD_ATTR_SUPER 1 ((1, True))
706+
8 LOAD_CONST 2 (1)
707+
10 LOAD_CONST 3 (('a',))
708+
12 CALL_FUNCTION_KW 1
709+
14 POP_TOP
710+
16 LOAD_CONST 0 (None)
711+
18 RETURN_VALUE
712+
"""
713+
g = {}
714+
exec(dedent(src), g)
715+
self.do_disassembly_test(g["C"].f, expected)
716+
717+
def test_super_two_args(self):
718+
src = """
719+
class C:
720+
def f(self): super(C, self).f1()
721+
"""
722+
expected = """\
723+
3 0 LOAD_GLOBAL 0 (super)
724+
2 LOAD_GLOBAL 1 (C)
725+
4 LOAD_FAST 0 (self)
726+
6 LOAD_METHOD_SUPER 1 ((2, False))
727+
8 CALL_METHOD 0
728+
10 POP_TOP
729+
12 LOAD_CONST 0 (None)
730+
14 RETURN_VALUE
731+
"""
732+
g = {}
733+
exec(dedent(src), g)
734+
self.do_disassembly_test(g["C"].f, expected)
735+
736+
737+
def test_super_zero_method_args(self):
738+
src = """
739+
class C:
740+
def f(): super().f1()
741+
"""
742+
expected = """\
743+
3 0 LOAD_GLOBAL 0 (super)
744+
2 CALL_FUNCTION 0
745+
4 LOAD_METHOD 1 (f1)
746+
6 CALL_METHOD 0
747+
8 POP_TOP
748+
10 LOAD_CONST 0 (None)
749+
12 RETURN_VALUE
750+
"""
751+
g = {}
752+
exec(dedent(src), g)
753+
self.do_disassembly_test(g["C"].f, expected)
754+
755+
def test_super_two_args_attr(self):
756+
src = """
757+
class C:
758+
def f(self): super(C, self).f1(a=1)
759+
"""
760+
expected = """\
761+
3 0 LOAD_GLOBAL 0 (super)
762+
2 LOAD_GLOBAL 1 (C)
763+
4 LOAD_FAST 0 (self)
764+
6 LOAD_ATTR_SUPER 1 ((2, False))
765+
8 LOAD_CONST 2 (1)
766+
10 LOAD_CONST 3 (('a',))
767+
12 CALL_FUNCTION_KW 1
768+
14 POP_TOP
769+
16 LOAD_CONST 0 (None)
770+
18 RETURN_VALUE
771+
"""
772+
g = {}
773+
exec(dedent(src), g)
774+
self.do_disassembly_test(g["C"].f, expected)
775+
776+
def test_super_attr_load(self):
777+
src = """
778+
class C:
779+
def f(self): return super().x
780+
"""
781+
expected = """\
782+
3 0 LOAD_GLOBAL 0 (super)
783+
2 LOAD_DEREF 0 (__class__)
784+
4 LOAD_FAST 0 (self)
785+
6 LOAD_ATTR_SUPER 1 ((1, True))
786+
8 RETURN_VALUE
787+
"""
788+
g = {}
789+
exec(dedent(src), g)
790+
self.do_disassembly_test(g["C"].f, expected)
791+
expected_dis_info = """\
792+
Name: f
793+
Filename: <string>
794+
Argument count: 1
795+
Positional-only arguments: 0
796+
Kw-only arguments: 0
797+
Number of locals: 1
798+
Stack size: 3
799+
Flags: OPTIMIZED, NEWLOCALS
800+
Constants:
801+
0: None
802+
1: (1, True)
803+
Names:
804+
0: super
805+
1: x
806+
Variable names:
807+
0: self
808+
Free variables:
809+
0: __class__"""
810+
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)
811+
812+
def test_super_attr_store(self):
813+
src = """
814+
class C:
815+
def f(self): super().x = 1
816+
"""
817+
expected = """\
818+
3 0 LOAD_CONST 1 (1)
819+
2 LOAD_GLOBAL 0 (super)
820+
4 CALL_FUNCTION 0
821+
6 STORE_ATTR 1 (x)
822+
8 LOAD_CONST 0 (None)
823+
10 RETURN_VALUE
824+
"""
825+
g = {}
826+
exec(dedent(src), g)
827+
self.do_disassembly_test(g["C"].f, expected)
828+
expected_dis_info = """\
829+
Name: f
830+
Filename: <string>
831+
Argument count: 1
832+
Positional-only arguments: 0
833+
Kw-only arguments: 0
834+
Number of locals: 1
835+
Stack size: 2
836+
Flags: OPTIMIZED, NEWLOCALS
837+
Constants:
838+
0: None
839+
1: 1
840+
Names:
841+
0: super
842+
1: x
843+
Variable names:
844+
0: self
845+
Free variables:
846+
0: __class__"""
847+
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)
848+
849+
def test_super_as_global_1(self):
850+
src = """
851+
super = 1
852+
class C:
853+
def f(self): super().f1(a=1)
854+
"""
855+
expected = """\
856+
4 0 LOAD_GLOBAL 0 (super)
857+
2 CALL_FUNCTION 0
858+
4 LOAD_ATTR 1 (f1)
859+
6 LOAD_CONST 1 (1)
860+
8 LOAD_CONST 2 (('a',))
861+
10 CALL_FUNCTION_KW 1
862+
12 POP_TOP
863+
14 LOAD_CONST 0 (None)
864+
16 RETURN_VALUE
865+
"""
866+
g = {}
867+
exec(dedent(src), g)
868+
self.do_disassembly_test(g["C"].f, expected)
869+
expected_dis_info = """\
870+
Name: f
871+
Filename: <string>
872+
Argument count: 1
873+
Positional-only arguments: 0
874+
Kw-only arguments: 0
875+
Number of locals: 1
876+
Stack size: 3
877+
Flags: OPTIMIZED, NEWLOCALS
878+
Constants:
879+
0: None
880+
1: 1
881+
2: ('a',)
882+
Names:
883+
0: super
884+
1: f1
885+
Variable names:
886+
0: self
887+
Free variables:
888+
0: __class__"""
889+
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)
890+
891+
892+
def test_super_as_local_1(self):
893+
src = """
894+
class C:
895+
def f(self, super):
896+
super().f1(a=1)
897+
"""
898+
expected = """\
899+
4 0 LOAD_FAST 1 (super)
900+
2 CALL_FUNCTION 0
901+
4 LOAD_ATTR 0 (f1)
902+
6 LOAD_CONST 1 (1)
903+
8 LOAD_CONST 2 (('a',))
904+
10 CALL_FUNCTION_KW 1
905+
12 POP_TOP
906+
14 LOAD_CONST 0 (None)
907+
16 RETURN_VALUE
908+
"""
909+
g = {}
910+
exec(dedent(src), g)
911+
self.do_disassembly_test(g["C"].f, expected)
912+
expected_dis_info = """\
913+
Name: f
914+
Filename: <string>
915+
Argument count: 2
916+
Positional-only arguments: 0
917+
Kw-only arguments: 0
918+
Number of locals: 2
919+
Stack size: 3
920+
Flags: OPTIMIZED, NEWLOCALS
921+
Constants:
922+
0: None
923+
1: 1
924+
2: ('a',)
925+
Names:
926+
0: f1
927+
Variable names:
928+
0: self
929+
1: super
930+
Free variables:
931+
0: __class__"""
932+
self.assertEqual(dedent(dis.code_info(g["C"].f)), expected_dis_info)
933+
934+
def test_super_as_local_2(self):
935+
src = """
936+
def f():
937+
super = lambda: 1
938+
class C:
939+
def f(self):
940+
super().f1(a=1)
941+
return C
942+
C = f()
943+
"""
944+
expected = """\
945+
6 0 LOAD_DEREF 1 (super)
946+
2 CALL_FUNCTION 0
947+
4 LOAD_ATTR 0 (f1)
948+
6 LOAD_CONST 1 (1)
949+
8 LOAD_CONST 2 (('a',))
950+
10 CALL_FUNCTION_KW 1
951+
12 POP_TOP
952+
14 LOAD_CONST 0 (None)
953+
16 RETURN_VALUE
954+
"""
955+
g = {}
956+
exec(dedent(src), g)
957+
self.do_disassembly_test(g["C"].f, expected)
958+
675959
class DisWithFileTests(DisTests):
676960

677961
# Run the tests again, using the file arg instead of print

Lib/test/test_importlib/test_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,7 +861,7 @@ def test_magic_number(self):
861861
in advance. Such exceptional releases will then require an
862862
adjustment to this test case.
863863
"""
864-
EXPECTED_MAGIC_NUMBER = 3413
864+
EXPECTED_MAGIC_NUMBER = 3435
865865
actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')
866866

867867
msg = (

0 commit comments

Comments
 (0)