Skip to content
This repository was archived by the owner on Mar 2, 2022. It is now read-only.

Commit 9f8cc2e

Browse files
committed
Code Python 3.7 compiler - LOAD_METHOD/CALL_METHOD
Summary: First step towards having a compiler that supports Python 3.7 - we now have a code generator which subclasses the normal one, and modifies the code we generate for calling methods. In order to support running/testing on Python 3.6 we need to define the new opcodes manually. Test Plan: ./python -m test.test_compiler
1 parent f72a5e0 commit 9f8cc2e

File tree

5 files changed

+71
-9
lines changed

5 files changed

+71
-9
lines changed

compiler/pyassem.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ def cast_signed_byte_to_unsigned(i):
3838
FVS_MASK = 0x4
3939
FVS_HAVE_SPEC = 0x4
4040

41+
OPNAMES = list(dis.opname)
42+
OPMAP = dict(dis.opmap)
43+
44+
if "LOAD_METHOD" not in OPMAP:
45+
OPMAP["LOAD_METHOD"] = 160
46+
OPNAMES[160] = "LOAD_METHOD"
47+
48+
if "CALL_METHOD" not in OPMAP:
49+
OPMAP["CALL_METHOD"] = 161
50+
OPNAMES[161] = "CALL_METHOD"
51+
4152

4253
class Instruction:
4354
__slots__ = ('opname', 'oparg', 'target')
@@ -465,10 +476,10 @@ def flattenGraph(self):
465476

466477
hasjrel = set()
467478
for i in dis.hasjrel:
468-
hasjrel.add(dis.opname[i])
479+
hasjrel.add(OPNAMES[i])
469480
hasjabs = set()
470481
for i in dis.hasjabs:
471-
hasjabs.add(dis.opname[i])
482+
hasjabs.add(OPNAMES[i])
472483

473484
def convertArgs(self):
474485
"""Convert arguments from symbolic to concrete form"""
@@ -545,6 +556,7 @@ def _convert_NAME(self, arg):
545556
_convert_STORE_ATTR = _convert_NAME
546557
_convert_LOAD_ATTR = _convert_NAME
547558
_convert_DELETE_ATTR = _convert_NAME
559+
_convert_LOAD_METHOD = _convert_NAME
548560

549561
def _convert_GLOBAL(self, arg):
550562
return self._lookupName(arg, self.names)
@@ -598,18 +610,13 @@ def makeByteCode(self):
598610
lnotab.addCode(EXTENDED_ARG, (oparg >> 16) & 0xff)
599611
if oparg > 0xff:
600612
lnotab.addCode(EXTENDED_ARG, (oparg >> 8) & 0xff)
601-
lnotab.addCode(self.opnum[t.opname], oparg & 0xff)
613+
lnotab.addCode(OPMAP[t.opname], oparg & 0xff)
602614
except ValueError:
603615
print(t.opname, oparg)
604-
print(self.opnum[t.opname], oparg)
616+
print(OPMAP[t.opname], oparg)
605617
raise
606618
self.stage = DONE
607619

608-
opnum = {}
609-
for num in range(len(dis.opname)):
610-
opnum[dis.opname[num]] = num
611-
del num
612-
613620
def newCodeObject(self):
614621
assert self.stage == DONE
615622
if (self.flags & CO_NEWLOCALS) == 0:
@@ -867,4 +874,6 @@ def getTable(self):
867874
# else 1->1.
868875
FORMAT_VALUE = lambda oparg: -1 if (oparg & FVS_MASK) == FVS_HAVE_SPEC else 0,
869876
SET_LINENO = 0,
877+
LOAD_METHOD = 1,
878+
CALL_METHOD = lambda oparg: -oparg - 1,
870879
)

compiler/pycodegen.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1937,6 +1937,23 @@ def flow_graph(cls, name, filename, args=(), kwonlyargs=(), starargs=(), optimiz
19371937
return pyassem.PyFlowGraph(name, filename, args, kwonlyargs, starargs, optimized, klass, peephole_enabled=False)
19381938

19391939

1940+
class Python37CodeGenerator(CodeGenerator):
1941+
def visitCall(self, node):
1942+
if (node.keywords or
1943+
not isinstance(node.func, ast.Attribute) or
1944+
not isinstance(node.func.ctx, ast.Load) or
1945+
any(isinstance(arg, ast.Starred) for arg in node.args)):
1946+
# We cannot optimize this call
1947+
return super().visitCall(node)
1948+
1949+
self.update_lineno(node)
1950+
self.visit(node.func.value)
1951+
self.emit('LOAD_METHOD', self.mangle(node.func.attr))
1952+
for arg in node.args:
1953+
self.visit(arg)
1954+
self.emit('CALL_METHOD', len(node.args))
1955+
1956+
19401957
def get_docstring(node):
19411958
if node.body and isinstance(node.body[0], ast.Expr) \
19421959
and isinstance(node.body[0].value, ast.Str):

test_compiler/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .test_code_sbs import CodeTests
33
from .test_errors import ErrorTests, ErrorTestsBuiltin
44
from .test_peephole import PeepHoleTests
5+
from .test_py37 import Python37Tests
56
from .test_flags import FlagTests
67
from .test_graph import GraphTests
78
from .test_sbs_stdlib import SbsCompileTests

test_compiler/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .test_peephole import PeepHoleTests
55
from .test_flags import FlagTests
66
from .test_graph import GraphTests
7+
from .test_py37 import Python37Tests
78
from .test_sbs_stdlib import SbsCompileTests
89
from .test_errors import ErrorTests, ErrorTestsBuiltin
910
from .test_symbols import SymbolVisitorTests

test_compiler/test_py37.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import dis
2+
from .common import CompilerTest
3+
from compiler.pycodegen import Python37CodeGenerator
4+
5+
6+
LOAD_METHOD = "LOAD_METHOD"
7+
if LOAD_METHOD not in dis.opmap:
8+
LOAD_METHOD = "<160>"
9+
10+
CALL_METHOD = "CALL_METHOD"
11+
if CALL_METHOD not in dis.opmap:
12+
CALL_METHOD = "<161>"
13+
14+
class Python37Tests(CompilerTest):
15+
def test_compile_method(self):
16+
code = self.compile('x.f()', Python37CodeGenerator)
17+
self.assertInBytecode(code, LOAD_METHOD)
18+
self.assertInBytecode(code, CALL_METHOD, 0)
19+
20+
code = self.compile('x.f(42)', Python37CodeGenerator)
21+
self.assertInBytecode(code, LOAD_METHOD)
22+
self.assertInBytecode(code, CALL_METHOD, 1)
23+
24+
def test_compile_method_varargs(self):
25+
code = self.compile('x.f(*foo)', Python37CodeGenerator)
26+
self.assertNotInBytecode(code, LOAD_METHOD)
27+
28+
def test_compile_method_kwarg(self):
29+
code = self.compile('x.f(kwarg=1)', Python37CodeGenerator)
30+
self.assertNotInBytecode(code, LOAD_METHOD)
31+
32+
def test_compile_method_normal(self):
33+
code = self.compile('f()', Python37CodeGenerator)
34+
self.assertNotInBytecode(code, LOAD_METHOD)

0 commit comments

Comments
 (0)