/
cpython.py
executable file
·201 lines (173 loc) · 9.06 KB
/
cpython.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python3
# PyCPython - interpret CPython in Python
# by Albert Zeyer, 2011
# code under BSD 2-Clause License
from __future__ import print_function
import argparse
import os
import sys
if __name__ == '__main__':
MyDir = os.path.dirname(sys.argv[0])
else:
MyDir = "."
CPythonDir = MyDir + "/CPython"
import cparser
import cparser.interpreter
class CPythonState(cparser.State):
def __init__(self):
super(CPythonState, self).__init__()
self.autoSetupSystemMacros()
self.autoSetupGlobalIncludeWrappers()
def findIncludeFullFilename(self, filename, local):
fullfn = CPythonDir + "/Include/" + filename
if os.path.exists(fullfn): return fullfn
return super(CPythonState, self).findIncludeFullFilename(filename, local)
def readLocalInclude(self, filename):
#print " ", filename, "..."
if filename == "pyconfig.h":
def reader():
# see CPython/pyconfig.h.in for reference
import ctypes
sizeofMacro = lambda t: cparser.Macro(rightside=str(ctypes.sizeof(t)))
self.macros["SIZEOF_SHORT"] = sizeofMacro(ctypes.c_short)
self.macros["SIZEOF_INT"] = sizeofMacro(ctypes.c_int)
self.macros["SIZEOF_LONG"] = sizeofMacro(ctypes.c_long)
self.macros["SIZEOF_LONG_LONG"] = sizeofMacro(ctypes.c_longlong)
self.macros["SIZEOF_DOUBLE"] = sizeofMacro(ctypes.c_double)
self.macros["SIZEOF_FLOAT"] = sizeofMacro(ctypes.c_float)
self.macros["SIZEOF_VOID_P"] = sizeofMacro(ctypes.c_void_p)
self.macros["SIZEOF_SIZE_T"] = sizeofMacro(ctypes.c_size_t)
self.macros["SIZEOF_UINTPTR_T"] = sizeofMacro(ctypes.POINTER(ctypes.c_uint))
self.macros["SIZEOF_PTHREAD_T"] = self.macros["SIZEOF_LONG"]
self.macros["SIZEOF_WCHAR_T"] = sizeofMacro(ctypes.c_wchar)
self.macros["SIZEOF_PID_T"] = self.macros["SIZEOF_INT"]
self.macros["SIZEOF_TIME_T"] = self.macros["SIZEOF_LONG"]
self.macros["SIZEOF__BOOL"] = cparser.Macro(rightside="1")
self.macros["HAVE_SIGNAL_H"] = cparser.Macro(rightside="1")
self.macros["HAVE_STDARG_PROTOTYPES"] = cparser.Macro(rightside="1")
self.macros["HAVE_STD_ATOMIC"] = cparser.Macro(rightside="1")
self.macros["HAVE_WCHAR_H"] = cparser.Macro(rightside="1")
self.macros["_POSIX_THREADS"] = cparser.Macro(rightside="1")
# _GNU_SOURCE, _POSIX_C_SOURCE or so?
return
yield None # make it a generator
return reader(), None
return super(CPythonState, self).readLocalInclude(filename)
def parse_cpython(self):
# We keep all in the same state, i.e. the same static space.
# This also means that we don't reset macro definitions. This speeds up header includes.
# Usually this is not a problem.
self.macros["Py_BUILD_CORE"] = cparser.Macro(rightside="1") # Makefile
self.macros["Py_BUILD_CORE_BUILTIN"] = cparser.Macro(rightside="1") # Makefile
cparser.parse(CPythonDir + "/Modules/main.c", self) # Py_Main
self.macros["FAST_LOOPS"] = cparser.Macro(rightside="0") # not sure where this would come from
cparser.parse(CPythonDir + "/Python/ceval.c", self) # PyEval_EvalFrameEx etc
del self.macros["EMPTY"] # will be redefined later
cparser.parse(CPythonDir + "/Python/getopt.c", self) # _PyOS_GetOpt
cparser.parse(CPythonDir + "/Python/pythonrun.c", self) # Py_Initialize
cparser.parse(CPythonDir + "/Python/pystate.c", self) # PyInterpreterState_New
cparser.parse(CPythonDir + "/Python/sysmodule.c", self) # PySys_ResetWarnOptions
cparser.parse(CPythonDir + "/Python/random.c", self) # _PyRandom_Init
cparser.parse(CPythonDir + "/Objects/object.c", self) # _Py_ReadyTypes etc
cparser.parse(CPythonDir + "/Objects/typeobject.c", self) # PyType_Ready
cparser.parse(CPythonDir + "/Objects/tupleobject.c", self) # PyTuple_New
del self.macros["Return"] # will be used differently
# We need these macro hacks because dictobject.c will use the same vars.
self.macros["length_hint_doc"] = cparser.Macro(rightside="length_hint_doc__dict")
self.macros["numfree"] = cparser.Macro(rightside="numfree__dict")
self.macros["free_list"] = cparser.Macro(rightside="free_list__dict")
cparser.parse(CPythonDir + "/Objects/dictobject.c", self) # PyDict_New
# We need this macro hack because stringobject.c will use the same var.
self.macros["sizeof__doc__"] = cparser.Macro(rightside="sizeof__doc__str")
cparser.parse(CPythonDir + "/Objects/stringobject.c", self) # PyString_FromString
cparser.parse(CPythonDir + "/Objects/obmalloc.c", self) # PyObject_Free
cparser.parse(CPythonDir + "/Modules/gcmodule.c", self) # _PyObject_GC_NewVar
cparser.parse(CPythonDir + "/Objects/descrobject.c", self) # PyDescr_NewWrapper
# We need these macro hacks because methodobject.c will use the same vars.
self.macros["numfree"] = cparser.Macro(rightside="numfree__methodobj")
self.macros["free_list"] = cparser.Macro(rightside="free_list__methodobj")
cparser.parse(CPythonDir + "/Objects/methodobject.c", self) # PyCFunction_NewEx
# We need these macro hacks because methodobject.c will use the same vars.
self.macros["numfree"] = cparser.Macro(rightside="numfree__list")
self.macros["free_list"] = cparser.Macro(rightside="free_list__list")
self.macros["sizeof_doc"] = cparser.Macro(rightside="sizeof_doc__list")
self.macros["length_hint_doc"] = cparser.Macro(rightside="length_hint_doc__list")
self.macros["index_doc"] = cparser.Macro(rightside="index_doc__list")
self.macros["count_doc"] = cparser.Macro(rightside="count__list")
cparser.parse(CPythonDir + "/Objects/listobject.c", self) # PyList_New
cparser.parse(CPythonDir + "/Objects/abstract.c", self) # PySequence_List
cparser.parse(CPythonDir + "/Python/modsupport.c", self) # Py_BuildValue
def init_faulthandler(sigusr1_chain=False):
"""
:param bool sigusr1_chain: whether the default SIGUSR1 handler should also be called.
"""
try:
import faulthandler
except ImportError as e:
print("faulthandler import error. %s" % e)
return
# Only enable if not yet enabled -- otherwise, leave it in its current state.
if not faulthandler.is_enabled():
faulthandler.enable()
if os.name != 'nt':
import signal
# This will print a backtrace on SIGUSR1.
# Note that this also works when Python is hanging,
# i.e. in cases where register_sigusr1_print_backtrace() will not work.
faulthandler.register(signal.SIGUSR1, all_threads=True, chain=sigusr1_chain)
def register_sigusr1_print_backtrace():
if os.name == "nt":
return
def sigusr1_handler(sig, frame):
print("--- SIGUSR1 handler")
better_exchook.print_tb(tb=frame)
import signal
signal.signal(signal.SIGUSR1, sigusr1_handler)
def main(argv):
argparser = argparse.ArgumentParser(
usage="%s [PyCPython options, see below] [CPython options, see via --help]" % argv[0],
description="Emulate CPython by interpreting its C code via PyCParser.",
epilog="All other options are passed on to CPython. Use --help to see them.",
add_help=False # we will add our own
)
argparser.add_argument(
'--pycpython-help', action='help', help='show this help message and exit')
argparser.add_argument(
'--dump-python', action='store', nargs=1,
help="Dumps the converted Python code of the specified function, e.g. Py_Main.")
argparser.add_argument(
'--verbose-jit', action='store_true',
help="Prints what functions and global vars we are going to translate.")
args_ns, argv_rest = argparser.parse_known_args(argv[1:])
argv = argv[:1] + argv_rest
print("PyCPython -", argparser.description,)
print("(use --pycpython-help for help)")
state = CPythonState()
print("Parsing CPython...", end="")
state.parse_cpython()
if state._errors:
print("finished, parse errors:")
for m in state._errors:
print(m)
else:
print("finished, no parse errors.")
interpreter = cparser.interpreter.Interpreter()
interpreter.register(state)
if args_ns.dump_python:
for fn in args_ns.dump_python:
print()
print("PyAST of %s:" % fn)
interpreter.dumpFunc(fn)
sys.exit()
if args_ns.verbose_jit:
interpreter.debug_print_getFunc = True
interpreter.debug_print_getVar = True
args = ("Py_Main", len(argv), argv + [None])
print("Run", args, ":")
interpreter.runFunc(*args)
if __name__ == '__main__':
import better_exchook
better_exchook.install()
register_sigusr1_print_backtrace()
init_faulthandler(sigusr1_chain=True)
main(sys.argv)