Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Introduce dedicated opcodes for super calls
  • Loading branch information
vladima committed Mar 19, 2021
commit ccbb515a0196295f3cdab30529525955dc9077ae
4 changes: 4 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ PyAPI_FUNC(int) _PyObject_LookupAttrId(PyObject *, struct _Py_Identifier *, PyOb

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

PyAPI_FUNC(PyObject *) _PySuper_Lookup(PyTypeObject *type, PyObject *obj,
PyObject *name, PyObject *super_instance,
int *meth_found);

PyAPI_FUNC(PyObject **) _PyObject_GetDictPtr(PyObject *);
PyAPI_FUNC(PyObject *) _PyObject_NextNotImplemented(PyObject *);
PyAPI_FUNC(void) PyObject_CallFinalizer(PyObject *);
Expand Down
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
284 changes: 284 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,289 @@ 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_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
2 changes: 1 addition & 1 deletion Lib/test/test_importlib/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -861,7 +861,7 @@ def test_magic_number(self):
in advance. Such exceptional releases will then require an
adjustment to this test case.
"""
EXPECTED_MAGIC_NUMBER = 3413
EXPECTED_MAGIC_NUMBER = 3435
actual = int.from_bytes(importlib.util.MAGIC_NUMBER[:2], 'little')

msg = (
Expand Down
Loading