├── .github
└── workflows
│ └── samples.yml
├── .gitignore
├── LICENSE.txt
├── README.md
├── __init__.py
├── cmake.in
├── cmake.py
├── codeobj.py
├── config.py
├── context.py
├── files.py
├── module.py
├── modulereducer.py
├── src
├── main.c
├── pypperoni_impl.c
└── pypperoni_impl.h
└── util.py
/.github/workflows/samples.yml:
--------------------------------------------------------------------------------
1 | name: Build and run samples
2 |
3 | on: [push]
4 |
5 | jobs:
6 | sample-00:
7 | name: 00-hello_world
8 | runs-on: ubuntu-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | with:
13 | path: pypperoni/pypperoni
14 | - name: Set up Python 3.6.8
15 | uses: actions/setup-python@v2
16 | with:
17 | python-version: 3.6.8
18 | - name: Install dependencies
19 | run: |
20 | sudo apt-get install -y cmake build-essential gcc g++ python3-dev libz-dev libssl-dev liblzma-dev libgdbm-dev
21 | - name: Clone Pypperoni/Python-3.6.8
22 | uses: actions/checkout@v2
23 | with:
24 | repository: Pypperoni/Python-3.6.8
25 | path: pypperoni/python
26 | - name: Build Pypperoni/Python-3.6.8
27 | run: |
28 | cd pypperoni/python
29 | mkdir -p build/linux2
30 | cd build/linux2
31 | cmake ../..
32 | make -j
33 | - name: Clone Pypperoni/samples
34 | uses: actions/checkout@v2
35 | with:
36 | repository: Pypperoni/samples
37 | path: pypperoni/samples
38 | - name: Build 00-hello_world
39 | run: |
40 | cd pypperoni/samples/00-hello_world
41 | python3.6 build.py
42 | cd build
43 | cmake -DCMAKE_BUILD_TYPE=Release .
44 | make -j8
45 | - name: Run 00-hello_world
46 | run: |
47 | cd pypperoni/samples/00-hello_world/build
48 | ./00-hello_world
49 | cd ..
50 | rm -rf build
51 |
52 | sample-01:
53 | name: 01-basic
54 | runs-on: ubuntu-latest
55 |
56 | steps:
57 | - uses: actions/checkout@v2
58 | with:
59 | path: pypperoni/pypperoni
60 | - name: Set up Python 3.6.8
61 | uses: actions/setup-python@v2
62 | with:
63 | python-version: 3.6.8
64 | - name: Install dependencies
65 | run: |
66 | sudo apt-get install -y cmake build-essential gcc g++ python3-dev libz-dev libssl-dev liblzma-dev libgdbm-dev
67 | - name: Clone Pypperoni/Python-3.6.8
68 | uses: actions/checkout@v2
69 | with:
70 | repository: Pypperoni/Python-3.6.8
71 | path: pypperoni/python
72 | - name: Build Pypperoni/Python-3.6.8
73 | run: |
74 | cd pypperoni/python
75 | mkdir -p build/linux2
76 | cd build/linux2
77 | cmake ../..
78 | make -j
79 | - name: Clone Pypperoni/samples
80 | uses: actions/checkout@v2
81 | with:
82 | repository: Pypperoni/samples
83 | path: pypperoni/samples
84 | - name: Build 01-basic
85 | run: |
86 | cd pypperoni/samples/01-basic
87 | python3.6 build.py
88 | cd build
89 | cmake -DCMAKE_BUILD_TYPE=Release .
90 | make -j8
91 | - name: Run 01-basic
92 | run: |
93 | cd pypperoni/samples/01-basic/build
94 | ./01-basic
95 | cd ..
96 | rm -rf build
97 |
98 | sample-02:
99 | name: 02-exceptions
100 | runs-on: ubuntu-latest
101 |
102 | steps:
103 | - uses: actions/checkout@v2
104 | with:
105 | path: pypperoni/pypperoni
106 | - name: Set up Python 3.6.8
107 | uses: actions/setup-python@v2
108 | with:
109 | python-version: 3.6.8
110 | - name: Install dependencies
111 | run: |
112 | sudo apt-get install -y cmake build-essential gcc g++ python3-dev libz-dev libssl-dev liblzma-dev libgdbm-dev
113 | - name: Clone Pypperoni/Python-3.6.8
114 | uses: actions/checkout@v2
115 | with:
116 | repository: Pypperoni/Python-3.6.8
117 | path: pypperoni/python
118 | - name: Build Pypperoni/Python-3.6.8
119 | run: |
120 | cd pypperoni/python
121 | mkdir -p build/linux2
122 | cd build/linux2
123 | cmake ../..
124 | make -j
125 | - name: Clone Pypperoni/samples
126 | uses: actions/checkout@v2
127 | with:
128 | repository: Pypperoni/samples
129 | path: pypperoni/samples
130 | - name: Build 02-exceptions
131 | run: |
132 | cd pypperoni/samples/02-exceptions
133 | python3.6 build.py
134 | cd build
135 | cmake -DCMAKE_BUILD_TYPE=Release .
136 | make -j8
137 | - name: Run 02-exceptions
138 | run: |
139 | cd pypperoni/samples/02-exceptions/build
140 | ./02-exceptions catchall
141 | cd ..
142 | rm -rf build
143 |
144 | sample-03:
145 | name: 03-async
146 | runs-on: ubuntu-latest
147 |
148 | steps:
149 | - uses: actions/checkout@v2
150 | with:
151 | path: pypperoni/pypperoni
152 | - name: Set up Python 3.6.8
153 | uses: actions/setup-python@v2
154 | with:
155 | python-version: 3.6.8
156 | - name: Install dependencies
157 | run: |
158 | sudo apt-get install -y cmake build-essential gcc g++ python3-dev libz-dev libssl-dev liblzma-dev libgdbm-dev
159 | - name: Clone Pypperoni/Python-3.6.8
160 | uses: actions/checkout@v2
161 | with:
162 | repository: Pypperoni/Python-3.6.8
163 | path: pypperoni/python
164 | - name: Build Pypperoni/Python-3.6.8
165 | run: |
166 | cd pypperoni/python
167 | mkdir -p build/linux2
168 | cd build/linux2
169 | cmake ../..
170 | make -j
171 | - name: Clone Pypperoni/samples
172 | uses: actions/checkout@v2
173 | with:
174 | repository: Pypperoni/samples
175 | path: pypperoni/samples
176 | - name: Build 03-async
177 | run: |
178 | cd pypperoni/samples/03-async
179 | python3.6 build.py
180 | cd build
181 | cmake -DCMAKE_BUILD_TYPE=Release .
182 | make -j8
183 | - name: Run 03-async
184 | run: |
185 | cd pypperoni/samples/03-async/build
186 | ./03-async
187 | cd ..
188 | rm -rf build
189 |
190 | sample-04:
191 | name: 04-threading
192 | runs-on: ubuntu-latest
193 |
194 | steps:
195 | - uses: actions/checkout@v2
196 | with:
197 | path: pypperoni/pypperoni
198 | - name: Set up Python 3.6.8
199 | uses: actions/setup-python@v2
200 | with:
201 | python-version: 3.6.8
202 | - name: Install dependencies
203 | run: |
204 | sudo apt-get install -y cmake build-essential gcc g++ python3-dev libz-dev libssl-dev liblzma-dev libgdbm-dev
205 | - name: Clone Pypperoni/Python-3.6.8
206 | uses: actions/checkout@v2
207 | with:
208 | repository: Pypperoni/Python-3.6.8
209 | path: pypperoni/python
210 | - name: Build Pypperoni/Python-3.6.8
211 | run: |
212 | cd pypperoni/python
213 | mkdir -p build/linux2
214 | cd build/linux2
215 | cmake ../..
216 | make -j
217 | - name: Clone Pypperoni/samples
218 | uses: actions/checkout@v2
219 | with:
220 | repository: Pypperoni/samples
221 | path: pypperoni/samples
222 | - name: Build 04-threading
223 | run: |
224 | cd pypperoni/samples/04-threading
225 | python3.6 build.py
226 | cd build
227 | cmake -DCMAKE_BUILD_TYPE=Release .
228 | make -j4
229 | - name: Run 04-threading
230 | run: |
231 | cd pypperoni/samples/04-threading/build
232 | ./04-threading
233 | cd ..
234 | rm -rf build
235 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *.pyc
3 | *.pyo
4 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Pypperoni
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pypperoni Compiler
2 |
3 |
4 |
5 | Pypperoni is a free and open source Python compiler and bytecode preprocessor designed and maintained by the developers of [The Legend of Pirates Online](https://tlopo.com/), a fan-made recreation of Disney's Pirates of the Caribbean Online.
6 |
7 | This is the main source code repository.
8 |
9 | ## Overview
10 |
11 | Python, by design, is an interpreted programming language. This means that a Python program's source code is first compiled to a bytecode format and then interpreted at runtime. This can lead to certain security issues, such as bytecode dumping and injection.
12 |
13 | Pypperoni's main objective is to eliminate the interpreter by preprocessing your bytecode and expanding it to Python C API calls at compile time.
14 |
15 |
16 | ## Getting Started
17 | ### Downloading Sample Projects
18 | To best get a feel of how Pypperoni works, try downloading one of our many [sample projects](http://github.com/pypperoni/samples). To get started, please follow each project's included guide on how to set them up. These samples are a good example of how to properly structure your own Pypperoni project.
19 |
20 |
21 | ## Why use Pypperoni?
22 | Pypperoni was designed with security as a central focus. Our compiler provides you with the necessary tools to run a secure and high quality Python application.
23 |
24 | With the removal of the interpreter, it is practically impossible to inject Python code into your program and/or recover the original source code.
25 |
26 | Additionally, by preprocessing the bytecode there may be a performance boost in your application.
27 |
28 | ## How does Pypperoni work?
29 | When Pypperoni is ran, it will compile all of your Python application's source code (`*.py`) into Python bytecode (`*.pyc`). An example of Python bytecode is shown below:
30 |
31 | ```
32 | 0 SETUP_LOOP 32 (to 34)
33 | 2 LOAD_NAME 0 (enumerate)
34 | ```
35 |
36 | Next, Pypperoni will read through the bytecode, interpreting the Python OP codes into the equivalent Python C API calls; this is what we call "preprocessing the bytecode." The following example is the output from preprocessing the above bytecode:
37 |
38 | ```c
39 | label_0:
40 | {
41 | void* __addr;
42 | GET_ADDRESS(__addr, label_34, 34);
43 | PyFrame_BlockSetup(f, 120, __addr, STACK_LEVEL());
44 | }
45 | label_2:
46 | {
47 | x = __pypperoni_IMPL_load_name(f, __consts_main[0]); /* 'enumerate' */
48 | if (x == NULL) {
49 | f->f_lineno = 5;
50 | goto error;
51 | }
52 | Py_INCREF(x);
53 | PUSH(x);
54 | }
55 | ```
56 |
57 | This C code is then compiled as a normal C application, and an executable is generated.
58 |
59 | ## Documentation
60 | Pypperoni is still in its infancy. We will be writing and publishing documentation over time [here](http://pypperoni.github.io/).
61 |
62 | ## Development
63 | ### Background
64 | Pypperoni was developed initially as an in-house compiler for the free online game, The Legend of Pirates Online ("TLOPO"). TLOPO, which is almost entirely written in Python, had to come up with many creative solutions for security problems intrinsic to the Python programming language, such as Python injection.
65 |
66 | They recognized the numerous security and performance issues associated with running a production application written in Python, and thus sought out to reinvent the way we traditionally think about Python compilers. Pypperoni is the result of this vision.
67 |
68 | Previously, TLOPO maintained their own custom and open source compiler named [Nirai](https://github.com/nirai-compiler). Unlike Pypperoni, Nirai was designed to be specifically used alongside the Panda3D game engine. Pypperoni is the successor to Nirai and is designed to be compatible with any application written in Python 2.7.
69 |
70 |
71 | ### Maintainers
72 | - **[@loblao](https://github.com/loblao) Nacib Neme** is Pypperoni's lead architect and designer.
73 | - **[@mfwass](https://github.com/mfwass) Michael Wass** is a maintainer of Pypperoni.
74 |
75 |
76 | ## License
77 | Pypperoni is licensed under the MIT License; you may not use it except in compliance with the License.
78 |
79 | You may obtain a copy of the License [here](LICENSE.txt).
80 |
81 | Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
82 |
83 |
84 | ## Contributors
85 | We welcome any potential contributors! But before hacking away and sending off a bunch of new pull requests, please check out the current issues and pull requests. If you would like to add a new feature or fix a bug, please submit an issue describing the bug or feature. This way we can always make sure we're all on the same page.
86 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
--------------------------------------------------------------------------------
/cmake.in:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
2 | set(CMAKE_CXX_STANDARD 11)
3 | set(CMAKE_CXX_STANDARD_REQUIRED ON)
4 | project($$project$$)
5 |
6 | find_package(OpenSSL REQUIRED)
7 | if(NOT OPENSSL_FOUND)
8 | message(FATAL_ERROR "You need OpenSSL to build this.")
9 | endif()
10 |
11 | find_package(ZLIB REQUIRED)
12 | if(NOT ZLIB_FOUND)
13 | message(FATAL_ERROR "You need ZLIB to build this.")
14 | endif()
15 |
16 | include_directories(${OPENSSL_INCLUDE_DIR})
17 | include_directories(${ZLIB_INCLUDE_DIRS})
18 |
19 | if(WIN32)
20 | add_definitions(-DWIN32)
21 | add_definitions(-D_WIN32)
22 | add_definitions(-D_USRDLL)
23 | add_definitions(-D_CRT_SECURE_NO_WARNINGS)
24 | add_definitions(-D_WINSOCK_DEPRECATED_NO_WARNINGS)
25 | endif()
26 |
27 | if (MSVC)
28 | add_definitions(/wd4102)
29 | endif()
30 |
31 | add_definitions(-DNDEBUG)
32 | add_definitions(-DPy_BUILD_CORE)
33 |
34 | include_directories(gen)
35 | include_directories($$python_root$$/Include)
36 | include_directories($$pypperoni_root$$/src)
37 |
38 | if (UNIX)
39 | if (APPLE)
40 | link_directories($$python_root$$/build/osx)
41 | else()
42 | link_directories($$python_root$$/build/linux2)
43 | endif()
44 | else()
45 | link_directories($$python_root$$/build/win64)
46 | endif()
47 |
48 | set(_FILES
49 | $$pypperoni_root$$/src/pypperoni_impl.c
50 | $$pypperoni_root$$/src/main.c
51 | $$files$$)
52 |
53 | add_executable($$project$$ ${_FILES})
54 | target_link_libraries($$project$$ python3.6 ${ZLIB_LIBRARIES} ${OPENSSL_LIBRARIES})
55 | if (WIN32)
56 | target_link_libraries($$project$$ ws2_32 crypt32)
57 | else()
58 | target_link_libraries($$project$$ pthread m util)
59 | endif()
60 |
--------------------------------------------------------------------------------
/cmake.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from .files import ConditionalFile, FileContainer
19 | from .module import Module, PackageModule, write_modules_file
20 | from .modulereducer import reduce_modules
21 | from .util import safePrint
22 |
23 | from threading import Thread, Lock
24 | from queue import Queue, Empty
25 | import traceback
26 | import hashlib
27 | import math
28 | import sys
29 | import os
30 |
31 | PYPPERONI_ROOT = os.path.abspath(os.path.dirname(__file__))
32 | PYTHON_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'python'))
33 |
34 |
35 | class CMakeFileGenerator:
36 | def __init__(self, project, outputdir='build', nthreads=4):
37 | self.project = project
38 | self.outputdir = outputdir
39 | self.nthreads = nthreads
40 |
41 | self.modules = {}
42 | self.__files = []
43 |
44 | self.cmake_in_file = os.path.join(PYPPERONI_ROOT, 'cmake.in')
45 | self.add_directory(os.path.join(PYTHON_ROOT, 'Lib'))
46 |
47 | self.generate_codecs_index()
48 |
49 | def add_file(self, filename, name=None):
50 | '''
51 | Adds a single file to modules.
52 | If name is not provided, it's inferred from filename:
53 | path/to/file -> path.to.file
54 | '''
55 | with open(filename, 'rb') as f:
56 | data = f.read()
57 |
58 | is_pkg = False
59 | if name is None:
60 | name = os.path.normpath(filename.rsplit('.', 1)[0]).replace(os.sep, '.')
61 | if name.endswith('.__init__'):
62 | name = name[:-9]
63 | is_pkg = True
64 |
65 | self.add_module(name, data, is_pkg)
66 |
67 | def add_module(self, name, data, is_pkg=False):
68 | if is_pkg:
69 | obj = PackageModule(name, data)
70 |
71 | else:
72 | obj = Module(name, data)
73 |
74 | self.modules[name] = obj
75 |
76 | def add_directory(self, path):
77 | '''
78 | Adds all Python files (.py) in a directory to modules.
79 | For example,
80 | dir1/
81 | file1.py
82 | file2.py
83 |
84 | will add the modules "file1" and "file2"
85 | '''
86 | cwd = os.getcwd()
87 | path = os.path.abspath(path)
88 | os.chdir(path)
89 |
90 | try:
91 | for root, _, filenames in os.walk('.'):
92 | for filename in filenames:
93 | if filename.endswith('.py'):
94 | self.add_file(os.path.join(root, filename))
95 |
96 | finally:
97 | os.chdir(cwd)
98 |
99 | def add_tree(self, path):
100 | '''
101 | Adds all Python files (.py) in a directory to modules, preserving the tree name.
102 | For example,
103 | tree1/
104 | file1.py
105 | file2.py
106 |
107 | will add the modules "tree1.file1" and "tree1.file2"
108 | '''
109 | cwd = os.getcwd()
110 | path = os.path.abspath(path)
111 | os.chdir(os.path.dirname(path))
112 |
113 | try:
114 | for root, _, filenames in os.walk(os.path.basename(path)):
115 | for filename in filenames:
116 | if filename.endswith('.py'):
117 | self.add_file(os.path.join(root, filename))
118 |
119 | finally:
120 | os.chdir(cwd)
121 |
122 | def generate_codecs_index(self):
123 | data = 'from encodings import register_mod\n'
124 | for k in self.modules:
125 | if k.startswith('encodings.'):
126 | name = k[10:]
127 | data += 'try: from encodings import %s\n' % name
128 | data += 'except (ImportError, LookupError): pass\n'
129 | data += 'else: register_mod(%s)\n' % name
130 |
131 | self.add_module('codecs_index', data)
132 |
133 | @staticmethod
134 | def hash_file(f):
135 | hash = hashlib.sha256()
136 | while True:
137 | data = f.read(8192)
138 | if not data:
139 | break
140 |
141 | if isinstance(data, str):
142 | data = data.encode('utf-8', 'ignore')
143 |
144 | hash.update(data)
145 |
146 | return hash.hexdigest()[:7]
147 |
148 | def __process_one(self, name, module):
149 | prefix = os.path.join(self.outputdir, 'gen', 'modules', name)
150 | f = FileContainer(prefix, self.hash_file)
151 | module.generate_c_code(f, self.modules)
152 | with Lock():
153 | for x in f.close():
154 | # build/gen/blah -> gen/blah
155 | x = x[0].replace('\\', '/')
156 | x = x.split('/', 1)[-1]
157 | self.__files.append(x)
158 |
159 | def __worker(self):
160 | while True:
161 | try:
162 | name, module, text = self.__queue.get_nowait()
163 |
164 | except Empty:
165 | break
166 |
167 | safePrint(text)
168 | error = False
169 | try:
170 | self.__process_one(name, module)
171 |
172 | except:
173 | sys.stderr.write('Exception in thread')
174 | error = True
175 | sys.stderr.write(traceback.format_exc())
176 |
177 | finally:
178 | self.__queue.task_done()
179 | if error:
180 | sys.stdout.flush()
181 | sys.stderr.flush()
182 | os._exit(1)
183 |
184 | def run(self):
185 | # Apply modulereducer
186 | reduce_modules(self.modules)
187 |
188 | modules_dir = os.path.join(self.outputdir, 'gen', 'modules')
189 | if not os.path.isdir(modules_dir):
190 | os.makedirs(modules_dir)
191 |
192 | self.__queue = Queue()
193 | total = len(self.modules)
194 | n = int(math.ceil(math.log(total, 10)))
195 | _format = '[%%%dd/%%%dd] %%s' % (n, n)
196 | i = 0
197 | for name, module in self.modules.items():
198 | i += 1
199 | text = _format % (i, total, name)
200 | self.__queue.put((name, module, text))
201 |
202 | for i in range(self.nthreads):
203 | t = Thread(target=self.__worker)
204 | t.daemon = True
205 | t.start()
206 |
207 | self.__queue.join()
208 |
209 | filename = os.path.join(self.outputdir, 'gen', 'modules.I')
210 | f = ConditionalFile(filename, self.hash_file)
211 | write_modules_file(f, self.modules)
212 | self.__files.append('gen/' + os.path.basename(f.close()[0]))
213 |
214 | files = ''
215 | for filename in self.__files:
216 | files += ' %s\n' % filename
217 |
218 | with open(self.cmake_in_file, 'r') as f:
219 | cmakein = f.read()
220 |
221 | cmakein = cmakein.replace('$$project$$', self.project)
222 | cmakein = cmakein.replace('$$files$$', files)
223 | cmakein = cmakein.replace('$$pypperoni_root$$', PYPPERONI_ROOT.replace('\\', '/'))
224 | cmakein = cmakein.replace('$$python_root$$', PYTHON_ROOT.replace('\\', '/'))
225 |
226 | with open(os.path.join(self.outputdir, 'CMakeLists.txt'), 'w') as f:
227 | f.write(cmakein)
228 |
--------------------------------------------------------------------------------
/codeobj.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from . import config
19 |
20 | from threading import Lock
21 | from opcode import HAVE_ARGUMENT, EXTENDED_ARG, opmap
22 | import dis
23 |
24 | NOP = opmap['NOP']
25 |
26 |
27 | class CodeObject:
28 | def __init__(self, code):
29 | for attr in dir(code):
30 | if attr.startswith('co_'):
31 | v = getattr(code, attr)
32 | setattr(self, attr, v)
33 |
34 | self.co_path = ''
35 |
36 | def get_full_name(self):
37 | return '%s.%s' % (self.co_path, self.co_name)
38 |
39 | def get_signature(self, label):
40 | sig = self.get_full_name()
41 | sig += '_%d_%d_%d' % (len(self.co_code), self.co_stacksize, label)
42 | return sig
43 |
44 | def read_code(self):
45 | code = self.co_code
46 | extended_arg = 0
47 | line = self.co_firstlineno
48 | linestarts = dict(dis.findlinestarts(self))
49 |
50 | for i in range(0, len(code), 2):
51 | if i in linestarts:
52 | line = linestarts[i]
53 |
54 | op = code[i]
55 | if op >= HAVE_ARGUMENT:
56 | oparg = code[i + 1] | extended_arg
57 | extended_arg = (oparg << 8) if op == EXTENDED_ARG else 0
58 | else:
59 | oparg = None
60 |
61 | if op != EXTENDED_ARG:
62 | yield (i, op, oparg, line)
63 |
--------------------------------------------------------------------------------
/config.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | IMPORT_ALIASES = {}
19 |
20 | def add_import_alias(name, alias):
21 | IMPORT_ALIASES[name] = alias
22 |
23 | MAX_FILE_SIZE = 250000 # 250kb
24 | SPLIT_INTERVAL = 4000 # Split code objects every instructions
25 |
--------------------------------------------------------------------------------
/context.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from .util import *
19 |
20 | from io import StringIO
21 | import marshal
22 |
23 |
24 | class Context:
25 | def __init__(self, file, name, modules, flags, nlocals):
26 | self.file = file
27 | self.name = name
28 | self.modules = modules
29 | self.flags = flags
30 | self.nlocals = nlocals
31 |
32 | self.indent = 2
33 | self.__indentstr = ' '
34 |
35 | self.codeobjs = []
36 | self.jump_table = {}
37 | self._last_label = -2
38 |
39 | self.buf = []
40 | self.i = 0
41 |
42 | self.codebuffer = StringIO()
43 | self.__decls = [
44 | # (type, name, default value, deref)
45 | ('err', 'int', '0', False),
46 | ('_jmpto', 'void*', 'NULL', False),
47 | ('retval', 'PyObject*', 'NULL', False),
48 | ('tmp', 'PyObject*', 'NULL', False),
49 | ('u', 'PyObject*', 'NULL', False),
50 | ('v', 'PyObject*', 'NULL', False),
51 | ('w', 'PyObject*', 'NULL', False),
52 | ('x', 'PyObject*', 'NULL', False),
53 | ('exc', 'PyObject*', 'NULL', False),
54 | ('tb', 'PyObject*', 'NULL', False),
55 | ('val', 'PyObject*', 'NULL', False),
56 | ]
57 |
58 | self._consts = []
59 |
60 | def finish(self, encapsulated):
61 | if self.jump_table:
62 | max_required_label = max(self.jump_table.keys())
63 | if self._last_label < max_required_label:
64 | self.insert_label(max_required_label)
65 |
66 | self.insert_line('goto end;')
67 | self.insert_line('error:')
68 | self.insert_line(' *why = WHY_EXCEPTION;')
69 | self.insert_line(' retval = NULL;')
70 |
71 | self.insert_line('fast_block_end:')
72 | self.insert_line('while (*why != WHY_NOT && f->f_iblock > 0)')
73 | self.begin_block()
74 |
75 | self.insert_line('PyTryBlock *b = &f->f_blockstack[f->f_iblock - 1];')
76 | self.insert_line('if (b->b_type == SETUP_LOOP && *why == WHY_CONTINUE)')
77 | self.begin_block()
78 | self.insert_line('*why = WHY_NOT;')
79 | self.insert_line('_jmpto = (void*)(PyLong_AsSsize_t(retval));')
80 | self.insert_line('Py_DECREF(retval);')
81 | self.insert_line('JUMP_TO_ADDR(_jmpto);')
82 | self.end_block()
83 |
84 | self.insert_line('f->f_iblock--;')
85 | self.insert_line('if (b->b_type == EXCEPT_HANDLER)')
86 | self.begin_block()
87 | self.insert_line('UNWIND_EXCEPT_HANDLER(b);')
88 | self.insert_line('continue;')
89 | self.end_block()
90 |
91 | self.insert_line('UNWIND_BLOCK(b);')
92 | self.insert_line('if (b->b_type == SETUP_LOOP && *why == WHY_BREAK)')
93 | self.begin_block()
94 | self.insert_line('*why = WHY_NOT;')
95 | self.insert_line('JUMP_TO_ADDR(b->b_handler);')
96 | self.end_block()
97 |
98 | self.insert_line('if (*why == WHY_EXCEPTION && (b->b_type == SETUP_EXCEPT || b->b_type == SETUP_FINALLY))')
99 | self.begin_block()
100 | self.insert_line('PyObject *exc, *val, *tb;')
101 | self.insert_line('_jmpto = b->b_handler;')
102 | self.insert_line('PyFrame_BlockSetup(f, EXCEPT_HANDLER, NULL, STACK_LEVEL());')
103 | self.insert_line('PUSH(tstate->exc_traceback);')
104 | self.insert_line('PUSH(tstate->exc_value);')
105 | self.insert_line('if (tstate->exc_type != NULL) {PUSH(tstate->exc_type);}')
106 | self.insert_line('else {Py_INCREF(Py_None); PUSH(Py_None);}')
107 | self.insert_line('PyErr_Fetch(&exc, &val, &tb);')
108 | self.insert_line('PyErr_NormalizeException(&exc, &val, &tb);')
109 | self.insert_line('if (tb != NULL) PyException_SetTraceback(val, tb);')
110 | self.insert_line('else PyException_SetTraceback(val, Py_None);')
111 | self.insert_line('Py_INCREF(exc);')
112 | self.insert_line('tstate->exc_type = exc;')
113 | self.insert_line('Py_INCREF(val);')
114 | self.insert_line('tstate->exc_value = val;')
115 | self.insert_line('tstate->exc_traceback = tb;')
116 | self.insert_line('if (tb == NULL) tb = Py_None;')
117 | self.insert_line('Py_INCREF(tb);')
118 | self.insert_line('PUSH(tb); PUSH(val); PUSH(exc);')
119 | self.insert_line('*why = WHY_NOT;')
120 | self.insert_line('JUMP_TO_ADDR(_jmpto);')
121 | self.end_block()
122 |
123 | self.insert_line('if (b->b_type == SETUP_FINALLY)')
124 | self.begin_block()
125 | self.insert_line('if (*why & (WHY_RETURN | WHY_CONTINUE)) PUSH(retval);')
126 | self.insert_line('PUSH(PyLong_FromLong((long)*why));')
127 | self.insert_line('*why = WHY_NOT;')
128 | self.insert_line('JUMP_TO_ADDR(b->b_handler);')
129 | self.end_block()
130 |
131 | self.end_block()
132 |
133 | self.insert_line('end:')
134 | for d in self.__decls:
135 | if d[3]:
136 | self.insert_line(' Py_XDECREF(%s);' % d[0])
137 |
138 | self.insert_line('f->f_stacktop = stack_pointer;')
139 |
140 | if encapsulated:
141 | self.insert_line('return retval;')
142 |
143 | else:
144 | self.insert_line('if (*why == WHY_EXCEPTION) goto non_encapsulated_error;')
145 | self.insert_line('else if (*why == WHY_YIELD) goto non_encapsulated_end;')
146 | self.insert_line('goto clear_stack;')
147 | self.insert_line('non_encapsulated_error:')
148 | self.insert_line('PyTraceBack_Here(f);')
149 | self.insert_line('clear_stack: /* Clear stack */')
150 | self.begin_block()
151 | self.insert_line('PyObject** stack_pointer = f->f_stacktop;')
152 | self.insert_line('while (STACK_LEVEL() > 0) {')
153 | self.insert_line('Py_XDECREF(TOP());')
154 | self.insert_line('STACKADJ(-1);')
155 | self.insert_line('}')
156 | self.insert_line('f->f_stacktop = NULL;')
157 | self.end_block()
158 | self.insert_line('non_encapsulated_end:')
159 | self.insert_line('return _Py_CheckFunctionResult(NULL, retval, %s);' % self.register_literal(self.name))
160 |
161 | if encapsulated:
162 | self.file.add_common_header('PyObject* %s(PyFrameObject* f, int* why);' % self.name)
163 | self.file.write('PyObject* %s(PyFrameObject* f, int* why) {\n' % self.name)
164 |
165 | else:
166 | # N.B. function prototype is already added by Module.__gen_code
167 | self.file.write('PyObject* %s(PyFrameObject* f) {\n' % self.name)
168 |
169 | for d in self.__decls:
170 | if d[2] is not None:
171 | self.file.write(' %s %s = %s;\n' % (d[1], d[0], d[2]))
172 |
173 | else:
174 | self.file.write(' %s %s;\n' % (d[1], d[0]))
175 |
176 | self.file.write(' PyThreadState *tstate = PyThreadState_GET();\n')
177 | self.file.write(' PyObject **stack_pointer = f->f_stacktop;\n')
178 | self.file.write(' PyObject **fastlocals = f->f_localsplus;\n')
179 | self.file.write(' PyObject **freevars = f->f_localsplus + %d;\n' % self.nlocals)
180 |
181 | if not encapsulated:
182 | self.file.write(' int _why, *why;\n')
183 | self.file.write(' why = &_why;\n')
184 | self.file.write(' __%s_load_consts();\n' % self.file.uid)
185 |
186 | self.file.write(' *why = WHY_NOT;\n')
187 | self.file.write(' goto pre_start;\n')
188 |
189 | # Write jump table
190 | self.file.write(' jump_table:\n')
191 | self.file.write(' switch ((unsigned long long)_jmpto) {\n')
192 | for idx, label in self.jump_table.items():
193 | self.file.write(' case %d: goto %s; break;\n' % (idx, label))
194 | self.file.write(' default: goto start;\n')
195 | self.file.write(' }\n')
196 |
197 | # Pre-start: for generators, jump to last instruction
198 | self.file.write(' pre_start:\n')
199 |
200 | if self.flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR):
201 | self.file.write(' if (PyErr_Occurred()) goto error; /* generator.throw() */\n')
202 | self.file.write(' f->f_lineno = 0;')
203 | self.file.write(' _jmpto = (void*)f->f_lasti;')
204 | self.file.write(' goto jump_table;')
205 |
206 | else:
207 | self.file.write(' assert(!PyErr_Occurred());\n')
208 |
209 | # Actual start of function code
210 | self.file.write(' start:\n')
211 |
212 | self.codebuffer.seek(0)
213 | self.file.write(self.codebuffer.read() + '}\n\n')
214 | self.file.consider_next()
215 |
216 | def flushconsts(self):
217 | self.flushconsts()
218 |
219 | def begin_block(self):
220 | self.insert_line('{')
221 | self.indent += 2
222 | self.__indentstr += ' '
223 |
224 | def end_block(self):
225 | self.indent -= 2
226 | self.__indentstr = self.__indentstr[:-2]
227 | self.insert_line('}')
228 |
229 | def insert_line(self, line):
230 | self.codebuffer.write(self.__indentstr)
231 | self.codebuffer.write(line)
232 | self.codebuffer.write('\n')
233 |
234 | def insert_yield(self, line, label):
235 | self.jump_table[label] = 'label_%d' % label
236 | self.insert_line('*why = WHY_YIELD;')
237 | self.insert_line('f->f_lasti = %d;' % label)
238 | self.insert_line('f->f_lineno = %d; /* in case of throw() */' % line)
239 | self.insert_line('goto end;')
240 |
241 | def insert_handle_error(self, line, label):
242 | self.insert_line('f->f_lineno = %d;' % line)
243 | self.insert_line('goto error;')
244 |
245 | def insert_get_address(self, idx):
246 | label = 'label_%d' % idx
247 | self.jump_table[idx] = label
248 | self.insert_line('GET_ADDRESS(__addr, %s, %d);' % (label, idx))
249 |
250 | def add_decl(self, name, type='PyObject*', val='NULL', deref=True):
251 | self.__decls.append((name, type, val, deref))
252 |
253 | def add_decl_once(self, name, type='PyObject*', val='NULL', deref=True):
254 | for n, _, _, _ in self.__decls:
255 | if n == name:
256 | return
257 |
258 | self.__decls.append((name, type, val, deref))
259 |
260 | def insert_label(self, label):
261 | if self._last_label != -2:
262 | while self._last_label < label:
263 | self._last_label += 2
264 | self.insert_line('label_%d:' % self._last_label)
265 |
266 | else:
267 | self._last_label = label
268 | self.insert_line('label_%d:' % self._last_label)
269 |
270 | def register_const(self, value):
271 | with Lock():
272 | self._consts.append(value)
273 | ret = '__consts_%s[%d]' % (self.file.uid, len(self._consts) - 1)
274 |
275 | return ret
276 |
277 | def register_literal(self, value):
278 | getter = self.register_const(value)
279 | return 'PyUnicode_AsUTF8(%s) /* %s */' % (getter, value)
280 |
281 | def dumpconsts(self):
282 | return marshal.dumps(tuple(self._consts))
283 |
284 | def flushconsts(self):
285 | blob = self.dumpconsts()
286 | blobsize = len(blob)
287 | blobptr = '__data_blob_' + self.file.uid
288 | pageptr = '__consts_' + self.file.uid
289 |
290 | self.file.write('static const unsigned char %s[%d] = {\n ' % (blobptr, blobsize))
291 |
292 | i = 0
293 | for c in blob:
294 | self.file.write('%d, ' % c)
295 | i += 1
296 | if not i % 16:
297 | self.file.write('\n ')
298 |
299 | self.file.write('};\n\n')
300 | self.file.add_common_header('void __%s_load_consts();' % self.file.uid)
301 | self.file.add_common_header('PyObject** %s;' % pageptr)
302 | self.file.write('void __%s_load_consts() {\n' % self.file.uid)
303 | self.file.write(' if (%s == NULL) {\n' % pageptr)
304 | self.file.write(' PyTupleObject* t = (PyTupleObject*)'
305 | 'PyMarshal_ReadObjectFromString((char*)%s, %d);\n' %
306 | (blobptr, blobsize))
307 | self.file.write(' %s = t->ob_item;\n' % pageptr)
308 | self.file.write(' }\n')
309 | self.file.write('}\n\n')
310 |
--------------------------------------------------------------------------------
/files.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from . import config
19 |
20 | from threading import Lock
21 | from io import StringIO
22 | import os
23 |
24 |
25 | class ConditionalFile:
26 | def __init__(self, filename, hashfunc):
27 | self.filename = filename
28 | self.hashfunc = hashfunc
29 |
30 | self._buf = StringIO()
31 |
32 | def write(self, data):
33 | self._buf.write(data)
34 |
35 | def read(self):
36 | return self._buf.read()
37 |
38 | def seek(self, *args):
39 | self._buf.seek(*args)
40 |
41 | def tell(self):
42 | return self._buf.tell()
43 |
44 | def close(self):
45 | self._buf.seek(0)
46 | newhash = self.hashfunc(self._buf)
47 | if not os.path.isfile(self.filename):
48 | self.__write()
49 | return (self.filename, newhash, False)
50 |
51 | f = open(self.filename, 'rb')
52 | oldhash = self.hashfunc(f)
53 | self.seek(0)
54 | f.close()
55 |
56 | modified = oldhash != newhash
57 | if modified:
58 | self.__write()
59 |
60 | return (self.filename, newhash, modified)
61 |
62 | def __write(self):
63 | self.seek(0)
64 | f = open(self.filename, 'wb')
65 | f.write(self._buf.read().encode('utf-8', errors='backslashreplace'))
66 | f.close()
67 |
68 |
69 | class FileContainer:
70 | def __init__(self, prefix, hashfunc, uid=None):
71 | self.prefix = prefix.replace('.', '/')
72 | self.hashfunc = hashfunc
73 |
74 | if uid:
75 | self.uid = uid
76 |
77 | else:
78 | self.uid = os.path.basename(prefix).replace('.', '_')
79 |
80 | self.headername = self.prefix + '.pyp.h'
81 | self.header = ConditionalFile(self.headername, self.hashfunc)
82 | self.header.write('#include "pypperoni_impl.h"\n')
83 |
84 | prefix_dir = os.path.dirname(self.prefix)
85 | if not os.path.isdir(prefix_dir):
86 | try:
87 | os.makedirs(prefix_dir)
88 |
89 | except FileExistsError:
90 | pass
91 |
92 | self.files = []
93 | self.filenames = []
94 | self.__next()
95 |
96 | def __next(self):
97 | self.filenames.append('%s_%d.c' % (self.prefix, len(self.filenames) + 1))
98 | f = ConditionalFile(self.filenames[-1], self.hashfunc)
99 | f.write('#include "%s"\n\n' % os.path.basename(self.headername))
100 | self.files.append(f)
101 |
102 | def write(self, *args):
103 | self.files[-1].write(*args)
104 |
105 | def consider_next(self):
106 | if self.files[-1].tell() > config.MAX_FILE_SIZE:
107 | self.__next()
108 |
109 | def add_common_header(self, header):
110 | self.header.write(header + '\n')
111 |
112 | def close(self):
113 | yield self.header.close()
114 | for f in self.files:
115 | yield f.close()
116 |
--------------------------------------------------------------------------------
/module.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from .codeobj import CodeObject
19 | from .config import IMPORT_ALIASES, SPLIT_INTERVAL
20 | from .context import Context
21 | from .util import *
22 |
23 | from opcode import *
24 | globals().update(opmap)
25 |
26 | import hashlib
27 | import struct
28 | import types
29 | import dis
30 | import ast
31 |
32 |
33 | IDX_LABEL = 0
34 | IDX_OP = 1
35 | IDX_OPARG = 2
36 | IDX_LINE = 3
37 |
38 | FVC_MASK = 0x3
39 | FVC_NONE = 0x0
40 | FVC_STR = 0x1
41 | FVC_REPR = 0x2
42 | FVC_ASCII = 0x3
43 | FVS_MASK = 0x4
44 | FVS_HAVE_SPEC = 0x4
45 |
46 |
47 | class ModuleBase:
48 | '''
49 | Base class for all module types.
50 | '''
51 | def __init__(self, name, code):
52 | self.name = name
53 | self.astmod = ast.parse(code, name)
54 |
55 | self._is_main = False
56 | self._id = -1
57 |
58 | def set_as_main(self):
59 | self._is_main = True
60 |
61 | def is_external(self):
62 | return False
63 |
64 | def is_package(self):
65 | return False
66 |
67 | def get_id(self):
68 | if self._is_main:
69 | return 0
70 |
71 | if self._id == -1:
72 | self._id = struct.unpack(' context.i + 1 and \
155 | context.buf[context.i + 1][IDX_OP] == IMPORT_NAME:
156 | context.end_block()
157 | context.insert_line('/* DETECTED IMPORT */')
158 | self.__handle_import(codeobj, context, value)
159 |
160 | else:
161 | if value is None:
162 | context.insert_line('x = Py_None;')
163 |
164 | else:
165 | getter = context.register_const(value)
166 | context.insert_line('x = %s; /* %s */' % (getter, safeRepr(value)))
167 |
168 | context.insert_line('Py_INCREF(x);')
169 | context.insert_line('PUSH(x);')
170 | context.end_block()
171 |
172 | elif op == STORE_NAME:
173 | context.begin_block()
174 |
175 | name = codeobj.co_names[oparg]
176 | context.insert_line('x = POP();')
177 | context.insert_line('v = f->f_locals;')
178 | context.insert_line('if (v == NULL) {')
179 | context.insert_line('PyErr_SetString(PyExc_SystemError, "no locals");')
180 | context.insert_handle_error(line, label)
181 | context.insert_line('}')
182 | context.insert_line('u = %s;' % context.register_const(name))
183 | context.insert_line('err = PyDict_CheckExact(v) ?')
184 | context.insert_line(' PyDict_SetItem(v, u, x) : PyObject_SetItem(v, u, x);')
185 | context.insert_line('Py_DECREF(x);')
186 | context.insert_line('if (err != 0) {')
187 | context.insert_handle_error(line, label)
188 | context.insert_line('}')
189 |
190 | context.end_block()
191 |
192 | elif op == STORE_GLOBAL:
193 | context.begin_block()
194 |
195 | name = codeobj.co_names[oparg]
196 | context.insert_line('x = POP();')
197 | context.insert_line('err = PyDict_SetItem(f->f_globals, %s, x);' %
198 | context.register_const(name))
199 | context.insert_line('Py_DECREF(x);')
200 | context.insert_line('if (err != 0) {')
201 | context.insert_handle_error(line, label)
202 | context.insert_line('}')
203 |
204 | context.end_block()
205 |
206 | elif op == STORE_FAST:
207 | context.begin_block()
208 |
209 | context.insert_line('x = POP();')
210 | context.insert_line('tmp = fastlocals[%d];' % oparg)
211 | context.insert_line('fastlocals[%d] = x;' % oparg)
212 | context.insert_line('Py_XDECREF(tmp);')
213 |
214 | context.end_block()
215 |
216 | elif op == STORE_ATTR:
217 | context.begin_block()
218 |
219 | attr = codeobj.co_names[oparg]
220 | context.insert_line('v = TOP();')
221 | context.insert_line('u = SECOND();')
222 | context.insert_line('STACKADJ(-2);')
223 | context.insert_line('err = PyObject_SetAttr(v, %s, u);' %
224 | context.register_const(attr))
225 | context.insert_line('Py_DECREF(u);')
226 | context.insert_line('Py_DECREF(v);')
227 | context.insert_line('if (err != 0) {')
228 | context.insert_handle_error(line, label)
229 | context.insert_line('}')
230 |
231 | context.end_block()
232 |
233 | elif op == STORE_SUBSCR:
234 | context.begin_block()
235 | context.insert_line('w = POP();')
236 | context.insert_line('v = POP();')
237 | context.insert_line('u = POP();')
238 | context.insert_line('err = PyObject_SetItem(v, w, u);')
239 | context.insert_line('Py_DECREF(w);')
240 | context.insert_line('Py_DECREF(v);')
241 | context.insert_line('Py_DECREF(u);')
242 | context.insert_line('if (err != 0) {')
243 | context.insert_handle_error(line, label)
244 | context.insert_line('}')
245 | context.end_block()
246 |
247 | elif op == STORE_DEREF:
248 | context.begin_block()
249 | context.insert_line('v = POP();')
250 | context.insert_line('x = freevars[%d]; /* cell */' % oparg)
251 | context.insert_line('tmp = PyCell_GET(x);')
252 | context.insert_line('PyCell_SET(x, v);')
253 | context.insert_line('Py_XDECREF(tmp);')
254 | context.end_block()
255 |
256 | elif op == DELETE_FAST:
257 | context.begin_block()
258 | context.insert_line('tmp = fastlocals[%d];' % oparg)
259 | context.insert_line('if (tmp == NULL) {')
260 | context.insert_line('PyErr_SetString(PyExc_UnboundLocalError, "DELETE_FAST failed");')
261 | context.insert_handle_error(line, label)
262 | context.insert_line('}')
263 | context.insert_line('fastlocals[%d] = NULL;' % oparg)
264 | context.insert_line('Py_DECREF(tmp);')
265 | context.end_block()
266 |
267 | elif op == DELETE_NAME:
268 | context.begin_block()
269 |
270 | name = codeobj.co_names[oparg]
271 | context.insert_line('v = f->f_locals;')
272 | context.insert_line('if (v == NULL) {')
273 | context.insert_line('PyErr_SetString(PyExc_SystemError, "no locals");')
274 | context.insert_handle_error(line, label)
275 | context.insert_line('}')
276 | context.insert_line('err = PyObject_DelItem(v, %s);' % context.register_const(name))
277 | context.insert_line('if (err != 0) {')
278 | context.insert_handle_error(line, label)
279 | context.insert_line('}')
280 |
281 | context.end_block()
282 |
283 | elif op == DELETE_GLOBAL:
284 | context.begin_block()
285 |
286 | name = codeobj.co_names[oparg]
287 | context.insert_line('err = PyDict_DelItem(f->f_globals, %s);' %
288 | context.register_const(name))
289 | context.insert_line('if (err != 0) {')
290 | context.insert_line('PyErr_Format(PyExc_NameError, "DELETE_GLOBAL failed");')
291 | context.insert_handle_error(line, label)
292 | context.insert_line('}')
293 | context.end_block()
294 |
295 | elif op == DELETE_ATTR:
296 | context.begin_block()
297 |
298 | name = codeobj.co_names[oparg]
299 | context.insert_line('v = POP();')
300 | context.insert_line('err = PyObject_SetAttr(v, %s, NULL);' %
301 | context.register_const(name))
302 | context.insert_line('Py_DECREF(v);')
303 | context.insert_line('if (err != 0) {')
304 | context.insert_handle_error(line, label)
305 | context.insert_line('}')
306 | context.end_block()
307 |
308 | elif op == DELETE_SUBSCR:
309 | context.begin_block()
310 | context.insert_line('w = POP();')
311 | context.insert_line('v = POP();')
312 | context.insert_line('err = PyObject_DelItem(v, w);')
313 | context.insert_line('Py_DECREF(v);')
314 | context.insert_line('Py_DECREF(w);')
315 | context.insert_line('if (err != 0) {')
316 | context.insert_handle_error(line, label)
317 | context.insert_line('}')
318 | context.end_block()
319 |
320 | elif op == DELETE_DEREF:
321 | context.begin_block()
322 | context.insert_line('tmp = freevars[%d]; /* cell */' % oparg)
323 | context.insert_line('if (PyCell_GET(tmp) == NULL) {')
324 | context.insert_line('PyErr_SetString(PyExc_UnboundLocalError, "DELETE_DEREF failed");')
325 | context.insert_handle_error(line, label)
326 | context.insert_line('}')
327 | context.insert_line('PyCell_Set(tmp, NULL);')
328 | context.end_block()
329 |
330 | elif op == COMPARE_OP:
331 | context.begin_block()
332 |
333 | context.insert_line('w = POP(); /* right */')
334 | context.insert_line('v = TOP(); /* left */')
335 | context.insert_line('err = __pypperoni_IMPL_compare(v, w, %d, &x);' % oparg)
336 | context.insert_line('Py_DECREF(w);')
337 | context.insert_line('Py_DECREF(v);')
338 | context.insert_line('if (err != 0) {')
339 | context.insert_handle_error(line, label)
340 | context.insert_line('}')
341 | context.insert_line('SET_TOP(x);')
342 | context.end_block()
343 |
344 | elif op == BUILD_STRING:
345 | context.begin_block()
346 | context.insert_line('u = PyUnicode_New(0, 0); /* empty */')
347 | context.insert_line('if (u == NULL) {')
348 | context.insert_handle_error(line, label)
349 | context.insert_line('}')
350 | context.insert_line('x = _PyUnicode_JoinArray(u, stack_pointer - %d, %d);' % (oparg, oparg))
351 | context.insert_line('Py_DECREF(u);')
352 | context.insert_line('if (x == NULL) {')
353 | context.insert_handle_error(line, label)
354 | context.insert_line('}')
355 |
356 | for i in range(oparg):
357 | context.insert_line('v = POP();')
358 | context.insert_line('Py_DECREF(v);')
359 |
360 | context.insert_line('PUSH(x);')
361 | context.end_block()
362 |
363 | elif op == BUILD_LIST:
364 | context.begin_block()
365 | context.insert_line('u = PyList_New(%d);' % oparg)
366 | context.insert_line('if (u == NULL) {')
367 | context.insert_handle_error(line, label)
368 | context.insert_line('}')
369 |
370 | for i in range(oparg, 0, -1):
371 | context.insert_line('v = POP();')
372 | context.insert_line('PyList_SET_ITEM(u, %d, v);' % (i - 1))
373 |
374 | context.insert_line('PUSH(u);')
375 | context.end_block()
376 |
377 | elif op in (BUILD_TUPLE_UNPACK_WITH_CALL, BUILD_TUPLE_UNPACK, BUILD_LIST_UNPACK):
378 | context.begin_block()
379 | context.insert_line('u = PyList_New(0); /* sum */')
380 | context.insert_line('if (u == NULL) {')
381 | context.insert_handle_error(line, label)
382 | context.insert_line('}')
383 |
384 | for i in range(oparg, 0, -1):
385 | context.insert_line('v = _PyList_Extend((PyListObject *)u, PEEK(%d));' % i)
386 | context.insert_line('if (v == NULL) {')
387 | context.insert_line('Py_DECREF(u);')
388 | context.insert_handle_error(line, label)
389 | context.insert_line('}')
390 | context.insert_line('Py_DECREF(v);')
391 |
392 | if op != BUILD_LIST_UNPACK:
393 | context.insert_line('x = PyList_AsTuple(u);')
394 | context.insert_line('Py_DECREF(u);')
395 | context.insert_line('if (x == NULL) {')
396 | context.insert_handle_error(line, label)
397 | context.insert_line('}')
398 |
399 | else:
400 | context.insert_line('x = u;')
401 |
402 | for i in range(oparg):
403 | context.insert_line('Py_DECREF(POP());')
404 |
405 | context.insert_line('PUSH(x);')
406 | context.end_block()
407 |
408 | elif op == BUILD_MAP_UNPACK_WITH_CALL:
409 | context.begin_block()
410 | context.insert_line('u = PyDict_New(); /* sum */')
411 | context.insert_line('if (u == NULL) {')
412 | context.insert_handle_error(line, label)
413 | context.insert_line('}')
414 |
415 | for i in range(oparg, 0, -1):
416 | context.insert_line('v = PEEK(%d);' % i)
417 | context.insert_line('if (_PyDict_MergeEx(u, v, 2) < 0) {')
418 | context.insert_line('__pypperoni_IMPL_handle_bmuwc_error(v, PEEK(%d));' % (oparg + 2))
419 | context.insert_line('Py_DECREF(u);')
420 | context.insert_handle_error(line, label)
421 | context.insert_line('}')
422 |
423 | for i in range(oparg):
424 | context.insert_line('x = POP();')
425 | context.insert_line('Py_DECREF(x);')
426 |
427 | context.insert_line('PUSH(u);')
428 | context.end_block()
429 |
430 | elif op == BUILD_MAP_UNPACK:
431 | context.begin_block()
432 | context.insert_line('u = PyDict_New(); /* sum */')
433 | context.insert_line('if (u == NULL) {')
434 | context.insert_handle_error(line, label)
435 | context.insert_line('}')
436 |
437 | for i in range(oparg, 0, -1):
438 | context.insert_line('v = PEEK(%d);' % i)
439 | context.insert_line('if (PyDict_Update(u, v) < 0) {')
440 | context.insert_line('if (PyErr_ExceptionMatches(PyExc_AttributeError)) {')
441 | context.insert_line('PyErr_Format(PyExc_TypeError, "\'%.200s\' object is not a mapping", v->ob_type->tp_name);')
442 | context.insert_line('}')
443 | context.insert_line('Py_DECREF(u);')
444 | context.insert_handle_error(line, label)
445 | context.insert_line('}')
446 | context.insert_line('Py_DECREF(v);')
447 |
448 | for i in range(oparg):
449 | context.insert_line('Py_DECREF(POP());')
450 |
451 | context.insert_line('PUSH(u);')
452 | context.end_block()
453 |
454 | elif op == LIST_APPEND:
455 | context.begin_block()
456 | context.insert_line('v = POP();')
457 | context.insert_line('x = PEEK(%d);' % oparg)
458 | context.insert_line('err = PyList_Append(x, v);')
459 | context.insert_line('Py_DECREF(v);')
460 | context.insert_line('if (err != 0) {')
461 | context.insert_handle_error(line, label)
462 | context.insert_line('}')
463 | context.end_block()
464 |
465 | elif op == BUILD_TUPLE:
466 | context.begin_block()
467 | context.insert_line('u = PyTuple_New(%d);' % oparg)
468 | context.insert_line('if (u == NULL) {')
469 | context.insert_handle_error(line, label)
470 | context.insert_line('}')
471 |
472 | for i in range(oparg, 0, -1):
473 | context.insert_line('v = POP();')
474 | context.insert_line('PyTuple_SET_ITEM(u, %d, v);' % (i - 1))
475 |
476 | context.insert_line('PUSH(u);')
477 | context.end_block()
478 |
479 | elif op == BUILD_SET:
480 | context.begin_block()
481 | context.insert_line('u = PySet_New(NULL);')
482 | context.insert_line('if (u == NULL) {')
483 | context.insert_handle_error(line, label)
484 | context.insert_line('}')
485 |
486 | for i in range(oparg, 0, -1):
487 | context.insert_line('v = PEEK(%d);' % i)
488 | context.insert_line('if (err == 0) err = PySet_Add(u, v);')
489 | context.insert_line('Py_DECREF(v);')
490 |
491 | context.insert_line('STACKADJ(-%d);' % oparg)
492 | context.insert_line('if (err != 0) {')
493 | context.insert_line('Py_DECREF(u);')
494 | context.insert_handle_error(line, label)
495 | context.insert_line('}')
496 | context.insert_line('PUSH(u);')
497 | context.end_block()
498 |
499 | elif op == SET_ADD:
500 | context.begin_block()
501 | context.insert_line('v = POP();')
502 | context.insert_line('x = PEEK(%d);' % oparg)
503 | context.insert_line('err = PySet_Add(x, v);')
504 | context.insert_line('Py_DECREF(v);')
505 | context.insert_line('if (err != 0) {')
506 | context.insert_handle_error(line, label)
507 | context.insert_line('}')
508 | context.end_block()
509 |
510 | elif op == BUILD_MAP:
511 | context.add_decl_once('i', 'int', None, False)
512 |
513 | context.begin_block()
514 | context.insert_line('u = _PyDict_NewPresized(%d);' % oparg)
515 | context.insert_line('if (u == NULL) {')
516 | context.insert_handle_error(line, label)
517 | context.insert_line('}')
518 |
519 | context.insert_line('for (i = %d; i > 0; i--)' % oparg)
520 | context.begin_block()
521 | context.insert_line('x = PEEK(2*i);')
522 | context.insert_line('v = PEEK(2*i - 1);')
523 | context.insert_line('err = PyDict_SetItem(u, x, v);')
524 | context.insert_line('if (err != 0)')
525 | context.begin_block()
526 | context.insert_line('Py_DECREF(u);')
527 | context.insert_handle_error(line, label)
528 | context.end_block()
529 | context.end_block()
530 |
531 | context.insert_line('for (i = %d; i > 0; i--)' % (oparg * 2))
532 | context.begin_block()
533 | context.insert_line('x = POP();')
534 | context.insert_line('Py_DECREF(x);')
535 | context.end_block()
536 |
537 | context.insert_line('PUSH(u);')
538 | context.end_block()
539 |
540 | elif op == MAP_ADD:
541 | context.begin_block()
542 | context.insert_line('x = POP();')
543 | context.insert_line('v = POP();')
544 | context.insert_line('u = PEEK(%d);' % oparg)
545 | context.insert_line('err = PyDict_SetItem(u, x, v);')
546 | context.insert_line('Py_DECREF(x);')
547 | context.insert_line('Py_DECREF(v);')
548 | context.insert_line('if (err != 0) {')
549 | context.insert_handle_error(line, label)
550 | context.insert_line('}')
551 | context.end_block()
552 |
553 | elif op == BUILD_CONST_KEY_MAP:
554 | context.begin_block()
555 |
556 | context.insert_line('x = POP(); /* keys */')
557 | context.insert_line('u = _PyDict_NewPresized(%d);' % oparg)
558 |
559 | context.insert_line('if (u == NULL)')
560 | context.begin_block()
561 | context.insert_line('Py_DECREF(x);')
562 | context.insert_handle_error(line, label)
563 | context.end_block()
564 |
565 | for i in range(oparg):
566 | context.insert_line('v = PyTuple_GET_ITEM(x, %d);' % i)
567 | context.insert_line('w = PEEK(%d);' % (oparg - i))
568 | context.insert_line('err = PyDict_SetItem(u, v, w);')
569 |
570 | context.insert_line('if (err != 0)')
571 | context.begin_block()
572 | context.insert_line('Py_DECREF(u);')
573 | context.insert_handle_error(line, label)
574 | context.end_block()
575 |
576 | context.insert_line('Py_DECREF(x); /* keys */')
577 |
578 | for i in range(oparg):
579 | context.insert_line('x = POP();')
580 | context.insert_line('Py_DECREF(x);')
581 |
582 | context.insert_line('PUSH(u);')
583 |
584 | context.end_block()
585 |
586 | elif op == BUILD_SLICE:
587 | context.begin_block()
588 |
589 | if oparg == 3:
590 | context.insert_line('w = POP();')
591 |
592 | else:
593 | context.insert_line('w = NULL;')
594 |
595 | context.insert_line('v = POP();')
596 | context.insert_line('u = POP();')
597 |
598 | context.insert_line('x = PySlice_New(u, v, w);')
599 | context.insert_line('Py_DECREF(u);')
600 | context.insert_line('Py_DECREF(v);')
601 | context.insert_line('Py_XDECREF(w);')
602 | context.insert_line('if (x == NULL) {')
603 | context.insert_handle_error(line, label)
604 | context.insert_line('}')
605 |
606 | context.insert_line('PUSH(x);')
607 |
608 | context.end_block()
609 |
610 | elif op == LOAD_NAME:
611 | context.begin_block()
612 | name = codeobj.co_names[oparg]
613 | context.insert_line('x = __pypperoni_IMPL_load_name(f, %s); /* %s */' % (
614 | context.register_const(name), safeRepr(name)))
615 | context.insert_line('if (x == NULL) {')
616 | context.insert_handle_error(line, label)
617 | context.insert_line('}')
618 | context.insert_line('Py_INCREF(x);')
619 | context.insert_line('PUSH(x);')
620 | context.end_block()
621 |
622 | elif op == LOAD_ATTR:
623 | context.begin_block()
624 | attr = codeobj.co_names[oparg]
625 | context.insert_line('v = TOP();')
626 | context.insert_line('x = PyObject_GetAttr(v, %s);' % context.register_const(attr))
627 | context.insert_line('if (x == NULL) {')
628 | context.insert_handle_error(line, label)
629 | context.insert_line('}')
630 | context.insert_line('Py_DECREF(v);')
631 | context.insert_line('SET_TOP(x);')
632 | context.end_block()
633 |
634 | elif op == LOAD_GLOBAL:
635 | context.begin_block()
636 | name = codeobj.co_names[oparg]
637 | context.insert_line('x = __pypperoni_IMPL_load_global(f, %s);' % context.register_const(name))
638 | context.insert_line('if (x == NULL) {')
639 | context.insert_handle_error(line, label)
640 | context.insert_line('}')
641 | context.insert_line('Py_INCREF(x);')
642 | context.insert_line('PUSH(x);')
643 | context.end_block()
644 |
645 | elif op == LOAD_FAST:
646 | context.begin_block()
647 | name = codeobj.co_varnames[oparg]
648 | context.insert_line('x = fastlocals[%d];' % oparg)
649 | context.insert_line('if (x == NULL) {')
650 | errormsg = "local variable '%.200s' referenced before assignment" % name
651 | context.insert_line('PyErr_SetString(PyExc_UnboundLocalError, %s);' % context.register_literal(errormsg))
652 | context.insert_handle_error(line, label)
653 | context.insert_line('}')
654 | context.insert_line('Py_INCREF(x);')
655 | context.insert_line('PUSH(x);')
656 | context.end_block()
657 |
658 | elif op == LOAD_DEREF:
659 | context.begin_block()
660 | context.insert_line('x = freevars[%d]; /* cell */' % oparg)
661 | context.insert_line('u = PyCell_GET(x);')
662 | context.insert_line('if (u == NULL) {')
663 |
664 | if oparg < len(codeobj.co_cellvars):
665 | name = codeobj.co_cellvars[oparg]
666 |
667 | else:
668 | name = codeobj.co_freevars[oparg - len(codeobj.co_cellvars)]
669 |
670 | errormsg = "free variable '%.200s' referenced before assignment in enclosing scope" % name
671 | context.insert_line('PyErr_SetString(PyExc_NameError, %s);' % context.register_literal(errormsg))
672 | context.insert_handle_error(line, label)
673 | context.insert_line('}')
674 | context.insert_line('Py_INCREF(u);')
675 | context.insert_line('PUSH(u);')
676 | context.end_block()
677 |
678 | elif op == LOAD_CLOSURE:
679 | context.begin_block()
680 | context.insert_line('x = freevars[%d];' % oparg)
681 | context.insert_line('Py_INCREF(x);')
682 | context.insert_line('PUSH(x);')
683 | context.end_block()
684 |
685 | elif op == LOAD_BUILD_CLASS:
686 | context.begin_block()
687 | context.insert_line('err = __pypperoni_IMPL_load_build_class(f, &x);')
688 | context.insert_line('if (err != 0) {')
689 | context.insert_handle_error(line, label)
690 | context.insert_line('}')
691 | context.insert_line('PUSH(x);')
692 | context.end_block()
693 |
694 | elif op == LOAD_CLASSDEREF:
695 | context.begin_block()
696 |
697 | name = codeobj.co_freevars[oparg - len(codeobj.co_cellvars)]
698 | name = context.register_const(name)
699 | context.insert_line('if (PyDict_CheckExact(f->f_locals)) {')
700 | context.insert_line('v = PyDict_GetItem(f->f_locals, %s);' % name)
701 | context.insert_line('Py_XINCREF(v);')
702 | context.insert_line('}')
703 | context.insert_line('else {')
704 | context.insert_line('v = PyObject_GetItem(f->f_locals, %s);' % name)
705 | context.insert_line('if (v == NULL) {')
706 | context.insert_line('if (!PyErr_ExceptionMatches(PyExc_KeyError)) {')
707 | context.insert_handle_error(line, label)
708 | context.insert_line('}')
709 | context.insert_line('PyErr_Clear();')
710 | context.insert_line('}')
711 | context.insert_line('}')
712 | context.insert_line('if (v == NULL) {')
713 | context.insert_line('v = PyCell_GET(freevars[%d]);' % oparg)
714 | context.insert_line('if (v == NULL) {')
715 | context.insert_line('PyErr_SetString(PyExc_UnboundLocalError, "LOAD_CLASSDEREF failed");')
716 | context.insert_handle_error(line, label)
717 | context.insert_line('}')
718 | context.insert_line('Py_INCREF(v);')
719 | context.insert_line('}')
720 | context.insert_line('PUSH(v);')
721 | context.end_block()
722 |
723 | elif op in (POP_JUMP_IF_TRUE, POP_JUMP_IF_FALSE):
724 | context.add_decl_once('result', 'int', None, False)
725 | context.insert_line('x = POP();')
726 | context.insert_line('err = __pypperoni_IMPL_check_cond(x, &result);')
727 | context.insert_line('Py_DECREF(x);')
728 | context.insert_line('if (err != 0) {')
729 | context.insert_handle_error(line, label)
730 | context.insert_line('}')
731 | context.insert_line('if (%sresult)' %
732 | ('!' if op == POP_JUMP_IF_FALSE else ''))
733 | context.begin_block()
734 | context.insert_line('goto label_%d;' % oparg)
735 | context.end_block()
736 |
737 | elif op in (JUMP_IF_TRUE_OR_POP, JUMP_IF_FALSE_OR_POP):
738 | context.add_decl_once('result', 'int', None, False)
739 | context.insert_line('x = TOP();')
740 | context.insert_line('err = __pypperoni_IMPL_check_cond(x, &result);')
741 | context.insert_line('if (err != 0) {')
742 | context.insert_handle_error(line, label)
743 | context.insert_line('}')
744 | context.insert_line('if (%sresult)' %
745 | ('!' if op == JUMP_IF_FALSE_OR_POP else ''))
746 | context.begin_block()
747 | context.insert_line('goto label_%d;' % oparg)
748 | context.end_block()
749 |
750 | context.insert_line('STACKADJ(-1);')
751 | context.insert_line('Py_DECREF(x);')
752 |
753 | elif op == JUMP_FORWARD:
754 | if oparg:
755 | context.begin_block()
756 | context.insert_line('goto label_%d;' % (oparg + label + 2))
757 | context.end_block()
758 |
759 | elif op == JUMP_ABSOLUTE:
760 | context.begin_block()
761 | context.insert_line('goto label_%d;' % oparg)
762 | context.end_block()
763 |
764 | elif op == BEFORE_ASYNC_WITH:
765 | context.begin_block()
766 | context.insert_line('u = TOP();')
767 | context.insert_line('v = PyObject_GetAttrString(u, "__aexit__");')
768 | context.insert_line('if (v == NULL) {')
769 | context.insert_line(' if (!PyErr_Occurred()) {PyErr_SetString(PyExc_AttributeError, "__aexit__");}')
770 | context.insert_handle_error(line, label)
771 | context.insert_line('}')
772 | context.insert_line('SET_TOP(v);')
773 | context.insert_line('w = PyObject_GetAttrString(u, "__aenter__");')
774 | context.insert_line('Py_DECREF(u);')
775 | context.insert_line('if (w == NULL) {')
776 | context.insert_line(' if (!PyErr_Occurred()) {PyErr_SetString(PyExc_AttributeError, "__aenter__");}')
777 | context.insert_handle_error(line, label)
778 | context.insert_line('}')
779 | context.insert_line('x = PyObject_CallFunctionObjArgs(w, NULL);')
780 | context.insert_line('Py_DECREF(w);')
781 | context.insert_line('if (x == NULL) {')
782 | context.insert_handle_error(line, label)
783 | context.insert_line('}')
784 | context.insert_line('PUSH(x);')
785 | context.end_block()
786 |
787 | elif op == SETUP_ASYNC_WITH:
788 | context.begin_block()
789 | context.insert_line('void* __addr;')
790 | context.insert_get_address(label + oparg + 2)
791 | context.insert_line('PyFrame_BlockSetup(f, SETUP_FINALLY, __addr, STACK_LEVEL() - 1);')
792 | context.end_block()
793 |
794 | elif op == GET_AWAITABLE:
795 | context.add_decl_once('type', 'PyTypeObject*', None, False)
796 | context.begin_block()
797 | context.insert_line('u = TOP(); /* iterable */')
798 | context.insert_line('v = _PyCoro_GetAwaitableIter(u); /* iter */')
799 |
800 | prevopcode = context.buf[context.i - 2][IDX_OP]
801 | prevopcode2msg = {
802 | BEFORE_ASYNC_WITH: 'enter',
803 | WITH_CLEANUP_START: 'exit'
804 | }
805 | if prevopcode in prevopcode2msg:
806 | msg = "'async with' received an object from __a%s__ that does not implement __await__: %%.100s"
807 | msg %= prevopcode2msg[prevopcode]
808 | context.insert_line('if (v == NULL)')
809 | context.begin_block()
810 | context.insert_line('type = Py_TYPE(u);')
811 | context.insert_line('if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {')
812 | context.insert_line('PyErr_Format(PyExc_TypeError, "%s", type->tp_name);' % msg)
813 | context.insert_line('}')
814 | context.end_block()
815 |
816 | context.insert_line('Py_DECREF(u);')
817 | context.insert_line('if (v != NULL && PyCoro_CheckExact(v))')
818 | context.begin_block()
819 | context.insert_line('tmp = _PyGen_yf((PyGenObject*)v);')
820 | context.insert_line('if (tmp != NULL) {')
821 | context.insert_line('Py_DECREF(tmp);')
822 | context.insert_line('Py_CLEAR(v);')
823 | context.insert_line('PyErr_SetString(PyExc_RuntimeError, "coroutine is being awaited already");')
824 | context.insert_line('}')
825 | context.end_block()
826 |
827 | context.insert_line('SET_TOP(v);')
828 | context.insert_line('if (v == NULL) {')
829 | context.insert_handle_error(line, label)
830 | context.insert_line('}')
831 | context.end_block()
832 |
833 | elif op == GET_AITER:
834 | context.begin_block()
835 | context.insert_line('u = POP();')
836 | context.insert_line('v = __pypperoni_IMPL_get_aiter(u);')
837 | context.insert_line('if (v == NULL) {')
838 | context.insert_handle_error(line, label)
839 | context.insert_line('}')
840 | context.insert_line('PUSH(v);')
841 | context.end_block()
842 |
843 | elif op == GET_ANEXT:
844 | context.begin_block()
845 | context.insert_line('u = TOP();')
846 | context.insert_line('v = __pypperoni_IMPL_get_anext(u);')
847 | context.insert_line('if (v == NULL) {')
848 | context.insert_handle_error(line, label)
849 | context.insert_line('}')
850 | context.insert_line('PUSH(v);')
851 | context.end_block()
852 |
853 | elif op == GET_ITER:
854 | context.begin_block()
855 | context.insert_line('u = TOP();')
856 | context.insert_line('v = PyObject_GetIter(u);')
857 | context.insert_line('if (v == NULL) {')
858 | context.insert_handle_error(line, label)
859 | context.insert_line('}')
860 | context.insert_line('Py_DECREF(u);')
861 | context.insert_line('SET_TOP(v);')
862 | context.end_block()
863 |
864 | elif op == FOR_ITER:
865 | context.begin_block()
866 | context.insert_line('u = TOP();')
867 | context.insert_line('x = (*u->ob_type->tp_iternext)(u);')
868 |
869 | context.insert_line('if (x == NULL)')
870 | context.begin_block()
871 |
872 | context.insert_line('if (PyErr_Occurred())')
873 | context.begin_block()
874 | context.insert_line('if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {')
875 | context.insert_handle_error(line, label)
876 | context.insert_line('}')
877 | context.insert_line('PyErr_Clear();')
878 | context.end_block()
879 |
880 | context.insert_line('Py_DECREF(u);')
881 | context.insert_line('STACKADJ(-1);')
882 | context.insert_line('goto label_%d;' % (label + oparg + 2))
883 |
884 | context.end_block()
885 |
886 | context.insert_line('PUSH(x);')
887 |
888 | context.end_block()
889 |
890 | elif op == UNPACK_SEQUENCE:
891 | context.begin_block()
892 | context.insert_line('u = POP();')
893 | context.insert_line('err = __pypperoni_IMPL_unpack_sequence(u, &stack_pointer, %d);' % oparg)
894 | context.insert_line('if (err != 0) {')
895 | context.insert_handle_error(line, label)
896 | context.insert_line('}')
897 | context.end_block()
898 |
899 | elif op == UNPACK_EX:
900 | context.begin_block()
901 | context.insert_line('u = POP();')
902 | context.insert_line('err = __pypperoni_IMPL_unpack_ex(u, &stack_pointer, %d);' % oparg)
903 | context.insert_line('if (err != 0) {')
904 | context.insert_handle_error(line, label)
905 | context.insert_line('}')
906 | context.end_block()
907 |
908 | elif op in (CALL_FUNCTION, CALL_FUNCTION_KW):
909 | context.begin_block()
910 |
911 | if op == CALL_FUNCTION_KW:
912 | context.insert_line('v = POP();')
913 |
914 | else:
915 | context.insert_line('v = NULL;')
916 |
917 | context.insert_line('u = __pypperoni_IMPL_call_func(&stack_pointer, %d, v);' % oparg)
918 |
919 | if op == CALL_FUNCTION_KW:
920 | context.insert_line('Py_DECREF(v);')
921 |
922 | context.insert_line('if (u == NULL) {')
923 | context.insert_handle_error(line, label)
924 | context.insert_line('}')
925 | context.insert_line('PUSH(u);')
926 | context.end_block()
927 |
928 | elif op == CALL_FUNCTION_EX:
929 | context.begin_block()
930 |
931 | if oparg & 0x01:
932 | context.insert_line('w = POP(); /* kwargs */')
933 | context.insert_line('w = __pypperoni_IMPL_ensure_kwdict(w, SECOND());')
934 | context.insert_line('if (w == NULL) {')
935 | context.insert_handle_error(line, label)
936 | context.insert_line('}')
937 |
938 | else:
939 | context.insert_line('w = NULL;')
940 |
941 | context.insert_line('v = POP(); /* callargs */')
942 | context.insert_line('v = __pypperoni_IMPL_ensure_args_iterable(v, TOP());')
943 | context.insert_line('if (v == NULL) {')
944 | if oparg & 0x01:
945 | context.insert_line('Py_DECREF(w);')
946 | context.insert_handle_error(line, label)
947 | context.insert_line('}')
948 |
949 | context.insert_line('x = TOP(); /* func */')
950 |
951 | context.insert_line('if (PyCFunction_Check(x)) u = PyCFunction_Call(x, v, w);')
952 | context.insert_line('else u = PyObject_Call(x, v, w);')
953 | if oparg & 0x01:
954 | context.insert_line('Py_DECREF(w);')
955 | context.insert_line('Py_DECREF(v);')
956 | context.insert_line('Py_DECREF(x);')
957 | context.insert_line('SET_TOP(u);')
958 | context.insert_line('if (u == NULL) {')
959 | context.insert_handle_error(line, label)
960 | context.insert_line('}')
961 | context.end_block()
962 |
963 | elif opname[op].startswith('UNARY_'):
964 | opstr = opname[op].lower()
965 | context.begin_block()
966 | context.insert_line('v = TOP();')
967 | context.insert_line('err = __pypperoni_IMPL_%s(v, &x);' % opstr)
968 | context.insert_line('Py_DECREF(v);')
969 | context.insert_line('if (err != 0) {')
970 | context.insert_line('STACKADJ(-1);')
971 | context.insert_handle_error(line, label)
972 | context.insert_line('}')
973 | context.insert_line('SET_TOP(x);')
974 | context.end_block()
975 |
976 | elif opname[op].startswith('BINARY_'):
977 | opstr = opname[op].lower()
978 | context.begin_block()
979 | context.insert_line('w = POP();')
980 | context.insert_line('v = TOP();')
981 | context.insert_line('err = __pypperoni_IMPL_%s(v, w, &x);' % opstr)
982 | context.insert_line('Py_DECREF(v);')
983 | context.insert_line('Py_DECREF(w);')
984 | context.insert_line('if (err != 0) {')
985 | context.insert_line('STACKADJ(-1);')
986 | context.insert_handle_error(line, label)
987 | context.insert_line('}')
988 | context.insert_line('SET_TOP(x);')
989 | context.end_block()
990 |
991 | elif opname[op].startswith('INPLACE_'):
992 | opstr = opname[op].lower()
993 | context.begin_block()
994 | context.insert_line('w = POP();')
995 | context.insert_line('v = TOP();')
996 | context.insert_line('err = __pypperoni_IMPL_%s(v, w, &x);' % opstr)
997 | context.insert_line('Py_DECREF(v);')
998 | context.insert_line('Py_DECREF(w);')
999 | context.insert_line('if (err != 0) {')
1000 | context.insert_handle_error(line, label)
1001 | context.insert_line('}')
1002 | context.insert_line('SET_TOP(x);')
1003 | context.end_block()
1004 |
1005 | elif op == MAKE_FUNCTION:
1006 | context.add_decl_once('codeobj', 'PyCodeObject*', None, False)
1007 | context.add_decl_once('func', 'PyFunctionObject*', None, False)
1008 | context.begin_block()
1009 |
1010 | context.insert_line('u = POP(); /* qualname */')
1011 |
1012 | funccode = context.codeobjs.pop()
1013 | funccode.co_path = '%s_%d' % (codeobj.get_full_name(), label)
1014 |
1015 | context.insert_line('tmp = PyBytes_FromString("");')
1016 | context.insert_line('codeobj = PyCode_New(')
1017 | context.insert_line(' %d, /* argcount */' % funccode.co_argcount)
1018 | context.insert_line(' %d, /* kwonlyargcount */' % funccode.co_kwonlyargcount)
1019 | context.insert_line(' %d, /* nlocals */' % funccode.co_nlocals)
1020 | context.insert_line(' %d, /* stacksize */' % funccode.co_stacksize)
1021 | context.insert_line(' %d, /* flags */' % funccode.co_flags)
1022 | context.insert_line(' NULL, /* code */')
1023 | context.insert_line(' NULL, /* consts */')
1024 | context.insert_line(' NULL, /* names */')
1025 | context.insert_line(' %s, /* varnames */' % context.register_const(funccode.co_varnames))
1026 | context.insert_line(' %s, /* freevars */' % context.register_const(funccode.co_freevars))
1027 | context.insert_line(' %s, /* cellvars */' % context.register_const(funccode.co_cellvars))
1028 | context.insert_line(' %s, /* filename */' % context.register_const(self.name))
1029 | context.insert_line(' %s, /* name */' % context.register_const(funccode.co_name))
1030 | context.insert_line(' %d, /* firstlineno */' % funccode.co_firstlineno)
1031 | context.insert_line(' tmp /* lnotab */')
1032 | context.insert_line(');')
1033 | context.insert_line('if (codeobj == NULL) {')
1034 | context.insert_line('Py_DECREF(u);')
1035 | context.insert_handle_error(line, label)
1036 | context.insert_line('}')
1037 | context.insert_line('func = (PyFunctionObject*) PyFunction_NewWithQualName'
1038 | '((PyObject*)codeobj, f->f_globals, u);')
1039 | context.insert_line('Py_DECREF(u);')
1040 | context.insert_line('if (func == NULL) {')
1041 | context.insert_handle_error(line, label)
1042 | context.insert_line('}')
1043 |
1044 | if oparg & 0x08:
1045 | context.insert_line('func->func_closure = POP();')
1046 |
1047 | if oparg & 0x04:
1048 | context.insert_line('func->func_annotations = POP();')
1049 |
1050 | if oparg & 0x02:
1051 | context.insert_line('func->func_kwdefaults = POP();')
1052 |
1053 | if oparg & 0x01:
1054 | context.insert_line('func->func_defaults = POP();')
1055 |
1056 | funcname = ('_%s_%s__' % (self.name, funccode.get_signature(label)))
1057 | funcname = funcname.replace('.', '_')
1058 | funcname = funcname.replace('<', '')
1059 | funcname = funcname.replace('>', '')
1060 | self.__gen_code(context.file, funcname, context.modules, funccode,
1061 | context._consts)
1062 |
1063 | context.insert_line('codeobj->co_meth_ptr = &%s;' % funcname)
1064 |
1065 | context.insert_line('PUSH((PyObject*)func);')
1066 | context.end_block()
1067 |
1068 | elif op in (SETUP_LOOP, SETUP_EXCEPT, SETUP_FINALLY):
1069 | context.begin_block()
1070 | context.insert_line('void* __addr;')
1071 | context.insert_get_address(label + oparg + 2)
1072 | context.insert_line('PyFrame_BlockSetup(f, %d, __addr, STACK_LEVEL());' % op)
1073 | context.end_block()
1074 |
1075 | elif op == RAISE_VARARGS:
1076 | context.begin_block()
1077 | context.insert_line('u = NULL;')
1078 | context.insert_line('v = NULL;')
1079 |
1080 | if oparg >= 2:
1081 | context.insert_line('v = POP();')
1082 |
1083 | if oparg >= 1:
1084 | context.insert_line('u = POP();')
1085 |
1086 | context.insert_line('if (__pypperoni_IMPL_do_raise(u, v) == 0) {')
1087 | context.insert_line(' *why = WHY_EXCEPTION; goto fast_block_end;')
1088 | context.insert_line('}')
1089 | context.insert_handle_error(line, label)
1090 | context.end_block()
1091 |
1092 | elif op == YIELD_VALUE:
1093 | context.begin_block()
1094 | context.insert_line('retval = POP();')
1095 |
1096 | if codeobj.co_flags & CO_ASYNC_GENERATOR:
1097 | context.insert_line('w = _PyAsyncGenValueWrapperNew(retval);')
1098 | context.insert_line('Py_DECREF(retval);')
1099 | context.insert_line('if (w == NULL) {')
1100 | context.insert_line('retval = NULL;')
1101 | context.insert_handle_error(line, label)
1102 | context.insert_line('}')
1103 | context.insert_line('retval = w;')
1104 |
1105 | context.insert_yield(line, label + 2)
1106 | context.end_block()
1107 |
1108 | elif op == RETURN_VALUE:
1109 | context.begin_block()
1110 | context.insert_line('retval = POP();')
1111 | context.insert_line('*why = WHY_RETURN; goto fast_block_end;')
1112 | context.end_block()
1113 |
1114 | elif op == CONTINUE_LOOP:
1115 | context.begin_block()
1116 | context.insert_line('void* __addr;')
1117 | context.insert_get_address(oparg)
1118 | context.insert_line('retval = PyLong_FromSsize_t((Py_ssize_t)__addr);')
1119 | context.insert_line('*why = WHY_CONTINUE; goto fast_block_end;')
1120 | context.end_block()
1121 |
1122 | elif op == BREAK_LOOP:
1123 | context.begin_block()
1124 | context.insert_line('*why = WHY_BREAK; goto fast_block_end;')
1125 | context.end_block()
1126 |
1127 | elif op == POP_BLOCK:
1128 | context.add_decl_once('block', 'PyTryBlock*', None, False)
1129 | context.begin_block()
1130 | context.insert_line('block = PyFrame_BlockPop(f);')
1131 | context.insert_line('UNWIND_BLOCK(block)')
1132 | context.end_block()
1133 |
1134 | elif op == POP_EXCEPT:
1135 | context.add_decl_once('block', 'PyTryBlock*', None, False)
1136 | context.begin_block()
1137 | context.insert_line('block = PyFrame_BlockPop(f);')
1138 | context.insert_line('UNWIND_EXCEPT_HANDLER(block);')
1139 | context.end_block()
1140 |
1141 | elif op == END_FINALLY:
1142 | context.add_decl_once('block', 'PyTryBlock*', None, False)
1143 | context.begin_block()
1144 | context.insert_line('x = POP(); /* status */')
1145 |
1146 | context.insert_line('if PyLong_Check(x)')
1147 | context.begin_block()
1148 | context.insert_line('*why = PyLong_AS_LONG(x);')
1149 | context.insert_line('if (*why == WHY_RETURN || *why == WHY_CONTINUE)')
1150 | context.insert_line(' retval = POP();')
1151 | context.insert_line('if (*why == WHY_SILENCED)')
1152 | context.begin_block()
1153 | context.insert_line('block = PyFrame_BlockPop(f);')
1154 | context.insert_line('UNWIND_EXCEPT_HANDLER(block);')
1155 | context.insert_line('*why = WHY_NOT;')
1156 | context.end_block()
1157 | context.insert_line('else')
1158 | context.begin_block()
1159 | context.insert_line('Py_DECREF(x);')
1160 | context.insert_line('goto fast_block_end;')
1161 | context.end_block()
1162 | context.end_block()
1163 |
1164 | context.insert_line('else if PyExceptionClass_Check(x)')
1165 | context.begin_block()
1166 | context.insert_line('exc = POP(); tb = POP();')
1167 | context.insert_line('PyErr_Restore(x, exc, tb);')
1168 | context.insert_line('*why = WHY_EXCEPTION; goto fast_block_end;')
1169 | context.end_block()
1170 |
1171 | context.insert_line('Py_DECREF(x);')
1172 |
1173 | context.end_block()
1174 |
1175 | elif op == SETUP_WITH:
1176 | context.begin_block()
1177 | context.insert_line('w = TOP();')
1178 | context.insert_line('err = __pypperoni_IMPL_setup_with(w, &u, &x);')
1179 | context.insert_line('Py_DECREF(w);')
1180 | context.insert_line('SET_TOP(u);') # exitptr
1181 | context.insert_line('if (err != 0) {')
1182 | context.insert_handle_error(line, label)
1183 | context.insert_line('}')
1184 | context.insert_line('void* __addr;')
1185 | context.insert_get_address(label + oparg + 2)
1186 | context.insert_line('PyFrame_BlockSetup(f, SETUP_FINALLY, __addr, STACK_LEVEL());')
1187 | context.insert_line('PUSH(x);')
1188 | context.end_block()
1189 |
1190 | elif op == WITH_CLEANUP_START:
1191 | context.add_decl_once('block', 'PyTryBlock*', None, False)
1192 | context.begin_block()
1193 | context.insert_line('exc = TOP();')
1194 | context.insert_line('val = Py_None;')
1195 | context.insert_line('tb = Py_None;')
1196 |
1197 | context.insert_line('if (exc == Py_None)')
1198 | context.begin_block()
1199 | context.insert_line('POP();')
1200 | context.insert_line('x = TOP(); /* exit_func */')
1201 | context.insert_line('SET_TOP(exc);')
1202 | context.end_block()
1203 |
1204 | context.insert_line('else if (PyLong_Check(exc))')
1205 | context.begin_block()
1206 | context.insert_line('STACKADJ(-1);')
1207 | context.insert_line('switch (PyLong_AS_LONG(exc))')
1208 | context.begin_block()
1209 | context.insert_line('case WHY_RETURN:')
1210 | context.insert_line('case WHY_CONTINUE:')
1211 | context.insert_line(' x = SECOND(); /* exit_func */')
1212 | context.insert_line(' SET_SECOND(TOP());')
1213 | context.insert_line(' SET_TOP(exc);')
1214 | context.insert_line(' break;')
1215 | context.insert_line('default:')
1216 | context.insert_line(' x = TOP();')
1217 | context.insert_line(' SET_TOP(exc);')
1218 | context.insert_line(' break;')
1219 | context.end_block()
1220 | context.insert_line('exc = Py_None;')
1221 | context.end_block()
1222 |
1223 | context.insert_line('else')
1224 | context.begin_block()
1225 | context.insert_line('val = SECOND();')
1226 | context.insert_line('tb = THIRD();')
1227 | context.insert_line('u = FOURTH(); /* tp2 */')
1228 | context.insert_line('v = PEEK(5); /* exc2 */')
1229 | context.insert_line('w = PEEK(6); /* tb2 */')
1230 | context.insert_line('x = PEEK(7); /* exit_func */')
1231 | context.insert_line('SET_VALUE(7, w);')
1232 | context.insert_line('SET_VALUE(6, v);')
1233 | context.insert_line('SET_VALUE(5, u);')
1234 | context.insert_line('SET_FOURTH(NULL);')
1235 | context.insert_line('block = &f->f_blockstack[f->f_iblock - 1];')
1236 | context.insert_line('block->b_level--;')
1237 | context.end_block()
1238 |
1239 | context.insert_line('tmp = PyObject_CallFunctionObjArgs(x, exc, val, tb, NULL);')
1240 | context.insert_line('Py_DECREF(x);')
1241 | context.insert_line('if (tmp == NULL) {')
1242 | context.insert_handle_error(line, label)
1243 | context.insert_line('}')
1244 | context.insert_line('Py_INCREF(exc);')
1245 | context.insert_line('PUSH(exc);')
1246 | context.insert_line('PUSH(tmp);')
1247 |
1248 | context.end_block()
1249 |
1250 | elif op == WITH_CLEANUP_FINISH:
1251 | context.begin_block()
1252 | context.insert_line('x = POP();')
1253 | context.insert_line('exc = POP();')
1254 | context.insert_line('err = (exc != Py_None) ? PyObject_IsTrue(x) : 0;')
1255 | context.insert_line('Py_DECREF(x);')
1256 | context.insert_line('Py_DECREF(exc);')
1257 | context.insert_line('if (err < 0) {')
1258 | context.insert_handle_error(line, label)
1259 | context.insert_line('}')
1260 | context.insert_line('else if (err > 0)')
1261 | context.begin_block()
1262 | context.insert_line('err = 0;')
1263 | context.insert_line('PUSH(PyLong_FromLong((long) WHY_SILENCED));')
1264 | context.end_block()
1265 | context.end_block()
1266 |
1267 | elif op == GET_YIELD_FROM_ITER:
1268 | context.begin_block()
1269 | context.insert_line('x = TOP(); /* iterable */')
1270 |
1271 | context.insert_line('if (PyCoro_CheckExact(x))')
1272 | context.begin_block()
1273 | if not codeobj.co_flags & (CO_COROUTINE | CO_ITERABLE_COROUTINE):
1274 | context.insert_line('Py_DECREF(x);')
1275 | context.insert_line('SET_TOP(NULL);')
1276 | context.insert_line('PyErr_SetString(PyExc_TypeError,')
1277 | context.insert_line(' "cannot \'yield from\' a coroutine object "')
1278 | context.insert_line(' "in a non-coroutine generator");')
1279 | context.insert_handle_error(line, label)
1280 | context.end_block()
1281 |
1282 | context.insert_line('else if (!PyGen_CheckExact(x))')
1283 | context.begin_block()
1284 | context.insert_line('u = PyObject_GetIter(x);')
1285 | context.insert_line('Py_DECREF(x);')
1286 | context.insert_line('SET_TOP(u);')
1287 | context.insert_line('if (u == NULL) {')
1288 | context.insert_handle_error(line, label)
1289 | context.insert_line('}')
1290 | context.end_block()
1291 |
1292 | context.end_block()
1293 |
1294 | elif op == YIELD_FROM:
1295 | context.begin_block()
1296 | context.insert_line('v = POP();')
1297 | context.insert_line('x = TOP(); /* receiver */')
1298 |
1299 | context.insert_line('if (PyGen_CheckExact(x) || PyCoro_CheckExact(x))')
1300 | context.begin_block()
1301 | context.insert_line('retval = _PyGen_Send((PyGenObject *)x, v);')
1302 | context.end_block()
1303 |
1304 | context.insert_line('else')
1305 | context.begin_block()
1306 | context.insert_line('_Py_IDENTIFIER(send);')
1307 | context.insert_line('if (v == Py_None) retval = Py_TYPE(x)->tp_iternext(x);')
1308 | context.insert_line('else retval = _PyObject_CallMethodIdObjArgs(x, &PyId_send, v, NULL);')
1309 | context.end_block()
1310 |
1311 | context.insert_line('Py_DECREF(v);')
1312 | context.insert_line('if (retval == NULL)')
1313 | context.begin_block()
1314 | context.insert_line('err = _PyGen_FetchStopIterationValue(&val);')
1315 | context.insert_line('if (err < 0) {')
1316 | context.insert_handle_error(line, label)
1317 | context.insert_line('}')
1318 | context.insert_line('Py_DECREF(x);')
1319 | context.insert_line('SET_TOP(val);')
1320 | context.end_block()
1321 |
1322 | context.insert_line('else')
1323 | context.begin_block()
1324 | context.insert_yield(line, label)
1325 | context.end_block()
1326 |
1327 | context.end_block()
1328 |
1329 | elif op == FORMAT_VALUE:
1330 | context.begin_block()
1331 |
1332 | if (oparg & FVS_MASK) == FVS_HAVE_SPEC:
1333 | context.insert_line('x = POP(); /* fmt_spec */')
1334 |
1335 | else:
1336 | context.insert_line('x = NULL; /* fmt_spec */')
1337 |
1338 | context.insert_line('v = POP();')
1339 |
1340 | conv_fn = {
1341 | FVC_STR: 'PyObject_Str',
1342 | FVC_REPR: 'PyObject_Repr',
1343 | FVC_ASCII: 'PyObject_ASCII'
1344 | }.get(oparg & FVC_MASK)
1345 | if conv_fn:
1346 | context.insert_line('u = %s(v);' % conv_fn)
1347 | context.insert_line('Py_DECREF(v);')
1348 | context.insert_line('if (u == NULL) {')
1349 | context.insert_line('Py_XDECREF(x);')
1350 | context.insert_handle_error(line, label)
1351 | context.insert_line('}')
1352 | context.insert_line('v = u;')
1353 |
1354 | context.insert_line('if (PyUnicode_CheckExact(v) && x == NULL) u = v;')
1355 | context.insert_line('else')
1356 | context.begin_block()
1357 | context.insert_line('u = PyObject_Format(v, x);')
1358 | context.insert_line('Py_DECREF(v);')
1359 | context.insert_line('Py_XDECREF(x);')
1360 | context.insert_line('if (u == NULL) {')
1361 | context.insert_handle_error(line, label)
1362 | context.insert_line('}')
1363 | context.end_block()
1364 | context.insert_line('PUSH(u);')
1365 | context.end_block()
1366 |
1367 | else:
1368 | context.codebuffer.seek(0)
1369 | with Lock():
1370 | safePrint(context.codebuffer.read())
1371 | dis.disassemble(codeobj)
1372 | raise ValueError('%d (%s) @ %s/%s/%d' % (op, opname[op], self.name,
1373 | codeobj.get_full_name(),
1374 | label))
1375 |
1376 | def __gen_code(self, f, name, modules, codeobj, consts, flushconsts=False):
1377 | buf = list(codeobj.read_code())
1378 | chunki = 0
1379 | chunks = list(self.__split_buf(buf, codeobj))
1380 |
1381 | f.add_common_header('PyObject* %s(PyFrameObject* f);' % name)
1382 |
1383 | if len(chunks) > 1:
1384 | context = self.__handle_chunks(chunks, f, name, modules, codeobj, consts,)
1385 |
1386 | else:
1387 | context = self.__handle_chunk(chunks[0], f, name, modules, codeobj, consts, [])
1388 | context.finish(False)
1389 |
1390 | if flushconsts:
1391 | context.flushconsts()
1392 |
1393 | def __handle_chunk(self, chunk, f, chunkname, modules, codeobj, consts, codeobjs):
1394 | '''
1395 | Handles a single chunk of code and returns a Context object.
1396 | '''
1397 | context = self.get_context(f, chunkname, modules,
1398 | codeobj.co_flags,
1399 | codeobj.co_nlocals)
1400 | context._consts = consts
1401 | context.codeobjs = codeobjs
1402 |
1403 | context.buf = tuple(chunk)
1404 | context.i = 0
1405 | while context.i < len(context.buf):
1406 | label, op, oparg, line = context.buf[context.i]
1407 | context.i += 1
1408 |
1409 | self.__handle_one_instr(codeobj, context, label, op, oparg, line)
1410 |
1411 | return context
1412 |
1413 | def __handle_chunks(self, chunks, f, name, modules, codeobj, consts):
1414 | '''
1415 | Handles and encapsulates multiple chunks of code.
1416 | '''
1417 | codeobjs = []
1418 | chunki = 0
1419 | for chunk in chunks:
1420 | chunki += 1
1421 | chunkname = '%s_%d' % (name, chunki)
1422 | context = self.__handle_chunk(chunk, f, chunkname, modules,
1423 | codeobj, consts, codeobjs)
1424 | context.finish(True)
1425 | codeobjs = context.codeobjs
1426 |
1427 | f.write('\nPyObject* %s(PyFrameObject* f) {\n' % name)
1428 | f.write(' PyObject* retval = NULL;\n')
1429 | f.write(' int why;\n\n')
1430 | f.write(' __%s_load_consts();\n' % f.uid)
1431 | for i in range(1, chunki + 1):
1432 | chunkname = '%s_%d' % (name, i)
1433 | f.write(' {\n')
1434 | f.write(' retval = %s(f, &why);\n' % chunkname)
1435 | f.write(' if (why == WHY_EXCEPTION) goto error;\n')
1436 | f.write(' else if (why == WHY_YIELD) goto end;\n')
1437 | f.write(' else if (retval != NULL) goto clear_stack;\n')
1438 | f.write(' }\n')
1439 | f.write(' goto clear_stack;\n')
1440 | f.write(' error:\n')
1441 | f.write(' PyTraceBack_Here(f);\n')
1442 | f.write(' clear_stack: /* Clear stack */\n')
1443 | f.write(' {\n')
1444 | f.write(' PyObject** stack_pointer = f->f_stacktop;\n')
1445 | f.write(' while (STACK_LEVEL() > 0) {\n')
1446 | f.write(' Py_XDECREF(TOP());\n')
1447 | f.write(' STACKADJ(-1);\n')
1448 | f.write(' }\n')
1449 | f.write(' f->f_stacktop = NULL;\n')
1450 | f.write(' }\n')
1451 | f.write(' end:\n')
1452 | f.write(' return _Py_CheckFunctionResult(NULL, retval, "%s");\n' % name)
1453 | f.write('}\n\n')
1454 |
1455 | return context
1456 |
1457 | def get_context(self, f, name, modules, flags, nlocals):
1458 | return Context(f, name, modules, flags, nlocals)
1459 |
1460 | def __split_buf(self, buf, codeobj):
1461 | if codeobj.co_flags & CO_GENERATOR:
1462 | # No splitting generators
1463 | yield buf
1464 | return
1465 |
1466 | split_interval = SPLIT_INTERVAL
1467 | yield_at = split_interval
1468 | _cur = []
1469 |
1470 | for i, instr in enumerate(buf):
1471 | if instr[IDX_LABEL] >= yield_at and len(_cur) >= split_interval:
1472 | yield _cur
1473 | _cur = []
1474 | yield_at = instr[IDX_LABEL] + split_interval
1475 |
1476 | _cur.append(instr)
1477 | if instr[IDX_OP] in hasjrel:
1478 | yield_at = max(yield_at, instr[IDX_LABEL] + instr[IDX_OPARG] + 4)
1479 |
1480 | elif instr[IDX_OP] in hasjabs:
1481 | yield_at = max(yield_at, instr[IDX_OPARG] + 1)
1482 |
1483 | elif instr[IDX_OP] == LOAD_CONST:
1484 | if len(buf) > i + 2 and buf[i + 2][IDX_OP] == IMPORT_NAME:
1485 | # Skip until next line:
1486 | import_instr_size = 0
1487 | while buf[i + import_instr_size][IDX_LINE] == instr[IDX_LINE]:
1488 | import_instr_size += 1
1489 | if i + import_instr_size >= len(buf):
1490 | import_instr_size -= 1
1491 | break
1492 |
1493 | yield_at = max(yield_at, buf[i + import_instr_size][IDX_LABEL])
1494 |
1495 | if _cur:
1496 | yield _cur
1497 |
1498 | def generate_c_code(self, f, modules):
1499 | self.code = self.get_code()
1500 | modname = '_%s_MODULE__' % self.name.replace('.', '_')
1501 | self.__gen_code(f, modname, modules, self.code, [], True)
1502 |
1503 | def get_code(self):
1504 | return CodeObject(compile(self.astmod, self.name, 'exec', optimize=2))
1505 |
1506 | def __handle_import(self, codeobj, context, level):
1507 | # Get fromlist
1508 | fromlist = codeobj.co_consts[context.buf[context.i][IDX_OPARG]]
1509 | context.i += 1
1510 |
1511 | # Get import_name
1512 | orig_name = codeobj.co_names[context.buf[context.i][IDX_OPARG]]
1513 | context.i += 1
1514 |
1515 | orig_name = self.__convert_relative_import(orig_name, level)
1516 | import_name = self.__lookup_import(orig_name, context.modules)
1517 | mod = context.modules[import_name]
1518 | label = context.buf[context.i][IDX_LABEL]
1519 | line = context.buf[context.i][IDX_LINE]
1520 |
1521 | context.begin_block()
1522 |
1523 | if not fromlist:
1524 | # Case 1: Import and store
1525 | import_handled = False
1526 | if import_name == orig_name and '.' in orig_name:
1527 | module, tail_list = import_name.split('.', 1)
1528 | if module in context.modules:
1529 | # Special case: "import ." (eg. "import os.path")
1530 | # First, import , which is what actually gets stored (x)
1531 | # unless this is "imported as" (ie. "import module.submodule as M")
1532 | # In that case, the import will be followed by LOAD_ATTR
1533 | store_tail = False
1534 | while context.buf[context.i][IDX_OP] == LOAD_ATTR:
1535 | store_tail = True
1536 | context.i += 1
1537 |
1538 | tail_list = tail_list.split('.')
1539 |
1540 | rootmod = context.modules[module]
1541 | context.insert_line('w = x = __pypperoni_IMPL_import((uint64_t)%dU);'
1542 | ' /* %s */' % (rootmod.get_id(), rootmod.name))
1543 | context.insert_line('if (x == NULL) {')
1544 | context.insert_handle_error(line, label)
1545 | context.insert_line('}')
1546 | context.insert_line('Py_INCREF(x);')
1547 |
1548 | modname = module + '.'
1549 | while tail_list:
1550 | # Now import and setattr
1551 | tail = tail_list.pop(0)
1552 | modname += tail + '.'
1553 | mod = context.modules[modname[:-1]]
1554 | context.insert_line('u = __pypperoni_IMPL_import((uint64_t)%dU);'
1555 | ' /* %s */' % (mod.get_id(), modname[:-1]))
1556 | context.insert_line('if (u == NULL) {')
1557 | context.insert_line('Py_DECREF(x);')
1558 | context.insert_line('Py_DECREF(w);')
1559 | context.insert_handle_error(line, label)
1560 | context.insert_line('}')
1561 | context.insert_line('PyObject_SetAttr(x, %s, u);' %
1562 | context.register_const(tail))
1563 | context.insert_line('Py_DECREF(x);')
1564 | context.insert_line('x = u;')
1565 |
1566 | if store_tail:
1567 | context.insert_line('Py_DECREF(w);')
1568 |
1569 | else:
1570 | context.insert_line('Py_DECREF(x);')
1571 | context.insert_line('x = w;')
1572 |
1573 | import_handled = True
1574 |
1575 | if not import_handled:
1576 | context.insert_line('x = __pypperoni_IMPL_import((uint64_t)%dU);'
1577 | ' /* %s */' % (mod.get_id(), mod.name))
1578 | context.insert_line('if (x == NULL) {')
1579 | context.insert_handle_error(line, label)
1580 | context.insert_line('}')
1581 |
1582 | context.insert_line('PUSH(x);')
1583 |
1584 | # Let __handle_one_instr handle STORE_*
1585 |
1586 | elif fromlist == ('*',):
1587 | # Case 2: Import all
1588 | context.insert_line('x = __pypperoni_IMPL_import((uint64_t)%dU);'
1589 | ' /* %s */' % (mod.get_id(), mod.name))
1590 | context.insert_line('if (x == NULL) {')
1591 | context.insert_handle_error(line, label)
1592 | context.insert_line('}')
1593 | context.insert_line('err = __pypperoni_IMPL_import_star(f, x);')
1594 | context.insert_line('Py_DECREF(x);')
1595 | context.insert_line('if (err != 0) {')
1596 | context.insert_handle_error(line, label)
1597 | context.insert_line('}')
1598 |
1599 | context.i += 1
1600 |
1601 | else:
1602 | # Case 3: Importing N names
1603 | context.add_decl_once('mod', 'PyObject*', 'NULL', False)
1604 | context.insert_line('mod = __pypperoni_IMPL_import((uint64_t)%dU);'
1605 | ' /* %s */' % (mod.get_id(), mod.name))
1606 | context.insert_line('if (mod == NULL) {')
1607 | context.insert_handle_error(line, label)
1608 | context.insert_line('}')
1609 |
1610 | for i in range(len(fromlist)):
1611 | label, op, oparg, line = context.buf[context.i]
1612 | context.i += 1
1613 |
1614 | name = codeobj.co_names[oparg]
1615 | fullname = mod.name + '.' + name
1616 | fullname = self.__lookup_import(fullname, context.modules,
1617 | can_be_external=False)
1618 | if fullname in context.modules:
1619 | # We're either importing a name or a module
1620 | _mod = context.modules[fullname]
1621 | context.insert_line('v = __pypperoni_IMPL_import_from_or_module'
1622 | '(mod, %s, (uint64_t)%dU); /* %s */' %
1623 | (context.register_const(name), _mod.get_id(), _mod.name))
1624 |
1625 | else:
1626 | # IMPORT_FROM
1627 | context.insert_line('v = __pypperoni_IMPL_import_from(mod, %s);' %
1628 | context.register_literal(name))
1629 |
1630 | context.insert_line('if (v == NULL) {')
1631 | context.insert_line('Py_DECREF(mod);')
1632 | context.insert_handle_error(line, label)
1633 | context.insert_line('}')
1634 | context.insert_line('PUSH(v);')
1635 |
1636 | storelabel, storeop, storeoparg, storeline = context.buf[context.i]
1637 | context.i += 1
1638 |
1639 | self.__handle_one_instr(codeobj, context, storelabel,
1640 | storeop, storeoparg, storeline)
1641 |
1642 | context.insert_line('Py_DECREF(mod);')
1643 | context.i += 1
1644 |
1645 | context.end_block()
1646 |
1647 | def __convert_relative_import(self, name, level):
1648 | '''
1649 | Converts relative to absolute imports.
1650 | '''
1651 | if level > 0:
1652 | if self.is_package():
1653 | level -= 1
1654 |
1655 | if self.name.count('.') < (level - 1):
1656 | raise ImportError('bogus relative import: %s %s (%d)' % (self.name, name, level))
1657 |
1658 | prefix = self.name.rsplit('.', level)[0]
1659 | if name:
1660 | # from [...]name import
1661 | name = prefix + '.' + name
1662 |
1663 | else:
1664 | # from [...] import
1665 | name = prefix
1666 |
1667 | return name
1668 |
1669 | def resolve_import_from_name(self, modules, name, level=0, can_be_external=True):
1670 | '''
1671 | Resolves a module from name and level. This returns None
1672 | if the module doesn't exist.
1673 | '''
1674 | name = self.__convert_relative_import(name, level)
1675 | name = self.__lookup_import(name, modules, can_be_external)
1676 | if not name:
1677 | return None
1678 |
1679 | return modules[name]
1680 |
1681 | def resolve_imports_from_node(self, modules, node):
1682 | '''
1683 | Yields a list of module parsed from an AST node.
1684 | 'node' must be of type Import or ImportFrom.
1685 | '''
1686 | if isinstance(node, ast.ImportFrom):
1687 | module = self.__convert_relative_import(node.module, node.level)
1688 | mod = self.resolve_import_from_name(modules, module)
1689 | yield mod
1690 |
1691 | for alias in node.names:
1692 | name = module + '.' + alias.name
1693 | mod = self.resolve_import_from_name(modules, name, can_be_external=False)
1694 | if mod:
1695 | yield mod
1696 |
1697 | else:
1698 | for alias in node.names:
1699 | mod = self.resolve_import_from_name(modules, alias.name)
1700 | yield mod
1701 |
1702 | def __lookup_import(self, name, modules, can_be_external=True):
1703 | '''
1704 | Lookups modules dict to find a module by name.
1705 | This will either return a name that is guaranteed
1706 | to exist in modules or None. If can_be_external is
1707 | True, it will register a builtin or external module
1708 | as required (in which case it will never return None).
1709 | '''
1710 | if name in IMPORT_ALIASES:
1711 | name = IMPORT_ALIASES[name]
1712 |
1713 | # Usually, it's in modules
1714 | if name in modules:
1715 | return name
1716 |
1717 | # Builtin?
1718 | if can_be_external:
1719 | try:
1720 | __import__(name)
1721 |
1722 | # Register it:
1723 | modules[name] = BuiltinModule(name)
1724 | return name
1725 |
1726 | except ImportError:
1727 | # OK, treat it as an external module
1728 | safePrint('Found unknown module %s imported by %s' % (name, self.name))
1729 | modules[name] = ExternalModule(name)
1730 | return name
1731 |
1732 |
1733 | class PackageModule(Module):
1734 | '''
1735 | Use this for modules that were originally __init__.py files.
1736 | '''
1737 | def is_package(self):
1738 | return True
1739 |
1740 |
1741 | class NullModule(Module):
1742 | '''
1743 | Use this for empty modules.
1744 | '''
1745 |
1746 | def __init__(self, name):
1747 | Module.__init__(self, name, '')
1748 |
1749 |
1750 | class ExternalModule(ModuleBase):
1751 | '''
1752 | This is used to represent a module that could not be found
1753 | at compile time (e.g. nt doesn't exist on Unix systems).
1754 | '''
1755 | def __init__(self, name):
1756 | ModuleBase.__init__(self, name, '')
1757 |
1758 | def is_external(self):
1759 | return True
1760 |
1761 |
1762 | class BuiltinModule(ExternalModule):
1763 | '''
1764 | This is used to represent a builtin module.
1765 | '''
1766 | pass
1767 |
1768 |
1769 | def write_modules_file(f, modules):
1770 | s = ' PypperoniModule* m;\n'
1771 | for i, module in enumerate(modules.values()):
1772 | is_ext = module.is_external()
1773 | parent = module.get_parent(modules)
1774 |
1775 | s += '\n'
1776 | s += ' m = malloc(sizeof(PypperoniModule));\n'
1777 | s += ' m->index = %dL;\n' % module.get_id()
1778 | s += ' m->type = %s;\n' % ('MODULE_BUILTIN' if is_ext else 'MODULE_DEFINED')
1779 | s += ' m->parent = %dL;\n' % (parent.get_id() if parent else -1)
1780 | s += ' m->name = "%s";\n' % module.name
1781 |
1782 | if is_ext:
1783 | s += ' m->stacksize = 0;\n'
1784 | s += ' m->nlocals = 0;\n'
1785 |
1786 | else:
1787 | modname = '_%s_MODULE__' % module.name.replace('.', '_')
1788 | f.write('PyObject* %s(PyFrameObject* f); /* fwd decl */\n' % modname)
1789 | s += ' m->ptr = %s;\n' % modname
1790 | s += ' m->stacksize = %d;\n' % module.code.co_stacksize
1791 | s += ' m->nlocals = %d;\n' % module.code.co_nlocals
1792 |
1793 | s += ' m->obj = NULL;\n'
1794 | s += ' modlist[%d] = m;\n' % i
1795 |
1796 | s += ' modlist[%d] = NULL;' % len(modules)
1797 |
1798 | f.write('\nstatic void get_pypperoni_modules(PypperoniModule*** modlist_ptr)\n')
1799 | f.write('{\n')
1800 | f.write(' static int loaded = 0;\n')
1801 | f.write(' static PypperoniModule *modlist[%d];\n' % (len(modules) + 1))
1802 | f.write(' *modlist_ptr = modlist;\n')
1803 | f.write(' if (loaded) return;\n')
1804 | f.write(' loaded = 1;\n')
1805 | f.write(s)
1806 | f.write('}\n')
1807 |
1808 | f.write('\nstatic PyObject* load_encodings(void)\n')
1809 | f.write('{\n')
1810 | f.write(' PyObject *encodings_mod, *_io_mod, *codecs_index_mod;')
1811 | f.write(' encodings_mod = __pypperoni_IMPL_import(%d);\n' % modules['encodings'].get_id())
1812 | f.write(' if (!encodings_mod) goto error;\n')
1813 | f.write(' _io_mod = PyImport_ImportModule("_io");\n')
1814 | f.write(' if (!_io_mod) goto error;\n')
1815 | f.write(' _PyImport_FixupBuiltin(_io_mod, "_io");\n')
1816 | f.write(' codecs_index_mod = __pypperoni_IMPL_import(%d);\n' % modules['codecs_index'].get_id())
1817 | f.write(' if (!codecs_index_mod) goto error;\n')
1818 | f.write(' return encodings_mod;\n')
1819 | f.write('error:\n')
1820 | f.write(' PyErr_Print();\n')
1821 | f.write(' return NULL;\n')
1822 | f.write('}\n')
1823 |
1824 | f.write('\n')
1825 |
--------------------------------------------------------------------------------
/modulereducer.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from collections import defaultdict
19 | import ast
20 |
21 | TAG_UNSET = object()
22 |
23 |
24 | class ModuleGraph:
25 | def __init__(self):
26 | self.connections = defaultdict(lambda: set())
27 | self.tags = defaultdict(lambda: TAG_UNSET)
28 |
29 | def add_connection(self, a, b):
30 | # a imports b
31 | if a is not b:
32 | self.connections[a.name].add(b)
33 |
34 | def dfs(self, v, tag, level=0):
35 | self.tags[v] = tag
36 | for v in self.connections[v]:
37 | if self.tags[v.name] is TAG_UNSET:
38 | self.dfs(v.name, tag, level + 1)
39 |
40 | def dfs_all(self, modules, tag):
41 | for name, m in modules.items():
42 | if self.tags[name] is TAG_UNSET:
43 | self.dfs(name, tag)
44 |
45 | def get_tag(self, key):
46 | return self.tags[key]
47 |
48 |
49 | class ModuleFinderVisitor(ast.NodeVisitor):
50 | def __init__(self, graph, modules):
51 | self.graph = graph
52 | self.modules = modules
53 |
54 | def visit_Import(self, node):
55 | modlist = self._module.resolve_imports_from_node(self.modules, node)
56 | for m in modlist:
57 | self.graph.add_connection(self._module, m)
58 |
59 | def visit_ImportFrom(self, node):
60 | self.visit_Import(node)
61 |
62 | def visit_module(self, module):
63 | self._module = module
64 | self.visit(module.astmod)
65 | del self._module
66 |
67 |
68 | def reduce_modules(modules):
69 | graph = ModuleGraph()
70 | v = ModuleFinderVisitor(graph, modules)
71 |
72 | modlist = list(modules.values()) # dict size may change
73 | for m in modlist:
74 | v.visit_module(m)
75 |
76 | # DFS main module
77 | for m in modlist:
78 | if m._is_main:
79 | graph.dfs(m.name, True)
80 |
81 | # DFS required modules
82 | graph.dfs('codecs_index', True)
83 | graph.dfs_all(modules, False)
84 |
85 | modlist = list(modules.items())
86 | for name, m in modlist:
87 | if not graph.get_tag(name):
88 | del modules[name]
89 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | /* Copyright (c) Pypperoni
2 |
3 | Pypperoni is licensed under the MIT License; you may
4 | not use it except in compliance with the License.
5 |
6 | You should have received a copy of the License with
7 | this source code under the name "LICENSE.txt". However,
8 | you may obtain a copy of the License on our GitHub here:
9 | https://github.com/Pypperoni/pypperoni
10 |
11 | Unless required by applicable law or agreed to in writing,
12 | software distributed under the License is distributed on an
13 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | either express or implied. See the License for the specific
15 | language governing permissions and limitations under the
16 | License.
17 | */
18 |
19 | #include "pypperoni_impl.h"
20 |
21 | int main(int argc, char* argv[])
22 | {
23 | int ret;
24 |
25 | setup_pypperoni();
26 | ret = __pypperoni_IMPL_main(argc, argv);
27 | if (ret != 0)
28 | {
29 | PyErr_Print();
30 | }
31 |
32 | return ret;
33 | }
34 |
--------------------------------------------------------------------------------
/src/pypperoni_impl.c:
--------------------------------------------------------------------------------
1 | /* Copyright (c) Pypperoni
2 |
3 | Pypperoni is licensed under the MIT License; you may
4 | not use it except in compliance with the License.
5 |
6 | You should have received a copy of the License with
7 | this source code under the name "LICENSE.txt". However,
8 | you may obtain a copy of the License on our GitHub here:
9 | https://github.com/Pypperoni/pypperoni
10 |
11 | Unless required by applicable law or agreed to in writing,
12 | software distributed under the License is distributed on an
13 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | either express or implied. See the License for the specific
15 | language governing permissions and limitations under the
16 | License.
17 | */
18 |
19 | #include "pypperoni_impl.h"
20 |
21 | #include /* PyCmp_* */
22 | #include
23 |
24 | /* Implementations */
25 | PyObject* __pypperoni_IMPL_load_name(PyFrameObject* f, PyObject* name)
26 | {
27 | if (f->f_locals == NULL) {
28 | PyErr_Format(PyExc_SystemError,
29 | "no locals when loading %R",
30 | name);
31 | return NULL;
32 | }
33 |
34 | PyObject* x = NULL;
35 | PyObject* v = f->f_locals;
36 | if (PyDict_CheckExact(v)) {
37 | x = PyDict_GetItem(v, name);
38 | }
39 | else {
40 | x = PyObject_GetItem(v, name);
41 | if (x == NULL && PyErr_Occurred()) {
42 | if (!PyErr_ExceptionMatches(PyExc_KeyError))
43 | {
44 | return NULL;
45 | }
46 | PyErr_Clear();
47 | }
48 | }
49 |
50 | if (x == NULL) {
51 | x = PyDict_GetItem(f->f_globals, name);
52 | if (x == NULL) {
53 | x = PyDict_GetItem(f->f_builtins, name);
54 | if (x == NULL) {
55 | PyErr_Format(PyExc_NameError, "name '%R' is not defined", name);
56 | return NULL;
57 | }
58 | }
59 | }
60 |
61 | return x;
62 | }
63 |
64 | PyObject* __pypperoni_IMPL_load_global(PyFrameObject* f, PyObject* name)
65 | {
66 | PyObject *v;
67 | if (PyDict_CheckExact(f->f_globals)
68 | && PyDict_CheckExact(f->f_builtins))
69 | {
70 | v = _PyDict_LoadGlobal((PyDictObject *)f->f_globals,
71 | (PyDictObject *)f->f_builtins,
72 | name);
73 | if (v == NULL) {
74 | if (!_PyErr_OCCURRED()) {
75 | /* _PyDict_LoadGlobal() returns NULL without raising
76 | * an exception if the key doesn't exist */
77 | PyErr_Format(PyExc_NameError, "name %R is not defined", name);
78 | }
79 | return NULL;
80 | }
81 | Py_INCREF(v);
82 | }
83 | else {
84 | /* Slow-path if globals or builtins is not a dict */
85 |
86 | /* namespace 1: globals */
87 | v = PyObject_GetItem(f->f_globals, name);
88 | if (v == NULL) {
89 | if (!PyErr_ExceptionMatches(PyExc_KeyError))
90 | return NULL;
91 | PyErr_Clear();
92 |
93 | /* namespace 2: builtins */
94 | v = PyObject_GetItem(f->f_builtins, name);
95 | if (v == NULL) {
96 | if (PyErr_ExceptionMatches(PyExc_KeyError))
97 | PyErr_Format(PyExc_NameError, "name '%R' is not defined", name);
98 | return NULL;
99 | }
100 | }
101 | }
102 |
103 | return v;
104 | }
105 |
106 | #define CANNOT_CATCH_MSG "catching classes that do not inherit from "\
107 | "BaseException is not allowed"
108 |
109 | int __pypperoni_IMPL_compare(PyObject* v, PyObject* w, int op, PyObject** result)
110 | {
111 | int res = 0;
112 | switch (op) {
113 | case PyCmp_IS:
114 | res = (v == w);
115 | break;
116 | case PyCmp_IS_NOT:
117 | res = (v != w);
118 | break;
119 | case PyCmp_IN:
120 | res = PySequence_Contains(w, v);
121 | if (res < 0)
122 | return 1;
123 | break;
124 | case PyCmp_NOT_IN:
125 | res = PySequence_Contains(w, v);
126 | if (res < 0)
127 | return 1;
128 | res = !res;
129 | break;
130 | case PyCmp_EXC_MATCH:
131 | if (PyTuple_Check(w)) {
132 | Py_ssize_t i, length;
133 | length = PyTuple_Size(w);
134 | for (i = 0; i < length; i += 1) {
135 | PyObject *exc = PyTuple_GET_ITEM(w, i);
136 | if (!PyExceptionClass_Check(exc)) {
137 | PyErr_SetString(PyExc_TypeError,
138 | CANNOT_CATCH_MSG);
139 | return 1;
140 | }
141 | }
142 | }
143 | else {
144 | if (!PyExceptionClass_Check(w)) {
145 | PyErr_SetString(PyExc_TypeError,
146 | CANNOT_CATCH_MSG);
147 | return 1;
148 | }
149 | }
150 | res = PyErr_GivenExceptionMatches(v, w);
151 | break;
152 | default:
153 | *result = PyObject_RichCompare(v, w, op);
154 | return (*result == NULL) ? 1 : 0;
155 | }
156 | *result = res ? Py_True : Py_False;
157 | Py_INCREF(*result);
158 | return 0;
159 | }
160 |
161 | static int
162 | unpack_iterable(PyObject *v, int argcnt, int argcntafter, PyObject **sp)
163 | {
164 | int i = 0, j = 0;
165 | Py_ssize_t ll = 0;
166 | PyObject *it; /* iter(v) */
167 | PyObject *w;
168 | PyObject *l = NULL; /* variable list */
169 |
170 | assert(v != NULL);
171 |
172 | it = PyObject_GetIter(v);
173 | if (it == NULL)
174 | goto Error;
175 |
176 | for (; i < argcnt; i++) {
177 | w = PyIter_Next(it);
178 | if (w == NULL) {
179 | /* Iterator done, via error or exhaustion. */
180 | if (!PyErr_Occurred()) {
181 | if (argcntafter == -1) {
182 | PyErr_Format(PyExc_ValueError,
183 | "not enough values to unpack (expected %d, got %d)",
184 | argcnt, i);
185 | }
186 | else {
187 | PyErr_Format(PyExc_ValueError,
188 | "not enough values to unpack "
189 | "(expected at least %d, got %d)",
190 | argcnt + argcntafter, i);
191 | }
192 | }
193 | goto Error;
194 | }
195 | *--sp = w;
196 | }
197 |
198 | if (argcntafter == -1) {
199 | /* We better have exhausted the iterator now. */
200 | w = PyIter_Next(it);
201 | if (w == NULL) {
202 | if (PyErr_Occurred())
203 | goto Error;
204 | Py_DECREF(it);
205 | return 1;
206 | }
207 | Py_DECREF(w);
208 | PyErr_Format(PyExc_ValueError,
209 | "too many values to unpack (expected %d)",
210 | argcnt);
211 | goto Error;
212 | }
213 |
214 | l = PySequence_List(it);
215 | if (l == NULL)
216 | goto Error;
217 | *--sp = l;
218 | i++;
219 |
220 | ll = PyList_GET_SIZE(l);
221 | if (ll < argcntafter) {
222 | PyErr_Format(PyExc_ValueError,
223 | "not enough values to unpack (expected at least %d, got %zd)",
224 | argcnt + argcntafter, argcnt + ll);
225 | goto Error;
226 | }
227 |
228 | /* Pop the "after-variable" args off the list. */
229 | for (j = argcntafter; j > 0; j--, i++) {
230 | *--sp = PyList_GET_ITEM(l, ll - j);
231 | }
232 | /* Resize the list. */
233 | Py_SIZE(l) = ll - argcntafter;
234 | Py_DECREF(it);
235 | return 1;
236 |
237 | Error:
238 | for (; i > 0; i--, sp++)
239 | Py_DECREF(*sp);
240 | Py_XDECREF(it);
241 | return 0;
242 | }
243 |
244 | int __pypperoni_IMPL_unpack_sequence(PyObject* seq, PyObject*** sp, int num)
245 | {
246 | PyObject *item, **items;
247 | PyObject **stack_pointer = *sp;
248 | int res = 0;
249 |
250 | if (PyTuple_CheckExact(seq) && PyTuple_GET_SIZE(seq) == num)
251 | {
252 | items = ((PyTupleObject *)seq)->ob_item;
253 | while (num--)
254 | {
255 | item = items[num];
256 | Py_INCREF(item);
257 | PUSH(item);
258 | }
259 | }
260 |
261 | else if (PyList_CheckExact(seq) && PyList_GET_SIZE(seq) == num)
262 | {
263 | items = ((PyListObject *)seq)->ob_item;
264 | while (num--)
265 | {
266 | item = items[num];
267 | Py_INCREF(item);
268 | PUSH(item);
269 | }
270 | }
271 |
272 | else if (unpack_iterable(seq, num, -1, stack_pointer + num))
273 | {
274 | STACKADJ(num);
275 | }
276 |
277 | else
278 | {
279 | /* unpack_iterable() raised an exception */
280 | Py_DECREF(seq);
281 | res = 1;
282 | }
283 |
284 | Py_DECREF(seq);
285 | *sp = stack_pointer;
286 | return res;
287 | }
288 |
289 | int __pypperoni_IMPL_unpack_ex(PyObject* seq, PyObject*** sp, int num)
290 | {
291 | int totalargs = 1 + (num & 0xFF) + (num >> 8);
292 | PyObject **stack_pointer = *sp;
293 | int res = 0;
294 |
295 | if (unpack_iterable(seq, num & 0xFF, num >> 8,
296 | stack_pointer + totalargs))
297 | stack_pointer += totalargs;
298 |
299 | else
300 | res = 1;
301 |
302 | Py_DECREF(seq);
303 | *sp = stack_pointer;
304 | return res;
305 | }
306 |
307 | void __pypperoni_IMPL_handle_bmuwc_error(PyObject* arg, PyObject* func)
308 | {
309 | if (PyErr_ExceptionMatches(PyExc_AttributeError))
310 | {
311 | PyErr_Format(PyExc_TypeError,
312 | "%.200s%.200s argument after ** "
313 | "must be a mapping, not %.200s",
314 | PyEval_GetFuncName(func),
315 | PyEval_GetFuncDesc(func),
316 | arg->ob_type->tp_name);
317 | }
318 |
319 | else if (PyErr_ExceptionMatches(PyExc_KeyError))
320 | {
321 | PyObject *exc, *val, *tb;
322 | PyErr_Fetch(&exc, &val, &tb);
323 | if (val && PyTuple_Check(val) && PyTuple_GET_SIZE(val) == 1)
324 | {
325 | PyObject *key = PyTuple_GET_ITEM(val, 0);
326 | if (!PyUnicode_Check(key)) {
327 | PyErr_Format(PyExc_TypeError,
328 | "%.200s%.200s keywords must be strings",
329 | PyEval_GetFuncName(func),
330 | PyEval_GetFuncDesc(func));
331 | } else {
332 | PyErr_Format(PyExc_TypeError,
333 | "%.200s%.200s got multiple "
334 | "values for keyword argument '%U'",
335 | PyEval_GetFuncName(func),
336 | PyEval_GetFuncDesc(func),
337 | key);
338 | }
339 | Py_XDECREF(exc);
340 | Py_XDECREF(val);
341 | Py_XDECREF(tb);
342 | }
343 | else
344 | {
345 | PyErr_Restore(exc, val, tb);
346 | }
347 | }
348 | }
349 |
350 | PyObject* __pypperoni_IMPL_ensure_args_iterable(PyObject* args, PyObject* func)
351 | {
352 | if (!PyTuple_CheckExact(args)) {
353 | if (args->ob_type->tp_iter == NULL && !PySequence_Check(args)) {
354 | PyErr_Format(PyExc_TypeError,
355 | "%.200s%.200s argument after * "
356 | "must be an iterable, not %.200s",
357 | PyEval_GetFuncName(func),
358 | PyEval_GetFuncDesc(func),
359 | args->ob_type->tp_name);
360 | Py_CLEAR(args);
361 | }
362 | else {
363 | Py_SETREF(args, PySequence_Tuple(args));
364 | }
365 | }
366 |
367 | return args;
368 | }
369 |
370 | PyObject* __pypperoni_IMPL_ensure_kwdict(PyObject* kwdict, PyObject* func)
371 | {
372 | if (!PyDict_CheckExact(kwdict)) {
373 | PyObject *d = PyDict_New();
374 | if (d != NULL && PyDict_Update(d, kwdict) != 0) {
375 | Py_DECREF(d);
376 | d = NULL;
377 | if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
378 | PyErr_Format(PyExc_TypeError,
379 | "%.200s%.200s argument after ** "
380 | "must be a mapping, not %.200s",
381 | PyEval_GetFuncName(func),
382 | PyEval_GetFuncDesc(func),
383 | kwdict->ob_type->tp_name);
384 | }
385 | }
386 |
387 | Py_SETREF(kwdict, d);
388 | }
389 |
390 | return kwdict;
391 | }
392 |
393 | #define GETLOCAL(i) (fastlocals[i])
394 | #define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \
395 | GETLOCAL(i) = value; \
396 | Py_XDECREF(tmp); } while (0)
397 |
398 |
399 | static void
400 | format_missing(const char *kind, PyCodeObject *co, PyObject *names)
401 | {
402 | int err;
403 | Py_ssize_t len = PyList_GET_SIZE(names);
404 | PyObject *name_str, *comma, *tail, *tmp;
405 |
406 | assert(PyList_CheckExact(names));
407 | assert(len >= 1);
408 | /* Deal with the joys of natural language. */
409 | switch (len) {
410 | case 1:
411 | name_str = PyList_GET_ITEM(names, 0);
412 | Py_INCREF(name_str);
413 | break;
414 | case 2:
415 | name_str = PyUnicode_FromFormat("%U and %U",
416 | PyList_GET_ITEM(names, len - 2),
417 | PyList_GET_ITEM(names, len - 1));
418 | break;
419 | default:
420 | tail = PyUnicode_FromFormat(", %U, and %U",
421 | PyList_GET_ITEM(names, len - 2),
422 | PyList_GET_ITEM(names, len - 1));
423 | if (tail == NULL)
424 | return;
425 | /* Chop off the last two objects in the list. This shouldn't actually
426 | fail, but we can't be too careful. */
427 | err = PyList_SetSlice(names, len - 2, len, NULL);
428 | if (err == -1) {
429 | Py_DECREF(tail);
430 | return;
431 | }
432 | /* Stitch everything up into a nice comma-separated list. */
433 | comma = PyUnicode_FromString(", ");
434 | if (comma == NULL) {
435 | Py_DECREF(tail);
436 | return;
437 | }
438 | tmp = PyUnicode_Join(comma, names);
439 | Py_DECREF(comma);
440 | if (tmp == NULL) {
441 | Py_DECREF(tail);
442 | return;
443 | }
444 | name_str = PyUnicode_Concat(tmp, tail);
445 | Py_DECREF(tmp);
446 | Py_DECREF(tail);
447 | break;
448 | }
449 | if (name_str == NULL)
450 | return;
451 | PyErr_Format(PyExc_TypeError,
452 | "%U() missing %i required %s argument%s: %U",
453 | co->co_name,
454 | len,
455 | kind,
456 | len == 1 ? "" : "s",
457 | name_str);
458 | Py_DECREF(name_str);
459 | }
460 |
461 | static void
462 | missing_arguments(PyCodeObject *co, Py_ssize_t missing, Py_ssize_t defcount,
463 | PyObject **fastlocals)
464 | {
465 | Py_ssize_t i, j = 0;
466 | Py_ssize_t start, end;
467 | int positional = (defcount != -1);
468 | const char *kind = positional ? "positional" : "keyword-only";
469 | PyObject *missing_names;
470 |
471 | /* Compute the names of the arguments that are missing. */
472 | missing_names = PyList_New(missing);
473 | if (missing_names == NULL)
474 | return;
475 | if (positional) {
476 | start = 0;
477 | end = co->co_argcount - defcount;
478 | }
479 | else {
480 | start = co->co_argcount;
481 | end = start + co->co_kwonlyargcount;
482 | }
483 | for (i = start; i < end; i++) {
484 | if (GETLOCAL(i) == NULL) {
485 | PyObject *raw = PyTuple_GET_ITEM(co->co_varnames, i);
486 | PyObject *name = PyObject_Repr(raw);
487 | if (name == NULL) {
488 | Py_DECREF(missing_names);
489 | return;
490 | }
491 | PyList_SET_ITEM(missing_names, j++, name);
492 | }
493 | }
494 | assert(j == missing);
495 | format_missing(kind, co, missing_names);
496 | Py_DECREF(missing_names);
497 | }
498 |
499 | static void
500 | too_many_positional(PyCodeObject *co, Py_ssize_t given, Py_ssize_t defcount,
501 | PyObject **fastlocals)
502 | {
503 | int plural;
504 | Py_ssize_t kwonly_given = 0;
505 | Py_ssize_t i;
506 | PyObject *sig, *kwonly_sig;
507 | Py_ssize_t co_argcount = co->co_argcount;
508 |
509 | assert((co->co_flags & CO_VARARGS) == 0);
510 | /* Count missing keyword-only args. */
511 | for (i = co_argcount; i < co_argcount + co->co_kwonlyargcount; i++) {
512 | if (GETLOCAL(i) != NULL) {
513 | kwonly_given++;
514 | }
515 | }
516 | if (defcount) {
517 | Py_ssize_t atleast = co_argcount - defcount;
518 | plural = 1;
519 | sig = PyUnicode_FromFormat("from %zd to %zd", atleast, co_argcount);
520 | }
521 | else {
522 | plural = (co_argcount != 1);
523 | sig = PyUnicode_FromFormat("%zd", co_argcount);
524 | }
525 | if (sig == NULL)
526 | return;
527 | if (kwonly_given) {
528 | const char *format = " positional argument%s (and %zd keyword-only argument%s)";
529 | kwonly_sig = PyUnicode_FromFormat(format,
530 | given != 1 ? "s" : "",
531 | kwonly_given,
532 | kwonly_given != 1 ? "s" : "");
533 | if (kwonly_sig == NULL) {
534 | Py_DECREF(sig);
535 | return;
536 | }
537 | }
538 | else {
539 | /* This will not fail. */
540 | kwonly_sig = PyUnicode_FromString("");
541 | assert(kwonly_sig != NULL);
542 | }
543 | PyErr_Format(PyExc_TypeError,
544 | "%U() takes %U positional argument%s but %zd%U %s given",
545 | co->co_name,
546 | sig,
547 | plural ? "s" : "",
548 | given,
549 | kwonly_sig,
550 | given == 1 && !kwonly_given ? "was" : "were");
551 | Py_DECREF(sig);
552 | Py_DECREF(kwonly_sig);
553 | }
554 |
555 | static PyObject*
556 | _PyFunction_FastCall(PyCodeObject *co, PyObject **args, Py_ssize_t nargs,
557 | PyObject *globals)
558 | {
559 | PyFrameObject *f;
560 | PyThreadState *tstate = PyThreadState_GET();
561 | PyObject **fastlocals;
562 | Py_ssize_t i;
563 | PyObject *result;
564 |
565 | assert(globals != NULL);
566 | /* XXX Perhaps we should create a specialized
567 | PyFrame_New() that doesn't take locals, but does
568 | take builtins without sanity checking them.
569 | */
570 | assert(tstate != NULL);
571 | f = PyFrame_New(tstate, co, globals, NULL);
572 | if (f == NULL) {
573 | return NULL;
574 | }
575 |
576 | fastlocals = f->f_localsplus;
577 |
578 | for (i = 0; i < nargs; i++) {
579 | Py_INCREF(*args);
580 | fastlocals[i] = *args++;
581 | }
582 | result = PyEval_EvalFrameEx(f,0);
583 |
584 | ++tstate->recursion_depth;
585 | Py_DECREF(f);
586 | --tstate->recursion_depth;
587 |
588 | return result;
589 | }
590 |
591 | static PyObject *
592 | _PyEval_EvalCodeWithName(PyObject *_co, PyObject *globals, PyObject *locals,
593 | PyObject **args, Py_ssize_t argcount,
594 | PyObject **kwnames, PyObject **kwargs,
595 | Py_ssize_t kwcount, int kwstep,
596 | PyObject **defs, Py_ssize_t defcount,
597 | PyObject *kwdefs, PyObject *closure,
598 | PyObject *name, PyObject *qualname)
599 | {
600 | PyCodeObject* co = (PyCodeObject*)_co;
601 | PyFrameObject *f;
602 | PyObject *retval = NULL;
603 | PyObject **fastlocals, **freevars;
604 | PyThreadState *tstate;
605 | PyObject *x, *u;
606 | const Py_ssize_t total_args = co->co_argcount + co->co_kwonlyargcount;
607 | Py_ssize_t i, n;
608 | PyObject *kwdict;
609 |
610 | if (globals == NULL) {
611 | PyErr_SetString(PyExc_SystemError,
612 | "PyEval_EvalCodeEx: NULL globals");
613 | return NULL;
614 | }
615 |
616 | /* Create the frame */
617 | tstate = PyThreadState_GET();
618 | assert(tstate != NULL);
619 | f = PyFrame_New(tstate, co, globals, locals);
620 | if (f == NULL) {
621 | return NULL;
622 | }
623 | fastlocals = f->f_localsplus;
624 | freevars = f->f_localsplus + co->co_nlocals;
625 |
626 | /* Create a dictionary for keyword parameters (**kwags) */
627 | if (co->co_flags & CO_VARKEYWORDS) {
628 | kwdict = PyDict_New();
629 | if (kwdict == NULL)
630 | goto fail;
631 | i = total_args;
632 | if (co->co_flags & CO_VARARGS) {
633 | i++;
634 | }
635 | SETLOCAL(i, kwdict);
636 | }
637 | else {
638 | kwdict = NULL;
639 | }
640 |
641 | /* Copy positional arguments into local variables */
642 | if (argcount > co->co_argcount) {
643 | n = co->co_argcount;
644 | }
645 | else {
646 | n = argcount;
647 | }
648 | for (i = 0; i < n; i++) {
649 | x = args[i];
650 | Py_INCREF(x);
651 | SETLOCAL(i, x);
652 | }
653 |
654 | /* Pack other positional arguments into the *args argument */
655 | if (co->co_flags & CO_VARARGS) {
656 | u = PyTuple_New(argcount - n);
657 | if (u == NULL) {
658 | goto fail;
659 | }
660 | SETLOCAL(total_args, u);
661 | for (i = n; i < argcount; i++) {
662 | x = args[i];
663 | Py_INCREF(x);
664 | PyTuple_SET_ITEM(u, i-n, x);
665 | }
666 | }
667 |
668 | /* Handle keyword arguments passed as two strided arrays */
669 | kwcount *= kwstep;
670 | for (i = 0; i < kwcount; i += kwstep) {
671 | PyObject **co_varnames;
672 | PyObject *keyword = kwnames[i];
673 | PyObject *value = kwargs[i];
674 | Py_ssize_t j;
675 |
676 | if (keyword == NULL || !PyUnicode_Check(keyword)) {
677 | PyErr_Format(PyExc_TypeError,
678 | "%U() keywords must be strings",
679 | co->co_name);
680 | goto fail;
681 | }
682 |
683 | /* Speed hack: do raw pointer compares. As names are
684 | normally interned this should almost always hit. */
685 | co_varnames = ((PyTupleObject *)(co->co_varnames))->ob_item;
686 | for (j = 0; j < total_args; j++) {
687 | PyObject *name = co_varnames[j];
688 | if (name == keyword) {
689 | goto kw_found;
690 | }
691 | }
692 |
693 | /* Slow fallback, just in case */
694 | for (j = 0; j < total_args; j++) {
695 | PyObject *name = co_varnames[j];
696 | int cmp = PyObject_RichCompareBool( keyword, name, Py_EQ);
697 | if (cmp > 0) {
698 | goto kw_found;
699 | }
700 | else if (cmp < 0) {
701 | goto fail;
702 | }
703 | }
704 |
705 | if (j >= total_args && kwdict == NULL) {
706 | PyErr_Format(PyExc_TypeError,
707 | "%U() got an unexpected keyword argument '%S'",
708 | co->co_name, keyword);
709 | goto fail;
710 | }
711 |
712 | if (PyDict_SetItem(kwdict, keyword, value) == -1) {
713 | goto fail;
714 | }
715 | continue;
716 |
717 | kw_found:
718 | if (GETLOCAL(j) != NULL) {
719 | PyErr_Format(PyExc_TypeError,
720 | "%U() got multiple values for argument '%S'",
721 | co->co_name, keyword);
722 | goto fail;
723 | }
724 | Py_INCREF(value);
725 | SETLOCAL(j, value);
726 | }
727 |
728 | /* Check the number of positional arguments */
729 | if (argcount > co->co_argcount && !(co->co_flags & CO_VARARGS)) {
730 | too_many_positional(co, argcount, defcount, fastlocals);
731 | goto fail;
732 | }
733 |
734 | /* Add missing positional arguments (copy default values from defs) */
735 | if (argcount < co->co_argcount) {
736 | Py_ssize_t m = co->co_argcount - defcount;
737 | Py_ssize_t missing = 0;
738 | for (i = argcount; i < m; i++) {
739 | if (GETLOCAL(i) == NULL) {
740 | missing++;
741 | }
742 | }
743 | if (missing) {
744 | missing_arguments(co, missing, defcount, fastlocals);
745 | goto fail;
746 | }
747 | if (n > m)
748 | i = n - m;
749 | else
750 | i = 0;
751 | for (; i < defcount; i++) {
752 | if (GETLOCAL(m+i) == NULL) {
753 | PyObject *def = defs[i];
754 | Py_INCREF(def);
755 | SETLOCAL(m+i, def);
756 | }
757 | }
758 | }
759 |
760 | /* Add missing keyword arguments (copy default values from kwdefs) */
761 | if (co->co_kwonlyargcount > 0) {
762 | Py_ssize_t missing = 0;
763 | for (i = co->co_argcount; i < total_args; i++) {
764 | PyObject *name;
765 | if (GETLOCAL(i) != NULL)
766 | continue;
767 | name = PyTuple_GET_ITEM(co->co_varnames, i);
768 | if (kwdefs != NULL) {
769 | PyObject *def = PyDict_GetItem(kwdefs, name);
770 | if (def) {
771 | Py_INCREF(def);
772 | SETLOCAL(i, def);
773 | continue;
774 | }
775 | }
776 | missing++;
777 | }
778 | if (missing) {
779 | missing_arguments(co, missing, -1, fastlocals);
780 | goto fail;
781 | }
782 | }
783 |
784 | /* Allocate and initialize storage for cell vars, and copy free
785 | vars into frame. */
786 | for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
787 | PyObject *c;
788 | int arg;
789 | /* Possibly account for the cell variable being an argument. */
790 | if (co->co_cell2arg != NULL &&
791 | (arg = co->co_cell2arg[i]) != CO_CELL_NOT_AN_ARG) {
792 | c = PyCell_New(GETLOCAL(arg));
793 | /* Clear the local copy. */
794 | SETLOCAL(arg, NULL);
795 | }
796 | else {
797 | c = PyCell_New(NULL);
798 | }
799 | if (c == NULL)
800 | goto fail;
801 | SETLOCAL(co->co_nlocals + i, c);
802 | }
803 |
804 | /* Copy closure variables to free variables */
805 | for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
806 | PyObject *o = PyTuple_GET_ITEM(closure, i);
807 | Py_INCREF(o);
808 | freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
809 | }
810 |
811 | /* Handle generator/coroutine/asynchronous generator */
812 | if (co->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)) {
813 | PyObject *gen;
814 | PyObject *coro_wrapper = tstate->coroutine_wrapper;
815 | int is_coro = co->co_flags & CO_COROUTINE;
816 |
817 | if (is_coro && tstate->in_coroutine_wrapper) {
818 | assert(coro_wrapper != NULL);
819 | PyErr_Format(PyExc_RuntimeError,
820 | "coroutine wrapper %.200R attempted "
821 | "to recursively wrap %.200R",
822 | coro_wrapper,
823 | co);
824 | goto fail;
825 | }
826 |
827 | /* Don't need to keep the reference to f_back, it will be set
828 | * when the generator is resumed. */
829 | Py_CLEAR(f->f_back);
830 |
831 | /* Create a new generator that owns the ready to run frame
832 | * and return that as the value. */
833 | if (is_coro) {
834 | gen = PyCoro_New(f, name, qualname);
835 | } else if (co->co_flags & CO_ASYNC_GENERATOR) {
836 | gen = PyAsyncGen_New(f, name, qualname);
837 | } else {
838 | gen = PyGen_NewWithQualName(f, name, qualname);
839 | }
840 | if (gen == NULL)
841 | return NULL;
842 |
843 | if (is_coro && coro_wrapper != NULL) {
844 | PyObject *wrapped;
845 | tstate->in_coroutine_wrapper = 1;
846 | wrapped = PyObject_CallFunction(coro_wrapper, "N", gen);
847 | tstate->in_coroutine_wrapper = 0;
848 | return wrapped;
849 | }
850 |
851 | return gen;
852 | }
853 |
854 | retval = PyEval_EvalFrameEx(f,0);
855 |
856 | fail: /* Jump here from prelude on failure */
857 |
858 | /* decref'ing the frame can cause __del__ methods to get invoked,
859 | which can call back into Python. While we're done with the
860 | current Python frame (f), the associated C stack is still in use,
861 | so recursion_depth must be boosted for the duration.
862 | */
863 | assert(tstate != NULL);
864 | ++tstate->recursion_depth;
865 | Py_DECREF(f);
866 | --tstate->recursion_depth;
867 | return retval;
868 | }
869 |
870 | static PyObject *
871 | fast_function(PyObject *func, PyObject **stack,
872 | Py_ssize_t nargs, PyObject *kwnames)
873 | {
874 | PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
875 | PyObject *globals = PyFunction_GET_GLOBALS(func);
876 | PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
877 | PyObject *kwdefs, *closure, *name, *qualname;
878 | PyObject **d;
879 | Py_ssize_t nkwargs = (kwnames == NULL) ? 0 : PyTuple_GET_SIZE(kwnames);
880 | Py_ssize_t nd;
881 |
882 | assert(PyFunction_Check(func));
883 | assert(nargs >= 0);
884 | assert(kwnames == NULL || PyTuple_CheckExact(kwnames));
885 | assert((nargs == 0 && nkwargs == 0) || stack != NULL);
886 | /* kwnames must only contains str strings, no subclass, and all keys must
887 | be unique */
888 |
889 | if (co->co_kwonlyargcount == 0 && nkwargs == 0 &&
890 | co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE))
891 | {
892 | if (argdefs == NULL && co->co_argcount == nargs) {
893 | return _PyFunction_FastCall(co, stack, nargs, globals);
894 | }
895 | else if (nargs == 0 && argdefs != NULL
896 | && co->co_argcount == Py_SIZE(argdefs)) {
897 | /* function called with no arguments, but all parameters have
898 | a default value: use default values as arguments .*/
899 | stack = &PyTuple_GET_ITEM(argdefs, 0);
900 | return _PyFunction_FastCall(co, stack, Py_SIZE(argdefs), globals);
901 | }
902 | }
903 |
904 | kwdefs = PyFunction_GET_KW_DEFAULTS(func);
905 | closure = PyFunction_GET_CLOSURE(func);
906 | name = ((PyFunctionObject *)func) -> func_name;
907 | qualname = ((PyFunctionObject *)func) -> func_qualname;
908 |
909 | if (argdefs != NULL) {
910 | d = &PyTuple_GET_ITEM(argdefs, 0);
911 | nd = Py_SIZE(argdefs);
912 | }
913 | else {
914 | d = NULL;
915 | nd = 0;
916 | }
917 | return _PyEval_EvalCodeWithName((PyObject*)co, globals, (PyObject *)NULL,
918 | stack, nargs,
919 | nkwargs ? &PyTuple_GET_ITEM(kwnames, 0) : NULL,
920 | stack + nargs,
921 | nkwargs, 1,
922 | d, (int)nd, kwdefs,
923 | closure, name, qualname);
924 | }
925 |
926 | PyObject* __pypperoni_IMPL_call_func(PyObject*** sp, int oparg, PyObject* kwargs)
927 | {
928 | PyObject **pfunc = (*sp) - oparg - 1;
929 | PyObject *func = *pfunc;
930 | PyObject *x, *w;
931 | Py_ssize_t nkwargs = (kwargs == NULL) ? 0 : PyTuple_GET_SIZE(kwargs);
932 | Py_ssize_t nargs = oparg - nkwargs;
933 | PyObject **stack;
934 |
935 | /* Always dispatch PyCFunction first, because these are
936 | presumed to be the most frequent callable object.
937 | */
938 | if (PyCFunction_Check(func)) {
939 | stack = (*sp) - nargs - nkwargs;
940 | x = _PyCFunction_FastCallKeywords(func, stack, nargs, kwargs);
941 | }
942 | else {
943 | if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
944 | /* optimize access to bound methods */
945 | PyObject *self = PyMethod_GET_SELF(func);
946 | Py_INCREF(self);
947 | func = PyMethod_GET_FUNCTION(func);
948 | Py_INCREF(func);
949 | Py_SETREF(*pfunc, self);
950 | nargs++;
951 | }
952 | else {
953 | Py_INCREF(func);
954 | }
955 |
956 | stack = (*sp) - nargs - nkwargs;
957 |
958 | if (PyFunction_Check(func)) {
959 | x = fast_function(func, stack, nargs, kwargs);
960 | }
961 | else {
962 | x = _PyObject_FastCallKeywords(func, stack, nargs, kwargs);
963 | }
964 |
965 | Py_DECREF(func);
966 | }
967 |
968 | assert((x != NULL) ^ (PyErr_Occurred() != NULL));
969 |
970 | /* Clear the stack of the function object. Also removes
971 | the arguments in case they weren't consumed already
972 | (fast_function() and err_args() leave them on the stack).
973 | */
974 | while ((*sp) > pfunc) {
975 | w = *--(*sp);
976 | Py_DECREF(w);
977 | }
978 |
979 | return x;
980 | }
981 |
982 | int __pypperoni_IMPL_load_build_class(PyFrameObject* f, PyObject** result)
983 | {
984 | _Py_IDENTIFIER(__build_class__);
985 |
986 | PyObject *bc;
987 | if (PyDict_CheckExact(f->f_builtins)) {
988 | bc = _PyDict_GetItemId(f->f_builtins, &PyId___build_class__);
989 | if (bc == NULL) {
990 | PyErr_SetString(PyExc_NameError,
991 | "__build_class__ not found");
992 | goto error;
993 | }
994 | Py_INCREF(bc);
995 | }
996 | else {
997 | PyObject *build_class_str = _PyUnicode_FromId(&PyId___build_class__);
998 | if (build_class_str == NULL)
999 | goto error;
1000 | bc = PyObject_GetItem(f->f_builtins, build_class_str);
1001 | if (bc == NULL) {
1002 | if (PyErr_ExceptionMatches(PyExc_KeyError))
1003 | PyErr_SetString(PyExc_NameError,
1004 | "__build_class__ not found");
1005 | goto error;
1006 | }
1007 | }
1008 | *result = bc;
1009 | return 0;
1010 |
1011 | error:
1012 | *result = NULL;
1013 | return 1;
1014 | }
1015 |
1016 | static PyObject *
1017 | special_lookup(PyObject *o, _Py_Identifier *id)
1018 | {
1019 | PyObject *res;
1020 | res = _PyObject_LookupSpecial(o, id);
1021 | if (res == NULL && !PyErr_Occurred()) {
1022 | PyErr_SetObject(PyExc_AttributeError, id->object);
1023 | return NULL;
1024 | }
1025 | return res;
1026 | }
1027 |
1028 | int __pypperoni_IMPL_setup_with(PyObject* v, PyObject** exitptr, PyObject** result)
1029 | {
1030 | _Py_IDENTIFIER(__exit__);
1031 | _Py_IDENTIFIER(__enter__);
1032 | PyObject *enter = special_lookup(v, &PyId___enter__);
1033 | if (enter == NULL)
1034 | goto error;
1035 |
1036 | *exitptr = special_lookup(v, &PyId___exit__);
1037 | if (*exitptr == NULL) {
1038 | Py_DECREF(enter);
1039 | goto error;
1040 | }
1041 |
1042 | *result = PyObject_CallFunctionObjArgs(enter, NULL);
1043 | Py_DECREF(enter);
1044 |
1045 | if (*result == NULL)
1046 | goto error;
1047 |
1048 | return 0;
1049 |
1050 | error:
1051 | *exitptr = NULL;
1052 | return 1;
1053 | }
1054 |
1055 | int __pypperoni_IMPL_do_raise(PyObject* exc, PyObject* cause)
1056 | {
1057 | PyObject *type = NULL, *value = NULL;
1058 |
1059 | if (exc == NULL) {
1060 | /* Reraise */
1061 | PyThreadState *tstate = PyThreadState_GET();
1062 | PyObject *tb;
1063 | type = tstate->exc_type;
1064 | value = tstate->exc_value;
1065 | tb = tstate->exc_traceback;
1066 | if (type == Py_None || type == NULL) {
1067 | PyErr_SetString(PyExc_RuntimeError,
1068 | "No active exception to reraise");
1069 | return 0;
1070 | }
1071 | Py_XINCREF(type);
1072 | Py_XINCREF(value);
1073 | Py_XINCREF(tb);
1074 | PyErr_Restore(type, value, tb);
1075 | return 1;
1076 | }
1077 |
1078 | /* We support the following forms of raise:
1079 | raise
1080 | raise
1081 | raise */
1082 |
1083 | if (PyExceptionClass_Check(exc)) {
1084 | type = exc;
1085 | value = PyObject_CallObject(exc, NULL);
1086 | if (value == NULL)
1087 | goto raise_error;
1088 | if (!PyExceptionInstance_Check(value)) {
1089 | PyErr_Format(PyExc_TypeError,
1090 | "calling %R should have returned an instance of "
1091 | "BaseException, not %R",
1092 | type, Py_TYPE(value));
1093 | goto raise_error;
1094 | }
1095 | }
1096 | else if (PyExceptionInstance_Check(exc)) {
1097 | value = exc;
1098 | type = PyExceptionInstance_Class(exc);
1099 | Py_INCREF(type);
1100 | }
1101 | else {
1102 | /* Not something you can raise. You get an exception
1103 | anyway, just not what you specified :-) */
1104 | Py_DECREF(exc);
1105 | PyErr_SetString(PyExc_TypeError,
1106 | "exceptions must derive from BaseException");
1107 | goto raise_error;
1108 | }
1109 |
1110 | if (cause) {
1111 | PyObject *fixed_cause;
1112 | if (PyExceptionClass_Check(cause)) {
1113 | fixed_cause = PyObject_CallObject(cause, NULL);
1114 | if (fixed_cause == NULL)
1115 | goto raise_error;
1116 | Py_DECREF(cause);
1117 | }
1118 | else if (PyExceptionInstance_Check(cause)) {
1119 | fixed_cause = cause;
1120 | }
1121 | else if (cause == Py_None) {
1122 | Py_DECREF(cause);
1123 | fixed_cause = NULL;
1124 | }
1125 | else {
1126 | PyErr_SetString(PyExc_TypeError,
1127 | "exception causes must derive from "
1128 | "BaseException");
1129 | goto raise_error;
1130 | }
1131 | PyException_SetCause(value, fixed_cause);
1132 | }
1133 |
1134 | PyErr_SetObject(type, value);
1135 | /* PyErr_SetObject incref's its arguments */
1136 | Py_XDECREF(value);
1137 | Py_XDECREF(type);
1138 | return 0;
1139 |
1140 | raise_error:
1141 | Py_XDECREF(value);
1142 | Py_XDECREF(type);
1143 | Py_XDECREF(cause);
1144 | return 0;
1145 | }
1146 |
1147 | PyObject* __pypperoni_IMPL_get_aiter(PyObject* obj)
1148 | {
1149 | unaryfunc getter = NULL;
1150 | PyObject *iter = NULL;
1151 | PyObject *awaitable = NULL;
1152 | PyTypeObject *type = Py_TYPE(obj);
1153 |
1154 | if (type->tp_as_async != NULL) {
1155 | getter = type->tp_as_async->am_aiter;
1156 | }
1157 |
1158 | if (getter != NULL) {
1159 | iter = (*getter)(obj);
1160 | Py_DECREF(obj);
1161 | if (iter == NULL) {
1162 | return NULL;
1163 | }
1164 | }
1165 | else {
1166 | PyErr_Format(
1167 | PyExc_TypeError,
1168 | "'async for' requires an object with "
1169 | "__aiter__ method, got %.100s",
1170 | type->tp_name);
1171 | Py_DECREF(obj);
1172 | return NULL;
1173 | }
1174 |
1175 | if (Py_TYPE(iter)->tp_as_async != NULL &&
1176 | Py_TYPE(iter)->tp_as_async->am_anext != NULL) {
1177 | PyObject *wrapper = _PyAIterWrapper_New(iter);
1178 | Py_DECREF(iter);
1179 | return wrapper;
1180 | }
1181 |
1182 | awaitable = _PyCoro_GetAwaitableIter(iter);
1183 | if (awaitable == NULL) {
1184 | _PyErr_FormatFromCause(
1185 | PyExc_TypeError,
1186 | "'async for' received an invalid object "
1187 | "from __aiter__: %.100s",
1188 | Py_TYPE(iter)->tp_name);
1189 | Py_DECREF(iter);
1190 | return NULL;
1191 | } else {
1192 | Py_DECREF(iter);
1193 |
1194 | if (PyErr_WarnFormat(
1195 | PyExc_DeprecationWarning, 1,
1196 | "'%.100s' implements legacy __aiter__ protocol; "
1197 | "__aiter__ should return an asynchronous "
1198 | "iterator, not awaitable",
1199 | type->tp_name))
1200 | {
1201 | /* Warning was converted to an error. */
1202 | Py_DECREF(awaitable);
1203 | return NULL;
1204 | }
1205 | }
1206 |
1207 | return awaitable;
1208 | }
1209 |
1210 | PyObject* __pypperoni_IMPL_get_anext(PyObject* aiter)
1211 | {
1212 | unaryfunc getter = NULL;
1213 | PyObject *next_iter = NULL;
1214 | PyObject *awaitable = NULL;
1215 | PyTypeObject *type = Py_TYPE(aiter);
1216 |
1217 | if (PyAsyncGen_CheckExact(aiter)) {
1218 | awaitable = type->tp_as_async->am_anext(aiter);
1219 | }
1220 | else {
1221 | if (type->tp_as_async != NULL) {
1222 | getter = type->tp_as_async->am_anext;
1223 | }
1224 |
1225 | if (getter == NULL) {
1226 | awaitable = NULL;
1227 | PyErr_Format(
1228 | PyExc_TypeError,
1229 | "'async for' requires an iterator with "
1230 | "__anext__ method, got %.100s",
1231 | type->tp_name);
1232 | }
1233 | else {
1234 | next_iter = (*getter)(aiter);
1235 | if (next_iter != NULL) {
1236 | awaitable = _PyCoro_GetAwaitableIter(next_iter);
1237 | if (awaitable == NULL) {
1238 | _PyErr_FormatFromCause(
1239 | PyExc_TypeError,
1240 | "'async for' received an invalid object "
1241 | "from __anext__: %.100s",
1242 | Py_TYPE(next_iter)->tp_name);
1243 | }
1244 |
1245 | Py_DECREF(next_iter);
1246 | }
1247 | }
1248 | }
1249 |
1250 | return awaitable;
1251 | }
1252 |
1253 | /* Modules */
1254 | #define MODULE_BUILTIN 1
1255 | #define MODULE_DEFINED 2
1256 |
1257 | #include "modules.I"
1258 |
1259 | static int __init_module_obj(PypperoniModule* mod)
1260 | {
1261 | PyObject *m, *d, *result;
1262 | PyCodeObject* co;
1263 | PyFrameObject* f;
1264 |
1265 | if (mod->type == MODULE_BUILTIN)
1266 | {
1267 | mod->obj = PyImport_ImportModule(mod->name);
1268 | if (mod->obj == NULL)
1269 | PyErr_Format(PyExc_ImportError, "unknown module %.200s", mod->name);
1270 |
1271 | return (mod->obj != NULL);
1272 | }
1273 |
1274 | m = PyImport_AddModule(mod->name);
1275 | Py_INCREF(m);
1276 | mod->obj = m;
1277 | d = PyModule_GetDict(m);
1278 |
1279 | /* Get code object */
1280 | co = PyCode_NewEmpty(mod->name, "", 0);
1281 | if (co == NULL)
1282 | return 0;
1283 |
1284 | co->co_nlocals = mod->nlocals;
1285 | co->co_stacksize = mod->stacksize;
1286 | co->co_meth_ptr = mod->ptr;
1287 |
1288 | /* Set a few attributes */
1289 | PyDict_SetItemString(d, "__file__", PyUnicode_FromString(mod->name));
1290 | PyDict_SetItemString(d, "__builtins__", PyThreadState_GET()->interp->builtins);
1291 |
1292 | /* Execute the function */
1293 | result = PyEval_EvalCode((PyObject*)co, d, NULL);
1294 | Py_XDECREF(result);
1295 | return (result == NULL) ? 0 : 1;
1296 | }
1297 |
1298 | static PypperoniModule* __get_module(int64_t index)
1299 | {
1300 | PypperoniModule *mod, **modlist;
1301 | get_pypperoni_modules(&modlist); /* provided by modules.I */
1302 |
1303 | while (mod = *modlist++)
1304 | if (mod->index == index)
1305 | return mod;
1306 |
1307 | return NULL;
1308 | }
1309 |
1310 | static int __init_module(int64_t index)
1311 | {
1312 | /* Returns 1 on success and 0 on failure */
1313 | PypperoniModule* mod = __get_module(index);
1314 | if (mod == NULL)
1315 | return 0;
1316 |
1317 | if (mod->obj != NULL)
1318 | return 1; /* already initialized */
1319 |
1320 | return __init_module_obj(mod);
1321 | }
1322 |
1323 | PyObject* __pypperoni_IMPL_import(int64_t index)
1324 | {
1325 | PypperoniModule* mod = __get_module(index);
1326 | if (mod == NULL)
1327 | {
1328 | PyErr_Format(PyExc_ImportError, "unknown module %lld", index);
1329 | return NULL;
1330 | }
1331 |
1332 | if (mod->obj != NULL)
1333 | {
1334 | Py_INCREF(mod->obj);
1335 | return mod->obj;
1336 | }
1337 |
1338 | if (mod->parent != -1 && !__init_module(mod->parent))
1339 | return NULL;
1340 |
1341 | if (!__init_module_obj(mod))
1342 | return NULL;
1343 |
1344 | Py_INCREF(mod->obj);
1345 | return mod->obj;
1346 | }
1347 |
1348 | PyObject* __pypperoni_IMPL_import_from(PyObject* mod, const char* name)
1349 | {
1350 | PyObject* x = PyObject_GetAttrString(mod, name);
1351 | if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
1352 | PyErr_Format(PyExc_ImportError, "cannot import name %.230s", name);
1353 | }
1354 | return x;
1355 | }
1356 |
1357 | PyObject* __pypperoni_IMPL_import_from_or_module(PyObject* mod, PyObject* name, int64_t index)
1358 | {
1359 | PyObject* x = PyObject_GetAttr(mod, name);
1360 | if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
1361 | PyErr_Clear();
1362 | x = __pypperoni_IMPL_import(index);
1363 | }
1364 | return x;
1365 | }
1366 |
1367 | int __pypperoni_IMPL_import_star(PyFrameObject* f, PyObject* mod)
1368 | {
1369 | _Py_IDENTIFIER(__all__);
1370 | _Py_IDENTIFIER(__dict__);
1371 | PyObject *all = _PyObject_GetAttrId(mod, &PyId___all__);
1372 | PyObject *dict, *name, *value;
1373 | int skip_leading_underscores = 0;
1374 | int pos, err;
1375 |
1376 | if (all == NULL) {
1377 | if (!PyErr_ExceptionMatches(PyExc_AttributeError))
1378 | return -1; /* Unexpected error */
1379 | PyErr_Clear();
1380 | dict = _PyObject_GetAttrId(mod, &PyId___dict__);
1381 | if (dict == NULL) {
1382 | if (!PyErr_ExceptionMatches(PyExc_AttributeError))
1383 | return -1;
1384 | PyErr_SetString(PyExc_ImportError,
1385 | "from-import-* object has no __dict__ and no __all__");
1386 | return -1;
1387 | }
1388 | all = PyMapping_Keys(dict);
1389 | Py_DECREF(dict);
1390 | if (all == NULL)
1391 | return -1;
1392 | skip_leading_underscores = 1;
1393 | }
1394 |
1395 | for (pos = 0, err = 0; ; pos++) {
1396 | name = PySequence_GetItem(all, pos);
1397 | if (name == NULL) {
1398 | if (!PyErr_ExceptionMatches(PyExc_IndexError))
1399 | err = -1;
1400 | else
1401 | PyErr_Clear();
1402 | break;
1403 | }
1404 | if (skip_leading_underscores && PyUnicode_Check(name)) {
1405 | if (PyUnicode_READY(name) == -1) {
1406 | Py_DECREF(name);
1407 | err = -1;
1408 | break;
1409 | }
1410 | if (PyUnicode_READ_CHAR(name, 0) == '_') {
1411 | Py_DECREF(name);
1412 | continue;
1413 | }
1414 | }
1415 | value = PyObject_GetAttr(mod, name);
1416 | if (value == NULL)
1417 | err = -1;
1418 | else if (PyDict_CheckExact(f->f_locals))
1419 | err = PyDict_SetItem(f->f_locals, name, value);
1420 | else
1421 | err = PyObject_SetItem(f->f_locals, name, value);
1422 | Py_DECREF(name);
1423 | Py_XDECREF(value);
1424 | if (err != 0)
1425 | break;
1426 | }
1427 | Py_DECREF(all);
1428 | return err;
1429 | }
1430 |
1431 | static PyMethodDef describeException_def;
1432 |
1433 | #define PyTraceBack_LIMIT 1000
1434 |
1435 | static PyObject* describeException(PyObject* self, PyObject* args)
1436 | {
1437 | _Py_IDENTIFIER(__name__);
1438 |
1439 | PyThreadState* tstate = PyThreadState_GET();
1440 | PyTracebackObject* tb = (PyTracebackObject*)tstate->exc_traceback;
1441 | PyFrameObject* exc_frame;
1442 | PyObject* result;
1443 | int depth = 0;
1444 |
1445 | result = PyUnicode_FromString("");
1446 | if ((PyObject*)tb == Py_None || result == NULL)
1447 | tb = NULL;
1448 |
1449 | while (tb != NULL && depth < PyTraceBack_LIMIT)
1450 | {
1451 | exc_frame = tb->tb_frame;
1452 | PyObject* modname = _PyDict_GetItemId(exc_frame->f_globals,
1453 | &PyId___name__);
1454 | PyObject* formatted = PyUnicode_FromFormat("#%d In \"%U\", instr %d, line %d\n",
1455 | depth++, modname,
1456 | exc_frame->f_lasti,
1457 | exc_frame->f_lineno);
1458 | PyUnicode_Append(&result, formatted);
1459 | tb = tb->tb_next;
1460 | }
1461 |
1462 | return result;
1463 | }
1464 |
1465 | /* Setup and main */
1466 | void setup_pypperoni()
1467 | {
1468 | const char* _def_encoding = "UTF-8";
1469 |
1470 | /* Register encodings module */
1471 | PyImport_AppendInittab("encodings", load_encodings); /* provided by modules.I */
1472 |
1473 | /* Initialize Python */
1474 | Py_IsolatedFlag++;
1475 | Py_IgnoreEnvironmentFlag++;
1476 | Py_NoSiteFlag++;
1477 | Py_FrozenFlag++;
1478 |
1479 | /* Py_FileSystemDefaultEncoding must be malloc'ed */
1480 | Py_FileSystemDefaultEncoding = malloc(sizeof(_def_encoding));
1481 | strcpy((char*)Py_FileSystemDefaultEncoding, _def_encoding);
1482 |
1483 | Py_Initialize();
1484 | PyEval_InitThreads();
1485 |
1486 | /* Setup __pypperoni__ */
1487 | PyObject* pypperonimod = PyImport_AddModule("__pypperoni__");
1488 | PyObject* bt = PyEval_GetBuiltins();
1489 | PyDict_SetItemString(bt, "__pypperoni__", pypperonimod);
1490 |
1491 | describeException_def.ml_name = "describeException";
1492 | describeException_def.ml_meth = (PyCFunction)describeException;
1493 | describeException_def.ml_flags = METH_NOARGS;
1494 | PyObject_SetAttrString(pypperonimod, "describeException",
1495 | PyCFunction_New(&describeException_def, NULL));
1496 |
1497 | PyObject_SetAttrString(pypperonimod, "platform", PyUnicode_FromString(
1498 | #ifdef _WIN32
1499 | "windows"
1500 | #elif ANDROID
1501 | "android"
1502 | #elif __APPLE__
1503 | "mac"
1504 | #elif __linux
1505 | "linux"
1506 | #endif
1507 | ));
1508 |
1509 | Py_DECREF(pypperonimod);
1510 | }
1511 |
1512 | int __pypperoni_IMPL_main(int argc, char* argv[])
1513 | {
1514 | /* XXX TODO: Handle unicode properly */
1515 | int i;
1516 | PyObject* av = PyList_New(argc);
1517 | if (av == NULL)
1518 | goto argv_error;
1519 |
1520 | for (i = 0; i < argc; i++)
1521 | {
1522 | PyObject* v = PyUnicode_FromString(argv[i]);
1523 | if (v == NULL)
1524 | goto argv_error;
1525 |
1526 | PyList_SetItem(av, i, v);
1527 | }
1528 |
1529 | if (PySys_SetObject("argv", av) != 0)
1530 | goto argv_error;
1531 |
1532 | if (__pypperoni_IMPL_import(0) != NULL)
1533 | return 0;
1534 |
1535 | return 1;
1536 |
1537 | argv_error:
1538 | Py_XDECREF(av);
1539 | Py_FatalError("can't assign sys.argv");
1540 | }
1541 |
--------------------------------------------------------------------------------
/src/pypperoni_impl.h:
--------------------------------------------------------------------------------
1 | /* Copyright (c) Pypperoni
2 |
3 | Pypperoni is licensed under the MIT License; you may
4 | not use it except in compliance with the License.
5 |
6 | You should have received a copy of the License with
7 | this source code under the name "LICENSE.txt". However,
8 | you may obtain a copy of the License on our GitHub here:
9 | https://github.com/Pypperoni/pypperoni
10 |
11 | Unless required by applicable law or agreed to in writing,
12 | software distributed under the License is distributed on an
13 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | either express or implied. See the License for the specific
15 | language governing permissions and limitations under the
16 | License.
17 | */
18 |
19 | #pragma once
20 |
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | #ifdef __cplusplus
27 | extern "C" {
28 | #endif
29 |
30 | typedef struct _pypperoni_module {
31 | int64_t index;
32 | int type;
33 | int64_t parent;
34 | PyObject*(*ptr)(PyFrameObject*);
35 | const char* name;
36 | int stacksize;
37 | int nlocals;
38 | PyObject* obj;
39 | } PypperoniModule;
40 |
41 | #define STACK_LEVEL() ((int)(stack_pointer - f->f_stacktop))
42 | #define TOP() (stack_pointer[-1])
43 | #define SECOND() (stack_pointer[-2])
44 | #define THIRD() (stack_pointer[-3])
45 | #define FOURTH() (stack_pointer[-4])
46 | #define PEEK(n) (stack_pointer[-(n)])
47 | #define SET_TOP(v) (stack_pointer[-1] = (v))
48 | #define SET_SECOND(v) (stack_pointer[-2] = (v))
49 | #define SET_THIRD(v) (stack_pointer[-3] = (v))
50 | #define SET_FOURTH(v) (stack_pointer[-4] = (v))
51 | #define SET_VALUE(n, v) (stack_pointer[-(n)] = (v))
52 | #define STACKADJ(n) (stack_pointer += n)
53 | #define PUSH(v) (*stack_pointer++ = (v))
54 | #define POP() (*--stack_pointer)
55 |
56 | #define UNWIND_BLOCK(b) \
57 | while (STACK_LEVEL() > (b)->b_level) { \
58 | PyObject *v = POP(); \
59 | Py_XDECREF(v); \
60 | }
61 |
62 | #define UNWIND_EXCEPT_HANDLER(b) \
63 | do { \
64 | PyObject *type, *value, *traceback; \
65 | assert(STACK_LEVEL() >= (b)->b_level + 3); \
66 | while (STACK_LEVEL() > (b)->b_level + 3) { \
67 | value = POP(); \
68 | Py_XDECREF(value); \
69 | } \
70 | type = tstate->exc_type; \
71 | value = tstate->exc_value; \
72 | traceback = tstate->exc_traceback; \
73 | tstate->exc_type = POP(); \
74 | tstate->exc_value = POP(); \
75 | tstate->exc_traceback = POP(); \
76 | Py_XDECREF(type); \
77 | Py_XDECREF(value); \
78 | Py_XDECREF(traceback); \
79 | } while(0)
80 |
81 | #define WHY_NOT 0x0001
82 | #define WHY_EXCEPTION 0x0002
83 | #define WHY_RETURN 0x0008
84 | #define WHY_BREAK 0x0010
85 | #define WHY_CONTINUE 0x0020
86 | #define WHY_YIELD 0x0040
87 | #define WHY_SILENCED 0x0080
88 |
89 | #ifdef HAVE_COMPUTED_GOTOS
90 | #define GET_ADDRESS(var, label, idx) var = &&label;
91 | #define JUMP_TO_ADDR(addr) goto *(addr);
92 | #else
93 | #define GET_ADDRESS(var, label, idx) var = (void*)idx;
94 | #define JUMP_TO_ADDR(addr) do { \
95 | _jmpto = addr; \
96 | goto jump_table; \
97 | } while (0)
98 | #endif
99 |
100 | PyObject* __pypperoni_IMPL_load_name(PyFrameObject* f, PyObject* name);
101 | PyObject* __pypperoni_IMPL_load_global(PyFrameObject* f, PyObject* name);
102 | int __pypperoni_IMPL_compare(PyObject* w, PyObject* v, int op, PyObject** result);
103 | int __pypperoni_IMPL_unpack_sequence(PyObject* seq, PyObject*** sp, int num);
104 | int __pypperoni_IMPL_unpack_ex(PyObject* seq, PyObject*** sp, int num);
105 | void __pypperoni_IMPL_handle_bmuwc_error(PyObject* arg, PyObject* func);
106 | PyObject* __pypperoni_IMPL_ensure_args_iterable(PyObject* args, PyObject* func);
107 | PyObject* __pypperoni_IMPL_ensure_kwdict(PyObject* kwdict, PyObject* func);
108 | PyObject* __pypperoni_IMPL_call_func(PyObject*** sp, int oparg, PyObject* kwargs);
109 | int __pypperoni_IMPL_load_build_class(PyFrameObject* f, PyObject** result);
110 | int __pypperoni_IMPL_setup_with(PyObject* v, PyObject** exitptr, PyObject** result);
111 | int __pypperoni_IMPL_do_raise(PyObject* exc, PyObject* cause);
112 | PyObject* __pypperoni_IMPL_get_aiter(PyObject* obj);
113 | PyObject* __pypperoni_IMPL_get_anext(PyObject* obj);
114 |
115 | PyObject* __pypperoni_IMPL_import(int64_t index);
116 | PyObject* __pypperoni_IMPL_import_from(PyObject* mod, const char* name);
117 | PyObject* __pypperoni_IMPL_import_from_or_module(PyObject* mod, PyObject* name, int64_t index);
118 | int __pypperoni_IMPL_import_star(PyFrameObject* f, PyObject* mod);
119 |
120 | static inline int __pypperoni_IMPL_check_cond(PyObject* obj, int* result)
121 | {
122 | int err;
123 |
124 | if (obj == Py_True) {
125 | *result = 1;
126 | return 0;
127 | }
128 |
129 | if (obj == Py_False) {
130 | *result = 0;
131 | return 0;
132 | }
133 |
134 | *result = PyObject_IsTrue(obj);
135 | if (*result < 0)
136 | return 1;
137 |
138 | return 0;
139 | }
140 |
141 | static inline int __pypperoni_IMPL_binary_power(PyObject* v, PyObject* w, PyObject** x)
142 | {
143 | *x = PyNumber_Power(v, w, Py_None);
144 | return (*x == NULL) ? 1 : 0;
145 | }
146 |
147 | static inline int __pypperoni_IMPL_binary_multiply(PyObject* v, PyObject* w, PyObject** x)
148 | {
149 | *x = PyNumber_Multiply(v, w);
150 | return (*x == NULL) ? 1 : 0;
151 | }
152 |
153 | static inline int __pypperoni_IMPL_binary_matrix_multiply(PyObject* v, PyObject* w, PyObject** x)
154 | {
155 | *x = PyNumber_MatrixMultiply(v, w);
156 | return (*x == NULL) ? 1 : 0;
157 | }
158 |
159 | static inline int __pypperoni_IMPL_binary_modulo(PyObject* v, PyObject* w, PyObject** x)
160 | {
161 | if (PyUnicode_CheckExact(v) && (!PyUnicode_Check(w) || PyUnicode_CheckExact(w)))
162 | *x = PyUnicode_Format(v, w);
163 | else
164 | *x = PyNumber_Remainder(v, w);
165 | return (*x == NULL) ? 1 : 0;
166 | }
167 |
168 | static inline int __pypperoni_IMPL_binary_add(PyObject* v, PyObject* w, PyObject** x)
169 | {
170 | if (PyUnicode_CheckExact(v) && PyUnicode_CheckExact(w))
171 | {
172 | Py_INCREF(v); // PyUnicode_Append steals a ref
173 | PyUnicode_Append(&v, w);
174 | *x = v;
175 | }
176 |
177 | else
178 | *x = PyNumber_Add(v, w);
179 |
180 | return (*x == NULL) ? 1 : 0;
181 | }
182 |
183 | static inline int __pypperoni_IMPL_binary_subtract(PyObject* v, PyObject* w, PyObject** x)
184 | {
185 | *x = PyNumber_Subtract(v, w);
186 | return (*x == NULL) ? 1 : 0;
187 | }
188 |
189 | static inline int __pypperoni_IMPL_binary_subscr(PyObject* v, PyObject* w, PyObject** x)
190 | {
191 | *x = PyObject_GetItem(v, w);
192 | return (*x == NULL) ? 1 : 0;
193 | }
194 |
195 | static inline int __pypperoni_IMPL_binary_floor_divide(PyObject* v, PyObject* w, PyObject** x)
196 | {
197 | *x = PyNumber_FloorDivide(v, w);
198 | return (*x == NULL) ? 1 : 0;
199 | }
200 |
201 | static inline int __pypperoni_IMPL_binary_true_divide(PyObject* v, PyObject* w, PyObject** x)
202 | {
203 | *x = PyNumber_TrueDivide(v, w);
204 | return (*x == NULL) ? 1 : 0;
205 | }
206 |
207 | static inline int __pypperoni_IMPL_binary_lshift(PyObject* v, PyObject* w, PyObject** x)
208 | {
209 | *x = PyNumber_Lshift(v, w);
210 | return (*x == NULL) ? 1 : 0;
211 | }
212 |
213 | static inline int __pypperoni_IMPL_binary_rshift(PyObject* v, PyObject* w, PyObject** x)
214 | {
215 | *x = PyNumber_Rshift(v, w);
216 | return (*x == NULL) ? 1 : 0;
217 | }
218 |
219 | static inline int __pypperoni_IMPL_binary_and(PyObject* v, PyObject* w, PyObject** x)
220 | {
221 | *x = PyNumber_And(v, w);
222 | return (*x == NULL) ? 1 : 0;
223 | }
224 |
225 | static inline int __pypperoni_IMPL_binary_xor(PyObject* v, PyObject* w, PyObject** x)
226 | {
227 | *x = PyNumber_Xor(v, w);
228 | return (*x == NULL) ? 1 : 0;
229 | }
230 |
231 | static inline int __pypperoni_IMPL_binary_or(PyObject* v, PyObject* w, PyObject** x)
232 | {
233 | *x = PyNumber_Or(v, w);
234 | return (*x == NULL) ? 1 : 0;
235 | }
236 |
237 | static inline int __pypperoni_IMPL_inplace_floor_divide(PyObject* v, PyObject* w, PyObject** x)
238 | {
239 | *x = PyNumber_InPlaceFloorDivide(v, w);
240 | return (*x == NULL) ? 1 : 0;
241 | }
242 |
243 | static inline int __pypperoni_IMPL_inplace_true_divide(PyObject* v, PyObject* w, PyObject** x)
244 | {
245 | *x = PyNumber_InPlaceTrueDivide(v, w);
246 | return (*x == NULL) ? 1 : 0;
247 | }
248 |
249 | static inline int __pypperoni_IMPL_inplace_add(PyObject* v, PyObject* w, PyObject** x)
250 | {
251 | if (PyUnicode_CheckExact(v) && PyUnicode_CheckExact(w))
252 | {
253 | Py_INCREF(v); // PyUnicode_Append steals a ref
254 | PyUnicode_Append(&v, w);
255 | *x = v;
256 | }
257 |
258 | else
259 | *x = PyNumber_InPlaceAdd(v, w);
260 |
261 | return (*x == NULL) ? 1 : 0;
262 | }
263 |
264 | static inline int __pypperoni_IMPL_inplace_subtract(PyObject* v, PyObject* w, PyObject** x)
265 | {
266 | *x = PyNumber_InPlaceSubtract(v, w);
267 | return (*x == NULL) ? 1 : 0;
268 | }
269 |
270 | static inline int __pypperoni_IMPL_inplace_multiply(PyObject* v, PyObject* w, PyObject** x)
271 | {
272 | *x = PyNumber_InPlaceMultiply(v, w);
273 | return (*x == NULL) ? 1 : 0;
274 | }
275 |
276 | static inline int __pypperoni_IMPL_inplace_matrix_multiply(PyObject* v, PyObject* w, PyObject** x)
277 | {
278 | *x = PyNumber_InPlaceMatrixMultiply(v, w);
279 | return (*x == NULL) ? 1 : 0;
280 | }
281 |
282 | static inline int __pypperoni_IMPL_inplace_modulo(PyObject* v, PyObject* w, PyObject** x)
283 | {
284 | *x = PyNumber_InPlaceRemainder(v, w);
285 | return (*x == NULL) ? 1 : 0;
286 | }
287 |
288 | static inline int __pypperoni_IMPL_inplace_power(PyObject* v, PyObject* w, PyObject** x)
289 | {
290 | *x = PyNumber_InPlacePower(v, w, Py_None);
291 | return (*x == NULL) ? 1 : 0;
292 | }
293 |
294 | static inline int __pypperoni_IMPL_inplace_lshift(PyObject* v, PyObject* w, PyObject** x)
295 | {
296 | *x = PyNumber_InPlaceLshift(v, w);
297 | return (*x == NULL) ? 1 : 0;
298 | }
299 |
300 | static inline int __pypperoni_IMPL_inplace_rshift(PyObject* v, PyObject* w, PyObject** x)
301 | {
302 | *x = PyNumber_InPlaceRshift(v, w);
303 | return (*x == NULL) ? 1 : 0;
304 | }
305 |
306 | static inline int __pypperoni_IMPL_inplace_and(PyObject* v, PyObject* w, PyObject** x)
307 | {
308 | *x = PyNumber_InPlaceAnd(v, w);
309 | return (*x == NULL) ? 1 : 0;
310 | }
311 |
312 | static inline int __pypperoni_IMPL_inplace_xor(PyObject* v, PyObject* w, PyObject** x)
313 | {
314 | *x = PyNumber_InPlaceXor(v, w);
315 | return (*x == NULL) ? 1 : 0;
316 | }
317 |
318 | static inline int __pypperoni_IMPL_inplace_or(PyObject* v, PyObject* w, PyObject** x)
319 | {
320 | *x = PyNumber_InPlaceOr(v, w);
321 | return (*x == NULL) ? 1 : 0;
322 | }
323 |
324 | static inline int __pypperoni_IMPL_unary_invert(PyObject* v, PyObject** x)
325 | {
326 | *x = PyNumber_Invert(v);
327 | return (*x == NULL) ? 1 : 0;
328 | }
329 |
330 | static inline int __pypperoni_IMPL_unary_positive(PyObject* v, PyObject** x)
331 | {
332 | *x = PyNumber_Positive(v);
333 | return (*x == NULL) ? 1 : 0;
334 | }
335 |
336 | static inline int __pypperoni_IMPL_unary_negative(PyObject* v, PyObject** x)
337 | {
338 | *x = PyNumber_Negative(v);
339 | return (*x == NULL) ? 1 : 0;
340 | }
341 |
342 | static inline int __pypperoni_IMPL_unary_not(PyObject* v, PyObject** x)
343 | {
344 | int err;
345 |
346 | err = PyObject_IsTrue(v);
347 |
348 | if (err == 0)
349 | {
350 | Py_INCREF(Py_True);
351 | *x = Py_True;
352 | }
353 |
354 | else if (err > 0)
355 | {
356 | Py_INCREF(Py_False);
357 | *x = Py_False;
358 | err = 0;
359 | }
360 |
361 | return err;
362 | }
363 |
364 | void setup_pypperoni();
365 | int __pypperoni_IMPL_main(int argc, char* argv[]);
366 |
367 | #ifdef __cplusplus
368 | }
369 | #endif
370 |
--------------------------------------------------------------------------------
/util.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) Pypperoni
2 | #
3 | # Pypperoni is licensed under the MIT License; you may
4 | # not use it except in compliance with the License.
5 | #
6 | # You should have received a copy of the License with
7 | # this source code under the name "LICENSE.txt". However,
8 | # you may obtain a copy of the License on our GitHub here:
9 | # https://github.com/Pypperoni/pypperoni
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14 | # either express or implied. See the License for the specific
15 | # language governing permissions and limitations under the
16 | # License.
17 |
18 | from threading import Lock
19 | import sys
20 |
21 |
22 | CO_OPTIMIZED = 0x0001
23 | CO_NEWLOCALS = 0x0002
24 | CO_VARARGS = 0x0004
25 | CO_VARKEYWORDS = 0x0008
26 | CO_NESTED = 0x0010
27 | CO_GENERATOR = 0x0020
28 | CO_NOFREE = 0x0040
29 | CO_COROUTINE = 0x0080
30 | CO_ITERABLE_COROUTINE = 0x0100
31 | CO_ASYNC_GENERATOR = 0x0200
32 |
33 | def safePrint(string):
34 | with Lock():
35 | sys.stdout.write('%s\n' % string)
36 | sys.stdout.flush()
37 |
38 | def safeRepr(obj):
39 | r = repr(obj)
40 | r = r.replace('\n', '\\n')
41 | r = r.replace('\r', '\\r')
42 | r = r.replace('\t', '\\t')
43 | r = r.replace('*', '')
44 | r = r[:40]
45 | return r.rstrip('\\')
46 |
--------------------------------------------------------------------------------