├── .gitignore ├── LICENSE.txt ├── NOTICE.txt ├── PyVM.sln ├── README.txt ├── src ├── BufferAccess.cpp ├── CodeDefinition.cpp ├── CodeDefinition.h ├── ObjPool.h ├── OpImp.h ├── PyCompile.cpp ├── PyCompile.h ├── PyVM.cpp ├── PyVM.h ├── VarArray.h ├── baseObject.h ├── bufferaccess.h ├── cfunc.h ├── defs.h ├── except.h ├── gen_string_method_names.h ├── instruction.cpp ├── libPyVM.vcxproj ├── libPyVM.vcxproj.filters ├── log.h ├── makeStringSwitch.py ├── objects.cpp ├── objects.h ├── opcodes.h ├── opcodes_def.h ├── pythonRoot.props ├── utils.cpp └── utils.h └── test ├── PyVMTest.cpp ├── VarArrayTest.cpp ├── imped_module.py ├── imped_module2.py ├── main.cpp ├── myTest.h ├── test_module.py ├── tester.vcxproj └── tester.vcxproj.filters /.gitignore: -------------------------------------------------------------------------------- 1 | /bin 2 | /obj 3 | *.sdf 4 | /.vs 5 | *.opensdf 6 | *.pyc 7 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | PyVM 2 | Copyright (C) 2015 by Intigua, Inc. 3 | This product includes software developed at 4 | Intigua, Inc. (http://www.intigua.com/). -------------------------------------------------------------------------------- /PyVM.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tester", "test\tester.vcxproj", "{F894A474-8167-46FF-86A3-ED249C8CAE82}" 7 | EndProject 8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libPyVM", "src\libPyVM.vcxproj", "{36027395-A85C-4DCE-A859-C23D1E3B9F87}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Win32 = Debug|Win32 13 | Debug|x64 = Debug|x64 14 | Release|Win32 = Release|Win32 15 | Release|x64 = Release|x64 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Debug|Win32.ActiveCfg = Debug|Win32 19 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Debug|Win32.Build.0 = Debug|Win32 20 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Debug|x64.ActiveCfg = Debug|x64 21 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Debug|x64.Build.0 = Debug|x64 22 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Release|Win32.ActiveCfg = Release|Win32 23 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Release|Win32.Build.0 = Release|Win32 24 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Release|x64.ActiveCfg = Release|x64 25 | {F894A474-8167-46FF-86A3-ED249C8CAE82}.Release|x64.Build.0 = Release|x64 26 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Debug|Win32.ActiveCfg = Debug|Win32 27 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Debug|Win32.Build.0 = Debug|Win32 28 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Debug|x64.ActiveCfg = Debug|x64 29 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Debug|x64.Build.0 = Debug|x64 30 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Release|Win32.ActiveCfg = Release|Win32 31 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Release|Win32.Build.0 = Release|Win32 32 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Release|x64.ActiveCfg = Release|x64 33 | {36027395-A85C-4DCE-A859-C23D1E3B9F87}.Release|x64.Build.0 = Release|x64 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | libPyVM is a compact Python 2.7 interpreter that can be used in various places where the standard CPython interpreter is not appropriate or cumbersome to use. 2 | 3 | - The entire interpreter is fully instantiatable. This means you can have multiple interpreters running in a single process. There is no global state or variables. 4 | - Very light weight. The entire interpreter is about 6000 lines of code, excluding tests and generated code. 5 | - All input and output interfaces are controllable. not dependency on environment variables or external files. 6 | - Predictable memory management - Save and restore memory snapshots instead of a garbage collector. 7 | - Runs compiled (byte-code) .pyc files produced by the standard CPython 8 | - Can be optionally linked to CPython for the purpose of compiling python code into bytecode and running an interactive interpreter. 9 | - Python code can easily interface with native C++ using an interface similar to boost::python 10 | - Written in standard C++11. 11 | 12 | 13 | Building 14 | ======== 15 | 16 | - Open PyVM.sln using Visual Studio 2015 (Community edition can be downloaded for free) 17 | 18 | - To build with linking to CPython open src/pythonRoot.props with a text editor and changed 19 | C:\Python27 20 | to point to where your installation of Python 2.7 resides. 21 | From Visual Studio this can be reached from - 22 | VIEW -> Property Manager -> project libPyVM -> "Debug|Win32" -> double click "pythonRoot" -> User Macros 23 | (This changes the root for all configs, not just Debug|Win32) 24 | 25 | Note that if your python27.lib is compiled for 32 bit, only the "Win32" configurations are going to successfully link and if it is compiled for 64 bit, only the "x64" configurations will build. It depends on which version of python you downloaded. 26 | 27 | - To build without CPython change in src/pythonRoot.props the variable USE_CPYTHON to 0 28 | From Visual Studio - 29 | VIEW -> Property Manager -> project libPyVM -> "Debug|Win32" -> double click "pythonRoot" -> C/C++ -> Preprocessor 30 | in "Preprocessor Definitions" change USE_CPYTHON to 0 31 | 32 | - Build the solution 33 | - Run the tester 34 | in the tester main.cpp you can choose between different main() functions which demonstrate aspects the the interpreter 35 | to run them, change main() to _main() and one of the other functions to be main() 36 | 37 | 38 | To build in earlier versions of Visual studio (up to 2010), open the project properties window, under 'General', change 'Platform Toolset' to the version of Visual Studio that you have. -------------------------------------------------------------------------------- /src/BufferAccess.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "BufferAccess.h" 12 | 13 | 14 | #define CATCH_ACCESS_VIOLATION(func) catch(const exception&) { throw; }\ 15 | catch(...) { THROW("Exception in " #func " " << (void*)m_p << "+" << m_offset); } 16 | // in case we didn't have ExceptionsTranslator 17 | 18 | template 19 | ObjRef AccessBuffer::readNum() { 20 | try { 21 | T v = 0; 22 | memcpy(&v, m_p + m_offset, sizeof(T)); 23 | m_offset += sizeof(T); 24 | return m_vm->alloc(new IntObject(v)); 25 | } CATCH_ACCESS_VIOLATION(readNum) 26 | } 27 | template 28 | void AccessBuffer::writeNum(const ObjRef& o) { 29 | try { 30 | T v = extract(o); 31 | memcpy(m_p + m_offset, &v, sizeof(v)); 32 | m_offset += sizeof(v); 33 | } CATCH_ACCESS_VIOLATION(writeNum) 34 | } 35 | 36 | ObjRef AccessBuffer::readPtr() { 37 | return readNum(); 38 | } 39 | ObjRef AccessBuffer::readInt(int numBytes) { 40 | switch (numBytes) { 41 | case 1: return readNum(); 42 | case 2: return readNum(); 43 | case 4: return readNum(); 44 | case 8: return readNum(); 45 | default: THROW("can't readInt " << numBytes); 46 | } 47 | } 48 | ObjRef AccessBuffer::readCStr() { 49 | try { 50 | int len = (int)strlen(m_p + m_offset); 51 | ObjRef r = readBuf(len); 52 | m_offset += 1; // consume nullptr termination 53 | return r; 54 | } CATCH_ACCESS_VIOLATION(readCStr) 55 | } 56 | ObjRef AccessBuffer::readBuf(int len) { 57 | try { 58 | string s(m_p + m_offset, len); 59 | m_offset += len; 60 | return m_vm->makeFromT(s); 61 | } CATCH_ACCESS_VIOLATION(readBuf) 62 | } 63 | ObjRef AccessBuffer::readWCStr(){ 64 | try { 65 | int count = (int)wcslen((wchar_t*)(m_p + m_offset)); 66 | wstring s; 67 | s.resize(count); 68 | memcpy((char*)s.data(), m_p + m_offset, count * sizeof(wchar_t)); 69 | m_offset += (count + 1) * sizeof(wchar_t); // consume nullptr termination 70 | return m_vm->makeFromT(s); 71 | } CATCH_ACCESS_VIOLATION(readWCStr) 72 | } 73 | 74 | void AccessBuffer::writePtr(const ObjRef& v) { 75 | writeNum(v); 76 | } 77 | void AccessBuffer::writeInt(int numBytes, const ObjRef v) { 78 | switch (numBytes) { 79 | case 1: return writeNum(v); 80 | //case 2: return writeNum(v); # TODO - Fix, Not compiling 81 | case 4: return writeNum(v); 82 | case 8: return writeNum(v); 83 | default: THROW("can't writeInt " << numBytes); 84 | } 85 | } 86 | 87 | void AccessBuffer::writeCStr(const string& s) { 88 | try { 89 | int count = (int)s.length() + 1; 90 | memcpy(m_p + m_offset, (char*)s.c_str(), count); // c_str guarantees that it will be nullptr terminated 91 | m_offset += count; 92 | } CATCH_ACCESS_VIOLATION(writeCStr) 93 | } 94 | 95 | void AccessBuffer::seekBytes(int origin, int offset) { 96 | int res = 0; 97 | if (origin == ORIGIN_BEG) 98 | res = offset; 99 | else if (origin == ORIGIN_CUR) 100 | res = m_offset + offset; 101 | else 102 | THROW("AccessBuffer::seekBytes: unexpected origin value " << origin); 103 | CHECK(res >= 0, "AccessBuffer::seekBytes: seek result is negative " << res); 104 | m_offset = res; 105 | } 106 | 107 | 108 | ClassObjRef AccessBuffer::addToModule(const ModuleObjRef& mod) { 109 | auto cls = mod->class_("AccessBuffer", CtorDef()); 110 | 111 | cls->def(&AccessBuffer::readPtr, "readPtr"); 112 | cls->def(&AccessBuffer::readInt, "readInt"); 113 | cls->def(&AccessBuffer::readCStr, "readCStr"); 114 | cls->def(&AccessBuffer::readWCStr, "readWCStr"); 115 | cls->def(&AccessBuffer::readBuf, "readBuf"); 116 | 117 | cls->def(&AccessBuffer::writeCStr, "writeCStr"); 118 | cls->def(&AccessBuffer::writePtr, "writePtr"); 119 | cls->def(&AccessBuffer::writeInt, "writeInt"); 120 | 121 | cls->def(&AccessBuffer::offset, "offset"); 122 | cls->def(&AccessBuffer::setOffset, "setOffset"); 123 | cls->def(&AccessBuffer::c_ptr, "c_ptr"); 124 | 125 | cls->def(&AccessBuffer::seekBytes, "seekBytes"); 126 | cls->addMember(mod->m_vm->makeFromT((int)ORIGIN_BEG), "ORIGIN_BEG"); 127 | cls->addMember(mod->m_vm->makeFromT((int)ORIGIN_CUR), "ORIGIN_CUR"); 128 | cls->addMember(mod->m_vm->makeFromT(sizeof(void*)), "SIZEOF_PTR"); 129 | 130 | return cls; 131 | } 132 | 133 | 134 | //--------------------------------------------------------------------------------------------- 135 | 136 | template 137 | void BufferBuilder::writeNum(const ObjRef& o) { 138 | if (m_offset + sizeof(T) > m_buf.size()) 139 | m_buf.resize(m_offset + sizeof(T)); 140 | T v = extract(o); 141 | memcpy((char*)m_buf.data() + m_offset, &v, sizeof(T)); 142 | m_offset += sizeof(T); 143 | } 144 | 145 | void BufferBuilder::writePtr(const ObjRef& v) { 146 | writeNum(v); 147 | } 148 | 149 | void BufferBuilder::writeInt(int numBytes, const ObjRef v) { 150 | switch (numBytes) { 151 | case 1: return writeNum(v); 152 | case 2: return writeNum(v); 153 | case 4: return writeNum(v); 154 | case 8: return writeNum(v); 155 | default: THROW("can't writeInt " << numBytes); 156 | } 157 | } 158 | 159 | void BufferBuilder::writeCStr(const string& s) { 160 | if (m_offset + s.length() + 1 > m_buf.size()) 161 | m_buf.resize(m_offset + s.length() + 1); 162 | memcpy((char*)m_buf.data() + m_offset, s.c_str(), s.length() + 1); 163 | m_offset += (int)s.length() + 1; 164 | } 165 | 166 | ClassObjRef BufferBuilder::addToModule(const ModuleObjRef& mod) { 167 | auto cls = mod->class_("BufferBuilder", CtorDef()); 168 | 169 | cls->def(&BufferBuilder::writePtr, "writePtr"); 170 | cls->def(&BufferBuilder::writeInt, "writeInt"); 171 | cls->def(&BufferBuilder::writeCStr, "writeCStr"); 172 | 173 | cls->def(&BufferBuilder::resize, "resize"); 174 | cls->def(&BufferBuilder::size, "size"); 175 | cls->def(&BufferBuilder::c_ptr, "c_ptr"); 176 | cls->def(&BufferBuilder::str, "str"); 177 | return cls; 178 | } 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /src/CodeDefinition.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "objects.h" 12 | #include "CodeDefinition.h" 13 | 14 | class Deserialize 15 | { 16 | public: 17 | Deserialize(istream& ins) : m_in(ins) 18 | {} 19 | template 20 | T read() { 21 | T v; 22 | m_in.read((char*)&v, sizeof(T)); 23 | return v; 24 | } 25 | string readStr(uint count) { 26 | string s; 27 | s.resize(count); 28 | m_in.read((char*)s.data(), count); 29 | return s; 30 | } 31 | 32 | vector m_internedStr; 33 | int offset() const { 34 | return (int)m_in.tellg(); 35 | } 36 | 37 | private: 38 | istream& m_in; 39 | }; 40 | 41 | ObjRef parseNext(Deserialize& s, PyVM* vm); 42 | 43 | 44 | // http://daeken.com/2010-02-20_Python_Marshal_Format.html 45 | void CodeDefinition::parseCode(Deserialize& s, PyVM* vm) 46 | { 47 | co_argcount = s.read(); 48 | co_nlocals = s.read(); 49 | co_stacksize = s.read(); 50 | co_flags = s.read(); 51 | co_code = extract(parseNext(s, vm)); 52 | co_consts = extract>(parseNext(s, vm)); 53 | 54 | co_names = extract>(parseNext(s, vm)); 55 | co_varnames = extract>(parseNext(s, vm)); 56 | co_freevars = extract>(parseNext(s, vm)); 57 | co_cellvars = extract>(parseNext(s, vm)); 58 | co_filename = extract(parseNext(s, vm)); 59 | co_name = extract(parseNext(s, vm)); 60 | 61 | co_firstlineno = s.read(); 62 | co_lnotab = extract(parseNext(s, vm)); 63 | } 64 | 65 | ObjRef parseNext(Deserialize& s, PyVM* vm) 66 | { 67 | uchar t = s.read(); 68 | int o = s.offset(); 69 | switch (t) { 70 | case '0': return ObjRef(); // should not happen 71 | case 'N': return vm->makeNone(); 72 | case 'F': return vm->makeFromT(false); 73 | case 'T': return vm->makeFromT(true); 74 | case 'i': return vm->makeFromT(s.read()); 75 | case 'I': return vm->makeFromT(s.read()); 76 | case 'g': return vm->makeFromT(s.read()); 77 | case 's': { 78 | uint sz = s.read(); 79 | return vm->makeFromT(s.readStr(sz)); 80 | } 81 | case '(': 82 | case '[': { 83 | uint sz = s.read(); 84 | ListObject* obj = (t == '(') ? new TupleObject : new ListObject; 85 | obj->v.resize(sz); 86 | for(uint i = 0; i < sz; ++i) 87 | obj->v[i] = parseNext(s, vm); 88 | return vm->alloc(obj); 89 | } 90 | case 'c': { 91 | auto* co = new CodeObject; 92 | co->m_co.parseCode(s, vm); 93 | return vm->alloc(co); 94 | } 95 | case 't': { // interned str 96 | uint sz = s.read(); 97 | ObjRef obj = vm->makeFromT(s.readStr(sz)); 98 | s.m_internedStr.push_back(obj); 99 | return obj; 100 | } 101 | case 'R': return s.m_internedStr[s.read()]; 102 | case 'l': { // long, encoded as digits in base 2^15 103 | int h = s.read(); // number of digits, sign is the sign of the number 104 | int sz = std::abs(h); 105 | int pos = 0; 106 | int64 res = 0; 107 | for(int i = 0; i < sz; ++i) { 108 | auto b = s.read(); 109 | res |= (int64)b << pos; 110 | pos += 15; 111 | } 112 | return vm->makeFromT(h < 0 ? -res : res); 113 | } 114 | case 'u': { // utf8 unicode 115 | uint sz = s.read(); 116 | wstring us; 117 | CHECK(wstrFromUtf8(s.readStr(sz), &us), "Failed reading UTF8"); 118 | return vm->makeFromT(us); 119 | } 120 | case '{': 121 | case '>': 122 | case 'y': // binary complex 123 | case 'x': // old complex 124 | case 'f': // old float 125 | case 'S': // stop iter 126 | case '.': // ellipsis 127 | default: 128 | THROW("Unsupported marshal type `" << t << "`"); 129 | } 130 | 131 | } 132 | 133 | 134 | ObjRef CodeDefinition::parsePyc(istream& iss, PyVM* vm, bool hasHeadr) 135 | { 136 | Deserialize d(iss); 137 | if (hasHeadr) { 138 | uint magic = d.read(); 139 | CHECK(magic == 0x0a0df303, "Unexpected magic number in marshal format " << hex << magic); 140 | uint timestamp = d.read(); 141 | } 142 | return parseNext(d, vm); 143 | } -------------------------------------------------------------------------------- /src/CodeDefinition.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include 13 | #include 14 | 15 | class Deserialize; 16 | 17 | class CodeDefinition 18 | { 19 | public: 20 | static ObjRef parsePyc(istream& iss, PyVM* vm, bool hasHeader); 21 | 22 | void parseCode(Deserialize& s, PyVM* vm); 23 | public: 24 | string co_name; 25 | uint co_argcount; 26 | uint co_nlocals; 27 | vector co_varnames; 28 | vector co_cellvars; 29 | vector co_freevars; 30 | string co_code; 31 | vector co_consts; 32 | vector co_names; 33 | string co_filename; 34 | uint co_firstlineno; 35 | string co_lnotab; 36 | uint co_stacksize; 37 | uint co_flags; 38 | }; -------------------------------------------------------------------------------- /src/ObjPool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "except.h" 13 | #include "defs.h" 14 | #include "log.h" 15 | 16 | 17 | template 18 | class ObjPool; 19 | 20 | template 21 | class PoolPtr 22 | { 23 | public: 24 | PoolPtr() : m_p(nullptr) {} 25 | PoolPtr(const PoolPtr& o) : m_p(o.m_p) { 26 | if (m_p != nullptr) { 27 | ++m_p->count.count; 28 | } 29 | } 30 | PoolPtr(PoolPtr&& o) { 31 | m_p = o.m_p; 32 | o.m_p = nullptr; 33 | } 34 | explicit PoolPtr(T* p) :m_p(p) { 35 | if (m_p != nullptr) 36 | ++m_p->count.count; 37 | } 38 | template 39 | explicit PoolPtr(const PoolPtr& o) : m_p(nullptr) { 40 | if (o.get() == nullptr) 41 | return; 42 | m_p = dynamic_cast(o.get()); 43 | CHECK(m_p != nullptr, "Incompatible conversion"); 44 | ++m_p->count.count; 45 | } 46 | 47 | ~PoolPtr() { 48 | reset(); 49 | } 50 | PoolPtr& operator=(const PoolPtr& o) { 51 | if (&o == this) 52 | return *this; 53 | reset(); 54 | m_p = o.m_p; 55 | if (m_p != nullptr) { 56 | ++m_p->count.count; 57 | } 58 | return *this; 59 | } 60 | void reset() { 61 | if (m_p == nullptr) 62 | return; 63 | if (--m_p->count.count == 0) { 64 | m_p->count.pool->remove(m_p); 65 | } 66 | m_p = nullptr; 67 | } 68 | T* get() const { 69 | return m_p; 70 | } 71 | T* operator->() const { 72 | return m_p; 73 | } 74 | const T& operator*() const { 75 | return *m_p; 76 | } 77 | T& operator*() { 78 | return *m_p; 79 | } 80 | bool isNull() const { 81 | return m_p == nullptr; 82 | } 83 | int use_count() const { 84 | if (m_p == nullptr) 85 | return 0; 86 | return m_p->count.count; 87 | } 88 | 89 | private: 90 | T* m_p; 91 | 92 | friend class ObjPool; 93 | }; 94 | 95 | template 96 | PoolPtr static_pcast(const PoolPtr& o) { 97 | return PoolPtr(static_cast(o.get())); 98 | } 99 | template 100 | PoolPtr dynamic_pcast(const PoolPtr& o) { 101 | return PoolPtr(dynamic_cast(o.get())); 102 | } 103 | 104 | 105 | // doubly linked list that hold objects of type T. T objects should have public data member count of type RefCount 106 | template 107 | class DList { 108 | private: 109 | T *m_head, *m_tail; 110 | int m_size; 111 | 112 | public: 113 | struct Entry { 114 | Entry() : next(nullptr), prev(nullptr) {} 115 | T *next, *prev; 116 | }; 117 | 118 | struct iterator { 119 | iterator(T* p) : ptr(p) {} 120 | T* get() { return ptr; } 121 | bool operator!=(iterator rhs) { return ptr != rhs.ptr; } 122 | bool operator==(iterator rhs) { return ptr == rhs.ptr; } 123 | iterator& operator++() {// prefix 124 | CHECK(ptr != nullptr, "unexpected nullptrptr"); 125 | ptr = ptr->count.ent.next; 126 | return *this; 127 | } 128 | T* ptr; 129 | }; 130 | 131 | DList() : m_head(nullptr), m_tail(nullptr), m_size(0) { 132 | // two dummy nodes that are always there. 133 | m_head = new T(); 134 | m_tail = new T(); 135 | m_head->count.ent.next = m_tail; 136 | m_tail->count.ent.prev = m_head; 137 | } 138 | ~DList() { 139 | if (m_size != 0) { 140 | LOG_ERROR("Object pool not empty. size=", m_size, " global objects?"); 141 | } 142 | delete m_head; 143 | delete m_tail; 144 | } 145 | // push at head 146 | void push_front(T* v) { 147 | Entry* n = &v->count.ent;// new Entry(m_head, v, m_head->next); 148 | n->prev = m_head; 149 | n->next = m_head->count.ent.next; 150 | m_head->count.ent.next->count.ent.prev = v; 151 | m_head->count.ent.next = v; 152 | ++m_size; 153 | } 154 | iterator begin() { 155 | return iterator(m_head->count.ent.next); 156 | } 157 | iterator end() { 158 | return iterator(m_tail); // one after last 159 | } 160 | void erase(T* v) { 161 | if (v == nullptr) 162 | return; 163 | CHECK(v != m_head && v != m_tail, "Unexpected state DList"); 164 | Entry* e = &v->count.ent; 165 | e->next->count.ent.prev = e->prev; 166 | e->prev->count.ent.next = e->next; 167 | --m_size; 168 | } 169 | int size() const { 170 | return m_size; 171 | } 172 | }; 173 | 174 | template 175 | struct RefCount { 176 | RefCount() : count(0), pool(nullptr) 177 | {} 178 | ~RefCount() { 179 | // delete remover; 180 | } 181 | int count; 182 | ObjPool *pool; 183 | typename DList::Entry ent; 184 | private: 185 | DISALLOW_COPY_AND_ASSIGN(RefCount); 186 | }; 187 | 188 | 189 | //#define OBJ_CNT_PRINT_DELTA_MS 10*60*1000 //10 mins 190 | 191 | /** A pool of reference counter objects of type T 192 | * T should have a public data member named 'count' of type RefCount 193 | * The pool can hold objects that inherit from T 194 | * it holds a doubly linked list of T objects. the next and prev pointes are inside RefCount::ent 195 | */ 196 | template 197 | class ObjPool 198 | { 199 | public: 200 | ObjPool() :m_hadRemove(false) //, m_lastTimePrintedObjCount(0) 201 | {} 202 | // takes ownership of p 203 | PoolPtr add(T* p) { 204 | PoolPtr ret(p); 205 | m_objs.push_front(p); 206 | p->count.pool = this; 207 | return ret; 208 | } 209 | 210 | void remove(T* v) { 211 | //printObjCntIfNeeded(); disabled since it garbages the log. instead, print the count before destruction of the pool in PyVM::clear() 212 | m_objs.erase(v); 213 | delete v; 214 | m_hadRemove = true; 215 | } 216 | 217 | /* void printObjCntIfNeeded(){ 218 | uint64 currentMsec = os::OSUtils::msecTimeFast(); 219 | if ((currentMsec - m_lastTimePrintedObjCount) > OBJ_CNT_PRINT_DELTA_MS){ 220 | LOG_DEBUG("There are ", m_objs.size(), " objects in pyvm object pool"); 221 | m_lastTimePrintedObjCount = currentMsec; 222 | } 223 | }*/ 224 | 225 | template 226 | bool foreach(const F& f) { 227 | PoolPtr p(m_objs.begin().ptr); 228 | typename DList::iterator it(p.get()); 229 | while( it != m_objs.end()) { 230 | // need to get the reference to the next one before it is possibly destroyed (delay its destruction) 231 | ++it; 232 | PoolPtr nextp( (it != m_objs.end()) ? (it.ptr) : nullptr ); 233 | if (!f(p)) 234 | return false; 235 | p = nextp; 236 | } 237 | return true; 238 | } 239 | 240 | int size() const { 241 | return m_objs.size(); 242 | } 243 | T* listHead() { 244 | return m_objs.begin().get(); 245 | } 246 | 247 | // the problem this avoids is that if the object we're on is just freed 248 | // since it was part of a circle, we can't get to the next one 249 | template 250 | void gradForeach(const F& f) { 251 | while (true) { 252 | m_hadRemove = false; 253 | auto it = m_objs.begin(); 254 | for(; it != m_objs.end(); ++it) { 255 | f(PoolPtr(it.get())); 256 | if (m_hadRemove) // maybe someone removed the iterator. 257 | break; 258 | } 259 | if (it == m_objs.end()) 260 | break; 261 | } 262 | } 263 | 264 | int countRefs() { 265 | int sum = 0; 266 | foreach([&](const PoolPtr& p)->bool { 267 | sum += p->count.count - 1; 268 | return true; 269 | }); 270 | return sum; 271 | } 272 | 273 | private: 274 | DList m_objs; 275 | bool m_hadRemove; // used in gradForeach 276 | //uint64 m_lastTimePrintedObjCount; 277 | 278 | }; 279 | 280 | -------------------------------------------------------------------------------- /src/OpImp.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "PyVM.h" 13 | 14 | 15 | class OpImp { 16 | public: 17 | OpImp(PyVM *_vm) :vm(_vm) {} 18 | 19 | template 20 | ObjRef addType(Object *lhs, Object *rhs); 21 | ObjRef add(const ObjRef& lhsref, const ObjRef& rhsref); 22 | 23 | template 24 | ObjRef multType(Object *lhs, Object *rhs); 25 | template 26 | ObjRef multStr(Object *str, Object *num); 27 | ObjRef mult(const ObjRef& lhsref, const ObjRef& rhsref); 28 | 29 | template 30 | ObjRef subType(Object *lhs, Object *rhs); 31 | ObjRef sub(const ObjRef& lhsref, const ObjRef& rhsref); 32 | 33 | template 34 | ObjRef divType(Object *lhs, Object *rhs); 35 | ObjRef div(const ObjRef& lhsref, const ObjRef& rhsref); 36 | 37 | template 38 | ObjRef minusType(Object *arg); 39 | ObjRef uminus(const ObjRef& argref); 40 | 41 | ObjRef uplus(const ObjRef& argref); 42 | ObjRef unot(const ObjRef& argref); 43 | 44 | template 45 | ObjRef makeListFromStack(Frame& frame, int count); 46 | void print(const ObjRef& vref, ostream& out); 47 | 48 | ObjRef len(const ObjRef& arg); 49 | 50 | ObjRef hash(const ObjRef& arg) { 51 | return vm->alloc(new IntObject(hashNum(arg))); 52 | } 53 | ObjRef str(const ObjRef& arg) { 54 | return vm->alloc(new StrObject(stdstr(arg, false))); 55 | } 56 | ObjRef repr(const ObjRef& arg) { 57 | return vm->alloc(new StrObject(stdstr(arg, true))); 58 | } 59 | ObjRef hex(const ObjRef& n) { 60 | int64 num = checked_cast(n)->v; 61 | stringstream ss; ss << "0x" << std::hex << num; 62 | return vm->alloc(new StrObject(ss.str())); 63 | } 64 | ObjRef int_(const ObjRef& arg); 65 | ObjRef bool_(const ObjRef& arg); 66 | 67 | bool compare(const ObjRef& lhsref, const ObjRef& rhsref, int op); 68 | bool compareList(const ListObject* lhs, const ListObject* rhs, int op); 69 | bool operIn(const ObjRef& lhs, const ObjRef& rhs, bool isPositive); 70 | 71 | ObjRef runtime_import(const ObjRef& filenameObj); 72 | 73 | ObjRef apply_slice(const ObjRef& o, int* startp, int* endp); 74 | 75 | private: 76 | PyVM* vm; 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /src/PyCompile.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "PyCompile.h" 12 | #include "log.h" 13 | 14 | #if USE_CPYTHON 15 | 16 | #define HAVE_ROUND // newer VS has this, uncomment in older VS 17 | #undef _DEBUG // don't get the debug lib 18 | #include 19 | #include 20 | #include 21 | 22 | bool initPython() 23 | { 24 | if (Py_IsInitialized()) 25 | return true; 26 | 27 | Py_Initialize(); 28 | if (Py_IsInitialized()) 29 | return true; 30 | 31 | return false; 32 | } 33 | 34 | // if the GIL needs to be locked, do that before calling this function 35 | bool compileTextToPycBuf(const std::string& text, const std::string& filename, std::string* outpyc) 36 | { 37 | if (!initPython()) 38 | return false; 39 | 40 | PyObject *pco = Py_CompileString(text.c_str(), filename.c_str(), Py_file_input); 41 | if (pco == nullptr) { 42 | LOG_ERROR("Error compiling python code"); 43 | PyErr_Print(); 44 | return false; 45 | } 46 | 47 | PyObject* pycstr = PyMarshal_WriteObjectToString(pco, 2); 48 | *outpyc = std::string(PyString_AsString(pycstr), PyString_Size(pycstr)); 49 | 50 | Py_DECREF(pycstr); 51 | Py_DECREF(pco); 52 | 53 | return true; 54 | } 55 | 56 | 57 | std::string getInteractiveLine() 58 | { 59 | bool inited = false; 60 | if (!inited) { 61 | if (!initPython()) 62 | return std::string(); 63 | inited = true; 64 | } 65 | 66 | int errcode = 0; 67 | PyArena *arena = PyArena_New(); 68 | mod_ty mod = PyParser_ASTFromFile(stdin, "blafilename", Py_single_input, ">>> ", "... ", nullptr, &errcode, arena); 69 | if (mod == nullptr) { 70 | PyArena_Free(arena); 71 | return std::string(); 72 | } 73 | 74 | PyCodeObject *pco = PyAST_Compile(mod, "blafilename", nullptr, arena); 75 | PyArena_Free(arena); 76 | 77 | PyObject* pycstr = PyMarshal_WriteObjectToString((PyObject*)pco, 2); 78 | std::string pycline = std::string(PyString_AsString(pycstr), PyString_Size(pycstr)); 79 | 80 | Py_DECREF(pycstr); 81 | Py_DECREF(pco); 82 | 83 | return pycline; 84 | } 85 | 86 | #endif // HAS_CPYTHON -------------------------------------------------------------------------------- /src/PyCompile.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include 12 | 13 | #if USE_CPYTHON 14 | 15 | bool compileTextToPycBuf(const std::string& text, const std::string& filename, std::string* outpyc); 16 | std::string getInteractiveLine(); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /src/PyVM.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "objects.h" 12 | #include "PyVM.h" 13 | #include "log.h" 14 | #include "utils.h" 15 | #include "PyCompile.h" 16 | 17 | #include 18 | #include 19 | 20 | 21 | // int g_maxStackSize; 22 | 23 | //map g_lookups; 24 | 25 | template ObjRef PyVM::makeFromT(const PoolPtr& v); 26 | 27 | 28 | 29 | void Frame::argsFromStack(Frame& from, int posCount, int kwCount, CallArgs& args) { 30 | for(int i = 0; i < kwCount; ++i) { 31 | ObjRef v = from.pop(); 32 | ObjRef k = from.pop(); 33 | args.kw[extract(k)] = v; 34 | } 35 | args.pos.reserve(posCount + 1); // avoid push_back allocating - all positional args + possible self 36 | for(int i = 0; i < posCount; ++i) { 37 | args.pos.push_back(from.pop()); 38 | } 39 | from.pop(); // the callable object 40 | } 41 | 42 | /* 43 | static void testNoneAndSet(NameDict& dest, const string& name, const ObjRef& v) { 44 | auto it = dest.find(name); 45 | CHECK(it == dest.end(), "Key already there `" << name << "`"); 46 | dest[name] = v; 47 | } 48 | */ 49 | 50 | static void testNoneAndSet(Frame::TFastLocalsList& dest, int index, const ObjRef& v) { 51 | CHECK(index < dest.size() && dest[index].isNull(), "Argument already threre " << index); 52 | dest[index] = v; 53 | } 54 | 55 | void Frame::localsFromStack(Frame& from, ObjRef self, int posCount, int kwCount) 56 | { 57 | const CodeDefinition& c = code()->m_co; 58 | int selfCount = self.isNull()?0:1; 59 | 60 | auto& dest = m_fastlocals; // size initialized with co_nlocals which includes the arguments and the *argv 61 | TupleObjRef starArgs; 62 | 63 | CHECK(checkFlag(c.co_flags, (uint)MCO_NEWLOCALS), "co_flags doesn't have CO_NEWLOCALS"); // using fast locals 64 | CHECK(!checkFlag(c.co_flags, (uint)MCO_VARKEYWORDS), "CO_VARKEYWORDS not supported"); 65 | if (!checkFlag(c.co_flags, (uint)MCO_VARARGS)) { 66 | CHECK(posCount + kwCount + selfCount == c.co_argcount, "unexpected number of arguments " << posCount + kwCount + selfCount << "!=" << c.co_argcount); 67 | } 68 | else { 69 | starArgs = m_vm->alloct(new TupleObject); 70 | testNoneAndSet(dest, c.co_argcount, static_pcast(starArgs)); 71 | } 72 | // go over the args in the stack, match to locals 73 | for(int i = 0; i < kwCount; ++i) { // arguments passed by key-value 74 | ObjRef val = from.pop(); 75 | string aname = checked_cast(from.pop())->v; 76 | int ci = 0; 77 | for(; ci < (int)c.co_argcount; ++ci) { 78 | const string& cname = c.co_varnames[ci]; 79 | if (cname == aname) { 80 | //testNoneAndSet(dest, cname, val); 81 | testNoneAndSet(dest, ci, val); 82 | break; 83 | } 84 | } 85 | CHECK(ci < (int)c.co_argcount, "Unknown key argument name " << aname); 86 | } 87 | for(int i = 0; i < posCount; ++i) { // arguments passed by position 88 | int posi = selfCount + posCount - i - 1; 89 | ObjRef a = from.pop(); 90 | //testNoneAndSet(dest, c.co_varnames(posi), from.pop()); 91 | if (posi < (int)c.co_argcount) 92 | testNoneAndSet(dest, posi, a); 93 | else 94 | starArgs->prepend(a); // arguments received by *args 95 | } 96 | if (selfCount) { 97 | //testNoneAndSet(dest, c.co_varnames(0), self); 98 | testNoneAndSet(dest, 0, self); 99 | } 100 | // check that all args were assigned: (redundant) 101 | // for(int i = 0; i < (int)c.co_argcount; ++i) { 102 | // CHECK(dest.find(c.co_varnames(i)) != dest.end(), "argument did not get a value " << c.co_varnames(i)); 103 | // } 104 | from.pop(); // the callable object 105 | } 106 | 107 | NameDict& Frame::globals() { 108 | return m_module->m_globals; 109 | } 110 | 111 | ObjRef Frame::lookupGlobal(const string& name) { 112 | ObjRef ret = tryLookup(globals(), name); 113 | if (!ret.isNull()) 114 | return ret; 115 | return m_vm->m_builtins->get(name); 116 | } 117 | 118 | ObjRef Frame::run() { 119 | EObjSlot retslot = SLOT_RETVAL; 120 | ObjRef retval; 121 | SetObjCallback setObj = [&](EObjSlot slot, const ObjRef& v) { 122 | retslot = slot; 123 | retval = v; 124 | }; 125 | 126 | do { 127 | doOpcode(setObj); 128 | } while (retval.isNull()); 129 | 130 | m_vm->m_lastFramei = m_lasti; 131 | m_retslot = retslot; 132 | return retval; 133 | } 134 | 135 | void Frame::clear() { 136 | m_code.reset(); 137 | m_module.reset(); 138 | m_stack.clear(); // vector clear 139 | m_fastlocals.clear(); // container clear 140 | } 141 | 142 | void Frame::setCode(const CodeObjRef& code) { 143 | m_code = code; 144 | m_fastlocals.resize(m_code->m_co.co_nlocals); 145 | } 146 | 147 | 148 | ObjRef FuncObject::call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) { 149 | frame.setCode(m_code); 150 | frame.localsFromStack(from, self, posCount, kwCount); 151 | return frame.run(); 152 | } 153 | 154 | 155 | ObjRef CFuncObject::call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) { 156 | CallArgs args; 157 | frame.argsFromStack(from, posCount, kwCount, args); 158 | if (!self.isNull()) 159 | args.pos.push_back(self); 160 | int fcount = wrap->argsCount(); 161 | if (fcount != -1) // means variable number of arguments, see cfunc.h 162 | CHECK(args.pos.size() == fcount && args.kw.size() == 0, "Wrong number of arguments " << args.pos.size() << "!=" << fcount); 163 | ObjRef ret = wrap->call(args); 164 | return ret; 165 | } 166 | 167 | 168 | ObjRef MethodObject::call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& inself) { 169 | // calling unbounded method is possible with the first argument as self. unlike CPython, the first argument is not checked to be of the right class 170 | return m_func->call(from, frame, posCount, kwCount, ObjRef(m_self)); 171 | } 172 | 173 | /* 174 | void ClassObject::makeMethods(const InstanceObjRef& i) { 175 | // first let the parent put its method 176 | if (!m_base.isNull()) 177 | m_base->makeMethods(i); 178 | // then this class's method, possibly overrriding 179 | if (!m_methods.isNull()) { 180 | for(auto it = m_methods->v.begin(); it != m_methods->v.end(); ++it) { 181 | if (it->second->type == METHOD) 182 | i->m_dict[it->first] = m_vm->alloc(new MethodObject( ((MethodObject*)it->second.get())->m_func ,i)); 183 | } 184 | } 185 | } 186 | */ 187 | 188 | ObjRef ClassObject::call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) 189 | { 190 | InstanceObjRef i(frame.m_vm->alloct(new InstanceObject(ClassObjRef(this)))); 191 | // set up methods with self 192 | //makeMethods(i); 193 | ObjRef inito = i->simple_attr("__init__"); 194 | if (!inito.isNull()) { 195 | MethodObjRef init = checked_cast(inito); 196 | ObjRef ret = init->call(from, frame, posCount, kwCount, ObjRef()); 197 | CHECK(ret.isNull() || ret->type == Object::NONE, "__init__() must return None"); 198 | } // we can either call __init__() or the cpp ctor, not both since the arguments are removed from the stack 199 | else if (!m_cwrap.isNull() && !m_cwrap->m_ctor.isNull()) { 200 | CallArgs args; 201 | frame.argsFromStack(from, posCount, kwCount, args); 202 | i->m_cwrap = m_cwrap->m_ctor->construct(m_vm, args); 203 | } 204 | 205 | return ObjRef(i); 206 | } 207 | 208 | //--------------------------------------- VM ------------------------------------------------------ 209 | 210 | PyVM::PyVM() 211 | : m_out(new LoggerPrinter(LOGLEVEL_DEBUG)) 212 | , m_currentFrame(nullptr), m_lastFramei(-1) 213 | , m_noneObject(alloc(new Object)), m_trueObject(alloc(new BoolObject(true))), m_falseObject(alloc(new BoolObject(false))) 214 | { 215 | m_defaultModule = alloct(new ModuleObject("__main__", this)); 216 | m_builtins = alloct(new Builtins(this)); 217 | } 218 | 219 | PyVM::~PyVM() { 220 | // m_defaultModule 221 | clear(); 222 | } 223 | 224 | string PyVM::instructionPointer() { 225 | std::ostringstream os; 226 | os << "(" << m_lastFramei << ") "; 227 | Frame* f = m_currentFrame; 228 | while (f != nullptr) { 229 | os << f->m_lasti << " < "; 230 | f = f->m_lastFrame; 231 | } 232 | return os.str(); 233 | } 234 | 235 | // where all functions go to and out of 236 | ObjRef PyVM::callFunction(Frame& from, int posCount, int kwCount) { 237 | ObjRef func = from.m_stack.peek(posCount + kwCount*2); 238 | 239 | func->checkProp(Object::ICALLABLE); 240 | CallableObjRef funcref = static_pcast(func); 241 | 242 | NameDict locals; 243 | Frame frame(this, funcref->m_module, &locals); // module needed for globals 244 | try { 245 | return funcref->call(from, frame, posCount, kwCount, ObjRef()); 246 | } 247 | catch(PyException& e) { 248 | std::ostringstream s; 249 | s << "in " << funcref->funcname() << " "; 250 | if (!frame.code().isNull()) 251 | s << frame.code()->lineFromIndex(frame.m_lasti) << " "; 252 | s << "[" << frame.m_lasti << "]"; 253 | e.addTrack(s.str()); 254 | throw; 255 | } 256 | 257 | } 258 | 259 | 260 | 261 | ObjRef PyVM::eval(const CodeObjRef& code, ModuleObjRef module) { 262 | validateCode(code); 263 | Frame frame(this, module, &module->m_globals); // in the module top level execution locals()==globals() 264 | frame.setCode(code); 265 | return frame.run(); 266 | } 267 | 268 | ModuleObjRef PyVM::addEmptyModule(const string& name) { 269 | ModuleObjRef module(alloct(new ModuleObject(name, this))); 270 | module->m_globals["__name__"] = alloc(new StrObject(name)); 271 | m_modules[name] = module; 272 | return module; 273 | } 274 | 275 | /* 276 | ModuleObjRef PyVM::importModule(const CodeDefinition& moduleDef, const string& name) 277 | { // co_name would always be so there's no sense in this 278 | string extName = name.empty() ? moduleDef.co_name : name; 279 | auto module = addEmptyModule(extName); 280 | eval(moduleDef, module); 281 | return module; 282 | } 283 | */ 284 | 285 | ModuleObjRef PyVM::importPycStream(istream& is, const string& path, bool hasHeader) 286 | { 287 | ObjRef obj = CodeDefinition::parsePyc(is, this, hasHeader); 288 | auto code = checked_cast(obj); 289 | string modName; 290 | if (!code->m_co.co_filename.empty()) 291 | modName = extractFileNameWithoutExtension(code->m_co.co_filename); 292 | else if (!code->m_co.co_name.empty() && code->m_co.co_name != "") 293 | modName = code->m_co.co_name; 294 | else { 295 | CHECK(!path.empty(), "Missing module name"); 296 | modName = extractFileNameWithoutExtension(path); 297 | } 298 | auto module = addEmptyModule(modName); 299 | eval(code, module); 300 | return module; 301 | } 302 | 303 | ModuleObjRef PyVM::importPycFile(const string& pycpath) 304 | { 305 | ifstream ifs(pycpath, ios::binary); 306 | if (!ifs.good()) 307 | return ModuleObjRef(); 308 | return importPycStream(ifs, pycpath, true); 309 | } 310 | 311 | ModuleObjRef PyVM::importPycBuf(const string& pyctext, bool hasHeader) 312 | { 313 | istringstream iss(pyctext); 314 | return importPycStream(iss, string(), hasHeader); 315 | } 316 | 317 | 318 | ModuleObjRef PyVM::getModule(const string& name) { 319 | ModuleObjRef mod = tryLookup(m_modules, name); 320 | if (!mod.isNull()) 321 | return mod; 322 | if (m_importCallback) { 323 | auto impret = m_importCallback(name); 324 | if (impret.first.get() != nullptr) 325 | return importPycStream(*impret.first.get(), name, impret.second); 326 | } 327 | THROW("Did not find module " << name); 328 | } 329 | 330 | vector split(const string &s, char delim) { 331 | vector elems; 332 | stringstream ss(s); 333 | string item; 334 | while (std::getline(ss, item, delim)) { 335 | elems.push_back(item); 336 | } 337 | return elems; 338 | } 339 | 340 | // restrictions of names: 341 | // - code can't import one module to another 342 | // - can't call one class from another 343 | ObjRef PyVM::lookupQual(const string& name, ModuleObjRef* mod) 344 | { 345 | ModuleObjRef module = m_defaultModule; 346 | string funcname = name; 347 | if (funcname.find('.') != string::npos) { 348 | vector names = split(funcname, '.'); 349 | CHECK(names.size() == 2, "Only one module nesting allowed: " << name); 350 | module = getModule(names[0]); 351 | funcname = names[1]; 352 | } 353 | ObjRef func = module->attr(funcname); 354 | if (!func.isNull()) { 355 | if (mod) 356 | *mod = module; 357 | return func; 358 | } 359 | func = m_builtins->attr(funcname); 360 | if (!func.isNull()) { 361 | if (mod) 362 | *mod = static_pcast(m_builtins); 363 | return func; 364 | } 365 | THROW("Did not find function " << name); 366 | } 367 | 368 | // from cpp code 369 | ObjRef PyVM::callv(const string& funcname, const vector& posargs) { 370 | //ModuleObjRef module; 371 | ObjRef func = lookupQual(funcname, nullptr); // parse module.funcname and get the object 372 | return callv(func, posargs); 373 | } 374 | 375 | // from cpp code 376 | ObjRef PyVM::callv(const ObjRef& ofunc, const vector& posargs) { 377 | ofunc->checkProp(Object::ICALLABLE); 378 | CallableObjRef func = static_pcast(ofunc); 379 | Frame dummyFrame(this, func->m_module, nullptr); 380 | dummyFrame.push(ofunc); 381 | for(auto it = posargs.begin(); it != posargs.end(); ++it) 382 | dummyFrame.push(*it); 383 | return callFunction(dummyFrame, (int)posargs.size(), 0); 384 | } 385 | 386 | void PyVM::addGlobalFunc(const CodeDefinition& cdef) { 387 | m_defaultModule->addGlobal(alloc(new FuncObject(alloct(new CodeObject(cdef)), m_defaultModule)), cdef.co_name); 388 | } 389 | 390 | void PyVM::memDump(ostream& os) { 391 | os << "Memory Dump: " << m_alloc.size() << " objects\n"; 392 | m_alloc.foreach([&](const ObjRef& o)->bool { 393 | os << "(" << o.use_count() << ") " << o->typeName() << ": " << stdstr(o, true) << "\n"; 394 | return true; 395 | }); 396 | } 397 | 398 | void PyVM::clear() { 399 | LOG_DEBUG("PyVM clear with ", m_alloc.size(), " objects"); 400 | m_modules.clear(); 401 | m_alloc.gradForeach([&](const ObjRef& o) { 402 | o->clear(); 403 | }); 404 | 405 | } 406 | 407 | void PyVM::addBuiltin(const ClassObjRef& v) { 408 | addBuiltin(v->funcname(), ObjRef(v) ); 409 | } 410 | 411 | #if USE_CPYTHON 412 | 413 | void PyVM::runInteractive() 414 | { 415 | while (true) { 416 | string pycline = getInteractiveLine(); 417 | if (pycline.empty()) 418 | break; 419 | istringstream iss(pycline); 420 | ObjRef c = CodeDefinition::parsePyc(iss, this, false); 421 | eval(dynamic_pcast(c), mainModule()); 422 | } 423 | } 424 | 425 | #endif -------------------------------------------------------------------------------- /src/PyVM.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "defs.h" 13 | #include "ObjPool.h" 14 | #include "baseObject.h" 15 | #include "VarArray.h" 16 | #include "log.h" 17 | #include "CodeDefinition.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #ifdef USE_BOOST 27 | #include 28 | #endif 29 | 30 | #pragma warning (disable: 4355) // 'this' : used in base member initializer list 31 | 32 | using namespace std; 33 | 34 | 35 | class ModuleObject; 36 | typedef PoolPtr ModuleObjRef; 37 | class Builtins; 38 | typedef PoolPtr BuiltinsObjRef; 39 | class CodeObject; 40 | typedef PoolPtr CodeObjRef; 41 | class ClassObject; 42 | typedef PoolPtr ClassObjRef; 43 | 44 | typedef map ModulesDict; 45 | 46 | #ifdef USE_BOOST 47 | typedef boost::container::flat_map NameDict; 48 | #else 49 | typedef map NameDict; 50 | #endif 51 | 52 | typedef map IntDict; 53 | 54 | 55 | string stdstr(const ObjRef& vref, bool repr=false); 56 | void print(const ObjRef& vref, ostream& out, bool repr); 57 | int vmVersion(); 58 | 59 | class PyVM; 60 | class Frame; 61 | 62 | enum EObjSlot { 63 | SLOT_RETVAL = 0, 64 | SLOT_YIELD = 1 65 | }; 66 | 67 | typedef std::function SetObjCallback; 68 | 69 | //extern int g_maxStackSize; 70 | 71 | template 72 | class Stack { 73 | public: 74 | void push(const T& ref) { 75 | m_stack.push_back(ref); 76 | //if (g_maxStackSize < m_stack.size()) 77 | // g_maxStackSize = (int)m_stack.size(); 78 | } 79 | // push an element some distance from the top. pushAt(0,r) is equivalent to push(r) 80 | void pushAt(int fromTop, const T& ref) { 81 | CHECK((int)m_stack.size() >= fromTop, "pushAt underflow"); 82 | //m_stack.insert(m_stack.size() - fromTop, ref); 83 | m_stack.insert(m_stack.end() - fromTop, ref); 84 | } 85 | T pop() { 86 | CHECK(m_stack.size() > 0, "stack underflow"); 87 | ObjRef r = m_stack.back(); 88 | m_stack.pop_back(); 89 | return r; 90 | } 91 | 92 | // i - offset from the top. 0=TOS 93 | T peek(int i) { 94 | CHECK((int)m_stack.size() > i, "peek underflow"); 95 | return m_stack[m_stack.size() - 1 - i]; 96 | } 97 | T top() { 98 | return peek(0); 99 | } 100 | int size() const { 101 | return (int)m_stack.size(); 102 | } 103 | void clear() { 104 | m_stack.clear(); 105 | } 106 | vector data() { // for testers 107 | vector r; 108 | m_stack.foreach([&](const T& v) { r.push_back(v); }); 109 | //r = m_stack; 110 | return r; 111 | } 112 | void reserve(int sz) { 113 | m_stack.reserve(sz); 114 | } 115 | private: 116 | // on average operation the stack does not go over 6 items 117 | VarArray m_stack; 118 | //vector m_stack; 119 | }; 120 | 121 | extern map g_lookups; 122 | 123 | template 124 | T tryLookup(const map& d, const string& name) { 125 | auto it = d.find(name); 126 | if (it == d.end()) 127 | return T(); 128 | return it->second; 129 | } 130 | 131 | inline ObjRef tryLookup(const NameDict& d, const string& name) { 132 | //++g_lookups[name]; 133 | auto it = d.find(name); 134 | if (it == d.end()) 135 | return ObjRef(); 136 | return it->second; 137 | } 138 | 139 | template 140 | T lookup(const map& d, const string& name) { 141 | auto it = d.find(name); 142 | CHECK(it != d.end(), "KeyError: could not find `" << name << "`"); 143 | return it->second; 144 | } 145 | 146 | inline ObjRef lookup(const NameDict& d, const string& name) { 147 | //++g_lookups[name]; 148 | auto it = d.find(name); 149 | CHECK(it != d.end(), "KeyError: could not find `" << name << "`"); 150 | return it->second; 151 | } 152 | 153 | //struct nullptrType {}; 154 | //template 155 | //struct IsType { enum { nullptrType = false; }; }; 156 | //template<> struct IsType { enum { nullptrType = true }; }; 157 | 158 | 159 | class StreamPrinter { 160 | public: 161 | StreamPrinter(ostream* os = nullptr) : m_os(os) {} 162 | virtual ~StreamPrinter() {} 163 | virtual void endL() { 164 | if (m_os != nullptr) 165 | (*m_os) << std::endl; 166 | } 167 | public: 168 | ostream* m_os; 169 | }; 170 | 171 | class LoggerPrinter : public StreamPrinter { 172 | public: 173 | LoggerPrinter(LogLevel lvl) : m_lvl(lvl) { 174 | m_os = &m_s; 175 | } 176 | virtual void endL() { 177 | log(m_lvl, m_s.str()); 178 | m_s.str(string()); // clear the buffer 179 | } 180 | private: 181 | DISALLOW_COPY_AND_ASSIGN(LoggerPrinter); // needed because we're taking m_s address 182 | std::ostringstream m_s; 183 | LogLevel m_lvl; 184 | }; 185 | 186 | 187 | class PyVM { 188 | public: 189 | PyVM(); 190 | ~PyVM(); 191 | 192 | void setStdout(ostream* s) { 193 | m_out.reset(new StreamPrinter(s)); 194 | } 195 | 196 | void addGlobalFunc(const CodeDefinition& code); 197 | 198 | ObjRef callv(const ObjRef& func, const vector& posargs); 199 | ObjRef callv(const string& funcname, const vector& posargs); 200 | 201 | template 202 | ObjRef call(const ObjRef& func, Args&&... args) { 203 | vector argv({ makeFromT(args)... }); 204 | return callv(func, argv); 205 | } 206 | 207 | template 208 | ObjRef call(const string& funcname, Args&&... args) { 209 | vector argv({ makeFromT(args)... }); 210 | return callv(funcname, argv); 211 | } 212 | 213 | ObjRef eval(const CodeObjRef& code, ModuleObjRef module); 214 | 215 | ModuleObjRef addEmptyModule(const string& name); 216 | // ModuleObjRef importModule(const CodeDefinition& moduleDef, const string& name); 217 | ModuleObjRef getModule(const string& name); 218 | 219 | ModuleObjRef importPycStream(istream& is, const string& path, bool hasHeader); 220 | ModuleObjRef importPycFile(const string& pycpath); 221 | ModuleObjRef importPycBuf(const string& pyctext, bool hasHeader = false); 222 | 223 | // void addDummyModule(const string& name) { 224 | // m_modules[name] = ModuleObjRef(); 225 | // } 226 | 227 | void clear(); 228 | 229 | ObjRef alloc(Object *o) { 230 | return m_alloc.add(o); 231 | } 232 | template 233 | PoolPtr alloct(T* t) { 234 | Object* o = static_cast(t); 235 | ObjRef r = m_alloc.add(o); 236 | return static_pcast(r); 237 | } 238 | 239 | template 240 | ObjRef makeFromT(T v); 241 | template 242 | ObjRef makeFromT(const PoolPtr& v) { return ObjRef(v); } 243 | ObjRef makeNone(void) { 244 | return m_noneObject; 245 | } 246 | template // give the object type to construct 247 | ObjRef makeFromT2(const InitT& v); 248 | 249 | template 250 | ObjRef makeTuple(const A1& a1, const A2& a2); 251 | 252 | // memory introspection 253 | void memDump(ostream& os); 254 | int countObjects() { 255 | return m_alloc.size(); 256 | } 257 | 258 | void validateCode(const CodeObjRef& def); // in instruction.cpp 259 | 260 | ModuleObjRef mainModule() { 261 | return m_defaultModule; 262 | } 263 | template // T should be some Object 264 | void addBuiltin(const string& name, const PoolPtr& v); 265 | void addBuiltin(const ClassObjRef& v); // name taken from the class 266 | 267 | string instructionPointer(); 268 | ObjRef lookupQual(const string& name, ModuleObjRef* mod); 269 | ObjPool& objPool() { 270 | return m_alloc; 271 | } 272 | const ModulesDict& modules() const { 273 | return m_modules; 274 | } 275 | 276 | // the import callback returns a pair with the stream to read the pyc from and a bool that says if the stream has a header 277 | typedef std::function,bool>(const string&)> TImportCallback; 278 | 279 | void setImportCallback(TImportCallback callback) { 280 | m_importCallback = callback; 281 | } 282 | Frame* currentFrame() { 283 | return m_currentFrame; 284 | } 285 | 286 | #if USE_CPYTHON 287 | void runInteractive(); 288 | #endif 289 | 290 | private: 291 | DISALLOW_COPY_AND_ASSIGN(PyVM); 292 | friend class Frame; 293 | friend class OpImp; 294 | 295 | ObjRef callFunction(Frame& from, int posCount, int kwCount); 296 | 297 | private: 298 | ObjPool m_alloc; // must be first member so it would be destructed last, after all references are down 299 | 300 | std::unique_ptr m_out; 301 | ModuleObjRef m_defaultModule; // module of __main__ 302 | BuiltinsObjRef m_builtins; 303 | ModulesDict m_modules; // this is sys.modules 304 | ObjRef m_noneObject, m_trueObject, m_falseObject; 305 | TImportCallback m_importCallback; 306 | 307 | // used for debugging 308 | Frame* m_currentFrame; // managed by Frame object c'tor and d'tor 309 | int m_lastFramei; // the m_lasti of the last frame that returned 310 | }; 311 | 312 | 313 | template<> inline ObjRef PyVM::makeFromT(ObjRef v) { 314 | return v; 315 | } 316 | int64 hashStr(const string& s); 317 | 318 | // this class is istantiated on the stack to save the current state of the object pool 319 | // when it goes out of scope it calls 'clear()' for all objects created after it's instantiation 320 | // This clears out any reference circles which occured in the run of the python code to avoid memory leaks. 321 | // NOTICE: this also means that there should not be globally referenced objects created in that scope. 322 | class StateClearer { 323 | public: 324 | StateClearer(PyVM* vm) :m_vm(vm), m_savedHead(vm->objPool().listHead()) { 325 | } 326 | ~StateClearer() { 327 | try { 328 | bool tillend = m_vm->objPool().foreach([this](const ObjRef& o)->bool { 329 | if (o.get() == m_savedHead.get()) 330 | return false; // stop iteration 331 | o->clear(); 332 | return true; 333 | }); 334 | if (tillend) 335 | LOG_ERROR("!!!!! StateClearer went too far (did not find savedHead)"); 336 | } 337 | catch(const PyException& e) { 338 | LOG_ERROR("!!!!! Caught exception in StateClearer ", e.what()); 339 | } 340 | } 341 | private: 342 | ObjRef m_savedHead; 343 | PyVM* m_vm; 344 | }; 345 | 346 | 347 | struct Block { 348 | Block(int _type, int _handler, int _stackSize) :type(_type), handlerAddr(_handler), stackSize(_stackSize) {} 349 | int type; 350 | int handlerAddr; 351 | int stackSize; 352 | }; 353 | 354 | struct CallArgs { 355 | // usually there are no more than 4 positional arguments for an internal function 356 | typedef VarArray TPosVector; 357 | 358 | TPosVector pos; 359 | NameDict kw; 360 | 361 | ObjRef operator[](int i) { 362 | return pos[i]; 363 | } 364 | void posReverse() { 365 | std::reverse(pos.begin(), pos.end()); 366 | } 367 | }; 368 | 369 | 370 | class Frame 371 | { 372 | public: 373 | Frame(PyVM* vm, const ModuleObjRef& module, NameDict *locals) 374 | :m_lasti(0), m_vm(vm), m_module(module), m_locals(locals), m_retslot(SLOT_RETVAL) 375 | { 376 | m_lastFrame = m_vm->m_currentFrame; 377 | m_vm->m_currentFrame = this; 378 | //m_stack.reserve(6); 379 | } 380 | ~Frame() { 381 | m_vm->m_currentFrame = m_lastFrame; 382 | } 383 | 384 | void push(const ObjRef& ref) { 385 | m_stack.push(ref); 386 | } 387 | ObjRef pop() { 388 | return m_stack.pop(); 389 | } 390 | ObjRef top() { 391 | return m_stack.top(); 392 | } 393 | ObjRef alloc(Object *o) { 394 | return m_vm->alloc(o); 395 | } 396 | 397 | void pushBlock(int type, int handler) { 398 | m_blocks.push_back(Block(type, handler, m_stack.size())); 399 | } 400 | Block popBlock() { 401 | CHECK(m_blocks.size() > 0, "block stack underflow"); 402 | Block r = m_blocks.back(); 403 | m_blocks.pop_back(); 404 | 405 | CHECK(r.stackSize <= m_stack.size(), "Wrong stack size"); 406 | while (r.stackSize < m_stack.size()) 407 | m_stack.pop(); 408 | 409 | return r; 410 | } 411 | 412 | void doOpcode(SetObjCallback& setObj); 413 | ObjRef lookupGlobal(const string& name); 414 | 415 | void argsFromStack(Frame& from, int posCount, int kwCount, CallArgs& args); 416 | // void localsFromArgs(const vector& args); 417 | void localsFromStack(Frame& from, ObjRef self, int posCount, int kwCount); 418 | 419 | NameDict& locals() { return *m_locals; } 420 | NameDict& globals(); 421 | ObjRef run(); 422 | 423 | void clear(); 424 | 425 | void setCode(const CodeObjRef& code); 426 | const CodeObjRef& code() { 427 | return m_code; 428 | } 429 | 430 | public: 431 | uint m_lasti; // index in the code object string of the curret instruction 432 | NameDict *m_locals; // points to a dict on the stack 433 | PyVM* m_vm; 434 | ModuleObjRef m_module; // for globals, needs an objet to reference, m_globals is not an object 435 | vector m_blocks; // every for,try,except,finally,with is a block 436 | Frame* m_lastFrame; // previous frame on the stack 437 | Stack m_stack; // value stack 438 | EObjSlot m_retslot; // the slot filled with the return value after a function call returns 439 | 440 | // "usually" there are no more than 5 locals in a function 441 | typedef VarArray TFastLocalsList; 442 | 443 | private: 444 | CodeObjRef m_code; 445 | TFastLocalsList m_fastlocals; 446 | 447 | }; 448 | 449 | -------------------------------------------------------------------------------- /src/VarArray.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "defs.h" 13 | #include "except.h" 14 | 15 | 16 | // based on QVarLengthArray from QT 17 | // doesn't yet support move semantics 18 | template 19 | class VarArray { 20 | public: 21 | VarArray() 22 | : m_size(0), m_alloc(StaticSize), m_ptr(reinterpret_cast(m_arrbuf)), m_allocbuf(nullptr) 23 | {} 24 | ~VarArray() { 25 | clear(); 26 | } 27 | 28 | typedef T* iterator; 29 | typedef T value_type; 30 | typedef const T* const_iterator; 31 | 32 | void reserve(int newAlloc) { 33 | if (newAlloc > m_alloc) // means we need to allocate dynamically 34 | realloc(m_size, newAlloc); 35 | } 36 | // use this to pre-init a fixed number of default constructed objects 37 | void resize(int newSize) { 38 | realloc(newSize, imax(newSize, m_alloc)); 39 | } 40 | 41 | void clear() { 42 | T *i = m_ptr + m_size; 43 | while (i != m_ptr) { 44 | --i; 45 | i->~T(); 46 | } 47 | if (m_allocbuf != nullptr) { 48 | delete[] m_allocbuf; 49 | m_allocbuf = nullptr; 50 | } 51 | m_size = 0; 52 | m_alloc = StaticSize; 53 | m_ptr = reinterpret_cast(m_arrbuf); 54 | } 55 | 56 | void push_back(const T& v) { 57 | incAlloc(); 58 | int idx = m_size++; 59 | // placement new running copy constructor 60 | new (m_ptr + idx) T(v); 61 | } 62 | 63 | void pop_back() { 64 | ASSERT(m_size > 0, "Unexpected size 0"); 65 | --m_size; 66 | (m_ptr + m_size)->~T(); 67 | if (m_alloc > StaticSize && m_size < m_alloc / 2) 68 | realloc(m_size, m_alloc / 2); 69 | } 70 | 71 | T& back() { 72 | return m_ptr[m_size - 1]; 73 | } 74 | const T& back() const { 75 | return m_ptr[m_size - 1]; 76 | } 77 | 78 | void insert(iterator before, const T& v) { 79 | int beforeIdx = int(before - m_ptr); 80 | ASSERT(beforeIdx >= 0 && beforeIdx <= m_size, "Unexpected beforeIdx "); 81 | 82 | incAlloc(); 83 | ++m_size; 84 | 85 | T *base = m_ptr + beforeIdx; 86 | T *dst = m_ptr + m_size; 87 | T *src = dst - 1; 88 | new (src) T(); // new element at the end of the line, will be assigned to shortly 89 | 90 | while (src != base) { 91 | --dst; 92 | --src; 93 | *dst = *src; 94 | } 95 | // just copied from src which is base, can set value to base 96 | *base = v; 97 | } 98 | 99 | int size() const { 100 | return m_size; 101 | } 102 | 103 | 104 | T& operator[](int idx) { 105 | ASSERT(idx >= 0 && idx < m_size, "Unexpected idx"); 106 | return m_ptr[idx]; 107 | } 108 | const T& operator[](int idx) const { 109 | ASSERT(idx >= 0 && idx < m_size, "Unexpected idx"); 110 | return m_ptr[idx]; 111 | } 112 | 113 | template 114 | void foreach(F& f) const { 115 | for(int i = 0; i < m_size; ++i) { 116 | f(m_ptr[i]); 117 | } 118 | } 119 | 120 | iterator begin() { 121 | return m_ptr; 122 | } 123 | iterator end() { 124 | return m_ptr + m_size; 125 | } 126 | const_iterator begin() const { 127 | return m_ptr; 128 | } 129 | const_iterator end() const { 130 | return m_ptr + m_size; 131 | } 132 | 133 | private: 134 | // inc by one. make sure m_size can be incremented by 1 135 | void incAlloc() { 136 | if (m_size == m_alloc) 137 | realloc(m_size, m_size * 2); 138 | } 139 | 140 | // always for m_allocated 141 | void realloc(int newSize, int newAlloc) 142 | { 143 | CHECK(newAlloc >= newSize, "Unexpected alloc size"); 144 | 145 | T *oldPtr = m_ptr; 146 | int oldSize = m_size; 147 | const int copySize = imin(newSize, oldSize); 148 | 149 | if (newAlloc != m_alloc) // need to copy stuff 150 | { 151 | m_allocbuf = new char[newAlloc * sizeof(T)]; 152 | m_ptr = reinterpret_cast(m_allocbuf); 153 | m_size = 0; 154 | m_alloc = newAlloc; 155 | 156 | // copy all the old elements 157 | try { 158 | while (m_size < copySize) { 159 | new (m_ptr + m_size) T(*(oldPtr + m_size)); // copy c'tor. can throw 160 | (oldPtr + m_size)->~T(); 161 | m_size++; 162 | } 163 | } 164 | catch(...) { 165 | // the c'tor is going to clean up to m_size but we have stuff in the old array that needs to go as well 166 | int sClean = m_size; 167 | while (sClean < oldSize) 168 | (oldPtr + (sClean++))->~T(); 169 | if (oldPtr != (T*)(m_arrbuf) && oldPtr != m_ptr) 170 | delete[] reinterpret_cast(oldPtr); 171 | throw; 172 | } 173 | } 174 | m_size = copySize; 175 | // destroy remaining old objects 176 | while (oldSize > newSize) 177 | (oldPtr + (--oldSize))->~T(); 178 | 179 | if (oldPtr != (T*)m_arrbuf && oldPtr != m_ptr) 180 | delete[] reinterpret_cast(oldPtr); 181 | 182 | // call default constructor for new objects (which can throw) 183 | while (m_size < newSize) 184 | new (m_ptr + (m_size++)) T; 185 | } 186 | 187 | private: 188 | DISALLOW_COPY_AND_ASSIGN(VarArray); 189 | 190 | int m_size; // number of T elements added 191 | int m_alloc; // we have enough allocated for this many T elements 192 | // points to either m_arrray or equals to m_allocated. 193 | // once it goes to m_allocated, it never goes back to m_arrbuf 194 | T* m_ptr; 195 | // not an array of T since we don't want to call ctors 196 | char m_arrbuf[sizeof(T) * StaticSize]; 197 | char *m_allocbuf; 198 | }; 199 | 200 | 201 | -------------------------------------------------------------------------------- /src/baseObject.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "ObjPool.h" 13 | #include "except.h" 14 | 15 | struct Object; 16 | typedef PoolPtr ObjRef; 17 | class Frame; 18 | class PyVM; 19 | 20 | 21 | // Base Object inherits from these but and throw an exception 22 | // objects that are really supposed to inherit from these have IATTRABLE oe ICALLABLE 23 | // in typeProp. this is to avoid a consty dynamic_cast<> and to be able to implement my 24 | // own cheap dynamic_cast<> using this bitmask. 25 | 26 | struct IAttrable { 27 | // try to lookup the name, if not found, return nullptr ref 28 | virtual ObjRef attr(const string& name) = 0; 29 | virtual void setattr(const string& name, const ObjRef& o) = 0; 30 | }; 31 | 32 | struct ICallable { 33 | virtual ObjRef call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) = 0; 34 | virtual string funcname() const = 0; // for logging 35 | }; 36 | 37 | 38 | 39 | //#define PYVM_COUNT_OBJECTS 40 | 41 | #ifdef PYVM_COUNT_OBJECTS 42 | // to debug object creation and deletion define the above #define and 43 | // declare this variable in your tester code 44 | extern int g_pyvmObjectCount; 45 | #endif 46 | 47 | // this is a superset of ConstValue from proto 48 | struct Object : public IAttrable, public ICallable 49 | { 50 | public: 51 | enum Type { 52 | NONE = 0, 53 | BOOL = 1, 54 | INT = 2, 55 | STR = 3, 56 | TUPLE = 4, 57 | LIST = 5, 58 | DICT = 6, 59 | CODE = 7, 60 | MODULE = 8, 61 | FUNC = 9, 62 | CLASS = 10, 63 | INSTANCE = 11, 64 | METHOD = 12, 65 | FLOAT = 13, 66 | CINSTANCE_WRAP = 14, 67 | ITERATOR = 15, 68 | STRDICT = 16, 69 | PRIMITIVE_ADAPTER = 17, 70 | USTR = 18, 71 | CCTOR_WRAP = 19, 72 | GENERATOR = 20, 73 | CFUNC_WRAP = 21, 74 | SLICE = 22, 75 | XRANGE = 23 76 | }; 77 | enum TypeProp { 78 | IATTRABLE = 1, // see tryAs 79 | ICALLABLE = 2 80 | }; 81 | 82 | RefCount count; 83 | Type type; 84 | int typeProp; // used instead of costy dynamic_cast 85 | 86 | public: 87 | virtual ~Object() { 88 | #ifdef PYVM_COUNT_OBJECTS 89 | g_pyvmObjectCount--; 90 | #endif 91 | } 92 | // clear all internal references of an object, to cleanup any reference cycle 93 | // must only call 'reset()' of held ObjRefs. never recursively call other objects clear() since that could cause infinite recursion 94 | Object(Type _type = NONE) :type(_type), typeProp(0) { 95 | #ifdef PYVM_COUNT_OBJECTS 96 | g_pyvmObjectCount++; 97 | #endif 98 | } 99 | Object(Type _type, uint _typeProp) :type(_type), typeProp(_typeProp) { 100 | #ifdef PYVM_COUNT_OBJECTS 101 | g_pyvmObjectCount++; 102 | #endif 103 | } 104 | 105 | virtual void clear() {} 106 | 107 | virtual ObjRef attr(const string& name) { 108 | THROW("Unimplemented Object::attr"); 109 | } 110 | virtual void setattr(const string& name, const ObjRef& o) { 111 | THROW("Unimplemented Object::setattr"); 112 | } 113 | virtual ObjRef call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) { 114 | THROW("Unimplemented Object::call"); 115 | } 116 | virtual string funcname() const { 117 | THROW("Unimplemented Object::funcname"); 118 | } 119 | public: 120 | template 121 | static Object::Type typeValue(); 122 | 123 | inline void checkType(Object::Type t) { 124 | CHECK(type == t, "wrong type expected:" << typeName(t) << " got:" << typeName()); 125 | } 126 | inline void checkProp(Object::TypeProp p) { 127 | CHECK(checkFlag(typeProp, (int)p) == true, "wrong prop expected:" << typeProp << " got:" << p); 128 | } 129 | template // T is a primitive type like int or string 130 | inline void checkTypeT(); 131 | 132 | const char* typeName() { 133 | return typeName(type); 134 | } 135 | static const char* typeName(Type t); 136 | template 137 | static const char* typeName() { 138 | return typeName( Object::typeValue() ); 139 | } 140 | 141 | // get a pointer to an interface from the object. 142 | // Notice not to loose the original reference to the object for as long as this is needed. 143 | template 144 | T* as() { 145 | T* ret = dynamic_cast(this); 146 | CHECK(ret != nullptr, "Wrong type"); 147 | return ret; 148 | } 149 | // used for interfaces 150 | template 151 | T* tryAs() { 152 | return dynamic_cast(this); 153 | } 154 | private: 155 | DISALLOW_COPY_AND_ASSIGN(Object); 156 | 157 | }; 158 | 159 | template<> inline IAttrable* Object::tryAs() { 160 | if (checkFlag(typeProp, (int)IATTRABLE)) { 161 | return static_cast(this); 162 | } 163 | return nullptr; 164 | } 165 | template<> inline ICallable* Object::tryAs() { 166 | if (checkFlag(typeProp, (int)ICALLABLE)) { 167 | return static_cast(this); 168 | } 169 | return nullptr; 170 | } 171 | -------------------------------------------------------------------------------- /src/bufferaccess.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "objects.h" 13 | 14 | // used for raw memory access from python 15 | // write into and read from an existing buffer of memory 16 | class AccessBuffer 17 | { 18 | public: 19 | enum ESeekOrigin { ORIGIN_BEG = 0, ORIGIN_CUR }; // there is not ORIGIN_END since we don't know the size of the buffer 20 | // add this class to the given module 21 | static ClassObjRef addToModule(const ModuleObjRef& mod); 22 | 23 | AccessBuffer(PyVM* vm, size_t addr) : m_vm(vm), m_p((char*)addr), m_offset(0) 24 | {} 25 | int offset() { 26 | return m_offset; 27 | } 28 | void setOffset(int v) { 29 | m_offset = v; 30 | } 31 | 32 | ObjRef readPtr(); 33 | ObjRef readInt(int numBytes); 34 | // read a nullptr terminated string from the buffer 35 | ObjRef readCStr(); 36 | ObjRef readBuf(int len); 37 | ObjRef readWCStr(); 38 | void writeCStr(const string& s); 39 | 40 | // set the cursor to a position in the buffer 41 | // origin is one of ORIGIN_BEG, ORIGIN_CUR, offset is positive or negative, can use SIZEOF_PTR 42 | // seeking before start of the buffer causes an exception. 43 | void seekBytes(int origin, int offset); 44 | 45 | void writePtr(const ObjRef& v); 46 | void writeInt(int numBytes, const ObjRef v); 47 | size_t c_ptr() { 48 | return (size_t)m_p + m_offset; 49 | } 50 | private: 51 | template 52 | ObjRef readNum(); 53 | template 54 | void writeNum(const ObjRef& o); 55 | 56 | private: 57 | char* m_p; 58 | int m_offset; // in bytes 59 | PyVM* m_vm; 60 | }; 61 | 62 | // create a new buffer and write to it 63 | class BufferBuilder 64 | { 65 | public: 66 | static ClassObjRef addToModule(const ModuleObjRef& mod); 67 | 68 | BufferBuilder(PyVM*) : m_offset(0) {} 69 | void resize(int sz) { 70 | m_buf.resize(sz); 71 | } 72 | int size() { 73 | return (int)m_buf.size(); 74 | } 75 | size_t c_ptr() { 76 | return (size_t)m_buf.data(); 77 | } 78 | string str() { 79 | return m_buf; 80 | } 81 | 82 | void writePtr(const ObjRef& v); 83 | void writeInt(int numBytes, const ObjRef v); 84 | void writeCStr(const string& s); 85 | private: 86 | template 87 | void writeNum(const ObjRef& o); 88 | 89 | private: 90 | string m_buf; 91 | int m_offset; 92 | }; 93 | 94 | -------------------------------------------------------------------------------- /src/cfunc.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | 13 | // included from objects.h 14 | 15 | 16 | template 17 | struct CWrap : public ICWrap { 18 | CWrap(TL f) : m_f(f) {} 19 | virtual ObjRef call(CallArgs& args) { 20 | return m_f(args); 21 | } 22 | 23 | virtual int argsCount() { return Acount; } 24 | virtual const string& name() { return m_name; } 25 | virtual void setName(const string& name) { 26 | m_name = name; 27 | } 28 | 29 | TL m_f; // the lambda function 30 | string m_name; 31 | }; 32 | 33 | // take a callable object that has argument (vector& args), make an ICWrap* of it 34 | template 35 | ICWrapPtr makeWrap(PyVM* vm, const LT& l) { 36 | return vm->alloct(new CWrap(l)); 37 | } 38 | 39 | class PyVM; 40 | 41 | // ------------------------------------------ functions ----------------------------------------------- 42 | 43 | // see http://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer 44 | // for details about this crazy meta programming variadic template voodoo. 45 | // its purpose is to build a parameter-pack so that a function with any number of arguments can be called using a vector that includes 46 | // ObjRefs of the arguments 47 | 48 | // these definitions purpose is to generate seq<0,1,2,3,4>, given gens<5> 49 | template struct seq 50 | {}; 51 | template struct gens : gens < N - 1, N - 1, S... > 52 | {}; 53 | template struct gens < 0, S... > 54 | { typedef seq type; }; 55 | 56 | template 57 | struct save_func_for_later 58 | { 59 | static std::tuple the_params; // used for knowning the types of the pack 60 | R(*func)(Args...); 61 | 62 | R delayed_dispatch(CallArgs& a) { 63 | return callFunc(a, typename gens::type() ); 64 | } 65 | 66 | template 67 | R callFunc(CallArgs& a, seq) { // the parameter pack here is actually integers "0,1,2,3..." 68 | // Extract needs to know the type of each argument 69 | // -S-1 since the arguments are in last to first order 70 | return func(Extract< typename std::tuple_element::type >()(a[sizeof...(Args)-S-1]) ...); 71 | } 72 | }; 73 | 74 | // wrap a function that returns R. return an ICWrap that wraps a lambda that (calls the function, given a CallArgs&). 75 | template 76 | ICWrapPtr makeCWrap( R(*f)(As...), PyVM* vm) { 77 | return makeWrap(vm, [=](CallArgs& args)->ObjRef { 78 | save_func_for_later saved = { f }; 79 | R ret = saved.delayed_dispatch(args); 80 | return vm->makeFromT(ret); 81 | }); 82 | } 83 | 84 | // same thing but with a function that returns void 85 | template 86 | ICWrapPtr makeCWrap( void(*f)(As...), PyVM* vm) { 87 | return makeWrap(vm, [=](CallArgs& args)->ObjRef { 88 | save_func_for_later saved = { f }; 89 | saved.delayed_dispatch(args); 90 | return vm->makeNone(); 91 | }); 92 | } 93 | 94 | // std::function 95 | template 96 | struct save_stdfunc_for_later 97 | { 98 | static std::tuple the_params; // used for knowning the types of the pack 99 | std::function func; 100 | 101 | R delayed_dispatch(CallArgs& a) { 102 | return callFunc(a, typename gens::type()); 103 | } 104 | 105 | template 106 | R callFunc(CallArgs& a, seq) { // the parameter pack here is actually integers "0,1,2,3..." 107 | // Extract needs to know the type of each argument 108 | // -S-1 since the arguments are in last to first order 109 | return func(Extract< typename std::tuple_element::type >()(a[sizeof...(Args)-S - 1]) ...); 110 | } 111 | }; 112 | 113 | 114 | template 115 | ICWrapPtr makeCWrap( std::function f, PyVM* vm) { 116 | return makeWrap(vm, [=](CallArgs& args)->ObjRef { 117 | save_stdfunc_for_later saved = { f }; 118 | saved.delayed_dispatch(args); 119 | return vm->makeNone(); 120 | }); 121 | } 122 | 123 | 124 | // some specialized signatures needed in our code 125 | 126 | template<> 127 | inline ICWrapPtr makeCWrap( ObjRef(*f)(ObjRef, PyVM*), PyVM* vm) { 128 | return makeWrap<1>(vm, [=](CallArgs& args)->ObjRef { 129 | return f(args[0], vm); 130 | }); 131 | } 132 | 133 | template<> 134 | inline ICWrapPtr makeCWrap( ObjRef(*f)(CallArgs&, PyVM*), PyVM* vm) { 135 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 136 | args.posReverse(); 137 | ObjRef ret = f(args, vm); 138 | return ret; 139 | }); 140 | } 141 | 142 | template<> 143 | inline ICWrapPtr makeCWrap( ObjRef(*f)(const vector&), PyVM* vm) { 144 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 145 | args.posReverse(); 146 | // need to copy it to a real vector 147 | vector argsCopy(args.pos.begin(), args.pos.end()); 148 | ObjRef ret = f(argsCopy); 149 | return ret; 150 | }); 151 | } 152 | 153 | inline ICWrapPtr makeCWrap(std::function&, PyVM*)> f, PyVM* vm) { 154 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 155 | args.posReverse(); 156 | // need to copy it to a real vector 157 | vector argsCopy(args.pos.begin(), args.pos.end()); 158 | ObjRef ret = f(argsCopy, vm); 159 | return ret; 160 | }); 161 | } 162 | 163 | inline ICWrapPtr makeCWrap(std::function f, PyVM* vm) { 164 | return makeWrap<1>(vm, [=](CallArgs& args)->ObjRef { 165 | return f(args[0], vm); 166 | }); 167 | } 168 | 169 | 170 | /// ----------------------------------------------- methods ------------------------------------------------- 171 | template 172 | C* extractCInst(const ObjRef o); 173 | 174 | template 175 | std::shared_ptr extractCSharedPtr(const ObjRef o); 176 | 177 | // wrapper for a class method pointer that calls it with a parameter pack. see above 178 | template 179 | struct save_method_for_later 180 | { 181 | static std::tuple the_params; 182 | R(C::*func)(Args...); 183 | 184 | R delayed_dispatch(CallArgs& a, C* self) { 185 | return callFunc(typename gens::type(), a, self); 186 | } 187 | 188 | template 189 | R callFunc(seq, CallArgs& a, C* self) { 190 | return (self->*func)( Extract< typename std::tuple_element::type >()(a[sizeof...(Args)-S - 1]) ...); 191 | } 192 | }; 193 | 194 | // wrap a class C method pointer that returns R and takes any arguments 195 | template 196 | ICWrapPtr makeCWrap(R(C::*f)(As...), PyVM* vm) { 197 | return makeWrap(vm, [=](CallArgs& args)->ObjRef { 198 | save_method_for_later saved = { f }; 199 | C* self = extractCInst(args[sizeof...(As)]); 200 | R ret = saved.delayed_dispatch(args, self); 201 | return vm->makeFromT(ret); 202 | }); 203 | } 204 | 205 | // wrap a class C method points that returns void and takes any arguments. 206 | template 207 | ICWrapPtr makeCWrap(void(C::*f)(As...), PyVM* vm) { 208 | return makeWrap(vm, [=](CallArgs& args)->ObjRef { 209 | save_method_for_later saved = { f }; 210 | C* self = extractCInst(args[sizeof...(As)]); 211 | saved.delayed_dispatch(args, self); 212 | return vm->makeNone(); 213 | }); 214 | } 215 | 216 | 217 | 218 | // class C return R and take a vector of ObjRef 219 | template 220 | ICWrapPtr makeCWrap( R(C::*f)(const vector&), PyVM* vm) { 221 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 222 | CHECK(args.pos.size() >= 1, "No self argument"); 223 | C* self = extractCInst(args.pos.back()); // self is the last argument. 224 | args.pos.pop_back(); 225 | args.posReverse(); 226 | vector argsCopy(args.pos.begin(), args.pos.end()); 227 | R ret = (self->*f)(argsCopy); 228 | return vm->makeFromT(ret); 229 | }); 230 | } 231 | 232 | // class C return void and take a vector of ObjRef 233 | template 234 | ICWrapPtr makeCWrap( void(C::*f)(const vector&), PyVM* vm) { 235 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 236 | CHECK(args.pos.size() >= 1, "No self argument"); 237 | C* self = extractCInst(args.pos.back()); // self is the last argument. 238 | args.pos.pop_back(); 239 | args.posReverse(); 240 | vector argsCopy(args.pos.begin(), args.pos.end()); 241 | (self->*f)(argsCopy); 242 | return vm->makeNone(); 243 | }); 244 | } 245 | 246 | template 247 | inline ICWrapPtr makeCWrap( ObjRef(C::*f)(CallArgs&, PyVM*), PyVM* vm) { 248 | return makeWrap<-1>(vm, [=](CallArgs& args)->ObjRef { 249 | CHECK(args.pos.size() >= 1, "No self argument"); 250 | C* self = extractCInst(args.pos.back()); 251 | args.pos.pop_back(); 252 | args.posReverse(); 253 | ObjRef ret = (self->*f)(args, vm); 254 | return ret; 255 | }); 256 | } 257 | -------------------------------------------------------------------------------- /src/defs.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | 12 | #pragma once 13 | 14 | #define DISALLOW_COPY_AND_ASSIGN(Cls) private: Cls(const Cls&) = delete; Cls& operator=(const Cls&) = delete; 15 | 16 | typedef unsigned __int64 uint64; 17 | typedef signed __int64 int64; 18 | typedef unsigned short ushort; 19 | typedef unsigned char uchar; 20 | typedef unsigned int uint; 21 | 22 | template 23 | inline bool checkFlag(T var, T flag) { 24 | return ((var & flag) == flag); 25 | } 26 | 27 | template 28 | inline T imin(T a, T b) { 29 | return (a < b)?a:b; 30 | } 31 | 32 | template 33 | inline T imax(T a, T b) { 34 | return (a > b)?a:b; 35 | } 36 | 37 | 38 | template 39 | const TC* _chooseLiteral(const char* c, const wchar_t* w); 40 | template<> inline const char* _chooseLiteral(const char* c, const wchar_t* w) { return c; } 41 | template<> inline const wchar_t* _chooseLiteral(const char* c, const wchar_t* w) { return w; } 42 | 43 | // with this define you can use a one literal string templated as either char* or wchar_t* 44 | // tc is a type, char or wchar_t, str is an ansi string literal 45 | #define LITERAL_TSTR(tc, str) _chooseLiteral(str, L##str) 46 | -------------------------------------------------------------------------------- /src/except.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | 20 | class PyException : public exception 21 | { 22 | public: 23 | PyException(const string& desc) : m_desc(desc) 24 | {} 25 | virtual const char* what() const { 26 | return m_desc.c_str(); 27 | } 28 | 29 | void addTrack(const string& t) { 30 | trackback += t + "\n"; 31 | } 32 | 33 | public: 34 | string trackback; 35 | private: 36 | string m_desc; 37 | }; 38 | 39 | #define CHECK(pred, msg) do { if (!(pred)) { stringstream ss; ss << msg; throw PyException(ss.str()); } } while(false) 40 | 41 | #define ASSERT(pred, msg) CHECK(pred, msg) 42 | 43 | #define THROW(msg) do { stringstream ss; ss << msg; throw PyException(ss.str()); } while(false) -------------------------------------------------------------------------------- /src/gen_string_method_names.h: -------------------------------------------------------------------------------- 1 | // Do not directly edit this file. this file is auto generated by a custom build step 2 | 3 | enum EStringMethods 4 | { 5 | STRM_BEGINSWITH = 1, 6 | STRM_CONTAINS, 7 | STRM_C_PTR, 8 | STRM_ENDSWITH, 9 | STRM_EQUALS, 10 | STRM_GLOB_PTR, 11 | STRM_IBEGINSWITH, 12 | STRM_ICONTAINS, 13 | STRM_IENDSWITH, 14 | STRM_IEQUALS, 15 | STRM_JOIN, 16 | STRM_LOWER, 17 | STRM_PATHBEGINSWITH, 18 | STRM_PATHCONTAINS, 19 | STRM_PATHENDSWITH, 20 | STRM_PATHEQUALS, 21 | STRM_SPLIT, 22 | STRM_STARTSWITH, 23 | STRM_STRIP, 24 | }; 25 | 26 | EStringMethods stringMethod_enumFromStr(const std::string& s) 27 | { 28 | const char *k = nullptr; 29 | EStringMethods v = (EStringMethods)0; 30 | int sz = (int)s.size(); 31 | switch(sz) 32 | { 33 | case 5: { // 4 options 34 | if (s[0] == 's') { // 2 options 35 | if (s[1] == 'p') { 36 | k = "split"; 37 | v = STRM_SPLIT; 38 | } 39 | else if (s[1] == 't') { 40 | k = "strip"; 41 | v = STRM_STRIP; 42 | } 43 | } 44 | else if (s[0] == 'c') { 45 | k = "c_ptr"; 46 | v = STRM_C_PTR; 47 | } 48 | else if (s[0] == 'l') { 49 | k = "lower"; 50 | v = STRM_LOWER; 51 | } 52 | break; 53 | } // case 5 54 | case 8: { // 4 options 55 | if (s[0] == 'e') { // 2 options 56 | if (s[4] == 'W') { 57 | k = "endsWith"; 58 | v = STRM_ENDSWITH; 59 | } 60 | else if (s[4] == 'w') { 61 | k = "endswith"; 62 | v = STRM_ENDSWITH; 63 | } 64 | } 65 | else if (s[0] == 'c') { 66 | k = "contains"; 67 | v = STRM_CONTAINS; 68 | } 69 | else if (s[0] == 'g') { 70 | k = "glob_ptr"; 71 | v = STRM_GLOB_PTR; 72 | } 73 | break; 74 | } // case 8 75 | case 10: { // 3 options 76 | if (s[0] == 'p') { 77 | k = "pathEquals"; 78 | v = STRM_PATHEQUALS; 79 | } 80 | else if (s[0] == 's') { 81 | k = "startswith"; 82 | v = STRM_STARTSWITH; 83 | } 84 | else if (s[0] == 'b') { 85 | k = "beginsWith"; 86 | v = STRM_BEGINSWITH; 87 | } 88 | break; 89 | } // case 10 90 | case 9: { // 2 options 91 | if (s[1] == 'C') { 92 | k = "iContains"; 93 | v = STRM_ICONTAINS; 94 | } 95 | else if (s[1] == 'E') { 96 | k = "iEndsWith"; 97 | v = STRM_IENDSWITH; 98 | } 99 | break; 100 | } // case 9 101 | case 12: { // 2 options 102 | if (s[4] == 'C') { 103 | k = "pathContains"; 104 | v = STRM_PATHCONTAINS; 105 | } 106 | else if (s[4] == 'E') { 107 | k = "pathEndsWith"; 108 | v = STRM_PATHENDSWITH; 109 | } 110 | break; 111 | } // case 12 112 | case 4: { 113 | k = "join"; 114 | v = STRM_JOIN; 115 | break; 116 | } // case 4 117 | case 6: { 118 | k = "equals"; 119 | v = STRM_EQUALS; 120 | break; 121 | } // case 6 122 | case 7: { 123 | k = "iEquals"; 124 | v = STRM_IEQUALS; 125 | break; 126 | } // case 7 127 | case 11: { 128 | k = "iBeginsWith"; 129 | v = STRM_IBEGINSWITH; 130 | break; 131 | } // case 11 132 | case 14: { 133 | k = "pathBeginsWith"; 134 | v = STRM_PATHBEGINSWITH; 135 | break; 136 | } // case 14 137 | } // switch(sz) 138 | 139 | if (k == nullptr || s != k) 140 | return (EStringMethods)0; 141 | return v; 142 | } 143 | -------------------------------------------------------------------------------- /src/libPyVM.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | {36027395-A85C-4DCE-A859-C23D1E3B9F87} 51 | Win32Proj 52 | PyVM 53 | 8.1 54 | libPyVM 55 | 56 | 57 | 58 | StaticLibrary 59 | true 60 | v140 61 | Unicode 62 | 63 | 64 | StaticLibrary 65 | false 66 | v140 67 | true 68 | Unicode 69 | 70 | 71 | StaticLibrary 72 | true 73 | v140 74 | Unicode 75 | 76 | 77 | StaticLibrary 78 | false 79 | v140 80 | true 81 | Unicode 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | true 107 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 108 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 109 | 110 | 111 | true 112 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 113 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 114 | 115 | 116 | false 117 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 118 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 119 | 120 | 121 | false 122 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 123 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 124 | 125 | 126 | 127 | Level3 128 | Disabled 129 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 130 | $(PYTHONROOT)\include 131 | 132 | 133 | Console 134 | true 135 | 136 | 137 | 138 | 139 | Level3 140 | Disabled 141 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 142 | $(PYTHONROOT)\include 143 | 144 | 145 | Console 146 | true 147 | 148 | 149 | 150 | 151 | Level3 152 | MaxSpeed 153 | true 154 | true 155 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 156 | $(PYTHONROOT)\include 157 | 158 | 159 | Console 160 | true 161 | true 162 | true 163 | 164 | 165 | 166 | 167 | Level3 168 | MaxSpeed 169 | true 170 | true 171 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 172 | $(PYTHONROOT)\include 173 | 174 | 175 | Console 176 | true 177 | true 178 | true 179 | 180 | 181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /src/libPyVM.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | obj 6 | 7 | 8 | vm 9 | 10 | 11 | vm 12 | 13 | 14 | base 15 | 16 | 17 | vm 18 | 19 | 20 | compile 21 | 22 | 23 | compile 24 | 25 | 26 | 27 | 28 | base 29 | 30 | 31 | base 32 | 33 | 34 | base 35 | 36 | 37 | obj 38 | 39 | 40 | obj 41 | 42 | 43 | obj 44 | 45 | 46 | vm 47 | 48 | 49 | vm 50 | 51 | 52 | vm 53 | 54 | 55 | vm 56 | 57 | 58 | obj 59 | 60 | 61 | base 62 | 63 | 64 | base 65 | 66 | 67 | vm 68 | 69 | 70 | compile 71 | 72 | 73 | compile 74 | 75 | 76 | vm 77 | 78 | 79 | 80 | 81 | {9086f81a-3e2b-42f2-a8bd-8e847c18c590} 82 | 83 | 84 | {da64b65a-f530-418f-bf50-a009f76cfb45} 85 | 86 | 87 | {d58c9ffe-1ded-4558-9240-ea4f5b6f2193} 88 | 89 | 90 | {9c7e3ee2-d77b-461f-9493-12b412aef422} 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/log.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include 13 | 14 | enum LogLevel { 15 | LOGLEVEL_DEBUG, 16 | LOGLEVEL_ERROR 17 | }; 18 | 19 | 20 | inline void tostream(std::ostream& os) { 21 | } 22 | 23 | template 24 | inline void tostream(std::ostream& os, const T& v, const Args&... args) { 25 | os << v; 26 | tostream(os, args...); 27 | } 28 | 29 | template 30 | void log(LogLevel lvl, const Args&... args) { 31 | std::ostream* ostr; 32 | if (lvl == LOGLEVEL_DEBUG) 33 | ostr = &std::cout; 34 | else 35 | ostr = &std::cerr; 36 | 37 | tostream(*ostr, args...); 38 | *ostr << "\n"; 39 | } 40 | 41 | #define LOG_DEBUG(...) do { log(LOGLEVEL_DEBUG, __VA_ARGS__ ); } while (false) 42 | #define LOG_ERROR(...) do { log(LOGLEVEL_ERROR, __VA_ARGS__ ); } while (false) 43 | -------------------------------------------------------------------------------- /src/makeStringSwitch.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 by Intigua, Inc. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | from __future__ import print_function 13 | import sys 14 | 15 | """ This module has stringSwitch - takes a dictionary and creates c++ code which maps string to value """ 16 | 17 | 18 | class RecStrSw: 19 | def __init__(self, strValue): 20 | self.strValue = strValue 21 | 22 | def _recStrSw(self, file, strsTL, level, charOffset, maxLen): 23 | """ maxLen - length of the strings in this case """ 24 | indent = " "*level 25 | if len(strsTL) == 1: 26 | k = strsTL[0][0] 27 | v = strsTL[0][1] 28 | 29 | print('%sk = "%s";' % (indent, k), file=file) 30 | print('%s%s' % (indent, self.strValue(v) % {"indent":indent}), file=file) 31 | 32 | return 33 | 34 | assert charOffset < maxLen # sanity 35 | byletter = {} 36 | for k,v in strsTL: 37 | d = byletter.get(k[charOffset], None) 38 | if d is None: 39 | d = byletter[k[charOffset]] = [] 40 | d.append((k,v)) 41 | if len(byletter) == 1: #only one option, skip doing if 42 | self._recStrSw(file, strsTL, level, charOffset + 1, maxLen) 43 | return 44 | items = byletter.items() 45 | items.sort(cmp=(lambda a,b: len(b[1])-len(a[1]))) # sort by the size of the set 46 | addElse = "" 47 | for l,strset in items: 48 | comment = " // %d options" % len(strset) if len(strset) > 1 else "" 49 | print("%s%sif (s[%d] == '%s') {%s" % (indent, addElse, charOffset, l, comment), file=file) 50 | self._recStrSw(file, strset, level + 4, charOffset + 1, maxLen) 51 | print("%s}" % indent, file=file) 52 | addElse = "else " 53 | 54 | def _strBySize(self, file, strsTL, level): 55 | """ strsTL - list of tuples (name, value) """ 56 | indent = " "*level 57 | bysize = {} 58 | for k,v in strsTL: 59 | d = bysize.get(len(k), None) 60 | if d is None: 61 | d = bysize[len(k)] = [] 62 | d.append((k,v)) 63 | items = bysize.items() 64 | items.sort(cmp=(lambda a,b: len(b[1])-len(a[1]))) # sort by the size of the set 65 | print("%sswitch(sz)\n%s{" % (indent, indent), file=file) 66 | for sz,strset in items: 67 | comment = " // %d options" % len(strset) if len(strset) > 1 else "" 68 | print("%scase %d: {%s" % (indent, sz, comment), file=file) 69 | self._recStrSw(file, strset, level + 4, 0, sz) 70 | print("%s break;" % indent, file=file) 71 | print("%s} // case %d" % (indent, sz), file=file) 72 | print("%s} // switch(sz)" % indent, file=file) 73 | 74 | class CharBufPrinter: 75 | def __init__(self): 76 | self.buffersSize = 0 77 | def printHeader(self): 78 | return "" 79 | def printInit(self): 80 | return " const char *v = NULL;\n int vsz = -1;" 81 | def printValue(self, v): 82 | self.buffersSize += v["lengthv"] 83 | return 'vsz = %d; v = "%s";' % (v["lengthv"], v["stringv"]) 84 | def printReturn(self): 85 | return " *retSz = vsz;\n return v;" 86 | def printReturnNone(self): 87 | return " return NULL;" 88 | def printSig(self): 89 | return ("const char*", ", int* retSz") 90 | def printFooter(self): 91 | return "// total buffers size=%d" % self.buffersSize 92 | 93 | def stringSwitch(file, strsDict, funcname): 94 | # gets a dict mapping string -> {"lengthv":123, "stringv":"XXXXXX"} 95 | genericSwitch(file, strsDict, funcname, CharBufPrinter()) 96 | 97 | class CharPVectorPrinter: 98 | def printHeader(self): 99 | return "#include\n\ntypedef std::vector> VPCC;" 100 | def printInit(self): 101 | return " VPCC v;" 102 | def printValue(self, v): 103 | return '\n%(indent)s'.join([ ('v.push_back(std::make_pair("%s", "%s"));' % (s[0], s[1]) ) for s in v]) 104 | def printReturn(self): 105 | return " return v;" 106 | def printReturnNone(self): 107 | return " return VPCC();" 108 | def printSig(self): 109 | return ("VPCC", "") 110 | def printFooter(self): 111 | return "" 112 | 113 | 114 | def charPVectorSwitch(file, d, funcname): 115 | genericSwitch(file, d, funcname, CharPVectorPrinter()) 116 | 117 | # this function calls functions in prn to print the various type-dependent items needed for the switch 118 | def genericSwitch(file, strsDict, funcname, prn): 119 | print("// Do not directly edit this file. this file is auto generated by a custom build step\n", file=file) 120 | print("#include\n", file=file) 121 | print(prn.printHeader(), file=file) 122 | retType, addType = prn.printSig() 123 | print(retType + " "+ funcname + "(const std::string& s" + addType + ")\n{", file=file) 124 | print(" const char *k = NULL;", file=file) 125 | print(prn.printInit(), file=file) 126 | print(" int sz = (int)s.size();", file=file) 127 | RecStrSw(prn.printValue)._strBySize(file, strsDict.items(), 4); 128 | print("\n if (k == NULL || s != k)\n %s\n" % prn.printReturnNone(), file=file) 129 | print(prn.printReturn(), file=file) 130 | print("}", file=file) 131 | print(prn.printFooter(), file=file) 132 | 133 | 134 | 135 | def stringEnumSwitch(inFileName, outName, funcname, enumName, prefix): 136 | names = {} 137 | for s in open(inFileName): 138 | s = s.strip() 139 | names[s] = prefix + s.upper() 140 | file = open(outName, 'w') 141 | headerFile = file 142 | 143 | print("// Do not directly edit this file. this file is auto generated by a custom build step\n", file=headerFile) 144 | print("enum " + enumName + "\n{", file=headerFile) 145 | index = 1 146 | for e in sorted(list(set(names.values()))): # sorted list of the unique values from names 147 | print(" " + e + (" = 1" if index == 1 else "") + ",", file=headerFile); 148 | index += 1 149 | print("};\n", file=headerFile) 150 | 151 | def printValue(v): 152 | return 'v = %s;' % v 153 | 154 | #print("// Do not directly edit this file. this file is auto generated by a custom build step\n", file=file) 155 | print(enumName + " "+ funcname + "(const std::string& s)\n{", file=file) 156 | print(" const char *k = NULL;", file=file) 157 | print(" " + enumName + " v = (" + enumName + ")0;", file=file) 158 | print(" int sz = (int)s.size();", file=file) 159 | RecStrSw(printValue)._strBySize(file, names.items(), 4); 160 | print("\n if (k == NULL || s != k)\n return (" + enumName + ")0;\n return v;", file=file) 161 | print("}", file=file) 162 | 163 | file.close() 164 | 165 | 166 | if __name__ == "__main__": 167 | if len(sys.argv) == 1: # demo 168 | class StrO(dict): 169 | def __init__(self, s): 170 | self["stringv"] = s 171 | self["lengthv"] = len(s) 172 | strsss = {"AAAA":StrO("a"), "BBB":StrO("b"), "CCCCC":StrO("c"), "DDD":StrO("d"), "HELLO":StrO("e"), "AAAB":StrO("ee"), 173 | "HFLLO":StrO("f"), "AHELLO":StrO("g"), "DDDDWOW":StrO("h"), "DDDDBOW":StrO("i"), "DDDDKOW":StrO("j"), "DDDDBLW":StrO("k") } 174 | stringSwitch(sys.stdout, strsss, "demo") 175 | else: 176 | if sys.argv[1] == 'enum': 177 | if len(sys.argv) != 7: 178 | print("Usage: makeStringSwitch.py inFilename outName funcName enumName enumPrefix") 179 | sys.exit(1) 180 | stringEnumSwitch(sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6]) 181 | 182 | 183 | -------------------------------------------------------------------------------- /src/objects.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma warning (disable: 4996) // copy unsafe 12 | #include "objects.h" 13 | #include "PyVM.h" 14 | #include "OpImp.h" 15 | 16 | 17 | using namespace std; 18 | 19 | const char* Object::typeName(Type type) { 20 | switch (type) { 21 | case NONE: return "NoneType"; 22 | case BOOL: return "bool"; 23 | case INT: return "int"; 24 | case STR: return "str"; 25 | case TUPLE: return "tuple"; 26 | case LIST: return "list"; 27 | case DICT: return "dict"; 28 | case CODE: return "code"; 29 | case MODULE: return "module"; 30 | case FUNC: return "function"; 31 | case CLASS: return "classobj"; 32 | case INSTANCE: return "instance"; 33 | case METHOD: return "method"; 34 | case USTR: return "unicode"; 35 | case SLICE: return "slice"; 36 | default: 37 | return ""; 38 | } 39 | } 40 | 41 | template 42 | static Object::Type typeValue() { 43 | class NoType; 44 | return sizeof(NoType); // compile error for types that are not instantiated in object.cpp 45 | } 46 | #define MAKE_TYPEVALUE(obj, val) template<> Object::Type Object::typeValue() { return Object::val; } 47 | MAKE_TYPEVALUE(BoolObject, BOOL) 48 | MAKE_TYPEVALUE(StrObject, STR) 49 | MAKE_TYPEVALUE(IntObject, INT) 50 | MAKE_TYPEVALUE(TupleObject, TUPLE) 51 | MAKE_TYPEVALUE(ListObject, LIST) 52 | MAKE_TYPEVALUE(DictObject, DICT) 53 | MAKE_TYPEVALUE(StrDictObject, STRDICT) 54 | MAKE_TYPEVALUE(CodeObject, CODE) 55 | MAKE_TYPEVALUE(ModuleObject, MODULE) 56 | MAKE_TYPEVALUE(FuncObject, FUNC) 57 | MAKE_TYPEVALUE(CFuncObject, FUNC) 58 | MAKE_TYPEVALUE(ClassObject, CLASS) 59 | MAKE_TYPEVALUE(InstanceObject, INSTANCE) 60 | MAKE_TYPEVALUE(MethodObject, METHOD) 61 | MAKE_TYPEVALUE(IteratorObject, ITERATOR) 62 | //MAKE_TYPEVALUE(GenericIterObject, ITERATOR) 63 | MAKE_TYPEVALUE(UnicodeObject, USTR) 64 | MAKE_TYPEVALUE(Object, NONE) 65 | MAKE_TYPEVALUE(FloatObject, FLOAT) 66 | MAKE_TYPEVALUE(SliceObject, SLICE) 67 | #undef MAKE_TYPEVALUE 68 | 69 | 70 | InstanceObjRef ClassObject::createInstance() { 71 | InstanceObjRef i(m_vm->alloct(new InstanceObject(ClassObjRef(this)))); 72 | //makeMethods(i); 73 | return i; 74 | } 75 | 76 | ObjRef ModuleObject::defIc(const string& name, const ICWrapPtr& ic) { 77 | ic->setName(name); 78 | return addGlobal(m_vm->alloc(new CFuncObject(ic)), name); 79 | } 80 | 81 | ClassObjRef ModuleObject::emptyClass(const string& name) { 82 | ClassObjRef ret = m_vm->alloct(new ClassObject(m_vm->alloct(new StrDictObject()), vector(/*bases*/), name, ModuleObjRef(this), m_vm)); 83 | addGlobal(ObjRef(ret), name); 84 | return ret; 85 | } 86 | 87 | string MethodObject::funcname() const { 88 | if (m_self.isNull()) 89 | return "nullptrSELF." + m_func->funcname(); 90 | if (m_self->m_class.isNull()) 91 | return "nullptrCLASS." + m_func->funcname(); 92 | return m_self->m_class->funcname() + "." + m_func->funcname(); 93 | } 94 | 95 | void Builtins::add(const string& name, const ObjRef& v) { 96 | addGlobal(v, name);; 97 | } 98 | 99 | 100 | 101 | // see "All about co_lnotab" http://svn.python.org/projects/python/branches/pep-0384/Objects/lnotab_notes.txt 102 | int CodeObject::lineFromIndex(int qi) const { 103 | const string& tab = m_co.co_lnotab; 104 | if ((tab.size() % 2) != 0) 105 | return -1; //strange format 106 | int lineno = m_co.co_firstlineno, addr = 0; 107 | for(int i = 0; i < (int)tab.size(); i += 2) { 108 | addr += tab[i]; 109 | if (addr > qi) 110 | return lineno; 111 | lineno += tab[i + 1]; 112 | } 113 | return lineno; 114 | } 115 | 116 | 117 | int ISubscriptable::extractIndex(const ObjRef& key, size_t size) { 118 | int i = extract(key); 119 | if (i < 0) 120 | i = (int)size + i; 121 | CHECK(i >= 0 && i < (int)size, "Out of range index " << i << ":" << size); 122 | return i; 123 | } 124 | 125 | //------------------------------------------------------------------------------------------ 126 | 127 | ObjRef UnicodeObject::fromStr(const ObjRef& s, PyVM* vm) { 128 | return vm->alloc(new UnicodeObject(checked_dynamic_pcast(s))); 129 | } 130 | 131 | ObjRef ClassObject::attr(const string& name) { 132 | ObjRef v = tryLookup(m_dict->v, name); 133 | if (!v.isNull()) 134 | return v; 135 | if (!m_base.isNull()) 136 | return m_base->attr(name); 137 | return v; 138 | } 139 | 140 | PoolPtr MethodObject::bind(const InstanceObjRef& newself) { 141 | CHECK(m_self.isNull(), "Can't bind an already bound method"); 142 | CHECK(!newself.isNull(), "Can't bind to a nullptr reference"); 143 | // get to the vm through the class 144 | return newself->m_class->m_vm->alloct(new MethodObject(m_func, newself)); 145 | } 146 | 147 | 148 | ObjRef InstanceObject::simple_attr(const string& name) 149 | { 150 | // try in the instance 151 | ObjRef v = tryLookup(m_dict, name); 152 | if (!v.isNull()) 153 | return v; 154 | // try in the class 155 | if (!m_class.isNull()) { 156 | v = m_class->attr(name); 157 | if (!v.isNull()) { 158 | // if it's a method, need to create a new bounded method object 159 | // the methods are not saved in the instance object to avoid a cycle instance->method->(m_self)instance 160 | // this is the way it is done in CPython 161 | if (v->type == Object::METHOD) { 162 | MethodObjRef m = checked_cast(v); 163 | // same as bind(), without the checks 164 | return m_class->m_vm->alloc(new MethodObject(m->m_func, InstanceObjRef(this))); 165 | } 166 | return v; 167 | } 168 | } 169 | return ObjRef(); 170 | } 171 | 172 | 173 | ObjRef InstanceObject::attr(const string& name) 174 | { 175 | ObjRef v = simple_attr(name); 176 | if (!v.isNull()) 177 | return v; 178 | 179 | ObjRef getatt = simple_attr("__getattr__"); 180 | if (!getatt.isNull()) { 181 | vector gargs; 182 | gargs.push_back(m_class->m_vm->makeFromT(name)); 183 | try { 184 | return m_class->m_vm->callv(getatt, gargs); 185 | } 186 | catch (const PyRaisedException& e) { 187 | auto inst = checked_cast(e.inst); 188 | if (inst->m_class->m_name == "AttributeError") 189 | return ObjRef(); // indicates the attribute does not exist 190 | throw; 191 | } 192 | } 193 | return v; 194 | } 195 | 196 | 197 | int toSlash(int c) { 198 | if (c == '\\') 199 | return '/'; 200 | return c; 201 | } 202 | template 203 | static T transformed(const T& str, TOp& op) { 204 | T ret = str; 205 | transform(ret.begin(), ret.end(), ret.begin(), op); 206 | return ret; 207 | } 208 | 209 | enum StrOp { SO_EQUALS, SO_CONTAINS, SO_BEGINS, SO_ENDS }; 210 | 211 | 212 | static void checkArgCountS(Object::Type t, const CallArgs::TPosVector& args, int c, const string& name) { 213 | CHECK(args.size() == c, "method " << Object::typeName(t) << "." << name << " takes exactly " << c << "arguments (" << args.size() << " given)"); 214 | } 215 | void PrimitiveAttrAdapter::checkArgCount(const ObjRef obj, const CallArgs::TPosVector& args, int c) { 216 | checkArgCountS(obj->type, args, c, m_name); 217 | } 218 | 219 | 220 | // casei: case insensitive compare 221 | // slashi: slash insensitive compare 222 | template 223 | static bool stringQuery(const ObjRef& thisv, const CallArgs::TPosVector& args, StrOp op, StrModifier mod) 224 | { 225 | checkArgCountS(Object::STR, args, 1, "cmp"); // STR is just for logging 226 | const basic_string* a = extractStrPtr(args[0], mod); 227 | const basic_string* v = extractStrPtr(thisv, mod); 228 | 229 | if (a->size() > v->size()) 230 | return false; 231 | switch (op) { 232 | case SO_CONTAINS: 233 | return v->find(*a) != basic_string::npos; 234 | case SO_BEGINS: 235 | return memcmp(v->data(), a->data(), a->size() * sizeof(TC)) == 0; 236 | case SO_ENDS: 237 | return memcmp(v->data() + v->size() - a->size(), a->data(), a->size() * sizeof(TC)) == 0; 238 | case SO_EQUALS: 239 | return *a == *v; 240 | } 241 | return false; 242 | } 243 | 244 | 245 | // takes 2 optional arguments 246 | // arg 1: separator (if not given, using white-space) 247 | // arg 2: if separator is given, should we add empty elements. default is true (while-space split always ignores empty elements) 248 | template 249 | static ObjRef split(const ObjRef& o, const CallArgs::TPosVector& args, PyVM* vm) 250 | { 251 | basic_string s = extract>(o); 252 | ListObjRef ret = vm->alloct(new ListObject); 253 | if (args.size() == 1 || args.size() == 2) 254 | { 255 | basic_string sep = extract >(args[0]); 256 | bool addEmpty = true; // the default documented behaviour 257 | if (args.size() == 2) 258 | addEmpty = extract(args[1]); 259 | size_t next = 0, current = 0; 260 | do { 261 | next = s.find(sep, current); 262 | basic_string v = s.substr(current, next - current); 263 | if (!v.empty() || addEmpty) { 264 | ret->append(vm->alloc(new PSTROBJ_TYPE(TC)(v))); 265 | } 266 | current = next + sep.size(); 267 | } while (next != string::npos); 268 | } 269 | else if (args.size() == 0) 270 | { 271 | size_t next = 0, current = 0; 272 | do { 273 | next = s.find_first_of( LITERAL_TSTR(TC, " \t\n\r"), current); 274 | basic_string v = s.substr(current, next - current); 275 | if (!v.empty()) { 276 | ret->append(vm->alloc(new PSTROBJ_TYPE(TC)(v))); 277 | } 278 | current = next + 1; 279 | } while (next != string::npos); 280 | } 281 | else 282 | THROW("Unexpected number of arguments (" << args.size() << ") in split()"); 283 | 284 | return ObjRef(ret); 285 | } 286 | 287 | 288 | 289 | template 290 | static ObjRef join(const ObjRef& s, const CallArgs::TPosVector& args, PyVM* vm) { 291 | checkArgCountS(Object::STR, args, 1, "join"); 292 | auto ito = args[0]->as()->iter(vm); // save a reference to the object 293 | auto it = ito->as(); 294 | ObjRef o; 295 | if (!it->next(o)) 296 | return vm->makeFromT(basic_string()); 297 | ObjRef result = o; 298 | OpImp ops(vm); 299 | while (it->next(o)) { 300 | CHECK(o->type == Object::STR || o->type == Object::USTR, "join(): wrong type expected: str or unicode got:" << o->typeName()); 301 | result = ops.add(result, s); 302 | result = ops.add(result, o); 303 | } 304 | return result; 305 | } 306 | 307 | #ifdef _WINDOWS 308 | #define PATH_MODIFIER_ STRMOD_PATH 309 | #else 310 | #define PATH_MODIFIER_ STRMOD_NONE 311 | #endif 312 | 313 | 314 | // to add a string method name, 315 | // - add it to "string_method_names.txt", 316 | // - enable it in the build (set "Exclude from build to 'No') doesn't matter which build config 317 | // - compile "string_method_names.txt" to create a new "gen_string_method_names.h" 318 | // - disable it again in the build (set "Exclude from build to 'Yes') 319 | // the custom build step that generates this file is currently only generated in the windows build since there is still no scons rule for it 320 | // this is why "gen_string_method_names.h" is in source control. 321 | // after re-generating this file, make sure you commit the change in the genrated file as well. 322 | #include "gen_string_method_names.h" 323 | 324 | template 325 | ObjRef PrimitiveAttrAdapter::stringMethod(const ObjRef& obj, const CallArgs::TPosVector& args) 326 | { 327 | EStringMethods nameEnum = stringMethod_enumFromStr(m_name); 328 | switch(nameEnum) 329 | { 330 | case STRM_CONTAINS: 331 | return m_vm->makeFromT(stringQuery(obj, args, SO_CONTAINS, STRMOD_NONE)); 332 | case STRM_BEGINSWITH: 333 | case STRM_STARTSWITH: 334 | return m_vm->makeFromT(stringQuery(obj, args, SO_BEGINS, STRMOD_NONE)); 335 | case STRM_ENDSWITH: 336 | return m_vm->makeFromT(stringQuery(obj, args, SO_ENDS, STRMOD_NONE)); 337 | case STRM_EQUALS: 338 | return m_vm->makeFromT(stringQuery(obj, args, SO_EQUALS, STRMOD_NONE)); 339 | 340 | case STRM_PATHCONTAINS: 341 | return m_vm->makeFromT(stringQuery(obj, args, SO_CONTAINS, PATH_MODIFIER_)); 342 | case STRM_PATHBEGINSWITH: 343 | return m_vm->makeFromT(stringQuery(obj, args, SO_BEGINS, PATH_MODIFIER_)); 344 | case STRM_PATHENDSWITH: 345 | return m_vm->makeFromT(stringQuery(obj, args, SO_ENDS, PATH_MODIFIER_)); 346 | case STRM_PATHEQUALS: 347 | return m_vm->makeFromT(stringQuery(obj, args, SO_EQUALS, PATH_MODIFIER_)); 348 | 349 | case STRM_ICONTAINS: 350 | return m_vm->makeFromT(stringQuery(obj, args, SO_CONTAINS, STRMOD_CASEI)); 351 | case STRM_IBEGINSWITH: 352 | return m_vm->makeFromT(stringQuery(obj, args, SO_BEGINS, STRMOD_CASEI)); 353 | case STRM_IENDSWITH: 354 | return m_vm->makeFromT(stringQuery(obj, args, SO_ENDS, STRMOD_CASEI)); 355 | case STRM_IEQUALS: 356 | return m_vm->makeFromT(stringQuery(obj, args, SO_EQUALS, STRMOD_CASEI)); 357 | 358 | case STRM_SPLIT: 359 | return split(obj, args, m_vm); 360 | case STRM_JOIN: 361 | return join(obj, args, m_vm); 362 | case STRM_LOWER: 363 | return m_vm->makeFromT(toLower(extract>(obj))); 364 | case STRM_C_PTR: 365 | return m_vm->makeFromT( static_cast(m_obj.get())->ptr()); 366 | case STRM_GLOB_PTR: { 367 | // leak this string 368 | basic_string *new_st = new basic_string(extract>(obj)); 369 | return m_vm->makeFromT((size_t)new_st->data()); 370 | } 371 | case STRM_STRIP: { 372 | basic_string nonConstS = extract>(obj); 373 | if (args.size() == 0){ 374 | trimSpaces(nonConstS); 375 | return m_vm->makeFromT(nonConstS); 376 | } 377 | strip(nonConstS, extract>(args[0])); 378 | return m_vm->makeFromT(nonConstS); 379 | } 380 | } // switch 381 | 382 | THROW("Unknown string method " << m_name); 383 | 384 | } 385 | 386 | // if anything is unicode, everything should be unicode 387 | ObjRef PrimitiveAttrAdapter::stringMethodConv(const ObjRef& obj, CallArgs::TPosVector& args) { 388 | bool uni = obj->type == Object::USTR; 389 | for(auto ait = args.begin(); !uni && ait != args.end(); ++ait) 390 | uni |= (*ait)->type == Object::USTR; 391 | if (!uni) 392 | return stringMethod(obj, args); 393 | // otherwise, conver all strings to unicode 394 | 395 | return stringMethod(obj, args); 396 | } 397 | 398 | ObjRef PrimitiveAttrAdapter::listMethod(const ObjRef& obj, CallArgs::TPosVector& args) { 399 | auto l = static_pcast(obj); 400 | if (m_name == "append") { 401 | checkArgCount(obj, args, 1); 402 | l->append(args[0]); 403 | return m_vm->makeNone(); 404 | } 405 | if(m_name == "pop"){ 406 | checkArgCount(obj, args, 1); 407 | return l->pop(extract(args[0])); 408 | } 409 | if (m_name == "extend") { 410 | checkArgCount(obj, args, 1); 411 | auto ito = args[0]->as()->iter(m_vm); // save the iterator reference 412 | auto it = ito->as(); 413 | ObjRef o; 414 | while (it->next(o)) 415 | l->v.push_back(o); 416 | return m_vm->makeNone(); 417 | } 418 | THROW("Unknown list method " << m_name); 419 | } 420 | 421 | template 422 | ObjRef PrimitiveAttrAdapter::dictMethod(const ObjRef& obj, CallArgs::TPosVector& args) 423 | { 424 | auto d = static_pcast(obj); 425 | if (m_name == "keys") { 426 | auto ret = m_vm->alloct(new ListObject); 427 | for(auto it = d->v.begin(); it != d->v.end(); ++it) 428 | ret->append(m_vm->makeFromT(it->first)); 429 | return ObjRef(ret); 430 | } 431 | if (m_name == "values"){ 432 | auto ret = m_vm->alloct(new ListObject); 433 | for (auto it = d->v.begin(); it != d->v.end(); ++it) 434 | ret->append(m_vm->makeFromT(it->second)); 435 | return ObjRef(ret); 436 | } 437 | if (m_name == "pop") { 438 | checkArgCount(obj, args, 1); 439 | return d->pop((args[0])); 440 | } 441 | if (m_name == "iteritems") { 442 | return m_vm->alloc(new MapKeyValueIterObject(d, m_vm)); 443 | } 444 | THROW("Unknown strdict method " << m_name); 445 | } 446 | 447 | 448 | 449 | ObjRef PrimitiveAttrAdapter::call(Frame& from, Frame& frame, int posCount, int kwCount, const ObjRef& self) 450 | { 451 | CallArgs args; 452 | frame.argsFromStack(from, posCount, kwCount, args); 453 | 454 | args.posReverse(); // arguments come in reverse order, we can just edit in place since it's not used after. 455 | 456 | switch (m_obj->type) { 457 | case Object::STR: 458 | case Object::USTR: 459 | return stringMethodConv(m_obj, args.pos); 460 | case Object::LIST: 461 | return listMethod(m_obj, args.pos); 462 | case Object::STRDICT: 463 | return dictMethod(m_obj, args.pos); 464 | case Object::DICT: 465 | return dictMethod(m_obj, args.pos); 466 | // if you add a case here, you also need to at it in adaptedType() 467 | default: 468 | THROW("Unknown primitive method " << m_name << " of " << Object::typeName(m_obj->type)); 469 | } 470 | 471 | }; 472 | 473 | 474 | -------------------------------------------------------------------------------- /src/opcodes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "defs.h" 13 | 14 | 15 | enum EPyOpcdes { 16 | #define def_op(name, num, flags) name = num, 17 | #include "opcodes_def.h" 18 | #undef def_op 19 | }; 20 | 21 | uchar opFlags(uchar op) { 22 | static bool inited = false; 23 | static uchar opsflags[256] = {0}; 24 | if (!inited) { 25 | #define def_op(name, num, flags) opsflags[num] = flags; 26 | #include "opcodes_def.h" 27 | #undef def_op 28 | inited = true; 29 | } 30 | return opsflags[op]; 31 | } 32 | 33 | const char* opName(uchar op) { 34 | static bool inited = false; 35 | static const char* names[256] = {0}; 36 | if (!inited) { 37 | #define def_op(name, num, flags) names[num] = #name; 38 | #include "opcodes_def.h" 39 | #undef def_op 40 | } 41 | return names[op]; 42 | } 43 | 44 | 45 | // cmp_op = ('<', '<=', '==', '!=', '>', '>=', 'in', 'not in', 'is', 'is not', 'exception match', 'BAD') 46 | 47 | enum EPyOperators { 48 | OPER_LESS = 0, 49 | OPER_LESS_EQ = 1, 50 | OPER_EQ = 2, 51 | OPER_NOT_EQ = 3, 52 | OPER_GREATER = 4, 53 | OPER_GREATER_EQ = 5, 54 | OPER_IN = 6, 55 | OPER_NOT_IN = 7, 56 | OPER_IS = 8, 57 | OPER_IS_NOT = 9, 58 | OPER_EXP_MATCH = 10 59 | }; 60 | 61 | -------------------------------------------------------------------------------- /src/opcodes_def.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #define name_op def_op 12 | #define jrel_op def_op 13 | #define jabs_op def_op 14 | #define _def_const def_op 15 | 16 | #define IMPL 1 17 | 18 | 19 | def_op(STOP_CODE, 0, 0) 20 | def_op(POP_TOP, 1, IMPL) 21 | def_op(ROT_TWO, 2, IMPL) 22 | def_op(ROT_THREE, 3, IMPL) 23 | def_op(DUP_TOP, 4, IMPL) 24 | def_op(ROT_FOUR, 5, IMPL) 25 | 26 | def_op(NOP, 9, 0) 27 | def_op(UNARY_POSITIVE, 10, IMPL) 28 | def_op(UNARY_NEGATIVE, 11, IMPL) 29 | def_op(UNARY_NOT, 12, IMPL) 30 | def_op(UNARY_CONVERT, 13, 0) 31 | def_op(UNARY_INVERT, 15, IMPL) 32 | 33 | def_op(BINARY_POWER, 19, 0) 34 | def_op(BINARY_MULTIPLY, 20, IMPL) 35 | def_op(BINARY_DIVIDE, 21, IMPL) 36 | def_op(BINARY_MODULO, 22, 0) 37 | def_op(BINARY_ADD, 23, IMPL) 38 | def_op(BINARY_SUBTRACT, 24, IMPL) 39 | def_op(BINARY_SUBSCR, 25, IMPL) 40 | def_op(BINARY_FLOOR_DIVIDE, 26, 0) 41 | def_op(BINARY_TRUE_DIVIDE, 27, 0) 42 | def_op(INPLACE_FLOOR_DIVIDE, 28, 0) 43 | def_op(INPLACE_TRUE_DIVIDE, 29, 0) 44 | def_op(SLICE_0, 30, IMPL) 45 | def_op(SLICE_1, 31, IMPL) 46 | def_op(SLICE_2, 32, IMPL) 47 | def_op(SLICE_3, 33, IMPL) 48 | 49 | def_op(STORE_SLICE_0, 40, 0) 50 | def_op(STORE_SLICE_1, 41, 0) 51 | def_op(STORE_SLICE_2, 42, 0) 52 | def_op(STORE_SLICE_3, 43, 0) 53 | 54 | def_op(DELETE_SLICE_0, 50, 0) 55 | def_op(DELETE_SLICE_1, 51, 0) 56 | def_op(DELETE_SLICE_2, 52, 0) 57 | def_op(DELETE_SLICE_3, 53, 0) 58 | 59 | def_op(STORE_MAP, 54, IMPL) 60 | def_op(INPLACE_ADD, 55, IMPL) 61 | def_op(INPLACE_SUBTRACT, 56, IMPL) 62 | def_op(INPLACE_MULTIPLY, 57, IMPL) 63 | def_op(INPLACE_DIVIDE, 58, IMPL) 64 | def_op(INPLACE_MODULO, 59, 0) 65 | def_op(STORE_SUBSCR, 60, IMPL) 66 | def_op(DELETE_SUBSCR, 61, 0) 67 | def_op(BINARY_LSHIFT, 62, IMPL) 68 | def_op(BINARY_RSHIFT, 63, IMPL) 69 | def_op(BINARY_AND, 64, IMPL) 70 | def_op(BINARY_XOR, 65, IMPL) 71 | def_op(BINARY_OR, 66, IMPL) 72 | def_op(INPLACE_POWER, 67, 0) 73 | def_op(GET_ITER, 68, IMPL) 74 | 75 | def_op(PRINT_EXPR, 70, IMPL) 76 | def_op(PRINT_ITEM, 71, IMPL) 77 | def_op(PRINT_NEWLINE, 72, IMPL) 78 | def_op(PRINT_ITEM_TO, 73, 0) 79 | def_op(PRINT_NEWLINE_TO, 74, 0) 80 | 81 | def_op(INPLACE_LSHIFT, 75, IMPL) 82 | def_op(INPLACE_RSHIFT, 76, IMPL) 83 | def_op(INPLACE_AND, 77, IMPL) 84 | def_op(INPLACE_XOR, 78, IMPL) 85 | def_op(INPLACE_OR, 79, IMPL) 86 | 87 | def_op(BREAK_LOOP, 80, 0) 88 | def_op(WITH_CLEANUP, 81, 0) 89 | def_op(LOAD_LOCALS, 82, IMPL) 90 | def_op(RETURN_VALUE, 83, IMPL) 91 | def_op(IMPORT_STAR, 84, IMPL) 92 | def_op(EXEC_STMT, 85, 0) 93 | def_op(YIELD_VALUE, 86, IMPL) 94 | def_op(POP_BLOCK, 87, IMPL) 95 | def_op(END_FINALLY, 88, 0) 96 | def_op(BUILD_CLASS, 89, IMPL) 97 | 98 | _def_const(HAVE_ARGUMENT, 90, 0) // Opcodes from here have an argument: 99 | 100 | name_op(STORE_NAME, 90, IMPL) // Index in name list 101 | name_op(DELETE_NAME, 91, 0) // "" 102 | def_op(UNPACK_SEQUENCE, 92, IMPL) // Number of tuple items 103 | jrel_op(FOR_ITER, 93, IMPL) 104 | def_op(LIST_APPEND, 94, IMPL) 105 | name_op(STORE_ATTR, 95, IMPL) // Index in name list 106 | name_op(DELETE_ATTR, 96, 0) // "" 107 | name_op(STORE_GLOBAL, 97, IMPL) // "" 108 | name_op(DELETE_GLOBAL, 98, 0) // "" 109 | def_op(DUP_TOPX, 99, 0) // number of items to duplicate 110 | def_op(LOAD_CONST, 100, IMPL) // Index in const list 111 | // hasconst.append(100) 112 | name_op(LOAD_NAME, 101, IMPL) // Index in name list 113 | def_op(BUILD_TUPLE, 102, IMPL) // Number of tuple items 114 | def_op(BUILD_LIST, 103, IMPL) // Number of list items 115 | def_op(BUILD_SET, 104, 0) // Number of set items 116 | def_op(BUILD_MAP, 105, IMPL) // Number of dict entries (upto 255) 117 | name_op(LOAD_ATTR, 106, IMPL) // Index in name list 118 | def_op(COMPARE_OP, 107, IMPL) // Comparison operator 119 | // hascompare.append(107) 120 | 121 | name_op(IMPORT_NAME, 108, IMPL) // Index in name list 122 | name_op(IMPORT_FROM, 109, 0) // Index in name list 123 | 124 | jrel_op(JUMP_FORWARD, 110, IMPL) // Number of bytes to skip 125 | jabs_op(JUMP_IF_FALSE_OR_POP, 111, IMPL) // Target byte offset from beginning of code 126 | jabs_op(JUMP_IF_TRUE_OR_POP, 112, IMPL) // "" 127 | jabs_op(JUMP_ABSOLUTE, 113, IMPL) // "" 128 | jabs_op(POP_JUMP_IF_FALSE, 114, IMPL) // "" 129 | jabs_op(POP_JUMP_IF_TRUE, 115, IMPL) // "" 130 | 131 | name_op(LOAD_GLOBAL, 116, IMPL) // Index in name list 132 | 133 | jabs_op(CONTINUE_LOOP, 119, 0) // Target address 134 | jrel_op(SETUP_LOOP, 120, IMPL) // Distance to target address 135 | jrel_op(SETUP_EXCEPT, 121, 0) // "" 136 | jrel_op(SETUP_FINALLY, 122, 0) // "" 137 | 138 | def_op(LOAD_FAST, 124, IMPL) // Local variable number 139 | // haslocal.append(124) 140 | def_op(STORE_FAST, 125, IMPL) // Local variable number 141 | // haslocal.append(125) 142 | def_op(DELETE_FAST, 126, 0) // Local variable number 143 | // haslocal.append(126) 144 | 145 | def_op(RAISE_VARARGS, 130, IMPL) // Number of raise arguments (1, 2, or 3) 146 | def_op(CALL_FUNCTION, 131, IMPL) // //args + (//kwargs << 8) 147 | def_op(MAKE_FUNCTION, 132, IMPL) // Number of args with default values 148 | def_op(BUILD_SLICE, 133, IMPL) // Number of items 149 | def_op(MAKE_CLOSURE, 134, 0) 150 | def_op(LOAD_CLOSURE, 135, 0) 151 | // hasfree.append(135) 152 | def_op(LOAD_DEREF, 136, 0) 153 | // hasfree.append(136) 154 | def_op(STORE_DEREF, 137, 0) 155 | // hasfree.append(137) 156 | 157 | def_op(CALL_FUNCTION_VAR, 140, 0) // //args + (//kwargs << 8) 158 | def_op(CALL_FUNCTION_KW, 141, 0) // //args + (//kwargs << 8) 159 | def_op(CALL_FUNCTION_VAR_KW, 142, 0) // //args + (//kwargs << 8) 160 | 161 | jrel_op(SETUP_WITH, 143, 0) 162 | 163 | def_op(EXTENDED_ARG, 145, 0) 164 | // EXTENDED_ARG = 145 165 | def_op(SET_ADD, 146, 0) 166 | def_op(MAP_ADD, 147, 0) 167 | 168 | 169 | 170 | #undef name_op 171 | #undef jrel_op 172 | #undef jabs_op 173 | #undef _def_const -------------------------------------------------------------------------------- /src/pythonRoot.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | C:\Python27 6 | 7 | 8 | 9 | 10 | USE_CPYTHON=1;%(PreprocessorDefinitions) 11 | 12 | 13 | 14 | 15 | $(PYTHONROOT) 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include 12 | #include "utils.h" 13 | 14 | bool wstrFromUtf8(const string& s, wstring *out) { 15 | if (s.empty()) { 16 | out->clear(); 17 | return true; 18 | } 19 | int sz = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), NULL, 0); 20 | if (sz == 0) 21 | return false; 22 | out->resize(sz); 23 | sz = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), (LPWSTR)out->data(), sz); 24 | return sz != 0; 25 | } 26 | string utf8FromWstr(const wstring& s) { 27 | if (s.empty()) { 28 | return string(); 29 | } 30 | int sz = WideCharToMultiByte(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), NULL, 0, NULL, NULL); 31 | if (sz == 0) 32 | return string(); 33 | string out; 34 | out.resize(sz); 35 | sz = WideCharToMultiByte(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), (int)s.size(), (LPSTR)out.data(), sz, NULL, NULL); 36 | return out; 37 | } 38 | 39 | wstring wstrFromAnsi(const string& s) { 40 | wstring out; 41 | out.resize(s.size()); 42 | std::copy(s.begin(), s.end(), out.begin()); 43 | return out; 44 | } 45 | string ansiFromwstr(const wstring& s) { 46 | string out; 47 | out.reserve(s.size()); 48 | for(wchar_t c: s) 49 | out.append(1, (char)c); // not correct at all 50 | return out; 51 | } 52 | 53 | #define SLASHES "/\\" 54 | 55 | string extractFileNameWithoutExtension(const string& path) { 56 | // find the last slash 57 | size_t startPos = path.find_last_of(SLASHES); 58 | if (startPos == wstring::npos) 59 | startPos = 0; 60 | else 61 | startPos += 1; 62 | size_t endPos = path.find_last_of('.'); 63 | if (endPos == path.npos || endPos < startPos){ 64 | endPos = path.length(); 65 | } 66 | return path.substr(startPos,endPos - startPos); 67 | } 68 | 69 | 70 | uint64 msecTime(){ 71 | return GetTickCount(); 72 | } 73 | 74 | void debugBreak() { 75 | DebugBreak(); 76 | } 77 | void MessageBoxCall() { 78 | MessageBoxA(nullptr, "Your Message Box", "Hello", MB_OK); 79 | } 80 | 81 | void consoleSetColor(int col) { 82 | SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), col); 83 | } -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include "defs.h" 13 | #include 14 | #include 15 | 16 | using namespace std; 17 | 18 | bool wstrFromUtf8(const string& s, wstring *out); 19 | wstring wstrFromAnsi(const string& s); 20 | string ansiFromwstr(const wstring& s); 21 | 22 | string utf8FromWstr(const wstring& s); 23 | 24 | string extractFileNameWithoutExtension(const string& s); 25 | 26 | uint64 msecTime(); 27 | void debugBreak(); 28 | void MessageBoxCall(); 29 | 30 | #define CONSOLE_GREEN 10 31 | #define CONSOLE_RED 12 32 | #define CONSOLE_GRAY 8 33 | void consoleSetColor(int col); 34 | 35 | 36 | 37 | template 38 | basic_string toLower(const basic_string& s) { 39 | basic_string c(s); 40 | transform(c.begin(), c.end(), c.begin(), tolower); 41 | return c; 42 | } 43 | 44 | template 45 | void trim(basic_string& str, const F& pred) { 46 | size_t first = 0; 47 | size_t len = str.length(); 48 | if (len == 0) 49 | return; 50 | while (first < len && pred(str[first])) 51 | ++first; 52 | if (first == len) { 53 | str = basic_string(); 54 | return; 55 | } 56 | int last = (int)len-1; 57 | while (last >= 0 && pred(str[last])) 58 | --last; 59 | if (first != 0 || last != str.length()-1) 60 | str = str.substr(first, last-first+1); 61 | } 62 | 63 | 64 | template 65 | void trimSpaces(basic_string& str) { 66 | trim(str, [](TC c)->bool { return c == ' ' || c == '\t' || c == '\n'; }); 67 | } 68 | 69 | template 70 | void strip(basic_string& str, const basic_string& remove) { 71 | trim(str, [&](TC c)->bool { 72 | return remove.find(c) != basic_string::npos; 73 | }); 74 | } 75 | 76 | 77 | -------------------------------------------------------------------------------- /test/PyVMTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "myTest.h" 12 | 13 | #include "../src/ObjPool.h" 14 | #include "../src/objects.h" 15 | #include "../src/BufferAccess.h" 16 | 17 | #include 18 | #include 19 | 20 | vector Test::s_allTests; 21 | 22 | using namespace std; 23 | 24 | #define EXPECT_NO_THROW_PYS( call ) try { call; } \ 25 | catch(const PyException& e) { cout << "*** EXCEPTION: " << e.trackback << "\n" << e.what() << endl; FAIL(); } \ 26 | catch(...) { cout << "unknown exception" << endl; FAIL(); } 27 | 28 | 29 | 30 | class TestObject { 31 | public: 32 | TestObject() : m_x(0) {} 33 | TestObject(int x) : m_x(x) {} 34 | RefCount count; 35 | int m_x; 36 | }; 37 | 38 | 39 | TEST(PyVM, pool_object_removal) 40 | { 41 | ObjPool pool; 42 | PoolPtr a = pool.add(new TestObject(1)); 43 | PoolPtr b = pool.add(new TestObject(2)); 44 | PoolPtr c = pool.add(new TestObject(3)); 45 | 46 | b.reset(); 47 | ASSERT_EQ(pool.size(), 2); 48 | a.reset(); 49 | ASSERT_EQ(pool.size(), 1); 50 | c.reset(); 51 | ASSERT_EQ(pool.size(), 0); 52 | } 53 | 54 | TEST(PyVM, stack_operations) { 55 | 56 | Stack s; 57 | s.push(1); 58 | s.push(2); 59 | s.push(3); 60 | ASSERT_EQ(s.peek(0), 3); 61 | ASSERT_EQ(s.peek(1), 2); 62 | ASSERT_EQ(s.peek(2), 1); 63 | 64 | ASSERT_THROW(s.peek(3), PyException); 65 | 66 | ASSERT_THAT(s.data(), ElementsAre(1,2,3)); 67 | s.pushAt(1, 4); 68 | ASSERT_THAT(s.data(), ElementsAre(1,2,4,3)); 69 | s.pushAt(4, 5); 70 | ASSERT_THAT(s.data(), ElementsAre(5,1,2,4,3)); 71 | ASSERT_THROW(s.pushAt(6, 66), PyException); 72 | } 73 | 74 | 75 | 76 | class PyVMTest : public Test 77 | { 78 | public: 79 | // called before the first 80 | static void SetUp() { 81 | vm.reset(new PyVM); 82 | vm->setStdout(&std::cout); 83 | 84 | s_path = "./"; 85 | 86 | vm->importPycFile(s_path + "imped_module.pyc"); 87 | mod = vm->importPycFile(s_path + "test_module.pyc"); 88 | ASSERT_FALSE(mod.isNull()); 89 | 90 | // s_interp.init(); 91 | } 92 | // called after the last 93 | static void TearDown() { 94 | mod.reset(); // global variable holds an internal reference in the vm 95 | vm.reset(); 96 | } 97 | 98 | static std::shared_ptr vm; 99 | static ModuleObjRef mod; 100 | static string s_path; 101 | // static Interpreter s_interp; // CPython interpreter initialized for bridge testing 102 | }; 103 | 104 | std::shared_ptr PyVMTest::vm; 105 | ModuleObjRef PyVMTest::mod; 106 | string PyVMTest::s_path; 107 | //Interpreter PyVMTest::s_interp; 108 | //Interpreter* g_interp = &PyVMTest::s_interp; 109 | 110 | int ctest0() { 111 | cout << "C0" << endl; 112 | return 1; 113 | } 114 | 115 | // 3 arguments with return value 116 | bool ctest1(int a, bool b, const string& c) { 117 | cout << "C a=" << a << " b=" << b << " c=" << c << endl; 118 | return false; 119 | } 120 | 121 | void ctest2(int a, const string& c) { 122 | cout << "C a=" << a << " c=" << c << endl; 123 | } 124 | 125 | string ctest3(int a, string b) { 126 | cout << "C a=" << a << " b=" << b << endl; 127 | return "XX"; 128 | } 129 | 130 | void ctest4(float f, const wstring& wa) { 131 | cout << "C f=" << f << " wa=" << ansiFromwstr(wa) << endl; 132 | } 133 | 134 | void ctest5(const vector& v) { 135 | cout << "C v5=" << v.size() << endl; 136 | } 137 | 138 | void ctest6(const vector& v) { 139 | cout << "C v6=" << v.size() << endl; 140 | } 141 | 142 | 143 | TEST_F(PyVMTest, calling_c_func) { 144 | ASSERT_FALSE(mod.isNull()); 145 | try { 146 | mod->def("ctest0", ctest0); 147 | mod->def("ctest1", ctest1); 148 | mod->def("ctest2", ctest2); 149 | mod->def("ctest3", ctest3); 150 | mod->def("ctest4", ctest4); 151 | mod->def("ctest5", ctest5); 152 | mod->def("ctest6", ctest6); 153 | EXPECT_NO_THROW( vm->call("test_module.testCfuncs") ); 154 | } 155 | catch(...) { FAIL(); } 156 | } 157 | 158 | 159 | 160 | class TestCClass { 161 | public: 162 | TestCClass(const string& id) :m_id(id) {} 163 | void cmethod1(const string& a) { 164 | cout << "Method1 this=" << (size_t)this << " " << m_id << " a=" << a << endl; 165 | } 166 | int cmethod2() { 167 | cout << "Method2 this=" << (size_t)this << " " << m_id << endl; 168 | return 2; 169 | } 170 | bool cmethod3(int a, bool b, const string& c) { 171 | cout << "Method3 a=" << a << " b=" << b << " c=" << c << " " << m_id << endl; 172 | return false; 173 | } 174 | int cmethod4(int a, bool b) { 175 | cout << "Method4 a=" << a << " b=" << b << " " << m_id << endl; 176 | return 42; 177 | } 178 | void cmethod5(int a, string b, bool c) { 179 | cout << "Method5 a=" << a << " b=" << b << " c=" << c << " " << m_id << endl; 180 | } 181 | 182 | string m_id; 183 | }; 184 | 185 | TEST_F(PyVMTest, calling_cpp_method) { 186 | try { 187 | auto cls = mod->class_("TestCClass"); 188 | cls->def(&TestCClass::cmethod1, "cmethod1"); 189 | cls->def(&TestCClass::cmethod2, "cmethod2"); 190 | cls->def(&TestCClass::cmethod3, "cmethod3"); 191 | cls->def(&TestCClass::cmethod4, "cmethod4"); 192 | cls->def(&TestCClass::cmethod5, "cmethod5"); 193 | TestCClass ci("me"); 194 | InstanceObjRef ins = cls->instancePtr(&ci); 195 | EXPECT_NO_THROW(vm->call("test_module.testCMethod", ins)); 196 | 197 | //attr of None vm->call("test_module.testCMethod", cls->instancePtr((TestCClass*)NULL)); 198 | } 199 | catch(...) { FAIL(); } 200 | } 201 | 202 | 203 | TEST_F(PyVMTest, builtin_types_and_funcs) 204 | { 205 | EXPECT_NO_THROW_PYS( vm->call("test_module.testf", 3, 1) ); 206 | EXPECT_NO_THROW_PYS( vm->call("test_module.testIntMathOps") ); 207 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrMathOps") ); 208 | EXPECT_NO_THROW_PYS( vm->call("test_module.testLogicOps") ); 209 | EXPECT_NO_THROW_PYS( vm->call("test_module.testFloatMath") ); 210 | EXPECT_NO_THROW_PYS( vm->call("test_module.testIntFloatComprasions") ); 211 | EXPECT_NO_THROW_PYS( vm->call("test_module.testIntFloatBasicOperations") ); 212 | EXPECT_NO_THROW_PYS( vm->call("test_module.testIs") ); 213 | EXPECT_NO_THROW_PYS( vm->call("test_module.testList") ); 214 | EXPECT_NO_THROW_PYS( vm->call("test_module.testCircular") ); 215 | EXPECT_NO_THROW_PYS( vm->call("test_module.testFor") ); 216 | EXPECT_NO_THROW_PYS( vm->call("test_module.testBuiltInFuncs") ); 217 | 218 | EXPECT_NO_THROW_PYS( vm->call("test_module.testSplit") ); 219 | EXPECT_NO_THROW_PYS( vm->call("test_module.testUnicode") ); 220 | EXPECT_NO_THROW_PYS( vm->call("test_module.testBitOp") ); 221 | EXPECT_NO_THROW_PYS( vm->call("test_module.testKeyWordArgs") ); 222 | 223 | EXPECT_NO_THROW_PYS( vm->call("test_module.testClass") ); 224 | EXPECT_NO_THROW_PYS( vm->call("test_module.testJoin") ); 225 | EXPECT_NO_THROW_PYS( vm->call("test_module.testImport") ); 226 | EXPECT_NO_THROW_PYS( vm->call("test_module.testListCompr") ); 227 | EXPECT_NO_THROW_PYS( vm->call("test_module.testUnpack") ); 228 | EXPECT_NO_THROW_PYS( vm->call("test_module.strIter") ); 229 | EXPECT_NO_THROW_PYS( vm->call("test_module.testGetAttr") ); 230 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrInOp") ); 231 | EXPECT_NO_THROW_PYS( vm->call("test_module.testTuple") ); 232 | EXPECT_NO_THROW_PYS( vm->call("test_module.testListInOp") ); 233 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrDictInOp") ); 234 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrDictSubScript") ); 235 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrDictValuesFunc")); 236 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrDictSize")); 237 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrip")); 238 | 239 | EXPECT_NO_THROW_PYS( vm->call("test_module.testEq") ); 240 | EXPECT_NO_THROW_PYS( vm->call("test_module.testTuple") ); 241 | 242 | EXPECT_NO_THROW_PYS( vm->call("test_module.testGen") ); 243 | EXPECT_NO_THROW_PYS( vm->call("test_module.testGetAttr") ); 244 | EXPECT_NO_THROW_PYS( vm->call("test_module.testLogger") ); 245 | EXPECT_NO_THROW_PYS( vm->call("test_module.testRound")); 246 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStringComparisons") ); 247 | 248 | EXPECT_NO_THROW_PYS( vm->call("test_module.testIntCast")); 249 | EXPECT_NO_THROW_PYS( vm->call("test_module.testGlobalInClass")); 250 | 251 | EXPECT_NO_THROW_PYS( vm->call("test_module.testDictCollision")); 252 | EXPECT_NO_THROW_PYS( vm->call("test_module.testXrange")); 253 | } 254 | 255 | 256 | ObjRef cfunc_kwa(CallArgs& d, PyVM* vm) { 257 | for(auto it = d.kw.begin(); it != d.kw.end(); ++it) { 258 | cout << it->first << " = " << stdstr(it->second, false) << endl; 259 | } 260 | return vm->makeNone(); 261 | } 262 | 263 | TEST_F(PyVMTest, call_c_kw) { 264 | try { 265 | mod->def("cfunc_kwa", cfunc_kwa); 266 | vm->call("test_module.testCKeyWordArgs"); 267 | } catch(...) { FAIL(); } 268 | } 269 | 270 | struct VarArgClass { 271 | string varArgFunc(const vector& args) { 272 | stringstream ss; 273 | cout << args.size() << " "; 274 | ss << args.size() << " "; 275 | for(auto it = args.begin(); it != args.end(); ++it) { 276 | cout << stdstr(*it) << " "; 277 | ss << stdstr(*it) << " "; 278 | } 279 | cout << endl; 280 | return ss.str(); 281 | } 282 | }; 283 | 284 | TEST_F(PyVMTest, call_var_arg_method) { 285 | try { 286 | // variable arguments in method 287 | VarArgClass i; 288 | auto cls = mod->class_("VarArgClass"); 289 | cls->def(&VarArgClass::varArgFunc, "varArgFunc"); 290 | vm->call("test_module.testVarArgFunc", cls->instancePtr(&i)); 291 | } 292 | catch(...) { FAIL(); } 293 | } 294 | 295 | 296 | 297 | TEST_F(PyVMTest, callFuncWithVarPosArgs_calledSuccessfully) { 298 | vm->call("test_module.withVarPos1", 1, 2, "Bla"); 299 | vm->call("test_module.withVarPos2", 1, 2, "Bla"); 300 | vm->call("test_module.withVarPos3"); 301 | vm->call("test_module.testVarPos"); 302 | } 303 | 304 | struct SomeClass { 305 | SomeClass(const string& _x) : x(_x) {} 306 | string x; 307 | }; 308 | 309 | void external_method(SomeClass* self, int arg1, const string& arg2) { 310 | cout << "in external_method " << self->x << " " << arg1 << " " << arg2 << endl; 311 | } 312 | 313 | TEST_F(PyVMTest, method_as_c_function) { 314 | try { 315 | SomeClass sc("XXX"); 316 | auto cls = mod->class_("SomeClass"); 317 | cls->def(external_method, "external_method"); 318 | vm->call(cls->instancePtr(&sc)->attr("external_method"), 3, "AAA"); 319 | } 320 | catch (...) { FAIL(); } 321 | } 322 | 323 | 324 | 325 | TEST_F(PyVMTest, raise_exception) { 326 | try { 327 | vm->call("test_module.testException"); 328 | } 329 | catch(const PyRaisedException& e) { 330 | EXPECT_EQ(stdstr(e.inst), "Blaa"); 331 | } 332 | } 333 | 334 | 335 | 336 | TEST_F(PyVMTest, numbers_conversion) 337 | { 338 | int HKLM_SIGNED_VAL = 0x80000002; // normal HKEY_LOCAL_MACHINE is unsigned so it doesn't extend well in 32 bit compile 339 | 340 | ObjRef n; 341 | n = vm->call("test_module.checkNumber", 1, 0); //HKEY_LOCAL_MACHINE 342 | EXPECT_EQ(extract(n), (int)0x80000002); 343 | 344 | EXPECT_EQ(extract(n), (int64)HKLM_SIGNED_VAL); 345 | 346 | EXPECT_EQ(extract(n), (uint)0x80000002); 347 | int64 c = extract(n), d = (uint64)0x80000002; 348 | 349 | uint64 a = extract(n); 350 | uint64 b = (uint64)HKLM_SIGNED_VAL; 351 | 352 | EXPECT_EQ(extract(n), (uint64)HKLM_SIGNED_VAL); 353 | 354 | n = vm->call("test_module.checkNumber", 2, 0); 355 | EXPECT_EQ(extract(n), 0x100000000); 356 | 357 | n = vm->call("test_module.checkNumber", 3, 0); 358 | EXPECT_EQ(extract(n), 0x7F00000000000000); 359 | 360 | n = vm->call("test_module.checkNumber", 4, 0); 361 | EXPECT_EQ(extract(n), 0xAF00000000000000); 362 | 363 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 11, (int)HKLM_SIGNED_VAL) ); 364 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 11, (int64)HKLM_SIGNED_VAL) ); 365 | 366 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 12, (int64)0x100000000) ); 367 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 12, (uint64)0x100000000) ); 368 | 369 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 13, (int64)0x7F00000000000000) ); 370 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 13, (uint64)0x7F00000000000000) ); 371 | 372 | EXPECT_NO_THROW( vm->call("test_module.checkNumber", 14, (uint64)0xAF00000000000000) ); 373 | 374 | 375 | } 376 | 377 | size_t g_got_v = 0; 378 | void charArrCall(size_t v) { 379 | // check array 380 | g_got_v = v; 381 | } 382 | 383 | template 384 | T extractInt(const string& s, int start) { 385 | CHECK(s.size() >= start + sizeof(T), "Unexpected string offset"); 386 | return *(T*)(s.data() + start); 387 | } 388 | 389 | 390 | TEST_F(PyVMTest, AccessBuffer_usecases) { 391 | try { 392 | char *envp[] = {"HellO", "CrueL", "WorlD", 0 }; 393 | 394 | vm->addBuiltin(AccessBuffer::addToModule(vm->mainModule())); 395 | vm->addBuiltin(BufferBuilder::addToModule(vm->mainModule())); 396 | vm->addBuiltin("charArrCall", vm->mainModule()->def("charArrCall", charArrCall)); 397 | vm->call("test_module.parseCharArray", (size_t)envp); // calls charArrCall 398 | EXPECT_EQ((char**)g_got_v, envp); // pointer received by the callback should be the same as envp 399 | 400 | string buf; 401 | buf.resize(15); 402 | EXPECT_NO_THROW( vm->call("test_module.testAccessBuf", buf) ); 403 | 404 | ObjRef a(vm->alloc(new StrObject("aaa"))), b(vm->alloc(new StrObject("bbb"))), c(vm->alloc(new StrObject("ccc"))); 405 | ObjRef r = vm->call("test_module.testBuildBuf", a, b, c); 406 | string rs = extract(r); 407 | EXPECT_EQ(rs.size(), 3*sizeof(void*) + sizeof(uint64) + sizeof(uint) + sizeof(char)); // should be an array of pointers to the strings 408 | char** rsp = (char**)rs.data(); 409 | EXPECT_EQ(rsp[0], ((StrObjRef&)a)->v.data()); 410 | EXPECT_EQ(rsp[1], ((StrObjRef&)b)->v.data()); 411 | EXPECT_EQ(rsp[2], ((StrObjRef&)c)->v.data()); 412 | EXPECT_EQ(88888, extractInt(rs, 3*sizeof(void*) )); 413 | EXPECT_EQ(666, extractInt(rs, 3*sizeof(void*) + sizeof(uint64))); 414 | EXPECT_EQ(111, extractInt(rs, 3*sizeof(void*) + sizeof(uint64) + sizeof(uint))); 415 | 416 | int argc = 4; 417 | char* argv[4] = { "HellO", "WORLD", "TeST", "this" }; 418 | char* origargv[4] = { argv[0], argv[1], argv[2], argv[3] }; 419 | vm->call("test_module.testArgcArgv", argc, (size_t)argv); 420 | EXPECT_STREQ("HellO", origargv[0]); // it didn't change the content 421 | EXPECT_STREQ("hello", argv[0]); // it changed the pointer 422 | EXPECT_STREQ("WORLD", origargv[1]); 423 | EXPECT_STREQ("world", argv[1]); 424 | EXPECT_STREQ("TeST", origargv[2]); 425 | EXPECT_STREQ("test", argv[2]); 426 | EXPECT_STREQ("this", origargv[3]); 427 | EXPECT_STREQ("this", argv[3]); 428 | } 429 | catch(const PyException& e) { 430 | FAIL(); 431 | cout << "EXCEPTION: " << e.what() << endl << e.trackback << endl; 432 | } 433 | } 434 | 435 | 436 | 437 | TEST_F(PyVMTest, StateClearer_saves_state) { 438 | int refs = vm->objPool().countRefs(); 439 | int objCount = vm->objPool().size(); 440 | { 441 | StateClearer sc(vm.get()); 442 | vm->call("test_module.testMem", false); 443 | } 444 | cout << "BEFORE " << refs << " " << objCount << " AFTER " << vm->objPool().countRefs() << " " << vm->objPool().size() << endl; 445 | 446 | EXPECT_EQ(vm->objPool().countRefs(), refs); 447 | EXPECT_EQ(vm->objPool().size(), objCount); 448 | } 449 | 450 | TEST_F(PyVMTest, StateClearer_on_leak) { 451 | int refs = vm->objPool().countRefs(); 452 | int objCount = vm->objPool().size(); 453 | { 454 | StateClearer sc(vm.get()); 455 | vm->call("test_module.testMem", true); // leak a reference to a global variable 456 | } 457 | cout << "BEFORE " << refs << " " << objCount << " AFTER " << vm->objPool().countRefs() << " " << vm->objPool().size() << endl; 458 | 459 | // stringstream ss; 460 | // vm->memDump(ss); 461 | // FSUtils::saveFile("c:/temp/pyvm/withleak.txt", ss.str()); 462 | 463 | // reference count will be the same since we remove a ref from None and add it to the string 464 | // object count will also be the same since the string existed in the code object 465 | EXPECT_EQ(vm->objPool().size(), objCount); 466 | 467 | } 468 | 469 | class CClass { 470 | public: 471 | CClass(PyVM*) {ctorWasCalled = true; } 472 | ~CClass() {dtorWasCalled = true;} 473 | int getSomething(){return 5;} 474 | 475 | static bool dtorWasCalled; 476 | static bool ctorWasCalled; 477 | }; 478 | 479 | bool CClass::dtorWasCalled = false; 480 | bool CClass::ctorWasCalled = false; 481 | 482 | TEST_F(PyVMTest, CInstanceInitFromPy_NoLeak){ 483 | { 484 | auto classObj = mod->class_("CClass", CtorDef()); 485 | classObj->def(&CClass::getSomething, "getSomething"); 486 | vm->call("test_module.instanceCObject"); 487 | } 488 | ASSERT_TRUE(CClass::ctorWasCalled); 489 | ASSERT_TRUE(CClass::dtorWasCalled); 490 | 491 | } 492 | 493 | 494 | class CClass2 { 495 | public: 496 | CClass2(PyVM*) {ctorWasCalled = true; } 497 | ~CClass2() {dtorWasCalled = true;} 498 | int getSomething(){return 5;} 499 | 500 | static bool dtorWasCalled; 501 | static bool ctorWasCalled; 502 | }; 503 | 504 | bool CClass2::dtorWasCalled = false; 505 | bool CClass2::ctorWasCalled = false; 506 | 507 | TEST_F(PyVMTest, CInstanceInitFromCUsedByPy_NoLeak){ 508 | { 509 | auto classObj = mod->class_("CClass2", CtorDef()); 510 | classObj->def(&CClass2::getSomething, "getSomething"); 511 | std::shared_ptr cObj(new CClass2(vm.get())); 512 | auto ref = classObj->instanceSharedPtr(cObj); 513 | vm->call("test_module.useCObject",ref); 514 | } 515 | 516 | ASSERT_TRUE(CClass2::ctorWasCalled); 517 | ASSERT_TRUE(CClass2::dtorWasCalled); 518 | 519 | } 520 | 521 | class CClass3 { 522 | public: 523 | CClass3(PyVM*, const string& id) :m_id(id) { } 524 | ~CClass3() { } 525 | 526 | string m_id; 527 | }; 528 | 529 | 530 | string gettingInstance(CClass3* inst) { 531 | cout << "got instance " << inst->m_id << endl; 532 | return inst->m_id; 533 | } 534 | 535 | TEST_F(PyVMTest, CInstanceInitFromPyPassedAsArgument_callsFunc) { 536 | auto classObj = mod->class_("CClass3", CtorDef()); 537 | mod->def("gettingInstance", gettingInstance); 538 | vm->call("test_module.callWithInstance"); 539 | } 540 | 541 | 542 | TEST_F(PyVMTest, testMap){ 543 | EXPECT_NO_THROW_PYS( vm->call("test_module.testMap") ); 544 | } 545 | 546 | TEST_F(PyVMTest, testMapItter){ 547 | EXPECT_NO_THROW_PYS( vm->call("test_module.testMapItter") ); 548 | } 549 | 550 | TEST_F(PyVMTest, testStrMapItter){ 551 | StrDictObject* objDict = new StrDictObject; 552 | auto dictObjRef = vm->alloc(objDict); 553 | objDict->v.insert(std::pair("a",vm->makeFromT((int)42) )); 554 | objDict->v.insert(std::pair("c",vm->makeFromT((int)43) )); 555 | 556 | EXPECT_NO_THROW_PYS( vm->call("test_module.testStrMapItter",dictObjRef) ); 557 | } 558 | 559 | 560 | TEST_F(PyVMTest, extractOfDouble_intNumber_floatReturned){ 561 | 562 | double expected =32.0; 563 | ObjRef res = vm->call("test_module.r",vm->makeFromT((int)expected)); 564 | double val = extract(res); 565 | ASSERT_EQ(val,expected); 566 | } 567 | 568 | TEST_F(PyVMTest, extractOfDouble_floatNumber_floatReturned){ 569 | 570 | double expected =32.0; 571 | ObjRef res = vm->call("test_module.r",vm->makeFromT((double)expected)); 572 | double val = extract(res); 573 | ASSERT_EQ(val,expected); 574 | } 575 | 576 | TEST_F(PyVMTest, class_with_metaclass_calls_metaclass) { 577 | EXPECT_NO_THROW_PYS( vm->call("test_module.testMetaClass")); 578 | 579 | } 580 | 581 | TEST_F(PyVMTest, unbounded_method_can_be_called_only_with_instance_of_same_class) { 582 | EXPECT_NO_THROW_PYS( vm->call("test_module.testUnboundedMethod1")); 583 | EXPECT_NO_THROW_PYS( vm->call("test_module.testUnboundedMethod2")); 584 | } 585 | 586 | template 587 | std::function make_function(T *t) { 588 | return { t }; 589 | } 590 | 591 | TEST_F(PyVMTest, c_func_lambda) { 592 | int gota = 0; 593 | string gotb; 594 | std::function f = [&](int a, const string& b) { 595 | gota = a; 596 | gotb = b; 597 | }; 598 | mod->def("ltest", f); 599 | EXPECT_NO_THROW_PYS( vm->call("test_module.testClambda")); 600 | } 601 | 602 | TEST_F(PyVMTest, string_slice) { 603 | EXPECT_NO_THROW_PYS( vm->call("test_module.testSubstr")); 604 | } 605 | 606 | 607 | 608 | TEST_F(PyVMTest, import_callback) { 609 | vm->setImportCallback([](const string& name) { 610 | auto p = unique_ptr(new ifstream("./" + name + ".pyc", ios::binary)); 611 | CHECK(p->good(), "Failed opening module " << name); 612 | return make_pair(std::move(p), true); 613 | }); 614 | EXPECT_NO_THROW_PYS( vm->call("test_module.testImportCallback")); 615 | } 616 | 617 | 618 | 619 | 620 | void testMemLeak(PyVM* vm) { 621 | for(int i = 0; i < 100; ++i) { 622 | { 623 | StateClearer sc(vm); 624 | cout << "refs=" << vm->objPool().countRefs() << " obj=" << vm->objPool().size() << endl; 625 | vm->call("test_module.testMem"); 626 | cout << "refs=" << vm->objPool().countRefs() << " obj=" << vm->objPool().size() << endl; 627 | } 628 | 629 | cout << "refs=" << vm->objPool().countRefs() << " obj=" << vm->objPool().size() << endl; 630 | } 631 | 632 | } 633 | 634 | void testPyPolicy() { 635 | 636 | //testMemLeak() 637 | //testInteractive(); 638 | } 639 | 640 | 641 | 642 | 643 | 644 | 645 | -------------------------------------------------------------------------------- /test/VarArrayTest.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "myTest.h" 12 | 13 | #include "../src/VarArray.h" 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | 20 | TEST(VarArray, realloc_movedCorrectly) 21 | { 22 | VarArray v; 23 | v.push_back("aa"); 24 | v.push_back("bb"); 25 | ASSERT_EQ(2, v.size()); 26 | ASSERT_EQ("aa", v[0]); 27 | ASSERT_EQ("bb", v[1]); 28 | 29 | v.push_back("cc"); 30 | v.push_back("dd"); 31 | ASSERT_EQ(4, v.size()); 32 | 33 | ASSERT_EQ("aa", v[0]); 34 | ASSERT_EQ("bb", v[1]); 35 | ASSERT_EQ("cc", v[2]); 36 | ASSERT_EQ("dd", v[3]); 37 | 38 | v.clear(); 39 | ASSERT_EQ(0, v.size()); 40 | 41 | v.push_back("ee"); 42 | ASSERT_EQ(1, v.size()); 43 | ASSERT_EQ("ee", v[0]); 44 | } 45 | 46 | template 47 | vector toVector(const FT& from) { 48 | vector r; 49 | from.foreach([&](const T& v) { r.push_back(v); }); 50 | return r; 51 | } 52 | template 53 | void addValues(FT& to, int count) { 54 | for (int i = 0; i < count; ++i) 55 | to.push_back(std::to_string(i)); 56 | } 57 | 58 | TEST(VarArray, popBackRealloc_movedCorrectly) 59 | { 60 | VarArray v; 61 | addValues(v, 5); 62 | v.pop_back(); 63 | v.pop_back(); 64 | v.pop_back(); 65 | ASSERT_EQ(2, v.size()); 66 | ASSERT_THAT(toVector(v), ElementsAre("0", "1")); 67 | v.pop_back(); 68 | v.pop_back(); 69 | ASSERT_EQ(0, v.size()); 70 | 71 | addValues(v, 10); 72 | v.pop_back(); 73 | v.pop_back(); 74 | v.pop_back(); 75 | v.pop_back(); 76 | v.pop_back(); 77 | ASSERT_THAT(toVector(v), ElementsAre("0", "1", "2", "3", "4")); 78 | } 79 | 80 | TEST(VarArray, insert_movedCorrectly) 81 | { 82 | VarArray v; 83 | v.push_back("1"); 84 | v.push_back("2"); 85 | v.insert(v.begin(), "000"); 86 | ASSERT_EQ(3, v.size()); 87 | ASSERT_THAT(toVector(v), ElementsAre("000", "1", "2")); 88 | 89 | v.insert(v.begin() + 2, "333"); 90 | v.insert(v.end(), "444"); 91 | ASSERT_EQ(5, v.size()); 92 | ASSERT_THAT(toVector(v), ElementsAre("000", "1", "333", "2", "444")); 93 | } 94 | 95 | TEST(VarArray, iterators) 96 | { 97 | VarArray v; 98 | v.push_back("1"); 99 | v.push_back("2"); 100 | v.insert(v.begin(), "000"); 101 | ASSERT_THAT(v, ElementsAre("000", "1", "2")); 102 | 103 | vector tmp; 104 | for (auto it = v.begin(); it != v.end(); ++it) { 105 | tmp.push_back(*it); 106 | } 107 | ASSERT_THAT(tmp, ElementsAre("000", "1", "2")); 108 | tmp.clear(); 109 | 110 | // const iterators 111 | const VarArray& constV = v; 112 | v.push_back("4"); 113 | ASSERT_THAT(v, ElementsAre("000", "1", "2", "4")); 114 | for (auto it = constV.begin(); it != constV.end(); ++it) { 115 | tmp.push_back(*it); 116 | } 117 | ASSERT_THAT(tmp, ElementsAre("000", "1", "2", "4")); 118 | 119 | // algorithms 120 | std::reverse(v.begin(), v.end()); 121 | ASSERT_THAT(v, ElementsAre("4", "2", "1", "000")); 122 | 123 | // copy via c'tor 124 | vector vCopy(v.begin(), v.end()); 125 | ASSERT_THAT(vCopy, ElementsAre("4", "2", "1", "000")); 126 | } 127 | 128 | 129 | 130 | class TestObj { 131 | public: 132 | TestObj() :m_x(0) { 133 | ++s_ctorEmpty; 134 | } 135 | explicit TestObj(int x) :m_x(x){ 136 | ++s_ctorInit; 137 | } 138 | TestObj(const TestObj& t) :m_x(t.m_x) { 139 | ++s_ctorCopy; 140 | } 141 | ~TestObj() { 142 | ++s_dtors; 143 | } 144 | int m_x; 145 | 146 | static void zeroCounts() { 147 | s_ctorEmpty = 0; s_ctorCopy = 0; s_dtors = 0, s_ctorInit = 0; 148 | } 149 | static int aliveObjects() { 150 | return s_ctorEmpty + s_ctorCopy + s_ctorInit - s_dtors; 151 | } 152 | static int s_ctorEmpty, s_ctorCopy, s_dtors, s_ctorInit; 153 | }; 154 | int TestObj::s_ctorEmpty = 0, TestObj::s_ctorCopy = 0, TestObj::s_dtors = 0, TestObj::s_ctorInit = 0; 155 | 156 | 157 | TEST(VarArray, realloc_allDestroyed) 158 | { 159 | TestObj::zeroCounts(); 160 | VarArray v; 161 | v.push_back(TestObj(11)); 162 | v.push_back(TestObj(22)); 163 | v.push_back(TestObj(33)); 164 | ASSERT_EQ(3, TestObj::aliveObjects()); 165 | v.push_back(TestObj(44)); 166 | ASSERT_EQ(4, TestObj::aliveObjects()); 167 | } 168 | 169 | TEST(VarArray, popBackRealloc_allDestroyed) 170 | { 171 | TestObj::zeroCounts(); 172 | VarArray v; 173 | v.push_back(TestObj(11)); 174 | v.push_back(TestObj(22)); 175 | v.push_back(TestObj(33)); 176 | ASSERT_EQ(3, TestObj::aliveObjects()); 177 | v.pop_back(); 178 | ASSERT_EQ(2, TestObj::aliveObjects()); 179 | 180 | v.push_back(TestObj(333)); 181 | v.push_back(TestObj(44)); 182 | ASSERT_EQ(4, TestObj::aliveObjects()); 183 | 184 | v.push_back(TestObj(55)); 185 | v.push_back(TestObj(66)); 186 | v.push_back(TestObj(77)); 187 | ASSERT_EQ(7, TestObj::aliveObjects()); 188 | 189 | v.pop_back(); 190 | v.pop_back(); 191 | ASSERT_EQ(5, TestObj::aliveObjects()); 192 | 193 | v.pop_back(); 194 | v.pop_back(); 195 | v.pop_back(); 196 | v.pop_back(); 197 | v.pop_back(); 198 | ASSERT_EQ(0, TestObj::aliveObjects()); 199 | 200 | } 201 | 202 | 203 | TEST(VarArray, resize_objectsDestroyed) 204 | { 205 | TestObj::zeroCounts(); 206 | VarArray v; 207 | v.resize(2); 208 | ASSERT_EQ(2, TestObj::aliveObjects()); 209 | ASSERT_EQ(2, v.size()); 210 | v.resize(4); 211 | ASSERT_EQ(4, TestObj::aliveObjects()); 212 | ASSERT_EQ(4, v.size()); 213 | 214 | v.push_back(TestObj(11)); 215 | v.push_back(TestObj(22)); 216 | v.push_back(TestObj(33)); 217 | v.push_back(TestObj(44)); 218 | v.push_back(TestObj(55)); 219 | 220 | ASSERT_EQ(9, v.size()); 221 | ASSERT_EQ(9, TestObj::aliveObjects()); 222 | 223 | v.resize(4); 224 | ASSERT_EQ(4, v.size()); 225 | ASSERT_EQ(4, TestObj::aliveObjects()); 226 | 227 | } 228 | 229 | 230 | typedef std::shared_ptr str_ptr; 231 | 232 | TEST(VarArray, insert_allDestroyed) 233 | { 234 | { 235 | TestObj::zeroCounts(); 236 | VarArray v; 237 | v.push_back(TestObj(11)); 238 | v.push_back(TestObj(22)); 239 | v.insert(v.begin() + 1, TestObj(000)); 240 | ASSERT_EQ(3, TestObj::aliveObjects()); 241 | v.insert(v.begin() + 1, TestObj(333)); 242 | ASSERT_EQ(4, TestObj::aliveObjects()); 243 | } 244 | 245 | VarArray v; 246 | v.push_back(str_ptr(new string("11"))); 247 | v.push_back(str_ptr(new string("22"))); 248 | v.insert(v.begin() + 1, str_ptr(new string("000"))); 249 | v.foreach([](const str_ptr& p) { 250 | ASSERT_EQ(p.use_count(), 1); 251 | }); 252 | v.insert(v.begin() + 1, str_ptr(new string("333"))); 253 | v.insert(v.end(), str_ptr(new string("444"))); 254 | v.foreach([](const str_ptr& p) { 255 | ASSERT_EQ(p.use_count(), 1); 256 | }); 257 | 258 | v.insert(v.end() - 1, str_ptr(new string("555"))); 259 | v.insert(v.end() - 1, str_ptr(new string("666"))); 260 | v.insert(v.end() - 1, str_ptr(new string("777"))); 261 | 262 | v.foreach([](const str_ptr& p) { 263 | ASSERT_EQ(p.use_count(), 1); 264 | }); 265 | 266 | v.pop_back(); 267 | v.pop_back(); 268 | v.pop_back(); 269 | 270 | v.foreach([](const str_ptr& p) { 271 | ASSERT_EQ(p.use_count(), 1); 272 | }); 273 | } 274 | 275 | 276 | -------------------------------------------------------------------------------- /test/imped_module.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 by Intigua, Inc. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | impedGlobs = 42 13 | 14 | def hello(): 15 | print "in imported module", impedGlobs 16 | print "hello" 17 | 18 | class HelloCls: 19 | def helloo(self): 20 | print "in imported module class", impedGlobs 21 | print "clshello" -------------------------------------------------------------------------------- /test/imped_module2.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 by Intigua, Inc. 2 | # Licensed under the Apache License, Version 2.0 (the "License"); 3 | # you may not use this file except in compliance with the License. 4 | # You may obtain a copy of the License at 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # Unless required by applicable law or agreed to in writing, software 7 | # distributed under the License is distributed on an "AS IS" BASIS, 8 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | # See the License for the specific language governing permissions and 10 | # limitations under the License. 11 | 12 | def hello2(): 13 | print "in imported module2" 14 | return 2 15 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #include "../src/PyVM.h" 12 | #include "../src/objects.h" 13 | #include "../src/PyCompile.h" 14 | 15 | #include "myTest.h" 16 | 17 | // main for running tests 18 | int main() 19 | { 20 | runAllTests(); 21 | return 0; 22 | } 23 | 24 | 25 | #if USE_CPYTHON 26 | // main for interactive interpreter 27 | int imain() 28 | { 29 | PyVM vm; 30 | vm.runInteractive(); 31 | return 0; 32 | } 33 | 34 | // main for running simple sample 35 | int xmain() 36 | { 37 | PyVM vm; 38 | vm.importPycFile("C:\\projects\\PyVM\\test.pyc"); 39 | 40 | string comp; 41 | compileTextToPycBuf(R"**( 42 | def pyfunc(a, b): 43 | return a + b; 44 | print pyfunc(1, 2) 45 | )**", "test2.py", &comp); 46 | 47 | auto module = vm.importPycBuf(comp); 48 | 49 | cout << extract(vm.call("test2.pyfunc", 2, 3)) << endl; 50 | 51 | return 0; 52 | } 53 | 54 | 55 | #endif 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /test/myTest.h: -------------------------------------------------------------------------------- 1 | // Copyright 2015 by Intigua, Inc. 2 | // Licensed under the Apache License, Version 2.0 (the "License"); 3 | // you may not use this file except in compliance with the License. 4 | // You may obtain a copy of the License at 5 | // http://www.apache.org/licenses/LICENSE-2.0 6 | // Unless required by applicable law or agreed to in writing, software 7 | // distributed under the License is distributed on an "AS IS" BASIS, 8 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9 | // See the License for the specific language governing permissions and 10 | // limitations under the License. 11 | #pragma once 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "../src/utils.h" 19 | 20 | using namespace std; 21 | 22 | struct TestAssert : public exception 23 | { 24 | TestAssert(const string& desc) :m_desc(desc) {} 25 | virtual const char* what() const { 26 | return m_desc.c_str(); 27 | } 28 | string m_desc; 29 | }; 30 | 31 | class Case 32 | { 33 | public: 34 | virtual void run() = 0; 35 | virtual const char* name() const = 0; 36 | 37 | void reportFailed(const string& str) { 38 | if (m_failed) 39 | return; 40 | m_failstr = str; 41 | m_failed = true; 42 | } 43 | bool m_failed = false; 44 | string m_failstr; 45 | }; 46 | 47 | 48 | typedef void(*TSetupFunc)(); 49 | 50 | 51 | class Test 52 | { 53 | public: 54 | void regSelf() { 55 | s_allTests.push_back(this); 56 | } 57 | 58 | void runAll() { 59 | m_failedCount = 0; 60 | m_passedCount = 0; 61 | try { 62 | (*m_setup)(); 63 | } 64 | catch (const TestAssert& e) { 65 | consoleSetColor(CONSOLE_RED); 66 | cout << "** FAILED SETUP"; 67 | consoleSetColor(CONSOLE_GRAY); 68 | cout << ":" << e.what() << endl; 69 | ++m_failedCount; 70 | return; 71 | } 72 | for(auto* c: m_cases) { 73 | try { 74 | c->run(); 75 | } 76 | catch(const TestAssert& e) { 77 | consoleSetColor(CONSOLE_RED); 78 | cout << "** FAILED: "; 79 | consoleSetColor(CONSOLE_GRAY); 80 | cout << c->name() << ":" << e.what() << endl; 81 | ++m_failedCount; 82 | continue; 83 | } 84 | if (c->m_failed) { 85 | consoleSetColor(CONSOLE_RED); 86 | cout << "** FAILED: "; 87 | consoleSetColor(CONSOLE_GRAY); 88 | cout << c->name() << ":" << c->m_failstr << endl; 89 | ++m_failedCount; 90 | } 91 | else { 92 | consoleSetColor(CONSOLE_GREEN); 93 | cout << "** PASSED: "; 94 | consoleSetColor(CONSOLE_GRAY); 95 | cout << c->name() << endl; 96 | ++m_passedCount; 97 | } 98 | } 99 | try { 100 | (*m_teardown)(); 101 | } 102 | catch (const TestAssert& e) { 103 | consoleSetColor(CONSOLE_RED); 104 | cout << "** FAILED TEARDOWN"; 105 | consoleSetColor(CONSOLE_GRAY); 106 | cout << ":" << e.what() << endl; 107 | ++m_failedCount; 108 | return; 109 | } 110 | } 111 | 112 | TSetupFunc m_setup; 113 | TSetupFunc m_teardown; 114 | vector m_cases; 115 | int m_failedCount = 0, m_passedCount = 0; 116 | 117 | static void SetUp() {} 118 | static void TearDown() {} 119 | 120 | static vector s_allTests; 121 | }; 122 | 123 | 124 | 125 | template 126 | struct CaseReg { 127 | CaseReg(Case* cs) { 128 | static TestCls s_instance; 129 | static bool s_wasReg = false; 130 | if (!s_wasReg) { 131 | s_instance.regSelf(); 132 | s_wasReg = true; 133 | } 134 | s_instance.m_setup = &TestCls::SetUp; 135 | s_instance.m_teardown = &TestCls::TearDown; // happens again and again 136 | s_instance.m_cases.push_back(cs); 137 | } 138 | }; 139 | 140 | #define TEST_COMMON(testName, caseName, regTo) \ 141 | struct testName##__##caseName : public Case, public regTo { \ 142 | static CaseReg g_register; \ 143 | virtual void run(); \ 144 | virtual const char* name() const { return #testName "::" #caseName; } \ 145 | }; \ 146 | CaseReg testName##__##caseName::g_register(new testName##__##caseName); \ 147 | void testName##__##caseName::run() 148 | 149 | #define TEST_F(testName, caseName) TEST_COMMON(testName, caseName, testName) 150 | #define TEST(testName, caseName) TEST_COMMON(testName, caseName, Test) 151 | 152 | inline void runAllTests() 153 | { 154 | int failed = 0, passed = 0; 155 | for(auto* t: Test::s_allTests) { 156 | t->runAll(); 157 | failed += t->m_failedCount; 158 | passed += t->m_passedCount; 159 | } 160 | consoleSetColor((failed > 0) ? CONSOLE_RED : CONSOLE_GREEN); 161 | 162 | cout << "\n**** failed: " << failed << " passed: " << passed << endl; 163 | consoleSetColor(CONSOLE_GRAY); 164 | } 165 | 166 | #define FAIL() do { ostringstream ss; ss << __LINE__ << " Failed"; reportFailed(ss.str()); } while(false) 167 | 168 | #define TEST_DO_THROW throw TestAssert 169 | #define TEST_DO_REPORT reportFailed 170 | 171 | #define COMMON_EQ(a, b, action) if (!((a) == (b))) { ostringstream ss; ss << __LINE__ << ": " #a " != " #b " Where\n first=" << a << "\n second=" << b; action(ss.str()); } 172 | #define ASSERT_EQ(a, b) COMMON_EQ(a, b, TEST_DO_THROW) 173 | #define EXPECT_EQ(a, b) COMMON_EQ(a, b, TEST_DO_REPORT) 174 | 175 | #define COMMON_FALSE(a, action) if (a) { ostringstream ss; ss << __LINE__ << ": " #a " is true (need to be false) Where\n arg=" << a; action(ss.str()); } 176 | #define ASSERT_FALSE(a) COMMON_FALSE(a, TEST_DO_THROW) 177 | #define EXPECT_FALSE(a) COMMON_FALSE(a, TEST_DO_REPORT) 178 | 179 | #define COMMON_TRUE(a, action) if (!(a)) { ostringstream ss; ss << __LINE__ << ": " #a " is true (need to be false) Where\n arg=" << a; action(ss.str()); } 180 | #define ASSERT_TRUE(a) COMMON_TRUE(a, TEST_DO_THROW) 181 | #define EXPECT_TRUE(a) COMMON_TRUE(a, TEST_DO_REPORT) 182 | 183 | #define EXPECT_STREQ(a, b) EXPECT_EQ(string(a), string(b)) 184 | 185 | 186 | 187 | #define COMMON_THROW(cmd, excp, action) do { \ 188 | bool didThrow = false; \ 189 | ostringstream ss; \ 190 | try { cmd; } \ 191 | catch(const excp&) { didThrow = true; } \ 192 | catch(...) { ss << __LINE__ << ": " "Statement `" << #cmd "` Threw something other than " #excp; action(ss.str()); } \ 193 | if (!didThrow) { ss << __LINE__ << ": " "Statement `" << #cmd "` did not throw any exception"; action(ss.str()); } \ 194 | } while(false) 195 | #define ASSERT_THROW(cmd, excp) COMMON_THROW(cmd, excp, TEST_DO_THROW) 196 | #define EXPECT_THROW(cmd, excp) COMMON_THROW(cmd, excp, TEST_DO_REPORT) 197 | 198 | #define COMMON_NO_THROW(cmd, action) do { \ 199 | ostringstream ss; \ 200 | try { cmd; } \ 201 | catch(...) { ss << __LINE__ << ": " "Statement `" << #cmd "` Threw something "; action(ss.str()); } \ 202 | } while(false) 203 | #define ASSERT_NO_THROW(cmd) COMMON_NO_THROW(cmd, TEST_DO_THROW) 204 | #define EXPECT_NO_THROW(cmd) COMMON_NO_THROW(cmd, TEST_DO_REPORT) 205 | 206 | template 207 | struct CElementsAre { 208 | vector elems; 209 | template 210 | bool isTrue(const TCont& cont) { 211 | set checked; 212 | for(const auto& o: cont) { 213 | auto it = find(elems.begin(), elems.end(), o); 214 | if (it == elems.end()) 215 | return false; 216 | checked.insert(o); 217 | } 218 | for(const auto& e: elems) { 219 | if (checked.count(e) == 0) 220 | return false; 221 | } 222 | return true; 223 | }; 224 | template 225 | string disp(const TCont& cont) { 226 | ostringstream ss; 227 | for(const auto& o: cont) 228 | ss << o << ", "; 229 | return ss.str(); 230 | } 231 | }; 232 | 233 | template 234 | CElementsAre ElementsAre(const T& first, const Args&... args) { 235 | vector e = { first, args... }; 236 | return CElementsAre {e}; 237 | } 238 | // specialization for string literlas 239 | template 240 | CElementsAre ElementsAre(const char first[], const Args&... args) { 241 | vector e = { first, args... }; 242 | return CElementsAre {e}; 243 | } 244 | 245 | #define ASSERT_THAT(argument, conditionCls) do { \ 246 | auto co = conditionCls; \ 247 | if (!(co.isTrue(argument))) { \ 248 | ostringstream ss; ss << __LINE__ << ": " "Failed " #conditionCls " for " #argument "\n which is: " << co.disp(argument); throw TestAssert(ss.str()); } \ 249 | } while(false) 250 | -------------------------------------------------------------------------------- /test/tester.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | Document 24 | cd %(RootDir)%(Directory) 25 | python -c "import %(Filename)" 26 | cd %(RootDir)%(Directory) 27 | python -c "import %(Filename)" 28 | cd %(RootDir)%(Directory) 29 | python -c "import %(Filename)" 30 | cd %(RootDir)%(Directory) 31 | python -c "import %(Filename)" 32 | making pyc for %(Filename) 33 | making pyc for %(Filename) 34 | making pyc for %(Filename) 35 | making pyc for %(Filename) 36 | %(RootDir)%(Directory)%(Filename).pyc 37 | %(RootDir)%(Directory)%(Filename).pyc 38 | %(RootDir)%(Directory)%(Filename).pyc 39 | %(RootDir)%(Directory)%(Filename).pyc 40 | 41 | 42 | Document 43 | cd %(RootDir)%(Directory) 44 | python -c "import %(Filename)" 45 | cd %(RootDir)%(Directory) 46 | python -c "import %(Filename)" 47 | cd %(RootDir)%(Directory) 48 | python -c "import %(Filename)" 49 | cd %(RootDir)%(Directory) 50 | python -c "import %(Filename)" 51 | making pyc for %(Filename) 52 | making pyc for %(Filename) 53 | making pyc for %(Filename) 54 | making pyc for %(Filename) 55 | %(RootDir)%(Directory)%(Filename).pyc 56 | %(RootDir)%(Directory)%(Filename).pyc 57 | %(RootDir)%(Directory)%(Filename).pyc 58 | %(RootDir)%(Directory)%(Filename).pyc 59 | 60 | 61 | Document 62 | cd %(RootDir)%(Directory) 63 | python -c "import %(Filename)" 64 | cd %(RootDir)%(Directory) 65 | python -c "import %(Filename)" 66 | cd %(RootDir)%(Directory) 67 | python -c "import %(Filename)" 68 | cd %(RootDir)%(Directory) 69 | python -c "import %(Filename)" 70 | making pyc for %(Filename) 71 | making pyc for %(Filename) 72 | making pyc for %(Filename) 73 | making pyc for %(Filename) 74 | %(RootDir)%(Directory)%(Filename).pyc 75 | %(RootDir)%(Directory)%(Filename).pyc 76 | %(RootDir)%(Directory)%(Filename).pyc 77 | %(RootDir)%(Directory)%(Filename).pyc 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | {36027395-a85c-4dce-a859-c23d1e3b9f87} 91 | 92 | 93 | 94 | {F894A474-8167-46FF-86A3-ED249C8CAE82} 95 | Win32Proj 96 | tester 97 | 8.1 98 | 99 | 100 | 101 | Application 102 | true 103 | v140 104 | Unicode 105 | 106 | 107 | Application 108 | false 109 | v140 110 | true 111 | Unicode 112 | 113 | 114 | Application 115 | true 116 | v140 117 | Unicode 118 | 119 | 120 | Application 121 | false 122 | v140 123 | true 124 | Unicode 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | true 150 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 151 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 152 | 153 | 154 | true 155 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 156 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 157 | 158 | 159 | false 160 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 161 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 162 | 163 | 164 | false 165 | $(SolutionDir)bin\$(Configuration)_$(Platform)\$(ProjectName)\ 166 | $(SolutionDir)obj\$(Configuration)_$(Platform)\$(ProjectName)\ 167 | 168 | 169 | 170 | Level3 171 | Disabled 172 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 173 | 174 | 175 | Console 176 | true 177 | $(PYTHONROOT)\libs 178 | 179 | 180 | 181 | 182 | Level3 183 | Disabled 184 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 185 | 186 | 187 | Console 188 | true 189 | $(PYTHONROOT)\libs 190 | 191 | 192 | 193 | 194 | Level3 195 | MaxSpeed 196 | true 197 | true 198 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 199 | 200 | 201 | Console 202 | true 203 | true 204 | true 205 | $(PYTHONROOT)\libs 206 | 207 | 208 | 209 | 210 | Level3 211 | MaxSpeed 212 | true 213 | true 214 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 215 | 216 | 217 | Console 218 | true 219 | true 220 | true 221 | $(PYTHONROOT)\libs 222 | 223 | 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /test/tester.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | test 6 | 7 | 8 | test 9 | 10 | 11 | 12 | 13 | 14 | test 15 | 16 | 17 | test 18 | 19 | 20 | 21 | 22 | {a38a32bb-0580-4ab0-a407-21df9116609b} 23 | 24 | 25 | 26 | 27 | test 28 | 29 | 30 | 31 | 32 | test 33 | 34 | 35 | --------------------------------------------------------------------------------