├── .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 | Pypperoni Logo 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 | --------------------------------------------------------------------------------